I'm not going to give you a complete answer since this appears to be homework. I'll provide enough to get you started. The following code will simply count from 1 to 100:
extern printf
section .data
fmt: db "number = %d", 10, 0 ; printf format string
section .text
global main
main:
push ebx ; EBX is callee saved so we need to save it so that it
; can be restored when we RETurn from main
mov ebx, 1 ; ebx = 1 (counter)
L1:
push ebx ; 2nd parameter is our number(counter) to print
push fmt ; 1st parameter is the address of the format string
call printf
add sp, 8 ; We pushed 8 bytes prior to printf call, we must adjust the stack
; by effectively removing those bytes.
inc ebx ; counter += 1
cmp ebx,100
jle L1 ; If counter is <= 100 go back and print again
end:
xor eax,eax ; Return 0 when our program exits
pop ebx ; Restore EBX before exiting main per calling convention
ret ; RETurn from main will cause program to gracefully exit
; because we are linked to the C runtime code and main was
; called by that C runtime code when our program started.
Primary changes from your code. We display the number using printf with a proper format string. I put the counter in EBX because you must be aware that EAX, ECX, and EDX are not preserved across a function call using CDECL calling convention:
In cdecl, subroutine arguments are passed on the stack. Integer values and memory addresses are returned in the EAX register, floating point values in the ST0 x87 register. Registers EAX, ECX, and EDX are caller-saved, and the rest are callee-saved.
[snip]
The caller cleans the stack after the function call returns.
After making CDECL function calls, we must restore the stack pointer. We push 2 DWORDs (a total of 8 bytes) as parameters to printf
therefore we must add 8 to the stack pointer to effectively remove them when the function returns.
The highlighted sentence is important. To simplify things I use EBX because its value is preserved across function calls and we don't have to save and restore it around the function calls we make (like printf
).
Since main
is being called as a C function using CDECL calling convention, we must preserve any callee registers (registers our function must preserve) so that we don't cause undefined behavior in the C library code that calls main
. EBX is a callee saved register we did modify, so we use PUSH/POP around our function so that EBX is preserved when our main
function returns.
The program has to be linked to a C library. The easiest way to do this is to link with GCC. The compile and link stage could look like:
nasm -f elf32 count100.asm -o count100.o
gcc -m32 count100.o -o count100
This should assemble and link your code to a 32-bit program called count100
that can be executed with this command:
./count100
I leave the rest of the assignment up to you.
The assembly code in the sample would be equivalent to this code in C:
#include <stdio.h>
int main()
{
int counter = 1;
do {
printf ("number = %d
", counter);
} while (++counter <= 100);
return 0;
}