Parameter passing and calling conventions

silverdr edited this page Jun 21, 2017 · 8 revisions

Table of Contents

Overview

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.

The compiler expects all return values to use X, even if they are declared as an 8-bit type. X should always contain the high byte of the return value as if it was promoted to a 16-bit integer.

Parameter passing in the presence of a prototype

In the presence of a prototype, parameters are pushed as their respective types. That especially does mean that characters are pushed as such (one byte), and are not promoted to integers.

Parameter passing without a prototype

If no prototype is available, the default promotions are applied before pushing parameters. That means that characters are promoted to integers before pushing them. The Y index register is set to the number of bytes (not parameters) that were pushed onto the stack.

Parameter passing in the presence of a K & R style prototype

In the presence of an old-fashioned Kernighan & Ritchie prototype, parameters are treated the same as if there were no prototype.

Variable argument lists

Parameters in variable argument lists (ellipsis, …) are treated the same as if there were no prototype. The Y register counts all of the bytes that were pushed, including the ones in the defined, fixed part of the list.

The 'fastcall' calling convention

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. That is A in the case of an eight-bit value, A/X in the case of a 16-bit value, and A/X/sreg in the 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. That means that, for C functions, 'fastcall' doesn't make the code really faster. Assembler functions, however, can take advantage of values being passed in registers.

Although 'fastcall' doesn't help to make C functions faster, it usually helps to make the whole program somewhat smaller, as all the callers of a 'fastcall' function can omit one call to a 'push' function.

Callee cleans up the stack

Contrary to most other C compilers, the callee (the called function) is responsible for cleaning up the stack (dropping stack space used for parameters) before returning. That is done in order to generate smaller code, because dropping parameters can be combined, in many places, with dropping local variables.

Preserving registers

The CPU's A, X, Y, and processor flags registers are used to pass data back-and-forth between the caller and the callee; therefore, they don't need to be saved and restored. Some of the pseudo-registers (sreg, regsave, ptr''n'', and tmp''n''), also, don't need preservation.

The pseudo-register array regbank does need to be preserved. And, the sp parameter stack pointer needs to be "protected" (it will change if you clean the stack).

Examples

The following example assumes that there are no local variables. The presence of local variables would change the stack offset of the parameters in the function.

Prototype:

 void cdecl 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 this 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
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.