7.5.1. The Pascal Calling Convention
------------------------------------
The 16-bit Pascal calling convention is as follows. In the following
description, the words _caller_ and _callee_ are used to denote the
function doing the calling and the function which gets called.
* The caller pushes the function's parameters on the stack, one after
another, in normal order (left to right, so that the first argument
specified to the function is pushed first).
* The caller then executes a far `CALL' instruction to pass control
to the callee.
* The callee receives control, and typically (although this is not
actually necessary, in functions which do not need to access their
parameters) starts by saving the value of `SP' in `BP' so as to be
able to use `BP' as a base pointer to find its parameters on the
stack. However, the caller was probably doing this too, so part
of the calling convention states that `BP' must be preserved by
any function. Hence the callee, if it is going to set up `BP' as a
frame pointer, must push the previous value first.
* The callee may then access its parameters relative to `BP'. The
word at `[BP]' holds the previous value of `BP' as it was pushed.
The next word, at `[BP+2]', holds the offset part of the return
address, and the next one at `[BP+4]' the segment part. The
parameters begin at `[BP+6]'. The rightmost parameter of the
function, since it was pushed last, is accessible at this offset
from `BP'; the others follow, at successively greater offsets.
* The callee may also wish to decrease `SP' further, so as to
allocate space on the stack for local variables, which will then
be accessible at negative offsets from `BP'.
* The callee, if it wishes to return a value to the caller, should
leave the value in `AL', `AX' or `DX:AX' depending on the size of
the value. Floating-point results are returned in `ST0'. Results
of type `Real' (Borland's own custom floating-point data type, not
handled directly by the FPU) are returned in `DX:BX:AX'. To return
a result of type `String', the caller pushes a pointer to a
temporary string before pushing the parameters, and the callee
places the returned string value at that location. The pointer is
not a parameter, and should not be removed from the stack by the
`RETF' instruction.
* Once the callee has finished processing, it restores `SP' from
`BP' if it had allocated local stack space, then pops the previous
value of `BP', and returns via `RETF'. It uses the form of `RETF'
with an immediate parameter, giving the number of bytes taken up
by the parameters on the stack. This causes the parameters to be
removed from the stack as a side effect of the return instruction.
* When the caller regains control from the callee, the function
parameters have already been removed from the stack, so it needs
to do nothing further.
Thus, you would define a function in Pascal style, taking two
`Integer'-type parameters, in the following way:
global myfunc
myfunc: push bp
mov bp,sp
sub sp,0x40 ; 64 bytes of local stack space
mov bx,[bp+8] ; first parameter to function
mov bx,[bp+6] ; second parameter to function
; some more code
mov sp,bp ; undo "sub sp,0x40" above
pop bp
retf 4 ; total size of params is 4
At the other end of the process, to call a Pascal function from your
assembly code, you would do something like this:
extern SomeFunc
; and then, further down...
push word seg mystring ; Now push the segment, and...
push word mystring ; ... offset of "mystring"
push word [myint] ; one of my variables
call far SomeFunc
This is equivalent to the Pascal code
procedure SomeFunc(String: PChar; Int: Integer);
SomeFunc(@mystring, myint);