This section summarizes details important to calling Microsoft Professional Pascal, Version 4.0, routines from MASM and MASM routines from Pascal. It includes information on parameters and data types specific to Pascal source modules. The information in this section does not apply to QuickPascal (see Section 20.3.5 for that).
The Pascal calling convention—the default—places arguments on the stack in the same order in which they appear in the Pascal source code. The first argument is highest in memory because it is also the first argument to be placed on the stack, and the stack grows downward. The default naming convention exports names in uppercase.
This list shows the Pascal types that are equivalent to the MASM 6.0 data types.
Pascal Type | Equivalent MASM Type | |
BYTE, CHAR, BOOLEAN | BYTE | |
WORD | WORD | |
INTEGER2 | SWORD | |
REAL, REAL4 | REAL4 | |
INTEGER4 | SDWORD | |
REAL8 | REAL8 |
Microsoft Pascal Version 4.0 recognizes only the first 8 characters of any name, while the assembler recognizes the first 256. Names used publicly with Pascal should not be longer than 8 characters.
Summary: The default for Pascal is passing by value.
By default, Pascal arguments are passed by value, but they can be passed by near reference when declared as VAR or CONST and as far reference when declared as VARS or CONSTS. A VARS or CONSTS argument includes both a two-byte segment address and a two-byte offset with the segment pushed first.
Pascal arguments are also passed by near (or far) reference when the ADR (or ADS) of a variable, or a pointer to a variable, is passed by value. In other words, the address of the variable is first determined. Then this address is passed by value.
Summary: Pascal routines can use the C calling convention.
Changing the Calling Convention
To use the C calling convention from Pascal, type [C] at the end of the declarations before the semicolon, as shown:
Procedure MyProc ( x : integer ) [C]; EXTERN;
The lower bound for Pascal arrays can be any integer. Subscripts vary in row-major order.
Pascal has two types of strings, each of which uses a different format: a fixed-length type STRING and the variable-length type LSTRING.
The format used for STRING is identical to that of the FORTRAN string.The format of an LSTRING stores the length in the first byte. For example, consider an LSTRING declared as
VAR Msg:LSTRING(14);
Msg := 'String of text'
Summary: Pascal strings store the string length in the first byte.
The string is stored in 15 bytes of memory. The first byte indicates the length of the string text. The remaining bytes contain the string text itself:
The Pascal data type LSTRING is not compatible with the formats used by the other languages. You can pass an LSTRING indirectly, however, by first assigning it to a STRING variable. Pascal supports such assignments by performing a conversion of the data.
Pascal passes an additional two-byte argument that indicates string length whenever you pass an argument of type STRING or LSTRING. To suppress the passing of this additional argument, declare a fixed-length type.
Pascal routines can directly access external data. You can declare data as external by adding the EXTERN attribute to the data declaration.
Pascal uses word alignment (unpacked storage) for all data objects larger than one byte. In addition, all nested structures and records start on a word boundary. You can turn on packing for Pascal modules, or you can define structures in MASM to have 2 for their alignment value.
Always use large model for the MASM module when linking with Pascal.
Functions that return REAL, REAL4, or REAL8 values use the long return method; that is, the caller passes an additional, hidden offset of a temporary stack variable that will receive the result.
INVOKE cannot handle long return values directly, but you can add an additional parameter to the prototype for the Pascal procedure. For example, a prototype for a Pascal procedure that expects an SWORD argument looks like this:
PascalProc PROTO Pascal arg1:SWORD, PtrRetVal:NEAR PTR
Before calling the Pascal procedure with INVOKE, allocate space on the stack with
add sp, space
mov cx, sp
INVOKE PascalProc, ax, cx
.
.
.
sub sp, space
These statements place the address of the allocated space in CX.
Since calls to Pascal procedures must be made from within a MASM procedure previously called from the Pascal module, an alternative way to handle a long return value is to create a local variable to receive the return value. This example illustrates this technique:
Proc1 PROC arg1:SWORD
LOCAL RetVal:REAL8
INVOKE PascalProc, ax, ADDR RetVal
To return structures from MASM using the Pascal calling convention, the calling program allocates space for the return value on the stack and passes a pointer (as a hidden argument) to the location where the return value is to be placed. Copy the MASM structure into the location pointed to by the hidden argument and return the pointer to that location in the AX register (or DX:AX for far data models).
Summary: Use the C and VARYING attributes for routines that will receive a variable number of arguments.
In Pascal, you can call routines with a variable number of arguments by including the VARYING attribute in your interface to the routine, along with the C attribute. You must use the C attribute for the Pascal routine, because a variable number of arguments is possible only with the C calling convention.
Each time you call the routine, you will not be required to pass the same number of arguments as are declared in the interface to the routine. However, each actual argument that you pass will be type-checked against whatever formal parameters you may have declared.
Structures, Records, and User-Defined Types
You can pass Pascal structures, records, and user-defined types as arguments by value or by reference depending on the size of the data.
The Pascal ADR and ADS types are equivalent to the C near and far pointers. You can pass ADR and ADS variables as ADRMEM or ADSMEM.
This example shows the Power2 procedure as it is called by Pascal.
Program Asmtest( input, output );
function Power2( a:integer; b:integer ): integer; extern;
begin
writeln( '3 times 2 to the power of 5 is ', Power2( 3, 5 ) );
end.
To understand how to write the assembly procedure, consider how the arguments are placed on the stack, as illustrated in Figure 20.8.
The first argument, 3, is higher in memory than 5 because Pascal pushes arguments in the same order they appear. Both arguments are passed by value.
The MASM 6.0 module can be written as follows:
.MODEL medium, PASCAL
.386
Power2 PROTO PASCAL factor:WORD, power:WORD
.CODE
Power2 PROC factor:WORD, power:WORD
mov ax, factor ; Load Factor into AX
mov cx, power ; Load Power into CX
shl ax, cl ; AX = AX * (2 to power of CX)
ret ; Leave return value in AX
Power2 ENDP
END
The AX and CX registers can be loaded directly because the arguments are passed by value.