A portion of a computer
set up to handle a function call
. This is where the variables
the function uses to operate, the saved registers
a function uses, the return
address of the calling function, and the base pointer
are located. It's an essential part of learning how to program functions in assembly language
A typical stack frame
The easiest way to understand this is to show a typical stack frame
| Top of Stack |
| Calling |
| Parameters |
| Return Address |
| Saved Base Ptr.|
------------------ <--- Base Pointer points to
| | this memory location
| Local |
| Variables |
| Saved |
| Registers |
| Rest of Stack |
An Explanation of these terms:
- Calling Parameters - These are the variables you're passing to the program to work on. In languages such as C, and Java, these are the portions inside the parentheses in your function call.
- Return Address - The location in the program you come back to after your function completes. If this gets screwed up, Bad Things can happen
- Saved Base Pointer - The old value of the base pointer. Important because your function could be called by other functions, and you want to make sure their stack frame is correct
- Base Pointer - A register set aside primarily for dealing with functions and stack frames. The Base Pointer provides a fixed reference for dealing with items located on the stack. This provides a baseline to refer to variables given to the function by the calling routine, plus variables local to the function itself.
- Local Variables - Unlike variables used in the main program, which are allocated at compile-time, variables used in local functions are allocated at execution time by taking space off the stack.
- Saved Registers - It's very important that one saves the registers they use in the function. Your function will be called by other programs which may need the content in those registers later on. If destroying a register cannot be avoided, notate it in your function's documentation so that people using the function can save the register for later use. Good programming practice, however, maintains that the only good reason for changing register values is to return values to the calling function.
A stack frame is set up by first pushing the values the function needs onto the stack. The function call instruction is then run, which pushes the memory address to return to onto the stack, and passes control to the called function. At this point, what occurs is up to the called function. If the function is well behaved, and done in accordance with any sort of programming styles, the base pointer is saved to the stack, the stack pointer is decremented by the size of all temporary variables used by the function, and all the registers used by the function are pushed onto the stack. At this point, the function is ready to be run.
Cleaning up the stack frame is the opposite of setting it up. The saved registers are popped off the stack, and the stack pointer is set to the value of the base pointer. The original value of the base pointer is then restored by popping it off the stack, and the return function call is then executed.
Depending on the program style, the way the return function call operates differs. If the program is done with pascal style functions, the function itself completely cleans the stack by removing the parameters of the function, usually by calling the return operation with the size of the parameters. If the program is written with C style functions, the calling function is placed in charge of cleaning up the parameters used in the function call. At this point, program control returns to the calling function.
The following example code is written using Intel-style assembly notation, which is as follows: operation operand, operator. Comments are prefixed with a semicolon, and are affected until the next carriage return.
; example calling function
... ;For the sake of this w/u,
;we'll assume you know how to set up a
push yadda ;Put the variable yadda onto the stack.
;Yadda is the size of one word
push emback ;Put the variable emback onto the stack.
;Emback is also the size of one word
call example ;Call our example function
... ;Run the rest of the program
example PROC ;This section is the example function
push bp ;Save the old Base Pointer
mov bp,sp ;Move the value of the stack into the
;base pointer register
sub sp, n ;Subtract n bytes from the stack
;pointer, for use by the function for
;n must be a multiple of the word
;size on your system
push ax ;Save our registers
... ;Do whatever the program's gotta do
pop cx ;Restore our registers in the reverse
pop bx ;order we saved them
mov sp, bp ;Free up the stack space used by the
pop bp ;Restore the base pointer
return 4 ;Return to the calling program, pascal
;style. Both variables pushed onto the
;stack were words, which take up 2
;bytes each. We're telling the return
;operation to clear the four bytes of
;the stack above the address of the
;function call. If there were 3 items
;pushed onto the stack, the number would
;be 6, if there were "n" items, the
;number would be 2n. If this were a C
;style function, there would be no
example ENDP ;That's all for this function
Stack frames are an essential part of assembly language programming, and essentially, all programming. Every language, be it C, Pascal, Fortran, Ada, all use stack frames internally in order to set up their functions. It provides an easy and safe way to transfer flow to another routine in order to make programming easier. Stack frames are troublesome at first, but they truly aren't as bad as some of the other areas of assembly programming.