When you use the PROC directive with its extended syntax and argument list, the assembler automatically generates the prologue and epilogue code in your procedure. “Prologue code” is generated at the start of the procedure; it sets up a stack pointer so you can access parameters from within the procedure. It also saves space on the stack for local variables, initializes registers such as DS, and pushes registers that the procedure uses. Similarly, “epilogue code” is the code at the end of the procedure that pops registers and returns from the procedure.
The assembler automatically generates the prologue code when it encounters the first instruction after the PROC directive. It generates the epilogue code when it encounters a RET or IRET instruction. Using the assembler-generated prologue and epilogue code saves you time and decreases the number of repetitive lines of code in your procedures.
The generated prologue or epilogue code depends on the
Local variables defined
Arguments passed to the procedure
Current processor selected (affects epilogue code only)
Current calling convention
Options passed in the prologuearg of the PROC directive
Registers being saved
The prologuearg list contains options specifying how the prologue or epilogue code should be generated. The next section explains how to use these options, gives the standard prologue and epilogue code, and explains the techniques for defining your own prologue and epilogue code.
The standard prologue and epilogue code handles parameters and local variables. If a procedure does not have any parameters or local variables, the prologue and epilogue code that sets up and restores a stack pointer is omitted, unless FORCEFRAME is included in the prologuearg list. (FORCEFRAME is discussed later in this section.) Prologue and epilogue code also generates a push and pop for each register in the register list unless the register list is empty.
Summary: RETN and RETF suppress epilogue code generation.
When a RET is used without an operand, the assembler generates the standard epilogue code. If you do not want the standard epilogue generated, you can use RETN or RETF with or without operands. RET with an integer operand does not generate epilogue code, but it does generate the right size of return.
In the examples below showing standard prologue and epilogue code, localbytes is a variable name used in this example to represent the number of bytes needed on the stack for the locals declared, parmbytes represents the number of bytes that the parameters take on the stack, and registers represents the list of registers to be pushed or popped.
The standard prologue code is the same in any processor mode:
push bp
mov bp, sp
sub sp, localbytes ; if localbytes is not 0
push registers
The standard epilogue code is:
pop registers
mov sp, bp ; if localbytes is not 0
pop bp
ret parmbytes ; use parmbytes only if lang is not C
The standard prologue and epilogue code recognizes two operands passed in the prologuearg list, LOADDS and FORCEFRAME. These operands modify the prologue code. Specifying LOADDS saves and initializes DS. Specifying FORCEFRAME as an argument generates a stack frame even if no arguments are sent to the procedure and no local variables are declared. If your procedure has any parameters or locals, you do not need to specify FORCEFRAME.
Specifying LOADDS generates this prologue code:
push bp
mov bp, sp
sub sp, localbytes ; if localbytes is not 0
push ds
mov ax, DGROUP
mov ds, ax
push registers
Specifying LOADDS generates the following epilogue code:
pop registers
pop ds
mov sp, bp
pop bp
ret parmbytes ; use parmbytes only if lang is not C
If you want a different set of instructions for prologue and epilogue code in your procedures, you can write macros that are executed instead of the standard prologue and epilogue code. For example, while you are debugging your procedures, you may want to include a stack check or track the number of times a procedure is called. You can write your own prologue code to do these things whenever a procedure executes. Different prologue code may also be necessary if you are writing applications for Microsoft Windows or any other environment application for DOS. User-defined prologue macros will respond correctly if you specify FORCEFRAME in the prologuearg of a procedure.
To write your own prologue or epilogue code, the OPTION directive must appear in your program. It disables automatic prologue and epilogue code generation. When you specify
OPTION PROLOGUE : macroname
OPTION EPILOGUE : macroname
the assembler calls the macro specified in the OPTION directive instead of generating the standard prologue and epilogue code. The prologue macro must be a macro function, and the epilogue macro must be a macro procedure.
The assembler expects your prologue or epilogue macro to have this form:
macroname MACRO procname, /
flag, /
parmbytes, /
localbytes, /
<reglist>, /
userparms
The following list explains the arguments passed to your macro. Your macro must have formal parameters to match all the actual arguments passed.
Argument | Description | ||
procname | The name of the procedure. | ||
flag | A 16-bit flag containing the following information: | ||
Bit = Value | Description | ||
Bit 0, 1, 2 | For calling conventions (000=unspecified language type, 001=C, 010=SYSCALL, 011=STDCALL, 100=PASCAL, 101=FORTRAN, 110=BASIC) | ||
Bit 3 | Undefined (not necessarily zero) | ||
Bit 4 | Set if the caller restores the stack (Use RET, not RETn) | ||
Bit 5 | Set if procedure is FAR | ||
Bit 6 | Set if procedure is PRIVATE | ||
Bit 7 | Set if procedure is EXPORT | ||
Bit 8 | Set if the epilogue was generated as a result of an IRET instruction and cleared if the epilogue was generated as a result of a RET instruction | ||
Bits 9–15 | Undefined (not necessarily zero) | ||
parmbytes | The byte count of all the parameters given in the PROC statement. | ||
localbytes | The count in bytes of all locals defined with the LOCAL directive. | ||
reglist | A list of the registers following the USES operator in the procedure declaration. This list is enclosed by angle brackets (< >), and each item is separated by commas. This list is reversed for epilogues. | ||
userparms | Any argument you want to pass to the macro. The prologuearg (if there is one) specified in the PROC directive is passed to this argument. |
<$I<< \ra (angle brackets);epilogues><$IAngle brackets (<< \ra);epilogues><$I<< \ra (angle brackets);prologues>
The following macro is an example of a user-defined prologue that counts the number of times a procedure is called.
ProfilePro MACRO procname, \
flag, \
bytecount, \
numlocals, \
regs, \
macroargs
.DATA
procname&count WORD 0
.CODE
inc procname&count ; Accumulates count of times the
; procedure is called
push bp
mov bp, sp
; Other BP operations
IFNB <regs>
FOR r, regs
push r
ENDM
ENDIF
EXITM %bytecount
ENDM
Your program must also include this statement before any procedures are called that use the prologue:
OPTION PROLOGUE:ProfilePro
If you define only a prologue or an epilogue macro, the standard prologue or epilogue code is used for the one you do not define. The form of the code generated depends on the .MODEL and PROC options used.
If you want to revert to the standard prologue or epilogue code, use PROLOGUEDEF or EPILOGUEDEF as the macroname in the OPTION statement.
OPTION EPILOGUE:EPILOGUEDEF
You can completely suppress prologue or epilogue generation with
OPTION PROLOGUE:None
OPTION EPILOGUE:None
In this case, no user-defined macro is called, and the assembler does not generate a default code sequence. This state remains in effect until the next OPTION PROLOGUE or OPTION EPILOGUE is encountered.
See Chapter 9 for additional information about writing macros. The PROLOGUE.INC file provided in the MASM 6.0 distribution disks can be used to create the prologue and epilogue sequences for the Microsoft C Professional Development System, version 6.0.