-
Notifications
You must be signed in to change notification settings - Fork 0
Parameter passing and calling conventions
Parameters are passed to functions on the parameter stack. In general, parameters are pushed from left to right, so the rightmost parameter is the last one pushed (and therefore the one at the lowest position on the parameter stack). Provided that there are no local variables, the last parameter is at offset zero on the parameter stack. If the function returns a value, it comes back in A/X.
In presence of a prototype, parameters are pushed as their respective types. This does especially mean that characters are pushed as such and are not promoted to integers.
If no prototype is available, the default promotions are applied before pushing parameters. This means that characters are promoted to integer before pushing them.
Parameters in variable argument lists (ellipsis, ...
) are treated the same as if there were no prototype.
If a function is declared as __fastcall__ (or fastcall), the last (rightmost) parameter is not passed on the stack, but passed in the primary register to the called function. This is A in case of an eight bit value, A/X in case of a 16 bit value, and A/X/sreg in case of a 32 bit value.
If the called function is a C function, its first instruction will be a call to one of the 'push' functions to push the passed value onto the stack. This means that for C functions 'fastcall' doesn't make the code really faster. Assembler functions however, can take advantage of the values passed in registers.
Although 'fastcall' doesn't help making C functions faster it usually helps making the whole program somewhat smaller as all the callers of a 'fastcall' function can omit one call to a 'push' function.
Contrary to most other C compilers, the callee is responsible for cleaning up the stack (dropping stack space used for parameters) before returning. This is done in order to generate smaller code, since dropping parameters can in many places be combined with dropping local variables.
The following example assume that there are no local variables. Presence of local variables would change the stack offset of the parameters in the function.
Prototype:
void foo (unsigned bar, unsigned char baz);
Stack layout within the function:
+------------------+ | High byte of bar | Offset 2 ->+------------------+ | Low byte of bar | Offset 1 ->+------------------+ | baz | Offset 0 ->+------------------+
Example code for accessing bar. The variable is in A/X after the code snippet:
ldy #2 ; Offset of high byte of bar lda (sp),y ; High byte now in A tax ; High byte now in X dey ; Offset of low byte of bar lda (sp),y ; Low byte now in A