14.7.1 Function Parameters

The first eight working registers, W0-W7, are used for function parameters. For dsPIC33A devices, the first eight F registers, F0-F7, are used for float type values. Parameters are allocated to registers in left-to-right order, with parameters being assigned to the first available register that is suitably aligned. Any unallocated values are pushed onto the stack, the last unallocated parameter is pushed first.

In the following example, all parameters are passed in registers, although not in the order they appear in the declaration. This format allows the compiler to make the most efficient use of the available parameter registers.

Function Call Model for dsPIC33C/E/F and dsPIC30F Devices

void params0(short p0, long p1, int p2, char p3, float p4, void *p5)
{
           /*
           ** W0    p0
           ** W1    p2
           ** W3:W2 p1
           ** W4    p3
           ** W5    p5
           ** W7:W6 p4
           */
           ...
}

Function Call Model for dsPIC33A Devices

void params0(short p0, long long p1, int p2, char p3, float p4, void *p5) {
     /*
     **  W0    p0
     **  W1    p2
     **  W3:W2 p1
     **  W4    p3
     **  W5    p5
     **  F0    p4
     */
}

The next example demonstrates how structures are passed to functions. If the complete structure can fit in the available registers, the structure is passed via registers; otherwise the structure argument is placed onto the stack.

Function Call Model, Passing Structures, for dsPIC33C/E/F and dsPIC30F Devices

typedef struct bar {
  int i;
  long double d;
} bar;

void params1(int i, bar b) {
           /*
           ** W0    i
           ** W1    b.i
           ** W5:W2 b.d
           */
}

Function Call Model, Passing Structures, for dsPIC33A Devices

typedef struct bar {
  int i;
  long double d;
} bar;

void params1(int i, bar b) {
     /*
     **  W0    i
     **  W1    b.i
     **  W3:W2 b.d
     */
}

Parameters corresponding to the ellipses (...) of a variable-length argument list are not allocated to registers. Neither is the argument immediately prior to the start of the variable arguments. Any argument not allocated is pushed onto the stack in right to left order.

In the next example, the structure parameter cannot be placed in registers because it is too large. However, this does not prevent the next parameter from using a register spot.

Function Call Model, Stack Based Arguments, for dsPIC33C/E/F and dsPIC30F Devices

typedef struct bar {
 long double d,e;
} bar;

void params2(int i, bar b, int j) {
             /*
             ** W0    i
             ** stack b
             ** W1    j
             */
}

Function Call Model, Stack Based Arguments, dsPIC33A Devices

typedef struct bar {
  long double d,e,f,g;
} bar;

void params2(int i, bar b, int j) {
     /*
     ** W0    i
     ** Stack b
     ** W1    j
     */
}

Accessing arguments that have been placed on the stack depends upon whether or not a Frame Pointer is being used; a Frame Pointer is created with a lnk instruction. Recall from section 11.2.3.2 The C Stack Usage, that a Frame Pointer points to the start of the frame for a function and the Stack Pointer points to the end of the frame. Arguments that are passed on the stack come before: the frame pointer and the previous value of the frame pointer (if present) and the return address.

From the figure above, the offset from the Frame Pointer (if present) is FP - Linked FP size - Return Address size - bar b size. For a dsPIC33C/E/F and dsPIC30F devices, this would be: FP - (Linked FP size == 2) - (Return Address size == 4) - (bar b size == 16), or W14 - 22. For a dsPIC33A device, this would be: FP - (Linked FP size == 4) - (Return Address size == 4) - (bar b size = 32), or W14 - 40.

The calculation from the Stack Pointer requires knowing how much space "Local objects / Scratchpad" has consumed, which can be difficult to count.