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!
