UMBC | CMSC 313 -- Subprograms and the Stack | Previous | Next |
The ebp (base pointer register points to a fixed location in the middle of the stack from of the currently executing procedure and the stack pointer esp points to the end of the procedure's temporary storage.
Because ebp is an index register, the parts of the stack frame can be referenced relative to its contents by adding or subtracting a displacement. (Displacements are always referencing words or double words, but the value is measured in bytes!) These displacements are going to be the same for each and every use of a particular procedure. That way we we are use age as tht way we we are use age as the third dword parameter, it will always be [bp + 12]. Since parameters are can be both words and dwords, you must keep track of the true displacement. Additional, the esi and edi registers can be used.
The calling procedure must push the parameters on the stack before making the procedure call. Then the called procedure must save and reset the ebp, reserve space for the local variables, save the appropriate registers, and do its work. Then it must restore things in reverse order:
mySub: ; Start of procedure push ebp mov ebp, esp sub esp, n ; reserve n bytes of local storage push dword [reg1] ; save registers push dword reg2 ; do some processing pop dword reg2 pop dword reg1 add esp, n ; just the opposite mov esp, ebp pop ebp ret ; we are done.
This results in memory that looks like this:
When working with C functions, some of them have a variable number
of parameters, where each time the function can have a different
(variable) number of arguments. Printf is an example of this.
It looks like:
add esp, n ; where n is two times the number of 2-byte parameters, ; plus four times the number of 4-byte parameters.
There are two kinds of parameters, call by value and call by reference. We have been using call by value when we do the push. Since the values are not retained, whatever changes that are made to them are lost. Notice that this is the default for C for everything except for arrays. Since the size of anything being pushed is in units of 16 bits or 32 bits, when the parameter is a character, it is promoted to 16-bits.
Both recusion and reentrancy are also solved with this method, because every call means that a separate frame are being manipulated. Each call becomes independent!
Procedures should be small, simple, and only do one thing. When possible, subprograms should not do any I/O. Then libraries can be built up and only the current outer driver has to worry about whether to use the keyboard, CRT, modem, disk file, etc. The inner procedures are independent of the I/O constraints that change with most programs.