7.3.8 Generating Prologue and Epilogue Code

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.

7.3.8.1 Using Automatic 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

7.3.8.2 User-Defined Prologue and Epilogue Code

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.