<- 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 ->