7.3.3 Declaring Parameters with the PROC Directive

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:

7.3.3.1 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.

7.3.3.2 Parameters

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

7.3.3.3 Using VARARG

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.