With the PROC directive, you can specify registers to be saved, define param-eters to the procedure, and assign symbol names to parameters (rather than as offsets from BP). This section describes how to use the PROC directive to automate the parameter-accessing techniques described in the last section.
For example, the diagram below shows a valid PROC statement for a procedure called from C. It takes two parameters, var1 and arg1, and uses (and must save) the DI and SI registers:
The syntax for PROC is
labelPROC [[attributes]] [[USESreglist]] [[,parameter[[:tag]]... ]]
The following list describes the parts of the PROC directive.
Argument | Description |
label | The name of the procedure. |
attributes | Any of several attributes of the procedure, including the distance, langtype, and visibility of the procedure. The syntax for attributes is given in Section 7.3.3.1. |
reglist | A list of registers following the USES keyword that the procedure uses and that should be saved on entry. Registers in the list must be separated by blanks or tabs, not by commas. The assembler generates prologue code to push these registers onto the stack. When you exit, the assembler generates epilogue code to pop the saved register values off the stack. |
parameter | The list of parameters passed to the procedure on the stack. The list can have a variable number of parameters. See the discussion below for the syntax of parameter. This list can be longer than one line if the continued line ends with a comma. |
This diagram shows a valid PROC definition that uses several attributes:
The syntax for the attributes field is
[[distance]] [[langtype]] [[visibility]] [[<prologuearg>]]
The list below explains each of these options.
Argument | Description |
distance | Controls the form of the RET instruction generated. Can be NEAR or FAR. If distance is not specified, it is determined from the model declared with the .MODEL directive. For TINY, SMALL, COMPACT, and FLAT, NEAR is assumed. For MEDIUM, LARGE, and HUGE, FAR is assumed. For 80386/486 programming with 16- and 32-bit segments, NEAR16, NEAR32, FAR16, or FAR32 can be specified. |
langtype | Determines the calling convention used to access param-eters and restore the stack. The BASIC, FORTRAN, and PASCAL langtypes convert procedure names to uppercase, place the last parameter in the parameter list lowest on the stack, and generate a RET, which adjusts the stack upward by the number of bytes in the argument list. |
The C and STDCALL langtype prefixes an underscore to the procedure name when the procedure's scope is PUBLIC or EXPORT and places the first parameter lowest on the stack. SYSCALL is equivalent to the C calling convention with no underscore prefixed to the procedure's name. STDCALL uses caller stack cleanup when :VARARG is specified; otherwise the called routine must clean up the stack (see Chapter 20). | |
visibility | Indicates whether the procedure is available to other modules. The visibility can be PRIVATE, PUBLIC, or EXPORT. A procedure name is PUBLIC unless it is explicitly declared as PRIVATE. If the visibility is EXPORT, the linker places the procedure's name in the export table for segmented executables. EXPORT also enables PUBLIC visibility. |
You can explicitly set the default visibility with the OPTION directive. OPTION PROC:PUBLIC sets the default to public. See Section 1.3.2 for more information. | |
prologuearg | Specifies the arguments that affect the generation of prologue and epilogue code (the code MASM generates when it encounters a PROC directive or the end of a procedure). See Section 7.3.8 for an explanation of prologue and epilogue code. |
The parameters are separated from the reglist by a comma if there is a list of registers. In the syntax:
parmname[[:tag]]
parmname is the name of the parameter. The tag can be either the qualifiedtype or the keyword VARARG. However, only the last parameter in a list of param-eters can use the VARARG keyword. The qualifiedtype is discussed in Section 1.2.6, “Data Types.” An example showing how to reference VARARG param-eters appears later in this section. Procedures can be nested if they do not have parameters or USES register lists. This diagram shows a procedure definition with one parameter definition.
The following example shows the procedure in Section 7.3.2, “Passing Arguments on the Stack,” rewritten to use the extended PROC functionality. Prior to the procedure call, you must push the arguments onto the stack unless you use INVOKE (see Section 7.3.7, “Calling Procedures with INVOKE”).
addup PROC NEAR C,
arg1:WORD, arg2:WORD, count:WORD
mov ax, arg1
add ax, count
add ax, arg2
ret
addup ENDP
If the arguments for a procedure are pointers, the assembler does not generate any code to get the value or values that the pointers reference; your program must still explicitly treat the argument as a pointer. (See Chapter 3, “Using Addresses and Pointers,” for more information about using pointers.)
In the example below, even though the procedure declares the parameters as near pointers, you still must code two MOV instructions to get the values of the param-eters—the first MOV gets the address of the parameters, and the second MOV gets the parameter.
; Call from C as a FUNCTION returning an integer
.MODEL medium, c
.CODE
myadd PROC arg1:NEAR PTR WORD, arg2:NEAR PTR WORD
mov bx, arg1 ; Load first argument
mov ax, [bx]
mov bx, arg2 ; Add second argument
add ax, [bx]
ret
myadd ENDP
END
You can use conditional-assembly directives to make sure that your pointer parameters are loaded correctly for the memory model. For example, the following version of myadd treats the parameters as FAR parameters if necessary:
.MODEL medium, c ; Could be any model
.CODE
myadd PROC arg1:PTR WORD, arg2:PTR WORD
IF @DataSize
les bx, arg1 ; Far parameters
mov ax, es:[bx]
les bx, arg2
add ax, es:[bx]
ELSE
mov bx, arg1 ; Near parameters
mov ax, [bx]
mov bx, arg2
add ax, [bx]
ENDIF
ret
myadd ENDP
END
In the PROC statement, you can append the :VARARG keyword to the last param-eter to indicate that a variable number of arguments can be passed if you use the C, SYSCALL, or STDCALL calling conventions (see Section 20.1). A label must precede :VARARG so that the arguments can be accessed as offsets from the variable name given. This example illustrates VARARG:
addup3 PROTO NEAR C, argcount:WORD, arg1:VARARG
invoke addup3, 3, 5, 2, 4
addup3 PROC NEAR C, argcount:WORD, arg1:VARARG
sub ax, ax ; Clear work register
sub si, si
.WHILE argcount > 0 ; Argcount has number of arguments
add ax, arg1[si] ; Arg1 has the first argument
dec arg1 ; Point to next argument
inc si
inc si
.ENDW
ret ; Total is in AX
addup3 ENDP
Passing non-default-sized pointers in the VARARG portion of the parameter list can be done by explicitly passing the segment portion and the offset portion of the address separately.
NOTE:
When you use the extended PROC features and the assembler encounters a RET instruction, it automatically generates instructions to pop saved registers, remove local variables from the stack, and, if necessary, remove parameters. It generates this code for each RET instruction it encounters. You can reduce code size by having only one return and jumping to it from various locations.