5.12.3.3 Equivalent Assembly Symbols
Most C symbols map to an corresponding assembly equivalent.
This mapping is such that an “ordinary” symbol defined in the assembly
domain cannot interfere with an “ordinary” symbol in the C domain. So for example, if the
symbol main
is defined in the assembly domain, it is quite distinct to the
main
symbol used in C code and they refer to different locations.
The name of a C function maps to an assembly label that will have the same
name, but with an underscore prepended. So the function main()
will define
an assembly label _main
.
Baseline PIC devices can use alternate assembly domain symbols for
functions. The destinations of call instructions on these devices are limited to the first
half of a program memory page. The compiler, thus, encodes functions in two parts, as
illustrated in the following example of a C function, add()
, compiled for
a Baseline device.
entry__add:
ljmp _add
The label entry__add
is the function’s entry point and
will always be located in the first half of a program memory page. The code associated with
this label is simply a long jump (see 6.1.1.7 Long Jumps And Calls) to the actual function
body located elsewhere and identified by the label _add
.
If you plan to call Baseline routines from assembly code, you must be
aware of this limitation in the device and the way the compiler works around it for C
functions. Hand-written assembly code should always call the
entry__funcName
label rather than the usual assembly-equivalent
function label.
If a C function is qualified static
and there is more
than one static function in the program with the exact same name, the name of the first
function will map to the usual assembly symbol and the subsequent functions will map to a
special symbol with the form:
_functionName@fileName$Fnumber
, where
functionName
is the name of the function,
fileName
is the name of the file that contains the function,
and number
is a unique number sequence. The definition of “first”
in this situation is complex. Typically, if the symbol is contained in the source module
that defines main()
, it will be processed first. If it is not in this
module, then the order in which the source files are listed on the compiler command line
determines which is considered first.
For example, a program contains the definition for two
static
functions, both called add()
. One lives in the
file main.c
and the other in lcd.c
. The first function
will generate an assembly label _add
. The second might generate the label
_add@lcd$F38
, for example.
The name of a C variable with static storage duration also maps to an
assembler label that will have the same name, but with an underscore prepended. So the
variable result
will define an assembly label:
_result
.
If the C variable is qualified static
, there is a chance
that there could be more than one variable in the program with exactly the same C name. The
rules that apply to static
variables defined outside of functions are
similar to those that apply to static
functions. The name of the first
variable will map to a symbol prepended with an underscore; the subsequent symbols will
have the form: _variableName@fileName$Fnumber
, where
variableName
is the name of the variable,
fileName
is the name of the file that contains the variable,
and number
is a unique number sequence.
All local static
variables (i.e., defined inside a
function definition) have an assembly name of the form:
functionName@variableName
. If there is a static
variable called output
in the function read()
and another
static
variable with the same name defined in the function
update()
, then the symbols in the assembly can be accessed using the
symbols read@output
and update@output
, respectively.
Functions that use the reentrant model do not define any symbols that allow you to access auto and parameter variables. You should not attempt to access these in assembly code. Special symbols for these variables are defined, however, by functions that use the nonreentrant model. These symbols are described in the following paragraphs.
To allow easy access to parameter and auto variables on the compiled
stack, special equates are defined which map a unique symbol to each variable. The symbol
has the form: functionName@variableName
. Thus, if the
function main()
defines an auto variable called foobar
,
the symbol main@foobar
can be used in assembly code to access this C
variable.
Function parameters use the same symbol mapping as auto variables. If a
function called read()
has a parameter called channel
,
then the assembly symbol for that parameter is read@channel
.
Function return values have no C identifier associated with them. The
return value for a function shares the same memory as that function’s parameter variables,
if they are present. The assembly symbol used for return values has the form
?_functionName
, where functionName
is
the name of the function returning the value. Thus, if a function,
getPort()
returns a value, it will be located the address held by the
assembly symbol ?_getPort
. If this return value is more than one byte in
size, then an offset is added to the symbol to access each byte, e.g.,
?_getPort+1
.
If the compiler creates temporary variables to hold intermediate results,
these will behave like auto variables. As there is no corresponding C variable, the
assembly symbol is based on the symbol that represents the auto block for the function plus
an offset. That symbol is ??_functionName
, where
functionName
is the function in which the symbol is being
used. So for example, if the function main()
uses temporary variables,
they will be accessed as an offset from the symbol ??_main
.