5.5 Using a Compiled Stack
Not to be confused with a software stack, which is a dynamic stack allocation accessed via a stack pointer, a compiled stack is a memory area that is designated for the static allocation of local objects that should only consume memory for the duration of the routine with which they are associated. This is similar to the stack-based auto and parameter objects defined by functions in C programs. When using the PIC Assembler, the allocation of memory on the stack is performed by the linker. The stack-based objects requested by each routine are assembled into blocks. Blocks from routines that are not active at the same time can be overlaid in memory, based on information in the program's callgraph, which is constructed from directives added to your program.
A more detailed example of using a compiled stack with the PIC Assembler is provided in the MPLAB℠ XC8 PIC Assembler User's Guide for Embedded Engineers. A summary of this information is provided here.
The FNCONF
directive (see 4.9.25 Fnconf Directive) should be used
once per program. It's three arguments indicate to the linker the name of the psect that
should be used to hold the compiled stack, the symbol prefix to be used for auto-style
objects, and the symbol prefix to be used for parameter objects.
The FNROOT
directive (see 4.9.27 Fnroot Directive) should be used
once for each routine that forms the root node in a callgraph. The memory allocated to
stack objects can be overlapped with that of other routines within the same callgraph,
but no overlapping will take place between the stack objects of routines that are in
different callgraphs. Typically you will define one callgraph root for the main part of
your program and then one for each interrupt routine. This way, the stack memory
associated with interrupt routines is kept separate and no data corruption can
occur.
The FNSIZE
directive (see 4.9.28 Fnsize Directive) should be
specified for each routine that needs to have objects plaed on the compiled stack. It
takes three arguments, those being the name of a routine, the total number of bytes
required for that routine's auto-like objects, and the total number of bytes for its
parameter-like objects. The directive can be placed anywhere in your code, but it is
often located near the routine it configures.
The FNCALL
directive (see 4.9.24 Fncall Directive) is used as many
times as required to indicate which routines (that use the compiled stack) are called
and from where. From this information, the linker can form the callgraph. As you develop
your program, you will need to ensure that there is an FNCALL
directive
for each unique call that takes place in your code. If a called routine does not define
any compiled stack objects, the directive is not required for that routine, but it is
good practice to include it anyway, in case there are subsequent changes made to the
program.
FNSIZE
directive. The following example
of a cut-down program shows the stack being set up, with the main and add routines
requesting that they each require space on the stack. The main routines reads the
special symbols created by the linker for its auto-like objects; the add routine uses
special symbols for its
parameters..FNCONF udata_acs,?au_,?pa_ ;setup the stack
FNROOT main ;the main routine is a callgraph root
PSECT code
FNSIZE main,4,0 ;the main routine needs 4 bytes of auto-like objects
GLOBAL ?au_main ;make the symbol created by the linker globally accessible
main:
...
loop:
movff ?au_main+0,?pa_add+0 ;load the first byte of the first parameter
movff ?au_main+1,?pa_add+1 ;load the second byte of the first parameter
movff ?au_main+2,?pa_add+2 ;load the first byte of the second parameter
movff ?au_main+3,?pa_add+3 ;load the second byte of the second parameter
FNCALL main,add ;the main routine will call the add routine in the callgraph
call add ;the actual call
...
FNSIZE add,0,4 ;the add routine needs 4 bytes of parameters
GLOBAL ?pa_add ;make the symbol created by the linker globally accessible
add:
movf ?pa_add+2,w,c ;the add routine uses its parameter symbols
addwf ?pa_add+0,f,c
...