Each time you call a procedure, you may want it to operate on different data. This data, called “arguments,” can be passed in various ways. For example, arguments can be passed to a procedure in registers or in variables. However, the most common method of passing arguments is to use the stack. Microsoft languages have specific conventions for passing arguments. Chapter 20, “Mixed-Language Programming,” explains these conventions for assembly-language modules shared with modules from high-level languages.
This section describes how a procedure accesses the arguments passed to it on the stack. Each argument is accessed as an offset from BP. However, if you use the PROC directive to declare parameters, the assembler calculates these offsets for you and lets you refer to parameters by name. The next section, “Declaring Parameters with the PROC Directive,” explains how to use PROC this way.
This example shows how to pass arguments to a procedure. The procedure expects to find those arguments on the stack. As this example shows, arguments must be accessed as offsets of BP.
; C-style procedure call and definition
mov ax, 10 ; Load and
push ax ; push constant as third argument
push arg2 ; Push memory as second argument
push cx ; Push register as first argument
call addup ; Call the procedure
add sp, 6 ; Destroy the pushed arguments
. ; (equivalent to three pops)
.
.
addup PROC NEAR ; Return address for near call
; takes two bytes
push bp ; Save base pointer - takes two bytes
; so arguments start at fourth byte
mov bp, sp ; Load stack into base pointer
mov ax, [bp+4] ; Get first argument from
; fourth byte above pointer
add ax, [bp+6] ; Add second argument from
; sixth byte above pointer
add ax, [bp+8] ; Add third argument from
; eighth byte above pointer
mov sp, bp
pop bp ; Restore BP
ret ; Return result in AX
addup ENDP
Figure 7.1 shows the stack condition at key points in the process.
Starting with the 80186 processor, the ENTER and LEAVE instructions simplify the stack setup and restore instructions at the beginning and end of procedures.
However, ENTER uses a lot of time. It is necessary only with nested, statically scoped procedures. Thus, a Pascal compiler may sometimes generate ENTER. The LEAVE instruction, on the other hand, is an efficient way to do the stack cleanup. LEAVE reverses the effect of the last ENTER instruction by restoring BP and SP to their values before the procedure call.