Latest Articles Latest Articles

Apr 21, 2008 Powered By SQLite
Apr 20, 2008 Double-Time Blogs Are Back!
Sep 05, 2006 Finding Prime Numbers Using C++
Jul 01, 2006 Finding Prime Numbers - A Simple Assembly Language Example Using NASM

Finding Prime Numbers - A Simple Assembly Language Example Using NASM

Lately I've been really getting into low level programming. As much as I love .NET, coding no longer feels like man vs machine; it's now more like man vs framework. Now don't get me wrong, I'm haven't gone off the deep end yet, I am in no way considering (or advocating) leaving our modern programming comforts to go back to the ways of old. I do, however, believe that any good software engineer should know what's going on under the hood, and there really is no other way of doing that other than coding in assembly (well, I guess you could code all the instructions in binary...).

For all intents and purposes, this is my first assembly program. Yes, like most other Computer Science students, I took an assembly language course in college. Unfortunately, our professor didn't get us much further than the "Hello World" program, which hardly qualifies me as being experienced with assembly. Please keep in mind that this is my first stab at it, and if you see stuff that doesn't look quite right, then it very well may be my inexperience coming to light.

I used NASM for this example. I feel that NASM is a bit cleaner and easier to understand than MASM. I really wish that there would be more NASM documentation, however. Most books and tutorials are written using MASM. Thankfully the two aren't too different. You can read the MASM stuff, do a few NASM tutorials to get a feel for the difference, and you'll be pretty much in sync. I also use a C skeleton program to call the assembly procedure. Yes, its "cheating" a bit, since I'm using C to get me into protected mode and to set up the segments and segment registers. Quite honestly though, that isn't the stuff I'm interested in, or at least not now. I use the DJGPP environment to use gcc as my c compiler.

Note (4/20/2008): There's PLENTY of room for optimization here - this is a very slow algorithm. Still, I think it serves as a good starting point for assembly language as solving primes is an easy, but not trivial problem.

Note 2: I had to move the code comments around because the way the code was laid out before was breaking IE (big surprise). If something looks out of place please let me know.

Assembly Code
    
;Amount of double words (32 bits each) that we will reserve
%define RESSIZE 10000000          

;Uninitialized data goes in bss segment   
segment .bss      
    
    ;Reserve plenty of space for primes (38MB)                
    Primes resd RESSIZE    
    
    ;Reserve space for answer
	RetVal resd 1                 

;Code goes in the text section
segment .text
     ;global <procedure name>                      
     global _asm_main lets C program "see" the procedure            
                                  
     ;procedure start
     _asm_main:     
        ;setup routine             
        enter 0,0    
        
         ;save register states for when we return to C               
        pusha                    
                                  
    ;indexer for prime number array            
	mov esi,0   
	
	;ebp-1 points to last found prime                  
	mov ebp,0                     
		
	;first prime given free		
	mov [Primes], DWORD 3
	
	;set up to hold next prime        
	inc ebp	                      
	
	;find primes from 3 to X		
	mov ecx, DWORD 3	         
	
	;outer loop
	i:	
	    ;check that we haven't gone past max		      
	    cmp ecx,10000000          
            ja  end
		
		 ;clear "j" (inner loop)		
	    mov esi,0	             
        
        j:
            ;if we've gone past our current list of primes, we're prime
		    cmp esi,ebp               
                                  
		    ja prime              
				
				
			;divide by previous prime
		    mov edx, 0                
		    mov eax, ecx
		    div DWORD [Primes + (esi-1)*4]
		    
		    ;if we have no remainder, skip
		    cmp edx, 0
		    je  next                  
				
			 ;go to next prime
		    inc esi	                 
		jmp j
				
	    ;Add new prime to list
	    prime:		              

	        mov [Primes + ebp * 4], ecx
	        inc ebp
		
            next:	
	        add ecx,2
	        jmp i

	end:
				
	mov eax,[Primes + (ebp-1)*4]
        mov [RetVal],eax
               
         ;Get register states from before we ran procedure         
        popa                   
                                
        ;return back to C
        mov eax, [RetVal]       
        leave
        ret 




   
C code
#include<stdio.h>
int main()
{
        int ret_status;
        
        ret_status = asm_main();
        printf("%d",ret_status);
        return 0;

}
    
How it works

In case you don't remember, a prime number is a number that is divisible only by two numbers. Lots of people say that a prime number is any number divisible by 1 and itself. This is incorrect, since 1 is NOT a prime number (it is only divisible by 1 number, itself).

The simplest prime number program you can write is to have two loops: the outer loop (i) controls the prime number candidate, and the second loop (j) runs from 2 to i-1. Within the middle loop, we divide i by j; if we have no remainder, we know its not a prime, and break from the second loop. While simple, this algorithm is O(n²). Sure, we can double efficiency by eliminating even numbers (which, other then 2, will never be prime), but n²/2 is still O(n²).

Our technique builds upon this little fact about composite (non prime) numbers:
composite numbers are multiples of prime numbers. We can use this to our advantage because now we can check our prime number candidate against previously found prime numbers instead of ALL of the numbers from 1 to i. This puts us at O(nlogn), which is definately better than where we were before. I don't claim that my algorithm is the best, if you search the web you'll probably find more clever algorithms. If you decide to implement it in assembly, please send me a copy!