Consult the Friendly Manual:
http://www.nasm.us/xdoc/2.09.07/html/nasmdoc9.html#section-9.1It isn't the greatest explanation, but it may help. We had a thread recently, demanding to know where the "C calling convention" was officially written, "cast in stone". I don't know - ask the C guys - but I know you've gotta do it!
First, before we get to C... it is not immediately obvious that the "call" instruction stores its return address on the stack, and the "ret" instruction gets the address to return to off the stack - so when you get to "ret", esp had better be pointing to the return address! You don't get this right.
Then, if you're going to play nicely with C, there are further rules. Besides the stack being right, ebp must be preserved (the caller is presumably using ebp for the same purpose you are). Further, ebx, esi and edi must be preserved - if you don't alter them, you don't have to do anything, but if you do, they must be returned to their original values before "ret". push/pop are usually used, though you could do it in other ways. The return value is in eax (edx:eax for 64-bit values, but this seems rare), ecx and edx are presumed "trashed". (this is a PITA if you're used to using ecx as a "counter"!) You can expect C to follow these rules when you call a C function, and you must follow 'em when C is calling you. "main" is called! If you "exit" by other means, you can "cheat" on these rules, but if you "ret" from "main", you should "play by the rules". There's no guarantee that ecx will be trashed, or that altering ebx will cause a crash - depends on C version - but if you expect it to "always" work - do it.
global main
extern printf
SECTION .data
array dd 0,1,2,3,4,5
fmt db "%i",10,0
SECTION .text
main:
push ebp
mov ebp, esp
sub esp, 0x40
mov ebx, [esp+8]
You've moved an uninitialized value into ebx. You probably intended "[ebp + 8]"(?). This would put the first parameter into ebx. The first parameter to "main" would be "argc". Why? Since you've altered ebx, you should save and restore its original value.
mov ebp, 4
mov eax, [array+ebp*4]
You probably don't want to alter ebp within your function. You can do it, but we "usually" use ebp to return esp to a known value, as well as using it to access parameters and local variables. Better to use some other register. Since "printf" will trash ecx and edx, those aren't a great choice (although you could save/restore 'em around the call to "printf"). ebx, esi, and edi must be "preserved" - if we do that at the start and end of "main", we can use 'em freely, and C won't mess with them.
push eax
push dword fmt
call printf
It is the caller's responsibility to "clean up the stack" ("remove" the pushed parameters). "add esp, 8"... or I like to write it as "add esp, 4 * 2" so I can alter it easily if I change the number of parameters...
mov eax, 0
ret
Well... you need to restore esp to its original state before the "ret". pop off any of the "non-volatile" registers (ebx, esi, edi) you've pushed, add that 0x40 back on, and pop caller's ebp. Then you're ready to "ret".
Here's a tested version that "seems to work".
; nasm -f elf32 myfile.asm
; gcc -o myfile myfile.o
;
; for Windows (untested)
; put underscores on global and extern variables
; nasm -f win32 --prefix _ myfile.asm
global main
extern printf
SECTION .data
array dd 0,1,2,3,4,5
array_size equ ($ - array) / 4 ; number of items, not bytes!
fmt db "%i",10,0
SECTION .text
main:
push ebp
mov ebp, esp
; make room for local variables
; you don't use any, but it won't hurt
sub esp, 0x40
; save caller's regs
push ebx
push esi
push edi
; what parameter is this intended to get???
mov ebx, [ebp+8]
mov esi, 0 ; starting index
mov edi, array_size ; number of items to print
top:
mov eax, [array+esi*4]
push eax
push dword fmt
call printf
; C calling convention - caller cleans up stack
add esp, 4 * 2 ; two parameters at 4 bytes each
add esi, 1 ; next index
sub edi, 1 ; until done
jnz top
mov eax, 0 ; claim "no error"
; restore caller's regs
pop edi
pop esi
pop ebx
; exit function
leave
; equivalent to:
; mov esp, ebp ; this "frees" your local variables
; pop ebp ; restore caller's ebp
ret
;------------------
This isn't too great. Too much stuff "hard-coded" into our routine. We probably should do something like....
...
push array_size
push array
call print_dword_array
...
I'll leave that as an "exercize for the reader" for now...
Best,
Frank