BTW... I strongly recommend, if you are dealing with real or protected mode, that you create a structure mirroring the state of the stack for your functions instead of calculating, by head, the corresponding offsets. For example:
; int f( int a, int b ) { return a + b; }
_f:
mov eax,[esp+4]
add eax,[esp+8]
ret
Here I get the a and b arguments from the stack using the offsets 4 and 8, but it is easier to use structures to let the compiler calculate the offsets:
struc fstk
resd 1 ; return address put here by call _f.
.a: resd 1
.b: resd 1
endstruc
_f:
mov eax,[esp + fstk.a] ; let NASM calculate the offset!
add eax,[esp + fstk.b]
ret
In cases where you need to use local vars, you can do:
struc fstk
.local_t: resd 1
.local_size:
resd 1 ; return address put here by call _f.
.a: resd 1
.b: resd 1
endstruc
_f:
sub esp,fstk.local_size ; let NASM calculate the local vars reservation size.
mov eax,[esp + fstk.a] ; let NASM calculate the offset!
add eax,[esp + fstk.b]
...
add esp,fstk.local_size
ret
Notice: No prologue/epilogue needed! But for 8086 we need to use `bp` to access the stack... in real mode for 8086/80286 processors (taking int as word) we could do:
struc fstk
resw 1 ; old_bp.
resw 1 ; return address put here by call _f.
.a: resw 1
.b: resw 1
endstruc
_f:
push bp
mov bp,sp ; prologue needed!
mov ax,[bp + fstk.a] ; let NASM calculate the offset!
add ax,[bp + fstk.b]
pop bp ; epilogue needed!
ret
And the same trick could be used for local vars.
struc fstk
.local_t: resw 1
.local_size:
resw 1 ; old bp.
resw 1 ; return address put here by call _f.
.a: resw 1
.b: resw 1
endstruc
_f:
push bp
sub sp,stk.local_size
mov bp,sp ; BP needed to access the stack.
mov ax,[bp + fstk.a] ; let NASM calculate the offset!
add ax,[bp + fstk.b]
...
add sp,stk.local_size
pop bp ; epilogue needed!
ret