Procedures and Functions are called with their parameters on the stack. Contrary to Turbo Pascal, all parameters are pushed on the stack, and they are pushed right to left, instead of left to right for Turbo Pascal. This is especially important if you have some assembly subroutines in Turbo Pascal which you would like to translate to Free Pascal.
Function results are returned in the accumulator, if they fit in the register.
The registers are not saved when calling a function or procedure. If you want to call a procedure or function from assembly language, you must save any registers you wish to preserve.
The first thing a procedure does is saving the base pointer, and setting the base pointer equal to the stack pointer. References to the pushed parameters and local variables are constructed using the base pointer.
When the procedure or function exits, it clears the stack.
When you want your code to be called by a C library or used in a C program, you will run into trouble because of this calling mechanism. In C, the calling procedure is expected to clear the stack, not the called procedure. In other words, the arguments still are on the stack when the procedure exits. To avoid this problem, Free Pascal supports the export modifier. Procedures that are defined using the export modifier, use a C-compatible calling mechanism. This means that they can be called from a C program or library, or that you can use them as a callback function.
This also means that you cannot call this procedure or function from your own program, since your program uses the Pascal calling convention. However, in the exported function, you can of course call other Pascal routines.
As of version 0.9.8, the Free Pascal compiler supports also the cdecl and stdcall modifiers, as found in Delphi. The cdecl modifier does the same as the export modifier, and stdcall does nothing, since Free Pascal pushes the paramaters from right to left by default. In addition to the Delphi cdecl construct, Free Pascal also supports the popstack directive; it is nearly the same a the cdecl directive, only it still mangles the name, i.e. makes it into a name such as the compiler uses internally.
All this is summarized in table (3.1). The first column lists the modifier you specify for a procedure declaration. The second one lists the order the paramaters are pushed on the stack. The third column specifies who is responsible for cleaning the stack: the caller or the called function. Finally, the last column specifies if registers are used to pass parameters to the function.
Modifier | Pushing order | Stack cleaned by | Parameters in registers |
(none) | Right-to-left | Function | No |
cdecl | Right-to-left | Caller | No |
export | Right-to-left | Caller | No |
stdcall | Right-to-left | Function | No |
popstack | Right-to-left | Caller | No |
More about this can be found in chapter (4) on linking.
Standard entry code for procedures and functions is as follows on the x86 architecture:
pushl %ebp movl %esp,%ebp
The generated exit sequence for procedure and functions looks as follows:
leave ret $xx
Where xx is the total size of the pushed parameters.
To have more information on function return values take a look at the section (3.5) section.
Standard entry code for procedures and functions is as follows on the 680x0 architecture:
move.l a6,-(sp) move.l sp,a6
The generated exit sequence for procedure and functions looks as follows:
unlk a6 move.l (sp)+,a0 ; Get return address add.l #xx,sp ; Remove allocated stack move.l a0,-(sp) ; Put back return address on top of the stack
Where xx is the total size of the pushed parameters.
To have more information on function return values take a look at the section (3.5) section.