• No results found

Chapter 3. C Language Features

3.5 Functions

Functions may be written in the usual way in accordance with the C language. Imple- mentation and special features associated with functions are discussed in the following sections.

3.5.1

Function Size

On baseline PIC devices, the size of the assembly code generated for each function must be less than the size of one page in the target device. A linker "can’t find space" error message will be issued if this restriction is not met, and in such cases, the func- tions must be re-coded or split into several smaller functions.

If the target device is a Mid-Range or enhanced Mid-Range PIC device, this restriction does not apply, and the assembly code generated for each C function may be of any size, limited only by the available program memory.

3.5.2

Absolute Functions

The assembly code associated with a C function can be placed at an absolute address. This can be accomplished by using an @ address construct in a similar fashion to that used with absolute variables. Such functions are called absolute functions. The following example of an absolute function which will place the function at address 400h:

int mach_status(int mode) @ 0x400 {

/* function body */ }

If you check the assembly list file you will see the function label and the first assembly instruction associated with the function located at 0x400.

If this construct is used with interrupt functions it will only affect the position of the code associated with the interrupt function body. The interrupt context switch code that pre- cedes the function code will not be relocated as it must be linked to the interrupt vector. See also Section2.7.22 “--CODEOFFSET: Offset Program Code to Address” for information on how to move reset and interrupt vector locations, which may be useful for designing applications such as bootloaders.

Functions can also be placed at specific positions by using the psect pragma, see

Section 3.10.3.6 “The #pragma psect Directive”. The decision whether functions should be positioned this way or using absolute functions should be based on the loca- tion requirements.

Using absolute functions is the easiest method, but only allows placement at an address which must be known prior to compilation. The psect pragma is more com- plex, but offers all the flexibility of the linker to position the new psect into memory. For example, you can specify that functions reside at a fixed address, or that they be placed after other psects, or that the they be placed anywhere in a compiler-defined or user-defined range of addresses.

3.5.3

Function Argument Passing

The method used to pass function arguments depends on the size and number of arguments involved.

C Language Features

The parameter area is grouped along with the function’s auto memory and is placed in the compiled stack. See Section 3.4.2 “Compiled Stack Operation” for detailed information on the compiled stack. The parameter variables will be referenced as an offset from the symbol ?_ function, where function is the name of the function in which the parameter is defined (i.e. the function that is to be called).

If the first parameter is one byte in size, it is passed in the W register. All other parame- ters are passed in the parameter memory. This applies to basic types and to aggregate types like structures.

The parameters for functions that take a variable argument list (defined using an ellipsis in the prototype) are placed in the parameter memory, along with named parameters. Take, for example, the following ANSI-style function.

void test(char a, int b);

The function test() will receive the parameter b in its function parameter block and

a in the W register. A call: test(xyz, 8);

would generate code similar to:

MOVLW 08h ; move literal 0x8 into...

MOVWF ?_test ; the parameter memory

CLRF ?_test+1 ; locations for the 16-bit parameter

MOVF _xyz,w ; move xyz into the W register

CALL (_test)

In this example, the parameter b is held in the memory locations ?_test (Least Significant Byte) and ?_test+1 (Most Significant Byte).

The exact code used to call a function, or the code used to access a parameters from within a function, can always be examined in the assembly list file. See

Section 2.7.17 “--ASMLIST: Generate Assembler List Files” for the option that generates this file. This is useful if you are writing an assembly routine that must call a function with parameters, or accept arguments when it is called.

3.5.4

Function Return Values

Function return values are passed to the calling function using either the W register, or the function’s parameter memory. Having return values also located in the parameter memory can reduce the code size for functions that return a modified copy of their parameter.

Eight-bit values are returned from a function in the W register.

Values larger than a byte are returned in the function’s parameter memory area, with the least significant word in the lowest memory location. For example, the function: int return_16(void)

{

return 0x1234; }

will exit with the code similar to: MOVLW 34h

3.5.5

Function Calling

3.5.5.0.1 Baseline Devices

The Baseline PIC devices have a two-level deep hardware stack which is used to store the return addresses of subroutine calls.

Typically, CALL instructions are used to transfer control to a C function when it is called, however where the depth of the stack will be exceeded, the compiler will automatically swap to using a method that involves the use of a lookup table and which does not require use of the hardware stack.

When the lookup method is being employed, a function is reached by a jump (not a call) directly to its address. Before this is done the address of a special "return" instruction (implemented as a jump instruction) is stored in a temporary location inside the called function. This return instruction will be able to return control back to the calling function. This means of calling functions allows functions to be nested deeply without overflow- ing the stack, however it does come at the expense of memory and program speed. By default the compiler will determine which functions are permitted to be called via a

CALL assembly instruction and which will be called via the lookup table. By disabling the stackcall suboption to the --RUNTIME option all function calls execute via a lookup table unless a function definition is qualified as fastcall.

A fastcall -qualified function will always be called via a CALL instruction. Extreme care must be taken when functions are qualified fastcall, since each nested fast- call function call will use one word of available stack space. Check the call graph depth in the assembly list file and monitor warning messages from the compiler to ensure that the stack will not overflow. See Section 3.2.1 “Stack” for more information on the hardware return address stack.

The function prototype for a Baseline fastcall function might look something like: fastcall void my_function(int a);

3.5.5.0.2 Mid-Range PIC Devices

The Mid-Range PIC devices have a larger hardware stack and are thus allow a higher degree of function nesting. These devices always use a CALL instruction when calling functions and the fastcall qualifier has no effect for such functions.

3.5.6

Bank Selection within Functions

A function can return with any RAM bank selected.

The compiler tracks the bank selections made in the generated code associated with each function, even across function calls to other functions. If the bank that is selected when a function returns can be determined, the compiler will use this information to try to remove redundant bank selection instructions which might otherwise be inserted into the generated code.

The compiler will not be able to track the bank selected by routines written in assembly, even if they are called from C code. The compiler will make no assumptions about the selected bank when such routines return.

The "Tracked objects" section associated with each function and which is shown in the assembly list file relates to this bank tracking mechanism.

C Language Features

Related documents