<- previous index next ->
Here is a basic subroutine (function, procedure, etc) Note the use of the stack pointer for passing parameters. Note saving and restoring the callers registers. (Yes, this is needed for CMSC 313 project 2) "call1" below, is called by the "C" program test_call1.c /* test_call1.c test call1.asm */ #include <stdio.h> int main() { int L[2]; L[0]=1; L[1]=2; call1(L); printf("L[0]=%d, L[1]=%d \n", L[0], L[1]); return 0; } The result is L{0]=0, L[1]=0, from the following: ; call1.asm a basic structure for a subroutine to be called from "C" ; ; This saves more registers than used here ; Parameters: int L[] or int *L ; Result: L[0]=0 L[1]=0 global call1 ; linker must know name of subroutine call1: ; name must appear as a nasm label push ebp ; save ebp mov ebp, esp ; ebp is callers stack push ebx ; save registers push edi push esi mov edi,[ebp+8] ; get address of L into edi mov eax,0 ; get a 32-bit zero mov [edi],eax ; L[0]=0 add edi,4 ; add one dword=32-bit int mov [edi],eax ; L[1]=0 pop esi ; restore registers pop edi ; in reverse order pop ebx mov esp,ebp ; restore callers stack frame pop ebp ret ; return ; ; Notes about the callers stack, ebp in our code: ; ebp+8 is the last argument passed to us by the caller, ; this is our first argument ; ebp+12 would be our second argument, etc. +4 each ; the arguments can be values or addresses, ; as defined by the "C" function prototypes ; ebp+4 is the return address in the caller, used by 'ret' ; ebp which is our starting esp, is the next available stack space Study call1.asm Now, to pass more arguments, call2.c can be implemented as call2.asm Note passing arrays including strings is via address, passing scalar values is via passing values. ; call2.asm code loopint.c as subroutine (void function) ; /* call2.c a very simple loop that will be coded for nasm */ ; #include <stdio.h> ; void call2(int *A, int start, int end, int value); ; int main() ; { ; int dd1[100]; ; int i; ; dd1[0]=5; /* be sure loop stays 1..98 */ ; dd1[1]=6; ; dd1[98]=8; ; dd1[99]=9; ; call2(dd1,1,98,7); /* fill dd1[1] thru dd1[98] with 7 */ ; printf("dd1[0]=%d, dd1[1]=%d, dd1[98]=%d, dd1[99]=%d\n", ; dd1[0], dd1[1], dd1[98],dd1[99]); ; return 0; ; } ; void call2(int *A, int start, int end, int value) ; { ; int i; ; ; for(i=start; i<=end; i++) A[i]=value; ; } ; execution output is dd1[0]=5, dd1[1]=7, dd1[98]=7, dd1[99]=9 section .bss i: resd 1 ; actually unused, kept in register section .text global call2 ; linker must know name of subroutine call2: ; name must appear as a nasm label push ebp ; save ebp mov ebp, esp ; ebp is callers stack push ebx ; save registers push edi mov edi,[ebp+8] ; get address of A into edi mov eax,[ebp+12] ; get value of start mov ebx,[ebp+16] ; get value of end mov edx,[ebp+20] ; get value of value loop1: mov [4*eax+edi],edx ; A[i]=value; add eax,1 ; i++; cmp eax,ebx ; i<=end jle loop1 ; loop i<=end is false pop edi ; in reverse order pop ebx mov esp,ebp ; restore callers stack frame pop ebp ret ; return ; ; Notes about the callers stack, ebp in our code: ; ebp+8 is the last argument passed to us by the caller, ; this is our first argument, the address of A. ; ebp+12 is our second argument, 'start' a value. A simple function, called and written in the same .asm file intfunc.asm ; intfunc.asm call integer function int sum(int x, int y) ; ; compile: nasm -f elf intfunc.asm ; link: gcc -o intfunc.o ; run: intfunc ; result: 5 = sum(2,3) extern printf section .data x: dd 2 y: dd 3 z: dd 1 fmt: db "%d = sum(%d,%d)",10,0 global main main: push ebp mov ebp,esp push ebx push dword [y] ; push arguments for sum push dword [x] call sum ; coded below add esp,8 mov [z],eax ; save result from sum push dword [y] ; print push dword [x] push dword [z] push dword fmt call printf add esp,16 pop ebx mov esp,ebp pop ebp mov eax,0 ret ; end main sum: push ebp ; function int sum(int x, int y) mov ebp,esp push ebx mov eax,[ebp+8] ; get argument x mov ebx,[ebp+12] ; get argument y add eax,ebx ; x+y with result in eax pop ebx mov esp,ebp pop ebp ret ; return value in eax ; end of function int sum(int x, int y) A simple demonstration of using a double sin(double x) function from the "C" math.h fltfunc.asm ; fltfunc.asm call math routine double sin(double x) ; ; compile: nasm -f elf fltfunc.asm ; link: gcc -o fltfunc.o -lm # needs math library ; run: fltfunc ; extern sin ; be sure to 'extern' functions extern printf section .data x: dq 0.7853975 ; Pi/4 = 45 degrees y: dq 1.0 ; should be about 7.07E-1 fmt: db "%e = sin(%e)",10,0 global main main: push ebp mov ebp,esp push ebx push dword [x+4] ; push quad word (double) for sin push dword [x] call sin ; call the library sin function add esp,8 fstp qword [y] ; save the return value push dword [x+4] ; print push dword [x] push dword [y+4] push dword [y] push dword fmt call printf add esp,20 pop ebx mov esp,ebp pop ebp mov eax,0 ret ; result: 7.071063e-01 = sin(7.853975e-01) ; end fltfunc.asm And a final example of a simple recursive function, factorial, written in unoptimized assembly language following the "C" code. test_factorial.asm test_factorial.c ; test_factorial.asm based on test_factorial.c ; /* test_factorial.c the simplest example of a recursive function */ ; /* a recursive function is a function that calls itself */ ; static int factorial(int n) /* n! is n factorial = 1*2*3*...*(n-1)*n */ ; { ; if( n <= 1 ) return 1; /* must have a way to stop recursion */ ; return n * factorial(n-1); /* factorial calls factorial with n-1 */ ; } /* n * (n-1) * (n-2) * ... * (1) */ ; #include <stdio.h> ; int main() ; { ; printf(" 0!=%d \n", factorial(0)); /* Yes, 0! is one */ ; printf(" 1!=%d \n", factorial(1)); ; ... ; printf("18!=%d \n", factorial(18)); /* wrong, uncaught in C */ ; return 0; ; } ; /* output of execution is: ; 0!=1 ; 1!=1 ; ... ; 12!=479001600 ; 13!=1932053504 wrong! 13! = 12! * 13, must end in two zeros ; 14!=1278945280 wrong! and no indication! ; 15!=2004310016 wrong! ; 16!=2004189184 wrong! ; 17!=-288522240 wrong and obvious if you check your results ; 18!=-898433024 Only sometimes does integer overflow go negative ; */ ; ; compile: nasm -f elf test_factorial.asm ; link: gcc -o test_factorial.o ; run: test_factorial section .bss tmp: resd 1 ; over written each call section .text factorial: ; not global is 'static' in "C" push ebp ; function int factorial(int n) mov ebp,esp push ebx mov eax,[ebp+8] ; get argument n cmp eax,1 ; compare for exit jle exitf ; go return a 1 sub eax,1 ; n-1 push dword eax ; compute factorial(n-1) call factorial pop edx ; get back our "n-1" add edx,1 ; have our "n" mov [tmp],edx imul eax,[tmp] ; n * factorial(n-1) in eax jmp returnf exitf: mov eax,1 returnf: pop ebx mov esp,ebp pop ebp ret ; return value in eax ; end of function static int factorial(int n) extern printf section .data n: dd 0 ; initial, will count to 18 fmt: db "%d! = %d",10,0 ; simple format section .bss nfact: resd 1 ; just a place to hold result section .text global main main: push ebp mov ebp,esp push ebx loop1: push dword [n] ; push arguments for factorial call factorial ; coded above add esp,4 mov [nfact],eax ; save result from factorial push dword [nfact] ; print push dword [n] push dword fmt call printf add esp,12 mov eax,[n] inc eax mov [n],eax cmp eax,18 jle loop1 ; print factorial 0..18 pop ebx mov esp,ebp pop ebp mov eax,0 ret ; end main
<- previous index next ->