The following example is included only for purposes of illustration and clarity and has not been optimized. The example assumes that you are familiar with programming in assembler. In the example, the stack grows toward the bottom of the page, and ESP always points to the top of the stack.
The following example shows the code sequences and a picture of the stack for a call to the function FUNC3 which has two local variables, x and y (both fixed bin(31)). For the call
dcl func3 entry( fixed bin(31),
fixed bin(31),
fixed bin(31) )
returns( fixed bin(31) )
options( byvalue nodescriptor linkage(system) );
m = func3(a,b,c);
the stack for the call to FUNC3 would look like this:
Stack
.------------------------. Higher Memory
| c |
'------------------------'
| b |
'------------------------'
| a |
'------------------------'
| caller's EIP |
'------------------------'
| caller's EBP |
EBP ------> '------------------------'
| x |
'------------------------'
| y |
'------------------------' <-.
| Saved EDI | |
'------------------------' | These would only be
| Saved ESI | | be pushed if they
'------------------------' | were used in this
| Saved EBX | | function.
ESP ------> '------------------------' <-'
Lower Memory
The instructions used to build this activation record on the stack look like this on the calling side:
PUSH c
PUSH b
PUSH a
MOV AL, 3H
CALL func3
.
.
ADD ESP, 12 ; Cleaning up the parameters
.
.
MOV m, EAX
.
.
For the callee, the code looks like this:
func3 PROC
PUSH EBP
MOV EBP, ESP ; Allocating 8 bytes of storage
SUB ESP, 8 ; for two local variables.
PUSH EDI ; These would only be
PUSH ESI ; pushed if they were used
PUSH EBX ; in this function.
.
.
MOV EAX, [EBP - 8] ; Load y into EAX
MOV EBX, [EBP + 12] ; Load b into EBX
.
.
XOR EAX, EAX ; Zero the return value
POP EBX ; Restore the saved registers
POP ESI
POP EDI
LEAVE ; Equivalent to MOV ESP, EBP
; POP EBP
RET
func3 ENDP
The saved register set is EBX, ESI, and EDI. The other registers (EAX, ECX, and EDX) can have their contents changed by a called routine.
Under some circumstances, the compiler does not use EBP to access automatic and parameter values, thus increasing the application's efficiency. Whether it is used or not, EBP does not change across the call.
When passing aggregates by value, the compiler generates code to copy the aggregate on to the 80386 stack. If the size of the aggregate is larger than an 80386 page size (4K), the compiler generates code to copy the aggregate backward (that is, the last byte in the aggregate is the first to be copied).
Aggregates are not returned on the stack. The caller pushes the address where the returned aggregate is to be placed as a lexically first hidden parameter. A function that returns an aggregate must be aware that all parameters are 4 bytes farther away from EBP than they would be if no aggregate return were involved. The address of the returned aggregate is returned in EAX.