Manual HiTECH
Manual HiTECH
Tutorials 2
Error Messages 7
Library Functions 8
Contents
1 - Introduction - - - - - - - - - - - - - - - - - - - - - - - - - 15
1
1.1 Typographic conventions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 15
1.2 Using This Manual - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 15
2 - Tutorials - - - - - - - - - - - - - - - - - - - - - - - - - - - 17
2.1 Overview of the compilation process - - - - - - - - - - - - - - - - - - - - - - - - 17
2.1.1 Compilation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 17
2.1.2 The compiler input - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 18
2.1.2.1 Steps before linking - - - - - - - - - - - - - - - - - - - - - - - - 21
2.1.2.2 The link stage - - - - - - - - - - - - - - - - - - - - - - - - - - - 28
2.2 Psects and the linker - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 29
2.2.1 Psects - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 29
2.2.1.1 The psect directive - - - - - - - - - - - - - - - - - - - - - - - - 30
2.2.1.2 Psect types - - - - - - - - - - - - - - - - - - - - - - - - - - - - 31
2.3 Linking the psects - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 33
2.3.1 Grouping psects - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 33
2.3.2 Positioning psects - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 34
2.3.3 Linker options to position psects - - - - - - - - - - - - - - - - - - - - - - 35
2.3.3.1 Placing psects at an address - - - - - - - - - - - - - - - - - - - - 35
2.3.3.2 Exceptional cases - - - - - - - - - - - - - - - - - - - - - - - - - 38
2.3.3.3 Psect classes - - - - - - - - - - - - - - - - - - - - - - - - - - - 38
2.3.3.4 User-defined psects - - - - - - - - - - - - - - - - - - - - - - - - 40
2.3.4 Issues when linking - - - - - - - - - - - - - - - - - - - - - - - - - - - - 42
2.3.4.1 Paged memory - - - - - - - - - - - - - - - - - - - - - - - - - - 42
2.3.4.2 Separate memory areas - - - - - - - - - - - - - - - - - - - - - - 43
2.3.4.3 Objects at absolute addresses - - - - - - - - - - - - - - - - - - - 44
2.3.5 Modifying the linker options - - - - - - - - - - - - - - - - - - - - - - - - 44
3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 47
2
Contents
4
Contents
6
Contents
8
Contents
8 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 167
11 - Index - - - - - - - - - - - - - - - - - - - - - - - - - - - - 309
12
List of Figures
16
Tutorials
The following are tutorials to aid in the understanding and usage of HI-TECH’s C cross compilers.
These tutorials should be read in conjunction with the appropriate sections in the manual as they are
aimed at giving a general overview of certain aspects of the compiler. Some of the tutorials here are
generic to all HI-TECH C compilers and may include information not specific for the compiler you are
using.
2
2.1 Overview of the compilation process
This tutorial gives an overview of the compilation process that takes place with HI-TECH C compilers
in terms of how the input source files are processed. The origin of files that are produced by the compiler
is discussed as well as their content and function.
2.1.1 Compilation
When a program is compiled, it is done so by many separate applications whose operations are
controlled by either the command-line driver (CLD) or HPD driver1 (HPD). In either case, HPD or the
CLD take the options specified by the programmer (menu options in the case of HPD, or command-line
arguments for the CLD) to determine which of the internal applications need to be executed and what
options should be sent to each. When the term compiler is used, this is intended to denote the entire
collection of applications and driver that are involved in the process. In the same way, compilation refers
to the complete transformation from input to output by the compiler. Each application and its function
is discussed further on in this document.
The compiler drivers use several files to store options and information used in the compilation process
and these file types are shown in Table 2 - 1 on page 18. The HPD driver stores the compiler options
into a project file which has a .prj extension. HPD itself stores its own configurational settings in an
INI file, e.g. HPD51.ini in the BIN directory of your distribution. This file stores information such as
colour values and mouse settings. Users who wish to use the CLD can store the command line arguments
in a DOS batch file.
Some compilers come with chip info files which describe the memory arrangements of different chip
types. If necessary this file can be edited to create new chip types which can then be selected with the
appropriate command-line option of from the select processor... menu. This file will also have a .ini
extension and is usually in the LIB directory.
1. The command line driver and HPD driver have processor-specific names, such as PICC, C51, or HPDXA, HPDPIC
etc.
The compilation process is discussed in the following sections both in terms of what takes place at each
stage and the files that are involved. Reference should be made to Figure 2 - 1 on page 19 which shows
the block diagram of the internal stages of the HI-TECH compiler, and the tables of file types throughout
this tutorial which list the filename extension2 used by different file formats and the information which
the file contains. Note that some older HI-TECH compilers do not include all the applications discussed
below.
The internal applications generate output files and pass these to the next application as indicated in the
figure. The arrows from one application (drawn as ellipses) to another is done via temporary files that
have non-descriptive names such as $$003361.001. These files are temporarily stored in a directory
pointed to by the DOS environment variable TEMP. Such a variable is created by a set DOS command.
These files are automatically deleted by the driver after compilation has been completed.
2.1.2 The compiler input
The user supplies several things to the compiler to make a program: the input files and the compiler
options, whether using the CLD or HPD. The compiler accepts many different input file types. These
are discussed below.
It is possible, and indeed in a large number of projects, that the only files supplied by the user are C
source files and possibly accompanying header files. It is assumed that anyone using our compiler is
familiar with the syntax of the C language. If not, there is a seemingly endless selection of texts which
cover this topic. C source files used by the HI-TECH compiler must use the extension .c as this
extension is used by the driver to determine the file's type. C source files can be listed in any order on
the command line if using the CLD, or entered into the source file list... dialogue box if using HPD.
A header file is usually a file which contains information related to the program, but which will not
directly produce executable code when compiled. Typically they include declarations (as opposed to
definitions) for functions and data types. These files are included into C source code by a preprocessor
directive and are often called include files. Since header files are referenced by a command that includes
2. The extensions listed in these tables are in lower case. DOS compilers do not distinguish between upper- and lower-
case file names and extensions, but in the interest of writing portable programs you should use lower-case exten-
sions in file names and in references to these files in your code as UNIX compilers do handle case correctly.
18
Overview of the compilation process
the file's name and extension (and possibly a path), there are no restrictions as to what this name can be
although convention dictates a .h extension.
Although executable C code may be included into a source file, a file using the extension .h is assumed
to have non-executable content. Any C source files that are to be included into other source files should
still retain a .c extension. In any case, the practise of including one source file into another is best
avoided as it makes structuring the code difficult, and it defeats many of the advantages of having a
compiler capable of handling multiple-source files in the first place. Header files can also be included
into assembler files. Again, it is recommended that the files should only contain assembler declarations.
HI-TECH compilers comes with many header files which are stored in a separate directory of the
distribution. Typically user-written header files are placed in the directory that contains the sources for
the program. Alternatively they can be placed into a directory which can be searched by using a -I (CPP
include paths...) option.
An assembler file contains assembler mnemonics which are specific to the processor for which the
program is being compiled. Assembler files may be derived from C source files that have been
previously compiled to assembler, or may be hand-written and highly-prized works of art that the
programmer has developed. In either case, these files must conform to the format expected of the HI-
TECH assembler that is part of the compiler. This processor-dependence makes assembly files quite un-
portable and they should be avoided if C source can be made to perform the task at hand. Assembler files
must have a .as extension as this is used by the compiler driver to determine the file’s type. Assembler
files can be listed in any order on the command line if using the CLD, or entered into the source file
list... dialogue box if using HPD, along with the C source files.
The compiler drivers can also be passed pre-compiled HI-TECH object files as input. These files are
discussed below in Section 2.1.2.1 on page 21. These files must have a .obj extension. Object files can
be listed in any order on the command line if using the CLD, or entered into the object file list... dialogue
box if using HPD. You should not enter the names of object files here that have been compiled from
source files already in the project, only include object files that have been pre-compiled and have no
corresponding source in the project, such as the run-time file. For example, if you have included init.c
into the project, you should not include init.obj into the object file list.
Commonly used program routines can be compiled into a file called a library file. These files are more
convenient to handle and can be accessed quickly by the compiler. The compiler can accept library files
directly like other source files. A .lib extension indicates the type of the file and so library files must
20
Overview of the compilation process
be named in this way. Library files can be listed in any order on the command line if using the CLD, or
entered into the library file list... dialogue box if using HPD.
The HI-TECH library functions come pre-compiled in a library format and are stored in the LIB
directory in your distribution.
2.1.2.1 Steps before linking
Of all the different types of files that can be accepted by the compiler, it is the C source files that require
the most processing. The steps involved in compiling the C source files are examined first. 2
For each C source file, a C listing file is produced by an application called CLIST. The listing files
contain the C source lines proceeded by a line number before any processing has occurred. The C listing
for a small test program called main.c is shown in Table 2 - 3 on page 21.
C source C listing
#define VAL 2 1: #define VAL 2
2:
int a, b = 1; 3: int a, b = 1;
4:
void 5: void
main(void) 6: main(void)
{ 7: {
/* set starting value */ 8: /* set starting value */
a = b + VAL; 9: a = b + 2;
} 10: }
The input C source files are also passed to the preprocessor, CPP. This application has the job of
preparing the C source for subsequent interpretation. The tasks performed by CPP include removing
comments and multiple spaces (such as tabs used in indentation) from the source, and executing any
preprocessor directives in the source. Directives may, for example, replace macros with their
replacement text (e.g. #define directives) or conditionally include source code subject to certain
conditions (e.g. #if, #ifdef etc. directives). The preprocessor also inserts header files, whether user-
or compiler-supplied, into the source. Table 2 - 4 on page 22 shows preprocessor output for the test
program.
The output of the preprocessor is C source, but it may contain code which has been included by the
preprocessor from header files and conditional code may have been omitted. Thus the preprocessor
output usually contains similar, but different code to the original source file. The preprocessor output is
often referred to as a module or translational unit. The term "module" is sometimes used to describe the
actual source file from which the "true" module is created. This is not strictly correct, but the meaning
is clear enough.
The code generation that follows operates on the CPP output module, not the C source and so special
steps must be taken to be able to reconcile errors and their position in the original C source files. The #
1 main.c line in the preprocessor output for the test program is included by the preprocessor to indicate
the filename and line number in the C source file that corresponds to this position. Notice in this example
that the comment and macro definition have been removed, but blank lines take their place so that line
numbering information is kept intact.
Like all compiler applications, the preprocessor is controlled by the compiler driver (either the CLD or
2 Table 2 - 4 preprocessor output
void int a, b = 1;
main(void)
{ void
/* set starting value */ main(void)
a = b + VAL; {
}
a = b + 2;
}
HPD). The type of information that the driver supplies the preprocessor includes directories to search
for header files that are included into the source file, and the size of basic C objects (such as int,
double, char *, etc.) using the -S, -SP options so that the preprocessor can evaluate preprocessor
directives which contain a sizeof(type) expression. The output of the preprocessor is not normally
seen unless the user uses the -PRE option in which case the compiler output can then be re-directed to
file.
The output of CPP is passed to P1, the parser. The parser starts the first of the hard work involved with
turning the description of a program written in the C language into the actual executable itself consisting
of assembler instructions. The parser scans the C source code to ensure that it is valid and then replaces
C expressions with a modified form of these. (The description of code generation that follows need not
be followed to understand how to use the HI-TECH compiler, but has been included for curious readers.)
For example the C expression a = b + 2 is re-arranged to a prefix notation like = a + b 2. This
notation can easily be interpreted as a tree with = at the apex, a and + being branches below this, and b
and 2 being sub-branches of the addition. The output of the parser is shown in Table 2 - 6 on page 23 for
our small C program. The assignment statement in the C source has been highlighted as well as the
output the parser generates for this statement. Notice that already the global symbols in the parser output
22
Overview of the compilation process
It is the parser that is responsible for finding a large portion of the errors in the source code. These errors
will relate to the syntax of the source code. The parser also reports warnings if the code is unusual.
The parser passes its output directly to the next stage in the compilation process. There are no driver
options to force the parser to generate parsed-source output files as these files contain no useful
information for the programmer.
Now the tricky part of the compilation: code generation. The code generator converts the parser output
2 into assembler mnemonics. This is the first step of the compilation process which is processor-specific.
Whereas all HI-TECH preprocessors and parsers have the same name and are in fact the same
application, the code generators will have a specific, processor-based name, for example CGPIC, or
CG51.
The code generator uses a set of rules, or productions, to produce the assembler output. To understand
how a production works, consider the following analogy of a production used to generate the code for
the addition expression in our test program. "If you can get one operand into a register" and "one operand
is a int constant" then here is the code that will perform a 2-byte addition of them. Here, each quoted
string would represent a sub-production which would have to be matched. The first string would try to
get the contents of _a into a register by matching further sub-productions. If it cannot, this production
cannot be used and another will be tried. If all the sub-productions can be met, then the code that they
produce can be put together in the order specified by the production tree. Not all productions actually
produce code, but are necessary for the matching process.
If no matching production/subproductions can be found, the code generator will produce a Can’t
generate code for this expression error. This means that the original C source code was legal
and that the code generator did try to produce assembler code for it, but that in this context, there are no
productions which can match the expression.
24
Overview of the compilation process
Typically there may be around 800 productions to implement a full code generator. There were about a
dozen matching productions used to generate code for the statement highlighted in Table 2 - 7 on page
24 using the XA code generator. It checked about 70 productions which were possible matches before
finding a solution. The exact code generation process is too complex to describe in this document and
is not required to be able to use the compiler efficiently.
The user can stop the compilation process after code generation by issuing a -s (compile to .as) option
to the driver. In this case, the code generator will leave behind assembler files with a .as extension.
Table 2 - 7 on page 24 shows output generated by the XA code generator. Only the assembler code for
the opening brace of _main and the highlighted source line is shown. This output will be different for
2
other compilers and compiler options.
The code generator may also produce debugging information in the form of an .sdb file. This operation
is enabled by using the -g (source level debug info) option. One debug file is produced for each module
that is being compiled. These ASCII files contain information regarding the symbols defined in each
module and can be used by debugging programs. Table 2 - 5 on page 23 shows the debug files that can
be produced by the compiler at different stages of the compilation. Several of the output formats also
contain debugging information in addition to the code and data.
The code generator optionally performs one other task: optimization. HI-TECH compilers come with
several different optimizer stages. The code generator is responsible for global optimization which can
be enabled using a -Zg (global optimization) option. This optimization is performed on the parsed
source. Amongst other things, this optimization stage allocates variables to registers whenever possible
and looks for constants that are used consecutively in source code to avoid reloading these values
unnecessarily
Assembly files are the first files in the compilation process that make reference to psects, or program
sections. The code generator will generate the psect directives in which code and data will be positioned.
The output of the code generator is then passed to the assembler which converts the ASCII
representation of the processor instructions - the ASCII mnemonics - to binary machine code. The
assembler is specific for each compiler and has a processor-dependent name such as ASPIC or ASXA.
Assembler code also contains assembler directives which will be executed by the assembler. Some of
these directives are to define ROM-based constants, others define psects and others declare global
symbols.
The assembler is optionally preceded by an optimization of the generated assembler. This is the peephole
optimization. With some HI-TECH compilers the peephole optimizer is contained in the assembler
itself, e.g. the PIC assembler, however others have a separate optimization application which is run
before the assembler is executed, e.g. OPT51. Peephole optimization is carried out separately over the
assembler code derived from each single function.
In addition to the peephole optimizer, the assembler itself may include a separate assembler optimizer
step which attempts to replace long branches with short branches where possible. The -O option enables
both assembler optimizers, even if they are performed by separate applications, however HPD includes
menu items for both optimizer stages (Peephole optimization and Assembler optimization). If the
peephole optimizer is part of the assembler, the assembler optimization item in HPD has no effect.
The output of the assembler is an object file. An object file is a formatted binary file which contains
machine code, data and other information relating to the module from which it has been generated.
2 Object files come in two basic types: relocatable and absolute object files. Although both contain
machine code in binary form, relocatable object files have not had their addresses resolved to be absolute
values. The binary machine code is stored as a block for each psect. Any addresses in this area are
temporarily stored as 00h. Separate relocation information in the object file indicates where these
unresolved addresses lie in the psect and what they represent. Object files also contain information
regarding any psects that are defined within so that the linker may position these correctly.
Object files produced by the assembler follow a format which is standard for all HI-TECH compilers,
but obviously their contents are machine specific. Table 2 - 8 on page 26 shows several sections of the
HI-TECH format relocatable object file that has been converted to ASCII for presentation using the
DUMP executable which comes with the compiler. The highlighted source line is represented by the
highlighted machine code in the object file. This code is positioned in a psect called text. The
underlined bytes in the object file are addresses that as yet are unknown and have been replaced with
zeros. The lines after the text psect in the object file show the information used to resolve the addresses
needed by the linker. The two bytes starting at offset 2 and the two single bytes at offset 9 and 10 are
represented here and as can be seen, their address will be contained at an address derived from the
position of the data and bss psects, respectively..
26
Overview of the compilation process
If a -ASMLIST (Generate assemble listing) option was specified, the assembler will generate an
assembler listing file which contains both the original C source lines and the assembler code that was
generated for each line. The assembler listing output is shown in Table 2 - 9 on page 27. Unresolved
addresses are listed as being zero with unresolved-address markers "’" and "*" used to indicate that the
values are not absolute. Note that code is placed starting from address zero in the new text psect. The
entire psect will be relocated by the linker..
Some HI-TECH assemblers also generate a relocatable listing file (extension: .rlf).3 This contains
address information which can be read by the linker and used to update the assembler listing file, if such
a file was created. After linking, the assembler listing file will have unresolved addresses and address
markers removed and replaced with their final absolute addresses
The above series of steps: pre-processing, parsing, code generation and assembly, are carried out for
each C source file passed to the driver in turn. Errors in the code are reported as they are detected. If a
file cannot be compiled due to an error, the driver halts compilation of that module after the application
that generated the error completes and continues with the next file which was passed to it, starting again
with the CLIST application.
For any assembler files passed to the driver, these do not require as much processing as C source files,
but they must be assembled. The compiler driver will pass any .as files straight to the assembler. If the
user specifies the -P (Pre-process assembler files) the assembler files are first run through the C
preprocessor allowing the using of all preprocessor directives within assembly code. The output of the
preprocessor is then passed to the assembler.
Object and library files passed to the compiler are already compiled and are not processed at all by the
first stages of the compiler. They are not used until the link stage which is explained below.
3. The generation of this file is not shown in Figure 2 - 1 on page 19 in the interests of clarity.
If you are using HPD, dependency information can be saved regarding each source and header file by
clicking the save dependency information switch. When enabled, the HPD driver determines only
which files in the project need be re-compiled from the modification dates of the input source files. If
the source file has not been changed, the existing object file is used.
2.1.2.2 The link stage
The format of relocatable object files are again processor-independent so the linker and other
applications discussed below are common across the whole range of HI-TECH compilers. The linker's
2 name is HLINK.4
The tasks of the linker are many. The linker is responsible for combining all the object and library files
into a single file. The files operated on by the linker include all the object files compiled from the input
C source files and assembler files, plus any object files or library files passed to the compiler driver, plus
any run-time object files and library files that the driver supplies. The linker also performs grouping and
relocation of the psects contained in all of the files passed to it, using a relatively complex set of linker
options. The linker also resolves symbol names to be absolute addresses after relocation has made it
possible to determine where objects are to be stored in ROM or RAM. The linker then adjusts references
to these symbols - a process known as address fixup. If the symbol address turns out to be too large to
fit into the space in the instruction generated by the code generator, a fixup overflow error occurs.
For example, if the address of the symbol _b in our running example was determined to be 20000h, the
linker would not be able to fit this address into the first underlined two byte "hole" in the object file
shown dumped in the Table Assembler output on page 26 since 20000h is larger than two bytes long.
The linker can also generate a map file which has detailed information regarding the position of the
psects and the addresses assigned to symbols. The linker may also produce a symbol file. These files
have a .sym extension and are generated when the -G (Source level debug info) option is used. This
symbol file is ASCII-based and contains information for the entire program. Addresses are absolute as
this file is generated after the link stage.
Although the object file produced by HLINK contains all the information necessary to run the program,
the program has to be somehow transferred from the host computer to the embedded hardware. There
are a number of standard formats that have been created for such a task. Emulators and chip
programmers often can accept a number of these formats. The Motorola HEX (S record) or Intel HEX
formats are common formats. These are ASCII formats allowing easy viewing by any text editor. They
include checksum information which can be used by the program which downloads the file to ensure that
it was transmitted without error. These formats include address information which allows those areas
which do not contain data to be omitted from the file. This can make these files significantly smaller
than, for example, a binary file.
28
Psects and the linker
The OBJTOHEX application is responsible for producing the output file requested by the user. It takes the
absolute object file produced by the linker and produces an output under the direction of the compiler
driver. The OBJTOHEX application can produce a variety of different formats to satisfy most
development systems. The output types available with most HI-TECH compilers are shown in Table 2
- 10.
In some circumstances, more than one output file is required. In this case an application called
CROMWELL, the reformatter, is executed to produce further output files. For example it is commonly used
with the PIC compiler to read in the HEX file and the SYM file and produce a COD file.
that represent the C source; others contain assembler directives that reserve space for variables in RAM;
others containing ROM-based constants that have been defined in the C source; and others which hold
data for special objects such as variables to be placed in non-volatile areas, interrupt vectors and
configuration words used by the processor. Since there can be more than one input source file there will
be similar sections of assembler spread over multiple assembler files which need to be grouped together
after all the code generation is complete.
These different sections of assembler need to be grouped in special ways: It makes sense to have all the
2 initialised data values together in contiguous blocks so they can be copied to RAM in one block move
rather than having them scattered in-between sections of code; the same applies to uninitialised global
objects which have to be allocated a space which is then cleared before the program starts; some code
or objects have to be positioned in certain areas of memory to conform to requirements in the processor’s
addressing capability; and at times the user needs to be able to position code or data at specific absolute
addresses to meet special software requirements. The code generator must therefore include information
which indicates how the different assembler sections should be handled and positioned by the linker later
in the compilation process.
The method used by the HI-TECH compiler to group and position different parts of a program is to place
all assembler instructions and directives into individual, relocatable sections. These sections of a
program are known as psects - short for program sections. The linker is then passed a series of options
which indicate the memory that is available on the target system and how all the psects in the program
should be positioned in this memory space.
2.2.1.1 The psect directive
The PSECT assembler directives (generated by the code generator or manually included in other
assembly files) define a new psect. The general form of this directive is shown below.
PSECT name,option,option...
It consists of the token PSECT followed by the name by which this psect shall be referred. The name can
be any valid assembler identifier and does not have to be unique. That is, you may have several psects
with the same name, even in the same file. As will be discussed presently, psects with the same name
are usually grouped together by the linker.
The directive options are described in the assembler section of the manual, but several of these will be
discussed in this tutorial. The options are instructions to the linker which describe how the psect should
be grouped and relocated in the final absolute object file.
Psects which all have the same name imply that their content is similar and that they should be grouped
and linked together in the same way. This allows you to place objects together in memory even if they
are defined in different files.
30
Psects and the linker
After a psect has been defined, the options may be omitted in subsequent psect directives in the same
module that use the same name. The following example shows two psects being defined and filled with
code and data.
PSECT text,global
begin:
mov R0,#10
mov R2,r4
add R2,#8 2
PSECT data
input:
DS 8
PSECT text
next:
mov r4,r2
rrc r4
In this example, the psect text is defined including an option to say that this is a global psect. Three
assembler instructions are placed into this psect. Another psect is created: data. This psect reserves 8
bytes of storage space for data in RAM. The last psect directive will continue adding to the first psect.
The options were omitted from the second PSECT directive in this example as there has already been a
psect directive in this file that defines the options for a psect of this name. The above example will
generate two psects. Other assembler files in the program may also create psects which have the same
name as those here. These will be grouped with the above by the linker in accordance with the PSECT
directive flags.
2.2.1.2 Psect types
Psects can be linked in three different ways: those that will reside permanently in ROM5; those that will
be allocated space in RAM after the program starts; and those that will reside in ROM, but which will
be copied into another reserved space in RAM after the program starts. A combination of code - known
as the run-time startup code - and psect and linker options allow all this to happen.
Typically, psects placed into ROM contain instructions and constant data that cannot be modified. Those
psects allocated space in RAM only are for global data objects that do not have to assume any non-zero
value when the program starts, i.e. they are uninitialised. Those psects that have both a ROM image and
space reserved in RAM are for modifiable, global data objects which are initialised, that is they contain
some specific value when the program begins, but that value can be changed by the program during its
execution.
Objects that are initialised are usually placed into psects with the name "data" or a name based on "data".
Variables that are qualified near typically use the psect rdata. The PIC data psects use names like
rdata_0 to indicates that they are "near" (there is no near qualifier on the PIC - essentially all PIC
objects are near by default) and the digit indicates a bank number.
Uninitialised objects are placed in psects whose name is "bss" or a name based on "bss". Again, rbss
would indicate uninitialised objects that are near. The PIC compiler uses names like rbss_0, where the
digit is a bank number. The abreviation "bss" stands for block started by symbol and was a assembler
2 pseudo-op used in IBM systems back in the days when computers were coal-fired. The continued usage
of this term is still appropriate as there are some similarities in the way these schemes worked.
The following C source shows two objects being defined. The object input will be placed into a data
psect; the value 22 will reside in ROM and be copied to the RAM space allocated for input by the run-
time code. The object output will not contribute directly to the ROM image. A an area of memory will
be reserved for it in RAM and this area will be cleared by the run-time code (output will be assigned
the value 0).
int input = 22; // an initialised object
int output; // an uninitialised object
Snippets from the assembler listing file show how the XA compiler handles these two objects. Other
compilers may produce differently structured code. The PSECT directive flags are discussed presently,
but note that for the initialised object, input, the code generator used a DW (define word) directive which
placed the two bytes of the int value (16 and 00) into the output which is destined for the ROM. Two
bytes of storage were reserved using the DS assembler directive for the uninitialised object, output, and
no values appear in the output.
1 0000' PSECT data,class=CODE,space=0,align=0
2 GLOBAL _input
3 ALIGN.W
4 0000' _input:
5 0000' 16 00 DW 22
32
Linking the psects
Auto variables and function parameters are local to the function in which they are defined and are
handled differently by the compilers. They may be allocated space dynamically (for example on the
stack) in which case they are not stored in psects by the compiler. Compilers or memory models which
do not use a hardware stack, use a compiled stack which is an area of memory set aside for the auto and
parameter objects for each function. These object will be positioned in a psect. The psect in which they
are allocated is defined by a FNCONF directive which is placed in the run-time startup code.
Two addresses are used to refer to the location of a psect: the link address and the load address. The link
address is the address at which the psect (and any objects or labels within the psect) can be accessed
whilst the program is executing. The load address is the address at which the psect will reside in the
2
output file that creates the ROM image, or, alternatively, the address of where the psect can be accessed
in ROM.
For the psect types that reside in ROM their link and load address will be the same, as they are never
copied to a new location. Psects that are allocated space in RAM only will have a link address, but a load
address is not applicable. They are assigned a load address, and you will see it listed in the map file, but
it is not used. The compiler usually makes the load address of these psects the same as the link address.
Since no ROM image of these psects is formed, the load address is meaningless and can be ignored. Any
access to objects defined in these psects is performed using the link address. The psects that reside in
ROM, but are copied to RAM have link and load addresses that are usually different. Any references to
symbols or labels in these psects are always made using the link addresses.
6. The assembler does not modify PSECT directives in any way other than encoding the details of each into the object
file.
34
Linking the psects
7. The -A option on the PIC compiler serves a different purpose. Most PIC devices only have internal memory and so
a memory option is not required by the compiler. High-end PICs may have external memory, this is indicated to the
compiler by using a -ROM option to the CLD or by the RAM & ROM addresses... dialogue box under HPDPIC.
The -A option is used to shift the entire ROM image, when using highend devices.
8. Some processors may require word alignment gaps between code or data. These gaps can be handled by the com-
piler, but are not considered here.
The linker will relocate the grouped text psect so that it starts at address C000h. The linker will then
define two global symbols with names: __Ltext and __Htext and equate these with the values: C000h
and C350h which are the start and end addresses, respectively, of the text psect group.
Now let us assume that the run-time file and one of the programmer's files contains interrupt vectors.
These vectors must be positioned at the correct location for this processor. Our fictitious processor
expects its vectors to be present between locations FFC0h and FFFFh. The reset vector takes up two
bytes at address FFFEh an FFFFh, and the remaining locations are for peripheral interrupt vectors. The
2 run-time code usually defines the reset vector using code like the following.
GLOBAL start
PSECT vectors,ovlrd
ORG 3Eh
DW start
This assembler code creates a new psect which is called vectors. This psect uses the overlaid flag
(ovlrd) which tells the linker that any other psects with this name should be overlaid with this one, not
concatenated with it. Since the psect defaults to being global, even vectors psects in other files will
be grouped with this one. The ORG directive tells the assembler to advance 3Eh locations into this psect.
It does not tell the assembler to place the object at address 3Eh. The final destination of the vector is
determined by the relocation of the psect performed by the linker later in the compilation process. The
assembler directive DW is used to actually place a word at this location. The word is the address of the
(global) symbol start. (start or powerup are the labels commonly associated with the beginning of
the run-time code.)
In one of the user's source files, the macro ROM_VECTOR() has been used to supply one of the peripheral
interrupts at offset 10h into the vector area. The macro expands to the following in-line assembler code.
GLOBAL _timer_isr
PSECT vectors,ovlrd
ORG 10h
DW _timer_isr
After the first stages of the compilation have been completed, the linker will group together all the
vectors psects it finds in all the object files, but they will all start from the same address, i.e. they are
all placed one over the other. The final vectors psect group will contain a word at offset 10h and another
at offset 3Eh. The space from 0h to offset 0Fh and in-between the two vectors is left untouched by the
linker. The linker options required to position this psect would be:
-pvectors=0FFC0h
The address given with this option is the base address of the vectors area. The ORG directives used to
move within the vectors psects hence were with respect to this base address.
36
Linking the psects
Both the user's files contain constants that are to be positioned into ROM. The code generator generates
the following PSECT directive which defines the psect in which it store the values.
PSECT const
The linker will group all these const psects together and they can be simply placed like the text psects.
The only problem is: where? At the moment the text psects end at address C34Fh so we could position
the const psects immediately after this at address C350h, but if we modify the program, we will have to
continually adjust the linker options. Fortunately we can issue a linker option like the following.
2
-ptext=0C000h,const
We have not specified an address for the psect const, so it defaults to being the address immediately
after the end of the preceding psect listed in the option, i.e. the address immediately after the end of the
text psect. Again, the const psect resides in ROM only, so this one address specifies both the link and
load addresses.
Now the RAM psects. The user's object files contain uninitialised data objects. The code generator
generates bss psects in which are used to hold the values stored by the uninitialised C objects. The area
of memory assigned to the bss psect will have to be cleared before main() is executed.
At link time, all bss psects are grouped and concatenated. The psect group is to be positioned at the
beginning of RAM. This is easily done via the following option.
-pbss=0h
The address 0h is the psect's link address. The load address is meaningless, but will default to the link
address. The run-time code will clear the area of memory taken up by the bss psect. This code will use
the symbols __Lbss and __Hbss to determine the starting address and the length of the area that has to
be cleared.
Both the user's source files contain initialised objects like the following.
int init = 34;
The value 34 has to be loaded into the object init before the main() starts execution. Another of the
tasks of the run-time code is to initialise these sorts of objects. This implies that the initial values must
be stored in ROM for use by the run-time code. But the object is a variable that can be written to, so it
must be present in RAM once the program is running. The run-time code must then copy the initialised
values from ROM into RAM just before main() begins. The linker will place all the initial values into
ROM in exactly the same order as they will appear in RAM so that the run-time code can simply copy
the values from ROM to RAM as a single block. The linker has to be told where in ROM these values
should reside as it generates the ROM image, but is must also know where in RAM these objects will
be copied to so that it can leave enough room for them and resolve the run-time addresses for symbols
in this area.
The complete linker options for our program, including the positioning of the data psects, might look
like:
-ptext=0C000h,const
-pvectors=0FFC0h
-pbss=0h,data/const
That is, the data psect should be positioned after the end of the bss psect in RAM. The address after the
2 slash indicates that this psect will be copied from ROM and that its position in ROM should be
immediately after the end of the const psect. As with other psects, the linker will define symbols
__Ldata and __Hdata for this psect, which are the start and end link addresses, respectively, that will
be used by the run-time code to copy the data psect group. However with any psects that have different
link and load addresses, another symbol is also defined, in this case: __Bdata. This is the load address
in ROM of the data psect.
2.3.3.2 Exceptional cases
The PIC compiler handles the data psects in a slightly different manner. It actually defines two separate
psects: one for the ROM image of the data psects; the other for the copy in RAM. This is because the
length of the ROM image is different to the length of the psect in RAM. (The ROM is wider than the
RAM and values stored in ROM may be encoded as retlw instructions.) Other compilers may also
operate this way if ROM and RAM are in different memory spaces. The linker options in this case will
contain two separate entries for both psects instead of the one psect with different link and load addresses
specified. The names of the data psects in RAM for the PIC compiler will be similar to rdata_0; those
in ROM are like idata_0. The digit refers to a PIC RAM bank number.
The link and load addresses reported for psects that contain objects of type bit have slightly different
meaning to ordinary link and load addresses. In the map file, the link address listed is the link address
of the psect specified as a bit address. The load address is the link address specified as a byte address.
Bit objects cannot be initialised, so separate link and load addresses are not required. The linker knows
to handle these psects differently because of the bit psect flag. Bit psects will be reported in the map
file as having a scale value of 8. This relates to the number of objects that can be positioned in an
addressable unit.
2.3.3.3 Psect classes
Now let us assume that more ROM is added to our system since the programmers have been busy and
filled the 16 kB currently available. Several peripheral devices were placed in the area from B000h to
BFFFh so the additional ROM is added below this from 7000h to AFFFh. Now there are two separate
areas of ROM and we can no longer give a single address for the text psects.
What we can now do to take advantage of this extra memory is define several ranges of addresses that
can be used by ROM-based psects. This can be done by creating a psect class. There are several ways
that psects can be linked when using classes. Classes are commonly used by HI-TECH C compilers to
38
Linking the psects
position the code or text psects. Different strategies are employed by different compilers to better suit
the processor architecture for which the compilation is taking place. Some of these schemes are
discussed below. If you intend to modify the default linker options or generate your own psects, check
the linker options and PSECT directives generated by the code generator for the specific compiler you
are using.
A class can be defined using another linker option. For example to use the additional memory added to
our system we could define a class using the linker option:
-ACODE=7000h-AFFFh,C000h-FFFFh 2
The option is a -A immediately followed by the class name and then a comma-separated list of address
ranges. Remember this is an option to the linker, not the CLD. The above example defines two address
ranges for a class called CODE.
Here is how drivers for the 8051, 8051XA and Z80 compilers define the options passed to the linker to
handle the class CODE psects. In large model the 8051 psect definitions for psects that contain code are
as follows.
PSECT text,class=CODE
The class psect flag specifies that the psect text is a member of the class called CODE.
If a single ROM space has been specified by either not using the -ROM option with the CLD or by
selecting single ROM in the ROM & RAM addresses dialogue box under HPD, no class is defined
and the psects are linked using a -p option as we have been doing above. Having the psects within a
class, but not having that class defined is acceptable, provided that there is a -p option to explicitly
position the psects after they have been grouped. If there is no class defined and no -p option a default
memory address is used which will almost certainly be inappropriate.
If multiple ROM spaces have been specified by using either the -ROMranges option with the CLD, or
specifying the address ranges in the ROM & RAM addresses (after selecting the multiple ROMs
button) dialogue box under HPD, a class is defined by the driver using the -A linker option similar to
that shown above and the -p option is omitted from the options passed to the linker.
The PIC compiler does things a little differently as it has to contend with multiple ROM pages that are
quite small. The PIC code generator defines the psects which hold code like the following.
PSECT text0,local,class=CODE,delta=2
The delta value relates to the ROM width and need not be considered here. The psects are placed in
the CODE class, but note that the they are made local using the local psect flag. The psects that are
generated from C functions each have unique names which proceed: text0, text1, text2 etc. Local
psects are not grouped across modules, i.e. if there are two modules, each containing a local psect of
the same name, they are treated are separate psects. Local psects cannot be positioned using a -p linker
option as there can be more than one psect with that name. Local psects must be made members of a
class, and the class defined using a -A linker option. The PIC works in this way to assist with the
placement of the code in its ROM pages. This is discussed further in Section 2.3.4 on page 42.
A few general rules apply when using classes: If, for example, you wanted to place a psect that is not
already in a class into the memory that a class occupies, you can replace an address or psect name in a
linker -p option with a class name. For instance, in the generic example discussed above, the const
psect was placed after the text psect in memory. If you would now like this psect to be positioned in the
2 memory assigned to the CODE class the following linker options could be used.
-pconst=CODE
-pvectors=0FFC0h
-pbss=0h,data/CODE
-ACODE=7000h-AFFFh,C000h-FFFFh
Note also that the data psect’s load location has been swapped from after the end of the const psect to
within the memory assigned to the CODE class to illustrate that the load address can be specified using
the class name.
Another class definition that is sometimes seen in PIC linker options specifies three addresses for each
memory range. Such an option might look something like:
-AENTRY=0h-FFh-1FFh
The first range specifies the address range in which the psect must start. The psects are allowed to
continue past the second address as long as they do not extend past the last address. For the example
above, all psects that are in the ENTRY class must start at addresses between 0 and FFh. The psects must
end before address 1FFh. No psect may be positioned so that its starting address lies between 100h and
1FFh. The linker may, for example, position two psects in this range: the first spanning addresses 0 to
4Fh and the second starting at 50h and finishing at 138h. Such linker options are useful on some PIC
processors (typically baseline PICs) for code psects that have to be accessible to instructions that modify
the program counter. These instructions can only access the first half of each ROM page.
2.3.3.4 User-defined psects
Let us assume now that the programmer wants to include a special initialised C object that has to be
placed at a specific address in memory, i.e. it cannot just be placed into, and linked with, the data psect.
In a separate source file the programmer places the following code.
#pragma psect data=lut
int lookuptable[] = {0, 2, 4, 7, 10, 13, 17, 21, 25};
The pragma basically says, from here onwards in this module, anything that would normally go into the
data psect should be positioned into a new psect called lut. Since the array is initialised, it would
40
Linking the psects
normally be placed into data and so it will be re-directed to the new psect. The psect lut will inherit
any psect options (defined by the PSECT directive flags) which applied to data.
The array is to be positioned in RAM at address 500h. The -p option above could be modified to include
this psect as well.
-pbss=0h,data/const,lut=500h/
(The load address of the data psect has been returned to its previous setting.) The addresses for this psect
are given as 500h/. The address 500h specifies the psect’s link address. The load address can be 2
anywhere, but it is desirable to concatenate it to existing psects in ROM. If the link address is not
followed by a load address at all, then the link and load addresses would be set to be the same, in this
case 500h. The "/", which is not followed by an address, tells the linker that the load address should be
immediately after the end of the previous psect’s load address in the linker options. In this case that is
the data psect’s load address, which in turn was placed after the const psect. So, in ROM will be placed
the const, data and lut psects, in that order.
Since this is an initialised data psect, it is positioned in ROM and must be copied to the memory reserved
for it in RAM. Although different link and load addresses have been specified with the linker option, the
programmer will have to supply the code that actually performs the copy from ROM to RAM. (The data
psects normally created by the code generator have code already supplied in the run-time file to copy
the psects.) The following is C code which could perform the copy.
extern unsigned char *_Llut, *_Hlut, *_Blut;
unsigned char *i;
void copy_my_psect(void)
{
for(i=_Llut; i<_Hlut; i++, _Blut++)
*i = *_Blut;
}
Note that to access the symbols __Llut etc. from within C code, the first underscore character is
dropped. These symbols hold the addresses of psects, so they are declared (not defined) as pointer
objects in the C code using the extern qualifier. Remember that the object lookuptable will not be
initialised until this C function has been called and executed. Reading from the array before it is
initialized will return incorrect values.
If you wish to have initialised objects copied to RAM before main() is executed, you can write
assembler code, or copy and modify the appropriate routine in the run-time code that is supplied with
the compiler. You can create you own run-time object file by pre-compiling the modified run-time file
and using this object file instead of the standard file that is automatically linked with user’s programs.
From assembler, both the underscore characters are required when accessing the psect address symbols.
If you define your own psect based on a bss psect, then, in the same way, you will have to supply code
to clear this area of memory if you are to assume that the objects defined within the psect will be cleared
when they are first used.
2.3.4 Issues when linking
2 The linker uses a relatively complicated algorithm to relocate the psects contained in the object and
library files passed to it, but the linker needs more information than that discussed above to know exactly
how to relocate each psect. This information is contained in other linker options passed to the linker by
the driver and in the psect flags which are used with each PSECT directive. The following explain some
of the issues the linker must take into account.
2.3.4.1 Paged memory
Let’s assume that a processor has two ROM areas in which to place code and constant data. The linker
will never split a psect over any memory boundary. A memory boundary is assumed to exist wherever
there is a discontinuity in the address passed to the linker in the linker options. For example, if a class is
specified using the addresses as follows:
-ADATA=0h-FFh,100h-1FFh
It is assumed that some boundary exists between address FFh and 100h, even though these addresses are
contiguous. This is why you will see contiguous address ranges specified like this, instead of having one
range covering the entire memory space. To make it easy to specify similar contiguous address ranges,
a repeat count can be used, like:
-ADATA=0h-FFhx2
can be used; in this example, two ranges are specified: 0 to FFh and then 100h to 1FFh. Some processors
have memory pages or banks. Again, a psect will not straddle a bank or page boundary.
Given that psects cannot be split over boundaries, having large psects can be a problem to relocate. If
there are two, 1 kB areas of memory and the linker has to position a single 1.8 kB psect in this space, it
will not be able to perform this relocation, even though the size of the psect is smaller than the total
amount of memory available. The larger the psects, the more difficult it is for the linker to position them.
If the above psect was split into three 0.6 kB psects, the linker could position two of them - one in each
memory area - but the third would still not fit in the remaining space in either area. When writing code
for processors like the PIC, which place the code generated from each C function into a separate, local
psect, functions should not become too long.
If the linker cannot position a psect, it generates a Can’t find space for psect xxxx error, where
xxxx is the name of the psect. Remember that the linker relocates psects so it will not report memory
42
Linking the psects
errors with specific C functions or data objects. Search the assembler listing file to identify which C
function is associated with the psect that is reported in the error message if local psects are generated by
the code generator.
Global psects that are not overlaid are concatenated to form a single psect by the linker before relocation
takes place. There are instances where this grouped psect appears to be split again to place it in memory.
Such instances occur when the psect class within which it is a member covers several address ranges and
the grouped psect is too large to fit any of the ranges. The linker may use intermediate groupings of the
psects, called clutches to facilitate relocation within class address ranges. Clutches are in no way
controllable by the programmer and a complete understanding of there nature is not required to able to
2
understand or use the linker options. It is suffice to say that global psects can still use the address ranges
within a class. Note that although a grouped psect can be comprised of several clutches, an individual
psect defined in a module can never be split under any circumstances.
2.3.4.2 Separate memory areas
Another issue faced by the linker is this: On some processors, there are distinct memory areas for
program and data, i.e. Harvard architecture chips like the XA. For example, ROM may extend from 0h
- FFFFh and separate RAM may extend from 0h - 7FFh. If the linker is asked to position a psect at
address 100h via a -p option, how does the linker know whether this is an address in program memory
or in the data space? The linker makes use of the space psect flag to determine this. Different areas are
assigned a different space value. For example ROM may be assigned a space value of 0 and RAM a
space flag of 1. The space flags for each psect are shown in the map file.
The space flag is not used when the linker can distinguish the destination area of an object from its
address. Some processors use memory banks which, from the processors’s point of view, cover the same
range of addresses, but which are within the same distinct memory area. In these cases, the compiler will
assign unique addresses to objects in banked areas. For example, some PIC processors can access four
banks of RAM, each bank covering addresses 0 to 7Fh. The compiler will assign objects in the first bank
(bank 0) addresses 0 to 7Fh; objects in the second bank: 80h to FFh; objects in the third bank: 100h to
17Fh etc. This extra bank information is removed from the address before it is used in an assembler
instruction. All PIC RAM banks use a space flag of 1, but the ROM area on the PIC is entirely separate
and uses a different space flag (0). The space flag is not relevant to psects which reside in both memory
areas, such as the data psects which are copied from ROM to RAM.
After relocation is complete, the linker will group psects together to form a segment. Segments, along
with clutches, are rarely mentioned with the HI-TECH compiler simply because they are an abstract
object used only by the linker during its operation. Segment details will appear in the map file. A
segment is a collection of psects that are contiguous and which are destined for a specific area in
memory. The name of a segment is derived from the name of the first psect that appears in the segment
and should not be confused with the psect which has that name.
2
unsigned char DDRA @ 0x200;
When the code generator makes reference to the object DDRA, instead of using a symbol in the generated
assembler code which will later be replaced with the object’s address after psect relocation, it will
immediately use the value 200h. The important thing to realise is that the instructions in the assembler
that access this object will not have any symbols that need to be resolved, and so the linker will simply
skip over them as they are already complete. If the linker has also been told, via its linker options, that
there is memory available at address 200h for RAM objects, it may very well position a psect such that
an object that resides in this psect also uses address 200h. As there is no symbol associated with the
absolute object, the linker will not see that two objects are sharing the same memory. If objects are
overlapping, the program will most likely fail unpredictably.
When positioning objects at absolute address, it vital to ensure that the linker will not position objects
over those defined as absolute. Absolute objects are intended for C objects that are mapped over the top
of hardware registers to allow the registers to be easily access from the C source code. The programmer
must ensure that the linker options do not specify that there is any general-purpose RAM in the memory
space taken up by the hardware. Ordinary variables to be positioned at absolute addresses should be
done so using a separate psect (by simply defining your own using a PSECT directive in assembler code,
or by using the #pragma psect directive in C code) and linker option to position the objects. If you
must use an absolute address for an object in general-purpose RAM, make sure that the linker options
are modified so that the linker will not position other psects in this area.
2.3.5 Modifying the linker options
In most applications, the default linker options do not need to be modified. It is recommended that if you
think the options should be modified, but you do not understand how the linker options work, that you
seek technical assistance in regard to the problem at hand.
If you do need to modify the linker options, there are several ways to do this. If you are simply adding
another option to those present by default, the option can be specified to the CLD using a -L option. To
position the lut psect that was used in the earlier example, the following option could be used.
-L-plut=500/const
The -L simply passes whatever follows to the linker. If you want to add another option to the default
linker options and you are using HPD and a project, then it is a simple case of opening the linker
44
Linking the psects
options... dialogue box and adding the option to the end of those already there. The options should be
entered exactly as they should be presented to the linker, i.e. you do not need the -L at the front.
If you want to modify existing linker options, other than simply changing the memory address that are
specified with the -A CLD option, then you cannot use the CLD to do this directly. What you will need
to do is to perform the compilation and link separately. Let’s say that the file main.c and extra.c are
to be compiled for the 8051 with modified linker options. First we can compile up to, but not include,
the link stage by using a command line something like this.
c51 -O -Zg -ASMLIST -C main.c extra.c 2
The -C options stops the compilation before the link stage. Include any other options which are normally
required. This will create two object files: main.obj and extra.obj, which then have to be linked
together.
Run the CLD again in verbose mode by giving a -V option on the command line, passing it the names
of the object files created above, and redirect the output to a file. For example:
c51 -V -A8000,0,100,0,0 main.obj extra.obj > main.lnk
Note that if you do not give the -A CLD option, the compiler will prompt you for the memory addresses,
but with the output redirected, you will not see the prompts.
The file generated (main.lnk) will contain the command line that CLD generated to run the linker with
the memory values specified using the -A option. Edit this file and remove any messages printed by the
compiler. Remove the command line for any applications run after the link stage, for example OBJTOHEX
or CROMWELL, although you should take note of what these command lines are as you will need to run
these applications manually after the link stage. The linker command line is typically very long and if a
DOS batch file is used to perform the link stage, it is limited to lines 128 characters long. Instead the
linker can be passed a command file which contains the linker options only. Break up the linker
command line in the file you have created by inserting backslash characters "\" followed by a return.
Also remove the name and path of the linker executable from the beginning of the command line so that
only the options remain. The above command line generated a main.lnk file that was then edited as
suggested above to give the following.
-z -pvectors=08000h,text,code,data,const,strings \
-prbit=0/20h,rbss,rdata/strings,irdata,idata/rbss \
-pbss=0100h/idata -pnvram=bss,heap -ol.obj \
-m/tmp/06206eaa /usr/hitech/lib/rt51--ns.obj main.obj \
extra.obj /usr/hitech/lib/51--nsc.lib
Now, with care, modify the linker options in this file as required by your application.
Now perform the link stage by running the linker directly and redirecting its arguments from the
command file you have created.
hlink < main.lnk
This will create an output file called l.obj. If other applications were run after the link stage, you will
need to run them to generate the correct output file format, for example a HEX file.
Modifying the options to HPD is much simpler. Again, simply open the linker options... dialogue box
2 and make the required changes, using the buttons at the bottom of the box to help with the editing. Save
and re-make your project.
The map file will contain the command line actually passed to the linker and this can be checked to
confirm that the linker ran with the new options.
46
PICC18 Command Line Driver
PICC18 is the driver invoked from the DOS command line to compile and/or link C programs. PICC18
has the following basic command format:
PICC18 [options] files [libraries]
It is conventional to supply the options (identified by a leading dash “-”) before the filenames, but in
3
fact this is not essential.
The options are discussed below. The files may be a mixture of source files (C or assembler) and object
files. The order of the files is not important, except that it will affect the order in which code or data
appears in memory. Libraries are a list of library names, or -L options (see page 58). Source files, object
files and library files are distinguished by PICC18 solely by the file type or extension. Recognized file
types are listed in Table 4 - 1. This means, for example, that an assembler file must always have a .as
extension (alphabetic case is not important).
PICC18 will check each file argument and perform appropriate actions. C files will be compiled;
assembler files will be assembled. At the end, unless suppressed by one of the options discussed later,
all object files resulting from compilation or assembly, or those listed explicitly on the command line,
will be linked together with the standard run-time code and libraries and any user-specified libraries.
Functions in libraries will be linked into the resulting output file only if referenced in the source code.
Invoking PICC18 with only object files specified as the file arguments (i.e. no source files) will mean
only the link stage is performed. It is typical in Makefiles to use PICC18 with a -C option to compile
several source files to object files, then to create the final program by invoking PICC18 again with only
the generated object files and appropriate libraries (and appropriate options).
3 If this was in the file xyz.cmd then PICC18 would be invoked as:
PICC18 < xyz.cmd
Since no command line arguments were supplied, PICC18 will read xyz.cmd for its command line.
The command file may also be read by using the @ symbol. For example:
PICC18 @xyz.cmd
48
PICC18 Compiler Options
Option Meaning
-processor Define the processor
-AAHEX Generate an American Automation symbolic HEX file
-ASMLIST Generate assembler .LST file for each compilation
-Aaddress Specify offset for ROM code
-A-option Specify -option to be passed directly to the assembler
-BIN Generate a Binary output file
-Bl Selects large memory model
-Bs Selects small memory model
-C Compile to object files only
3 -CKfile
-CP16
Make OBJTOHEX use a checksum file
Use 16-bit wide pointers to program space
-CP24 Use 24-bit wide pointers to program space
-CRfile Generate cross-reference listing
-D24 Use truncated 24-bit floating point format for doubles
-D32 Use IEEE754 32-bit floating point format for doubles
-Dmacro Define preprocessor macro
-E Define format for compiler errors
-Efile Redirect compiler errors to a file
-E+file Append errors to a file
-FAKELOCAL Produce MPLAB-specific debug information
-FDOUBLE Enables the use of faster 32-bit floating point math routines
-Gfile Generate enhanced source-level symbol table
-HELP Print summary of options
-ICD Compile code for MPLAB-ICD
-Ipath Specify a directory pathname for include files
-INTEL Generate an Intel HEX format output file (default)
-Llibrary Specify a library to be scanned by the linker
-L-option Specify -option to be passed directly to the linker
-Mfile Request generation of a MAP file
-MOT Generate a Motorola S1/S9 HEX format output file
-MPLAB Specify compilation and debugging under MPLAB IDE
-Nsize Specify identifier length
-NODEL Do not remove temporary/intermediate files
-NOERRATA Disable errata-fix modifications of the output code
-NORT Do not link standard runtime module
-O Enable post-pass optimization
-Ofile Specify output filename
50
PICC18 Compiler Options
Option Meaning
-O-option Specify -option to be passed to objtohex
-P Preprocess assembler files
-PRE Produce preprocessed source files
-PROTO Generate function prototype information
-PSECTMAP Display complete memory segment usage after linking
-q Specify quiet mode
-RESRAMranges Reserve the specified RAM address ranges.
-RESROMranges Reserve the specified ROM address ranges.
-ROMranges Specify program space memory for ROM-less devices
-S
-SIGNED_CHAR
Compile to assembler source files only
Make the default char signed. 3
-STRICT Enable strict ANSI keyword conformance
-TEK Generate a Tektronix HEX format output file
-Usymbol Undefine a predefined preprocessor symbol
-UBROF Generate an UBROF format output file
-V Verbose: display compiler pass command lines
-Wlevel Set compiler warning level
-Wlevel! Set compiler warning level and stop on warnings
-X Eliminate local symbols from symbol table
-XDATArange Specify an address range for external data memory
-Zg[level] Enable global optimization in the code generator
(extension) of .lst. Provided the link stage has successfully concluded, the listing file will be updated
by the linker so that it contains absolute addresses and symbol values. Thus you may use the assembler
listing file to determine the position of, and exact op codes corresponding to, instructions.
4.4.6 -BIN: Generate Binary Output File
The -BIN option tells PICC18 to generate a binary image output file. The output file will be given an
extension of .bin. Binary output may also be selected by specifying an output file of type .bin using
the -Ofile option.
4.4.7 -Bl: Select Large Memory Model
The -Bl option tells PICC18 to compile using the large memory model. See section or 5.6.3 on page 97
for more details about this memory model.
52
PICC18 Compiler Options
dereferences are made to addresses above the 64k byte limit. See Pointer Types on page 89 for more
information on pointer types.
Note: if using the -CP24 option when building a project, it is critical that this option is applied to all
compile stages and the link stage of the project.
4.4.13 -CRfile: Generate Cross Reference Listing
The -CR option will produce a cross reference listing. If the file argument is omitted, the “raw” cross
reference information will be left in a temporary file, leaving the user to run the CREF utility. If a
filename is supplied, for example -CRtest.crf, PICC18 will invoke CREF to process the cross
reference information into the listing file, in this case test.crf. If multiple source files are to be
included in the cross reference listing, all must be compiled and linked with the one PICC18 command.
For example, to generate a cross reference listing which includes the source modules main.c,
module1.c and nvram.c, compile and link using the command: 3
PICC18 -18C452 -CRmain.crf main.c module1.c nvram.c
4.4.14 -D24: Use 24-bit Doubles
This option is the default if no double size option is specified on the command line. It specifies the use
of truncated 24-bit floating point format for objects of type double. It has no affect for object defined
as float. See Floating Point Types and Variables on page 84 for more details.
4.4.15 -D32: Use 32-bit Doubles
This option tells the compiler to use the IEEE754 32-bit floating point format for double variables. See
Floating Point Types and Variables on page 84 for more details. This option is automatically enabled if
the -FDOUBLE option is selected.
4.4.16 -Dmacro: Define Macro
The -D option is used to define a preprocessor macro on the command line, exactly as if it had been
defined using a #define directive in the source code. This option may take one of two forms, -Dmacro
which is equivalent to:
#define macro 1
placed at the top of each module compiled using this option, or -Dmacro=text which is equivalent to:
#define macro text
where text is the textual substitution required. Thus, the command:
PICC18 -18C252 -Ddebug -Dbuffers=10 test.c
will compile test.c with macros defined exactly as if the C source code had included the directives:
#define debug 1
#define buffers 10
4.4.17 -E: Define Format for Compiler Errors
If the -E option is not used, the default behaviour of the compiler is to display any errors in a “human
readable” format line with a caret “^” and error message pointing out the offending characters in the
source line, for example:
x.c: main()
4: PORT_A = xFF;
^ undefined identifier: xFF
3 This standard format is perfectly acceptable to a person reading the error output, but is not usable with
environments which support compiler error handling. The following sections indicate how this option
may be used in such situations.
4.4.17.1 Using the -E Option
Using the -E option instructs the compiler to generate error messages in a format which is acceptable to
some text editors and development environments.
If the same source code as used in the example above were compiled using the -E option, the error output
would be:
x.c 4 9: undefined identifier: xFF
indicating that the error occurred in file x.c at line 4, offset 9 characters into the statement. The second
numeric value - the column number - is relative to the left-most non-space character on the source line.
If an extra space or tab character were inserted at the start of the source line, the compiler would still
report an error at line 4, column 9.
4.4.17.2 Modifying the Standard -E Format
If the -E option does not meet your editor’s requirement, you can redefine its format by setting two
environment variables: HTC_ERR_FORMAT and HTC_WARN_FORMAT. These environment variables are in
the form of a printf-style string in which you can use the specifiers shown in Table 4 - 3.
The column number is relative to the left-most non-space character on the source line. Here is an
example of setting the environment variables from within DOS:
set HTC_WARN_FORMAT=WARNING: file %f; line %l; column %c; %s
set HTC_ERR_FORMAT=ERROR: file %f; line %l; column %c; %s
54
PICC18 Compiler Options
Specifier Expands To
%f Filename
%l Line number
%c Column number
%s Error string
Using the previous source code, the output from the compiler when using the above environment
variables would be:
ERROR: file x.c; line 4; column 9; undefined identifier: xFF
Remember that if these environment variables are set in a batch file, you must prepend the specifiers
with an additional percent character to stop the specifiers being interpreted immediately by DOS, e.g.
3
the filename specifier would become %%f.
The Microchip MPLAB IDE requires the following definitions for HTC_WARN_FORMAT and
HTC_ERR_FORMAT, respectively, to enable it to display the corresponding line with the error or warning
in its editor.
Warning[ ] file %f %l : %s
Error[ ] file %f %l : %s
4.4.17.3 Redirecting Errors to a File
Error output, either in standard or -E format, can be redirected into files using UNIX- or DOS-style
standard output redirection. The error from the example above could have been redirected into a file
called errlist using the command:
PICC18 -18C242 -E x.c > errlist
Compiler errors can also be appended onto existing files using the redirect and append syntax. If the
error file specified does not exist it will be created. To append compiler errors to a file, use a command
like:
PICC18 -18C242 -E x.c >> errlist
4.4.18 -Efile: Redirect Compiler Errors to a File
Some editors do not allow the standard command line redirection facilities to be used when invoking the
compiler. To work with these editors, PICC18 allows the error listing filename to be specified as part
of the -E option. Error files generated using this option will always be in -E format. For example, to
compile x.c and redirect all errors to x.err, use the command:
56
PICC18 Compiler Options
58
PICC18 Compiler Options
An alternative version of printf() with additional flags and functionality can be selected by using the
command line option:
-Lw
This version of printf() can print long and float variables by default, and is larger than the standard
version of printf().
4.4.27 -L-option: Specify Extra Linker Option
The -L option can also be used to specify an extra “-” option which will be passed directly to the linker
by PICC18. If -L is followed immediately by any text starting with a dash character “-”, the text will be
passed directly to the linker without being interpreted by PICC18. For example, if the option -L-FOO is
specified, the -FOO option will be passed on to the linker when it is invoked.
The -L option is especially useful when linking code which contains extra program sections (or psects), 3
as may be the case if the program contains C code which makes use of the #pragma psect directive
or assembler code which contains user-defined psects. See Section The #pragma psect Directive for
more information. If the -L option did not exist, it would be necessary to invoke the linker manually to
link code which uses the extra psects.
One commonly used linker option is -N, which sorts the symbol table in the map file by address, rather
than by name. This would be passed to PICC18 as the option -L-N.
The -L option can also be used to replace default linker options. For example, -L-ARAM=0-35Fh will
inform the linker that the available memory address for objects in the RAM psect class range from 0 to
35Fh. By default, this range is 0 to 5FFh for the PIC18C452 processor. The default option that you are
replacing must contain an equal character.
4.4.28 -Mfile: Generate Map File
The -M option is used to request the generation of a map file. If no filename is specified, the map
information is displayed on the screen, otherwise the filename specified with -M will be used.
4.4.29 -MPLAB: Compile and Debug using MPLAB IDE
The -MPLAB option informs the HI-TECH C that both compilation and subsequent debugging will be
performed from within the Microchip MPLAB IDE. This option turns on source level debugging (-G),
turns on the -FAKELOCAL option to allow enhanced source and variable tracking, and adjusts the
compiler's error message format (-E) to be that required by the MPLAB IDE.
If compilation is performed under a separate make facility, but debugging is performed under the
MPLAB IDE, then the -G, -E and -FAKELOCAL options can be used separately.
60
PICC18 Compiler Options
{
while (count--)
printf("%d ", *list++);
putchar(’\n’);
}
If compiled with the command:
PICC18 -18C252 -PROTO test.c
PICC18 will produce test.pro containing the following declarations which may then be edited as
necessary:
/* Prototypes from test.c */
3 /* extern functions - include these in a header file */
#if PROTOTYPES
extern int add(int *, int *);
extern void printlist(int *, int);
#else /* PROTOTYPES */
extern int add();
extern void printlist();
#endif /* PROTOTYPES */
4.4.41 -PSECTMAP: Display Complete Memory Usage
The -PSECTMAP option is used to display a complete memory and psect (program section) dump after
linking the user code. The information provided by this option is more detailed than the standard
memory usage map which is normally printed after linking. The -PSECTMAP option causes the compiler
to print a listing of every compiler and user generated psect, followed by the standard memory usage
map. For example:
Psect Usage Map:
62
PICC18 Compiler Options
RAM data $0000F6 - $0000FF $00000A ( 10) bytes total RAM data
Near RAM $000000 - $000001 $000002 ( 2) bytes total Near RAM
ROM data $000004 - $000007 $000004 ( 4) bytes total ROM data
3
Program statistics:
This will reserve the ROM address ranges 0x1000 to 0x10FF and 0x2000 to 0x20FF. Reserved memory
will not be considered by the linker for the placement of objects, thus they can be used by the
programmer for any purpose.
4.4.45 -ROMranges: Specify External Memory
Some PIC18 devices allow external memory to be placed in the chip’s program space. To indicate the
additional program space memory present in such systems, the -ROM option can be used. The syntax for
this option is a comma-separated list of address ranges, e.g.
-ROM0-2FFF,6000-7FFF
This option specifies the CODE psect class definition passed to the linker, in which is placed all code and
ROM data, such as objects qualified as const. Memory specified by this option is appended to any
3 memory already defined for the target device in the pic18.ini file.
If no -ROM option is passed to the PICC18 driver when compiling for a ROM-less device, an error is
generated. All ROM-less devices specify a ROM size of zero in the pic18.ini file.
This option can be used for any PIC18 device, even those which do not have external memory interfaces.
It is up to the programmer to ensure that the memory selected by this option matches their hardware
setup.
4.4.46 -S: Compile to Assembler Code
The -S option stops compilation after generating an assembler source file. An assembler file will be
generated for each C source file passed on the command line. The command:
PICC18 -18C252 -O -Zg -S test.c
will produce an assembler file called test.as which contains the code generated from test.c. The
optimization options -O and -Zg can be used with -S, making it possible to examine the compiler
output for any given set of options. This option is particularly useful for checking function calling
conventions and signature values when attempting to write external assembly language routines. The
file produced by this option differs to that produced by the -ASMLIST option in that it does not contain
op-codes or addresses and it may be used as a source file and subsequently passed to the assembler to
be assembled.
4.4.47 -SIGNED_CHAR: Make Char Type Signed
Unless this option is used, the default behaviour of the compiler is to make all character values and
variables of type unsigned char unless explicitly declared or cast to signed char. This option will
make the default char type signed char. When using this option, any unsigned character object will
have to be explicitly declared unsigned char.
64
PICC18 Compiler Options
The range of a signed character type is -128 to +127 and the range of similar unsigned objects is 0 to 255
4.4.48 -STRICT: Strict ANSI Conformance
The -STRICT option is used to enable strict ANSI conformance of all special keywords. HI-TECH C
supports various special keywords (for example the persistent type qualifier). If the -STRICT option
is used, these keywords are changed to include two underscore characters at the beginning of the
keyword (e.g. __persistent) so as to strictly conform to the ANSI standard. Be warned that use of
this option may cause problems with some standard header files (e.g. <intrpt.h>).
4.4.49 -TEK: Generate Tektronix HEX File
The -TEK option tells the compiler to generate a Tektronix format HEX file if producing a file with .hex
extension. This option has no effect if used with an option which specifies a .bin file output.
4.4.50 -Umacro: Undefine a Macro 3
The -U option, the inverse of the -D option, is used to undefine predefined macros. This option takes the
form -Umacro. The option, -Udraft, for example, is equivalent to:
#undef draft
placed at the top of each module compiled using this option.
4.4.51 -UBROF: Generate UBROF Format Output File
The -UBROF option tells the compiler to generate a UBROF format output file suitable for use with
certain in-circuit emulators. The output file will be given an extension .ubr. UBROF output may also
be selected by specifying an output file of type .ubr using the -O option. This option has no effect if
used with an option which specifies a .bin file output.
4.4.52 -V: Verbose Compile
The -V is the verbose option. The compiler will display the full command lines used to invoke each of
the compiler applications or compiler passes. This option may be useful for determining the exact linker
options if you need to directly invoke the HLINK command.
4.4.53 -Wlevel[!]: Set Warning Level
The -W option is used to set the compiler warning level. Allowable warning levels range from -9 to 9.
The warning level determines how “picky” the compiler is about dubious type conversions and
constructs. The default warning level -W0 will allow all normal warning messages. Warning level -W1
will suppress the message Func() declared implicit int. -W3 is recommended for compiling
code originally written with other, less strict, compilers. -W9 will suppress all warning messages.
Negative warning levels -W-1, -W-2 and -W-3 enable special warning messages including compile-
time checking of arguments to printf() against the format string specified.
Use this option with care as some warning messages indicate code that is likely to fail during execution.
If it is desired to halt execution on any warning messages, concluding the -Wlevel option with an
exclaimation mark (!) will cause the compiler to stop if any warnings are produced.
4.4.54 -X: Strip Local Symbols
The option -X strips local symbols from any files compiled, assembled or linked. Only global symbols
will remain in any object files or symbol files produced.
4.4.55 -XDATAstart-end: Specify a range for external data memory
3 The -XDATA option is used to specify to the linker that additional data memory is available within the
given range. The usage of this option requires a start and an end address be specified as hexadecimal
entries, for example to specify data memory between 30000h and 3FFFFh the option would be:
-XDATA30000-3FFFF
Additional data memory can only be specified for those devices with an external memory interface and
the range specified cannot overlap with the boundaries of user program memory.
This option is mandatory if using data variables declared using the far qualifier.
4.4.56 -Zg[level]: Global Optimization
The -Zg option invokes global optimization during the code generation pass. This can result in
significant reductions to code size and internal RAM usage. This optimizer is less critical than the post-
pass optimizer, but can still significantly reduce the code size.
Global optimization attempts to optimize register usage on a function-by-function basis. It also takes
advantage of constant propagation in code to avoid un-necessary accesses to memory.
The default level for this option is 1 (the least optimization). The level can be set anywhere from 1 to 9
(the most optimization). The number indicates how hard the optimizer tries to reduce code size. For
PICC18, there is usually little advantage in using levels above 3.
66
Features and Runtime Environment
PICC-18 supports a number of special features and extensions to the C language which are designed to
ease the task of producing ROM-based applications. This chapter documents the compiler options and
special language features which are specific to the Microchip PIC 18 family of processors.
(there are two leading underscore characters) where n is the configuration register number and x is the
value that is to be the configuration word. The macro is defined in <pic18.h> so be sure to include that
into each module that uses this macro.
The configuration macro programs the upper and lower half of each register, i.e. it programs 16 bits with
each call. Special named quantities are defined in the header file appropriate for the processor you are
using to help you enable the required features. Table 5 - 1 on page 69 for the available configuration bit
settings associated with the 18Cxxx chip types, and Table 5 - 2 on page 70 for the settings for flash
devices.
For example, to set a PIC18Cxx1 chip to have an RC type oscillator, an 8-bit bus width, the powerup
timer disabled, the watchdog timer enabled with a post scale factor of 1:1, and the stack full/underflow
reset disabled, the following could be used.
#include <pic18.h>
__CONFIG(1, RC);
__CONFIG(2, BW8 & PWRTDIS & WDTPS1 & WDTEN);
__CONFIG(4, STVRDIS);
4 Note that the individual selections are ANDed together. Any bits which are not selected in these macros
will remain unprogrammed. You should ensure that you have specified all bits correctly to ensure proper
operation of the part when programmed. Consult your PIC datasheet for more details.
The __CONFIG macro does not produce executable code and should be placed outside function
definitions.
5.2.3 ID Locations
The PIC18 devices have locations outside the addressable memory area that can be used for storing
program information, such as an ID number. The __IDLOC macro may be used to place data into these
locations. The macro is used in a manner similar to:
#include <pic18.h>
__IDLOC(x);
where x is a list of nibbles which are to be positioned in to the ID locations. The upper nibble of each
location is programmed as 0FH so that the whole byte is treated as a nop instruction if executed. The
following:
__IDLOC(15F01);
will attempt to fill five ID locations with the hexadecimal values: F1H, F5H, FFH, F0H and F1H. The
base address of the ID locations is specified by the idloc psect which will be automatically assigned an
address dependent on the type of processor selected.
68
Processor-related Features
Config
Meaning 18Cxx1 18Cxx2 18Cxx8
register
Code protection 1 low byte n/a PROTECT, PROTECT,
UNPROTECT UNPROTECT
Oscillator system clock 1 high byte n/a OSCSEN, OSCSEN,
switch OSCSDIS OSCSDIS
Oscillator types 1 high byte RC, HS, EC, RCIO, HSPLL, RCIO, HSPLL,
LP ECIO, EC, RC, ECIO, EC, RC,
HS, XT, LP HS, XT, LP
Bus width 2 low byte BW16, BW8 n/a n/a
Powerup timer enable 2 low byte PWRTEN, PWRTEN, PWRTEN,
PWRTDIS PWRTDIS PWRTDIS
Brown out reset voltage 2 low byte n/a BORV25, BORV25,
BORV27, BORV27,
BORV42,
BORV45
BORV42,
BORV45 4
Brown out reset enable 2 low byte n/a BOREN, BOREN,
BORDIS BORDIS
Watchdog timer post 2 high byte WDTPS1 - WDTPS1 - WDTPS1 -
scale select WDTPS128 WDTPS128 WDTPS128
Watchdog timer enable 2 high byte WDTEN, WDTEN, WDTEN,
WDTDIS WDTDIS WDTDIS
CCP2 Mux bit 3 high byte n/a CCP2RC1, n/a
CCP2RB3
Stack full/underflow 4 low byte STVREN, STVREN, STVREN,
reset enable STVRDIS STVRDIS STVRDIS
5.2.4 EEPROM Data
For those PIC 18 devices that support external programming of their EEPROM data area, the
__EEPROM_DATA() macro can be used to place the initial EEPROM data values into the HEX file ready
for programming. The macro is used as follows.
#include <pic18.h>
__EEPROM_DATA(0, 1, 2, 3, 4, 5, 6, 7);
The macro accepts eight parameters, being eight data values. Each value should be a byte in size. Unused
values should be specified as a parameter of zero. The macro may be called multiple times to define the
Config
Meaning 18Fxx2 18Fxx8 18Fxx20
register
Oscillator sys- 1 high OSCSEN, OSCSEN, OSCSEN,
tem clock byte OSCSDIS OSCSDIS OSCSDIS
switch
Oscillator types 1 high RCRA6, RCRA6, RCRA6,
byte HSPLL, HSPLL, HSPLL,
ECRA6, ECRA6, ECRA6,
ECDB4, RC, ECDB4, RC, ECDB4, RC,
HS, XT, LP HS, XT, LP HS, XT, LP
Powerup timer 2 low PWRTEN, PWRTEN, PWRTEN,
enable byte PWRTDIS PWRTDIS PWRTDIS
Brown out 2 low BORV20, BORV20, BORV25,
reset voltage byte BORV27, BORV27, BORV27,
BORV42, BORV42, BORV42,
4 BORV45 BORV45 BORV45
Brown out 2 low BOREN, BOREN, BORDIS
reset enable byte BORDIS BORDIS
Watchdog 2 high WDTPS1 - WDTPS1 - WDTPS1 -
timer post scale byte WDTPS128 WDTPS128 WDTPS128
select
Watchdog 2 high WDTEN, WDTEN, WDTEN,
timer enable byte WDTDIS WDTDIS WDTDIS
External bus 3 low n/a n/a WAITEN*,
data wait ena- byte WAITDIS*
ble
*18F8x20 only
Microcontrol- 3 high n/a n/a MCU*,
ler/Microproc- byte MPU*,
essor mode MPUBB*,
*18F8x20 only XMCU*
CCP2 Mux bit 3 high CCP2RC1, n/a CCP2RC1,
*18F8x20 only byte CCP2RB3 CCP2RE7,
CCP2RB3 *
70
Processor-related Features
Config
Meaning 18Fxx2 18Fxx8 18Fxx20
register
Stack full/ 4 low STVREN, STVREN, STVREN,
underflow reset byte STVRDIS STVRDIS STVRDIS
enable
Debug enable 4 low DEBUGEN, DEBUGEN, DEBUGEN,
byte DEBUGDIS DEBUGDIS DEBUGDIS
Low voltage 4 low LVPEN, LVPEN, LVPEN,
ICSP byte LVPDIS LVPDIS LVPDIS
Code protec- 5 low CPA, CP3, CPA, CP3**, CPA, CP7*,
tion byte CP2, CP1, CP2**, CP1, CP6*, CP5*,
*18Fx720 only CP0 CP0 CP4*, CP3,
**18Fx58 only CP2, CP1,
CP0
Data protection 5 high
byte
CPD CPD CPD
4
Boot code pro- 5 high CPB CPB CPB
tection byte
Protect all 5 CPALL CPALL CPALL
blocks
Write protect/ 6 low WP3, WP2, WPA, WP3**, WPA, WP7*,
enable byte WP1, WP0 WP2**, WP1, WP6*, WP5*,
*18Fx720 only WP0 WP4*, WP3,
**18Fx58 only WP2, WP1,
WP0
Write protect/ 6 high WPB, WPB, WPU WPB, WPU
enable boot byte WRTEN
block
Write protect/ 6 high WPC, WPC, WPU WPC, WPU
enable configu- byte WRTEN
ration registers
Write protect/ 6 high WPD, WPD, WPU WPD, WPU
enable data byte WRTEN
block
Config
Meaning 18Fxx2 18Fxx8 18Fxx20
register
Write protect 6 WPALL WPALL WPALL
all blocks
Table read pro- 7 low TRP3, TRP2, TRPA, TRPA,
tection/enable byte TRP1, TRP0 TRP3**, TRP7*,
* 18Fx720 only TRP2**, TRP6*,
** 18Fx58 only TRP1, TRP0 TRP5*,
TRP4*,
TRP3, TRP2,
TRP1, TRP0
Table read pro- 7 high TRPB TRPB TRPB
tection boot byte
block
Protect all 7 TRPALL TRPALL TRPALL
4 blocks from
table reads
required amount of EEPROM data. It is recommended that the macro be placed outside any function
definitions.
The macro defines, and places the data within, a psect called eeprom_data. This psect is positioned by
a linker option in the usual way.
This macro is not used to write to EEPROM locations during run-time. The macros EEPROM_READ()
and EEPROM_WRITE(), and the function versions of these macros, can be called to read from, and write
to, the EEPROM during program execution.
5.2.5 EEPROM and Flash Runtime Access
EEPROM and flash memory macros are defined for convenience and are available for chips that have
EEPROM or flash memory. The predefined EEPROM and flash memory macros can be used in the
following manner.
To write a byte-size value to an address in EEPROM memory:
EEPROM_WRITE(address, value);
To read a byte of data from an address in EEPROM memory, and store it in a variable:
variable=EEPROM_READ(address);
72
Processor-related Features
For convenience, EEPROM_SIZE predefines the total size of data EEPROM available on chip.
To copy a block of code/data to an area in flash memory:
flash_write(source_pointer, length, dest_pointer);
To read a byte of data from an address in flash memory, and store in a variable:
variable=FLASH_READ(address);
5.2.6 Bit Instructions
Wherever possible, PICC-18 will attempt to use the PIC18 bit instructions. For example, when using a
bitwise operator and a mask to alter a bit within an integral type, the compiler will check the mask value
to determine if a bit instruction can achieve the same functionality.
int foo;
foo |= 0x40;
will produce the instruction
bsf _foo,6 4
To set or clear individual bits within integral types, the following macros could be defined and used.
#define bitset(var, bitno) ((var) |= 1 << (bitno))
#define bitclr(var, bitno) ((var) &= ~(1 << (bitno)))
To perform the same operation as the above example, the bitset macro could be employed as follows.
bitset(foo, 6);
5.2.7 Multi-byte SFRs
Some of the SFRs associated with the PIC18 can be grouped to form multi-byte values, e.g. the TMRxH
and TMRxL register together form a 16-bit timer count value. Depending on the device and mode of
operation, there may be hardware requirements to read these registers correctly, e.g. the TMRxL register
often must be read before trying to read the TMRxH register to obtain a valid 16-bit result.
Although it is possible to define an absolute non-char C variable to map over such registers, the order
in which PICC-18 reads the bytes of a multi-byte object varies depending on the context of the variable
in an expression, i.e. it may read the most significant byte first, or the least. Thus, it highly recommended
that the existing SFR char definitions in the chip header files be used. Each SFR should be accessed
directly and in the required order by the programmer’s code. This will ensure a much higher degree of
portability.
The following code copies the two byte registers into C unsigned variable i for subsequent use.
i = TMR0L;
i += TMR0H << 8;
5.3 Files
5.3.1 Source Files
The extension used with source files is important as it is used by the compiler drivers to determine their
content. Source files containing C code should have the extension .c, assembler files should have
extensions of .as, relocatable object files require the .obj extension, and library files should be named
with a .lib extension. See the tutorial Section 2.1.2 on page 18 for more information on how these input
files are processed by the compiler.
5.3.2 Output File Formats
The compiler is able to directly produce a number of the output file formats which are used by common
PROM programmers and in-circuit emulators.
The default behaviour of the PICC18 command is to produce Bytecraft COD and Intel HEX output. If
no output filename or type is specified, PICC18 will produce a Bytecraft COD and Intel HEX file with
4 the same base name as the first source or object file specified on the command line. Table 5 - 3 on
page 74 shows the output format options available with PICC18. The File Type column lists the filename
extension which will be used for the output file.
PICC18
Format Name Description File Type
Option
Motorola HEX S1/S9 type hex file -MOT .hex
Intel HEX Intel style hex records (default) -INTEL .hex
Binary Simple binary image -BIN .bin
UBROF “Universal Binary Image Relocatable Format” -UBROF .ubr
Tektronix HEX Tektronix style hex records -TEK .hex
American Hex format with symbols for American -AAHEX .hex
Automation HEX Automation emulators
Bytecraft .COD Bytecraft code format (default) n/a (default) .cod
Library HI-TECH library file n/a .lib
In addition to the options shown, the -O option may be used to request generation of binary or UBROF
files. If you use the -O option to specify an output filename with a .bin type, for example -Otest.bin,
74
Files
PICC18 will produce a binary file. Likewise, if you need to produce UBROF files, you can use the -O
option to specify an output file with type .ubr, for example -Otest.ubr.
5.3.3 Symbol Files
The PICC18 -G option tells the compiler to produce a symbol file which can be used by debuggers and
simulators to perform symbolic and source-level debugging. This option produces symbol files which
contain both assembler- and C-level information. If no symbol filename is specified, by default a file
called file.sym will be produced, where file is the basename of the first source file specified on the
command line. For example, to produce a symbol file called test.sym which includes C source-
level information:
PICC18 -18C252 -Gtest.sym test.c init.c
This option will also generate a different symbol file for each module compiled. These files do not
contain absolute address and have the type .sdb. The base name will be the same as the base name of
the module being compiled. Thus the above command line would also generate symbols files with the
names test.sdb and init.sdb.
5.3.4 Standard Libraries
4
PICC-18 includes a number of standard libraries, each with the range of functions described in the
Library Functions chapter.
Figure 5 - 1 on page 76 illustrates the naming convention used for the standard libraries. The meaning
of each field is described here, where:
T Processor Type is always pic.
T Processor Range is 8 for the PIC18 family.
T Configuration is a digit, bit 0 of which is either 1 for 24-bit wide program space pointers;
otherwise 0. Bit 1 is 0 to disallow the use of the LFSR instruction; 1 to allow this instruction.
T Memory Model is either l for large or s for small model.
T Double Type is - for 24-bit doubles, and d for 32-bit doubles.
T Library Type is c for standard library, l for the library which contains only printf-related
functions with additional support for longs, and f for the library which contains only printf-
related functions with additional support for longs and floats.
5.3.4.1 Limitations of Printf
The printf() function is provided but some features have been removed. For more details on this
function, see the documentation on page 276.
.LIB
Processor Type (pic) Library Type (c,l,f)
Processor Range (8) Double Type (-, d)
Configuration (0-3)
Memory Model (l,s)
76
Files
The run-time startup code jumps to the function main(), which is referred to as _main by the run-time
startup code. Note the underscore “_” prepended to the function name. A jump rather than a call is
executed to save one level of stack. The function main() is, by definition of the C language, the “main
program”.
The run-time startup code is provided by the pre-compiled, standard module, picrt800.obj, found in
the LIB directory.
The source code used to generate the run-time startup module is called picrt18x.as which is in the
SOURCES directory of your distribution. This module an be excluded from a program by using the -
NORT command line option. A user-defined version of this module can then be written and used to
replace the default module.
In addition to this module will be the routines, mentioned above, to copy data or clear memory as
required. These routines are not called by name, but are linked in, if required, to a position in
picrt18x.as indicated by a comment.
5.3.6.1 The powerup Routine
Some hardware configurations require special initialisation, often within the first few cycles of
4 execution after reset. Rather than having to modify the run-time startup module to achieve this there is
a hook to the reset vector provided via the powerup routine. This is a user-supplied assembler module
that will be executed immediately on reset. Often this can be embedded in a C module as embedded
assembler code. A “dummy” powerup routine is included in the file powerup.as. The file can be
copied, modified and included into your project to replace the default powerup routine that is present in
the standard libraries.
The powerup routine should be written assuming that little or no RAM is working and should only use
system resources after it has tested and enabled them. The following example code shows the default
powerup routine which are in the standard library:
#include ”sfr.h”
global powerup,start
psect powerup,class=CODE,delta=1
powerup:
goto start
end powerup
The assembler header file sfr.h has been included so reference to the PIC’s registers can be made. You
will need to specify the -p command-line driver option.
78
Supported Data Types and Variables
The powerup routine is intended to be relatively small, and since it is linked to an address lower than the
interrupt vectors, it may interfere with them if it grows too large in size. The lower interrupt vector
address is 08h on the PIC18 devices. To avoid overwriting the interrupt vectors, the powerup routine can
be made to jump to a separate function, which will be linked at a different location, and this separate
function can call the jump to start. The following gives an example of this:
global powerup,start,big_powerup
psect powerup,class=CODE,delta=1
powerup:
goto big_powerup
psect big_powerup,class=CODE,delta=1
big_powerup:
; powerup code...
goto start
In this example, the big_powerup psect will be positioned somewhere in the memory allocated to the
CODE class unless an explicit linker option is added by the user.
Size
Type Arithmetic Type
(in bits)
bit 1 boolean
char 8 signed or unsigned integera
unsigned char 8 unsigned integer
short 16 signed integer
unsigned short 16 unsigned integer
int 16 signed integer
unsigned int 16 unsigned integer
long 32 signed integer
unsigned long 32 unsigned integer
float 24 real
double 24 or 32b real
80
Supported Data Types and Variables
Note that when assigning a larger integral type to a bit variable, only the least-significant bit is used.
For example, if the bit variable bitvar was assigned as in the following:
int data = 0x54;
bit bitvar;
bitvar = data;
it will be cleared by the code since the least significant bit of data is zero.
If you want to set a bit variable to be 0 or 1 depending on whether the larger integral type is zero (false)
or non-zero (true), use the form:
bitvar = data != 0;
The psects in which bit objects are allocated storage are declared using the bit psect directive flag.
Eight bit objects will take up one byte of storage space which is indicated by the bit psects’ scale value
of 8 in the map file. The length given in the map file for bit psects is in units of bits, not bytes. All
addresses specified for bit objects are also bit addresses.
4 The bit psects are cleared on startup, but are not initialised. To create a bit object which has a non-zero
initial value, explicitly initialise it at the beginning of your code.
If the PICC18 flag -STRICT is used, the bit keyword becomes unavailable.
5.4.2.1 Using Bit-Addressable Registers
The bit variable facility may be combined with absolute variable declarations (see page 93) to access
bits at specific addresses. Absolute bit objects are numbered from 0 (the least significant bit of the first
byte) up. Therefore, bit number 3 (the fourth bit in the byte since numbering starts with 0) in byte number
5 is actually absolute bit number 43 (that is 8bits/byte * 5 bytes + 3 bits).
For example, to access the power down detection flag bit in the RCON register, declare RCON to be a C
object at absolute address 03h, then declare a bit variable at absolute bit address 27:
static unsigned char RCON @ 0xFD0;
82
Supported Data Types and Variables
biased
Format Sign mantissa
exponent
IEEE 754 32-bit x xxxx xxxx xxx xxxx xxxx xxxx xxxx xxxx
Modified IEEE 754 24-bit x xxxx xxxx xxx xxxx xxxx xxxx
Here are some examples of the IEEE 754 32-bit and modified IEEE 754 24-bit formats:
84
Supported Data Types and Variables
biased
Format Number 1.mantissa decimal
exponent
IEEE 754 32-bit 7DA6B69Bh 11111011b 1.01001101011011010011011b 2.77000e+37
(251) (1.302447676659)
Modified 42123Ah 10000100b 1.001001000111010b 36.557
IEEE 754 24-bit (132) (1.142395019531)
Note that the most significant bit of the mantissa column in Table 5 - 7 on page 85 (that is the bit to the
left of the radix point) is the implied bit, which is assumed to be 1 unless the exponent is zero (in which
case the float is zero).
The 32-bit example in Table 5 - 7 on page 85 can be calculated manually as follows.
The sign bit is zero; the biased exponent is 251, so the exponent is 251-127=124. Take the binary number
to the right of the decimal point in the mantissa. Convert this to decimal and divide it by 223 where 23 is
the number of bits taken up by the mantissa, to give 0.302447676659. Add one to this fraction. The
floating-point number is then given by:
(-1)0 x 2 (124) x 1.302447676659 = 1 x 2.126764793256e+37 x 1.302447676659 ≈ 2.77000e+37 4
Variables may be declared using the float and double keywords, respectively, to hold values of these
types. Floating point types are always signed and the unsigned keyword is illegal when specifying a
floating point type. Types declared as long double will use the same format as types declared as
double.
5.4.7 Structures and Unions
PICC-18 supports struct and union types of any size from one byte upwards. Structures and unions
only differ in the memory offset applied for each member. The members of structures and unions may
not be objects of type bit, but bit fields are fully supported.
Structures and unions may be passed freely as function arguments and return values. Pointers to
structures and unions are fully supported.
5.4.7.1 Bit Fields in Structures
PICC-18 fully supports bit fields in structures.
Bit fields are always allocated within 8-bit words. The first bit defined will be the least significant bit of
the word in which it will be stored. When a bit field is declared, it is allocated within the current 8-bit
unit if it will fit, otherwise a new 8-bit byte is allocated within the structure. Bit fields can never cross
the boundary between 8-bit allocation units. For example, the declaration:
struct {
unsigned lo : 1;
unsigned dummy : 6;
unsigned hi : 1;
} foo;
will produce a structure occupying 1 byte. If foo was ultimately linked at address 10H, the field lo will
be bit 0 of address 10H, hi will be bit 7 of address 10H. The least significant bit of dummy will be bit 1
of address 10H and the most significant bit of dummy will be bit 6 of address 10h.
Unnamed bit fields may be declared to pad out unused space between active bits in control registers. For
example, if dummy is never used the structure above could have been declared as:
struct {
unsigned lo : 1;
unsigned : 6;
unsigned hi : 1;
} foo;
4 If a bit field is declared in a structure that is assigned an absolute address, no storage will be allocated
for the structure. Absolute structures would be used when mapping a structure over a register to allow a
portable method of accessing individual bits within the register.
A structure with bitfields may be initialised by supplying a comma-separated list of initial values for
each field. For example:
struct {
unsigned lo : 1;
unsigned mid : 6;
unsigned hi : 1;
} foo = {1, 8, 0};
5.4.7.2 Structure and Union Qualifiers
PICC-18 supports the use of type qualifiers on structures. When a qualifier is applied to a structure, all
of its members will inherit this qualification. In the following example the structure is qualified const.
const struct {
int number;
int *ptr;
} record = { 0x55, &i};
In this case, the structure will be placed into ROM and each member will, obviously, be read-only.
Remember that all members must be initialised if a structure is const.
86
Supported Data Types and Variables
If the members of the structure were individually qualified const but the structure was not, then the
structure would be positioned into RAM, but each member would be read-only. Compare the following
structure with the above.
struct {
const int number;
int * const ptr;
} record = { 0x55, &i};
5.4.8 Standard Type Qualifiers
Type qualifiers provide information regarding how an object may be used, in addition to its type which
defines it storage size and format. PICC-18 supports both ANSI qualifiers and additional special
qualifiers which are useful for embedded applications and which take advantage of PIC18 architecture.
5.4.8.1 Const and Volatile Type Qualifiers
PICC-18 supports the use of the ANSI type qualifiers const and volatile.
The const type qualifier is used to tell the compiler that an object has a constant value and will not be
modified. If any attempt is made to modify an object declared const, the compiler will issue a warning.
User defined objects declared const are placed in a special psects in ROM. Obviously, a const object
4
must be initialised when it is declared as it cannot be assigned a value at any point in the code following.
For example:
const int version = 3;
The volatile type qualifier is used to tell the compiler that an object cannot be guaranteed to retain its
value between successive accesses. This prevents the optimizer from eliminating apparently redundant
references to objects declared volatile because it may alter the behaviour of the program to do so. All
Input/Output ports and any variables which may be modified by interrupt routines should be declared
volatile, for example:
volatile static near unsigned char PORTA @ 0xF80;
Volatile objects may be accessed in a different way to non-volatile objects. For example, when
assigning a non-volatile object the value 1, the object will be cleared and then incremented, but the
same operation performed on a volatile object will load the W register with 1 and then store this to
the appropriate address.
5.4.9 Special Type Qualifiers
PICC-18 supports special type qualifiers, persistent,near and far to allow the user to control
placement of static and extern class variables into particular address spaces. If the PICC18 option,
-STRICT is used, these type qualifiers are changed to __persistent, __near and __far,
respectively. These type qualifiers may also be applied to pointers. These type qualifiers may not be used
on variables of class auto; if used on variables local to a function they must be combined with the
static keyword. For example, you may not write:
void test(void)
{
persistent int intvar; /* WRONG! */
.. other code ..
}
because intvar is of class auto. To declare intvar as a persistent variable local to function
test(), write:
static persistent int intvar;
PICC-18 also supports the keywords bank1, bank2 and bank3. These keywords have been included to
allow code to be easily ported from PICC. These keywords are accepted by PICC-18, but have no effect
in terms of the object’s storage or how they are accessed. These keywords do, however, affect the storage
4 of objects when compiling with the PIC C compiler - see your PIC C manual for more details.
5.4.9.1 Persistent Type Qualifier
By default, any C variables that are not explicitly initialised are cleared to zero on startup. This is
consistent with the definition of the C language. However, there are occasions where it is desired for
some data to be preserved across resets or even power cycles (on-off-on).
The persistent type qualifier is used to qualify variables that should not be cleared on startup. In
addition, any persistent variables will be stored in a different area of memory to other variables.
Persistent objects are placed within one of the non-volatile psects. If the persistent object is also
qualified near, it placed in the nvrram psect. Persistent bit objects are placed within the nvbit
psect. All other persistent objects are placed in the nvram psect.
There are some library routines provided to check and initialise persistent data - see page 274 for
more information, and for an example of using persistent data.
5.4.9.2 Near Type Qualifier
The near type qualifier is used to place static variables in the access bank of the PIC18. Near objects
are represented by 8 bit addresses and the access bank is always accessable regardless of the currently
selected RAM bank so accessing near objects may be faster than accessing other objects, and typically
results in smaller code sizes.
Here is an example of an unsigned char object placed within the access bank:
88
Supported Data Types and Variables
A pointer to near is only 8 bits wide and can access the general-purpose RAM area of the access bank.
In other words they can be used to dereference any variable qualified as near. The amount of general
purpose RAM in the access bank varies from device to device. Being smaller in size, using pointers to
near result in smaller code sizes. If a pointer only ever accesses near-qualified objects, then that
pointer should be qualified as a pointer to near.
The operation of RAM pointers are unaffected by the -CP24/-CP16 switch, nor are they affected by the
choice of memory model.
5.4.11.2 Const and Far Pointers
Const and far pointers can either be 16 or 24 bits wide. Their size can be toggled with the -CP24 or -
CP16 command line option. The code used to dereference them also changes with their size. The same
pointer size must be used for all modules in a project.
A pointer to far is identical to a pointer to const, except that pointers to far may be used to write to
the address they hold. A pointer to const objects cannot be used to write as the const qualifier imposes
that the object is read-only.
Const and far pointers which are 16 bits wide can access all RAM areas and most of the program
4 space. At runtime when dereferenced, the contents of the pointer are examined. For addresses above the
upper limit of RAM the program space is accessed using table read or table write instructions. Addresses
below the upper limit of RAM access the data space. Even if the address held by a pointer to const is
in RAM, the RAM location may not be changed.
The default linker options always place const data at addresses above the upper limit of the data space
so that the correct memory space is accessed when dereferencing with pointers.
If the target device selected has more than 64k bytes of program space memory, then only the lower 64k
bytes may be accessed with 16-bit wide pointers. Provided that all program space objects that need to
be dereferenced are in the lower 64k bytes, 16-bit pointers to const and far objects may still be used.
The smaller pointer size results in less RAM required and less code produced and so should be used
whenever possible.
Const and far pointers which are 24 bits wide can access all RAM areas and all of the program space.
At runtime when dereferenced, the contents of the pointer are examined. If bit number 21 in the address
is set, the address is assumed to be a RAM address. Bit number 21 of the address is then ignored. If Bit
number 21 is clear, then the address is assumed to be of an object in the program space and the access
is performed using table read or table write instructions. Again, no writes to objects are permitted using
a pointer to const.
Note that when dereferencing a 24-bit pointer, the most significant implemented bit (bit number 21) of
the TBLPTRU register may be overwritten. This bit may be used to enable access to the configuration
90
Supported Data Types and Variables
area of the PIC18 device. If loading the table pointer registers from hand-written assembler code, make
no assumptions about the state of bit number 21 prior to executing table read or write instructions.
5.4.11.3 Function Pointers
Function pointers can be defined to indirectly call functions or routines in the program space. The size
of these pointers are 16 or 24 bits wide and is controlled by the -CP24/-CP16 command line option.
When 16-bit wide function pointers are used only routines within the lower 64k bytes can be indirectly
called. The larger 24-bit function pointer allows indirect calls to be made to any routine, but at the
expense of increased code size and RAM usage.
It should be stressed that the -CP16 option affects function pointer sizes and that it does not affect code
that calls functions directly, i.e. by their name rather than indirectly via a pointer. Thus you can still
directly call functions residing at any location even if you are using the -CP16 option, however you can
only indirectly call functions that reside in the lower 64k byte area of the program space.
The addresses for all code labels are always shown in the map file as an untruncated byte address
regardless of the options used.
5.4.11.4 Combining Type Qualifiers and Pointers
Pointers can be qualified like any other C object, but care must be taken when doing so as there are two 4
quantities associated with pointers. The first is the actual pointer itself, which is treated like any ordinary
C variable and has memory reserved for it. The second is the object that the pointer references, or to
which the pointer points. The general form of a pointer definition looks like the following.
“object’s type & qualifiers“ *“pointer’s qualifiers”“pointer’s name” ;
Here are three examples, highlighting the fields with spacing:
near int * nip ;
int * nearinp ;
near int * nearninp;
The first example is a pointer called nip. It contains the address of an int object that is qualified near.
Since a near object is in the access bank the pointer is only 8 bits wide as discussed above. The pointer
itself (i.e. the 8-bit value the pointer holds) will reside somewhere in the main banked memory.
The second example is a pointer called inp which contains the address of an int object. Since this
object is not qualified near, the pointer needs 16 bits to access the object’s location. The near keyword
after the * indicates that the pointer itself has been qualified near and so the pointer (i.e. the 16-bit value
the pointer holds) will reside in the access bank, but the object whose address the pointer holds is located
in the main banked memory.
The last example is of a pointer called ninp which is itself qualified near and which also holds the
address of an object that is also qualified near. In this example, both the pointer and the object that the
pointer references will be located in the access bank. The pointer will be 8 bits wide.
The rule is as follows: if the modifier is to the left of the * in the pointer declaration, it applies to the
object which the pointer addresses. If the modifier is to the right of the *, it applies to the pointer variable
itself.
The const, volatile, far and persistent modifiers may also be applied to pointers in the above
manner.
To allow portability between PICC and PICC-18 code, the use of the bank1, bank2 and bank3
keywords is allowed with PICC-18 pointer definitions in the manner described above. These keywords
have no effect and do not alter the way in which indirect accesses are made. The use of these keywords
with pointers defined in PICC does affect the operation of pointer dereferences - see your PICC manual
for more details.
92
Storage Class and Object Placement
Auto objects are referenced with a symbol that consists of a question mark, “?”, concatenated with
a_function plus some offset, where function is the name of the function in which the object is
defined. For example, if the int object test is the first object placed in main()’s auto-variable block
it will be accessed using the addresses ?a_main and ?a_main+1 since an int is two bytes long.
Auto variables may be accessed using the banked instructions of the PIC18. When accessing auto
objects with banked instructions, the compiler will ensure that the bank of the auto-variable block is
selected using a movlb instruction, and then access the locations using the appropriate instructions. In
essence this amounts to an 8 bit access within the selected bank.
5.5.1.2 Static Variables
Uninitialized static variables are allocated in one of the bss, rbss or bigbss psects. Objects qualified
near appear in the rbss psect; objects larger than one bank in size or byte long objects are placed in the
bigbss psect and the remainder in the bss psect. They will occupy fixed memory locations which will
not be overlapped by storage for other functions. Static variables are local in scope to the function in
which they are declared, but may be accessed by other functions via pointers since they have permanent
duration. Static variables are guaranteed to retain their value between calls to a function, unless
explicitly modified via a pointer. Static variables are not subject to any architectural limitations on the
PIC18. 4
Static variables which are initialised are only done so once during the program’s execution. Thus, they
may be preferable over initialised auto objects which are assigned a value every time the block in which
the definition is placed is executed.
5.5.2 Absolute Variables
A global or static variable can be located at an absolute address by following its declaration with the
construct @ address, for example:
volatile unsigned charPortvar @ 0x06;
will declare a variable called Portvar located at 06h. Note that the compiler does not reserve any
storage, but merely equates the variable to that address, the compiler-generated assembler will include
a line of the form:
_Portvar EQU 06h
Note also that the compiler and linker do not make any checks for overlap of absolute variables with
other variables of any kind, so it is entirely the programmer’s responsibility to ensure that absolute
variables are allocated only in memory not in use for other purposes.
This construct is primarily intended for equating the address of a C identifier with a microprocessor
register. To place a user-defined variable at an absolute address, define it in a separate psect and instruct
the linker to place this psect at the required address. See “The #pragma psect Directive” on page 112.
Absolute variables have their address supplied by the code generator, not the linker, and hence no
symbols are used which require fixup by the linker. This means that the name of the object will not be
present in the map file, or any symbol information produced by the linker.
5.5.3 Objects in Program Space
Const objects are usually placed in program space. On the PIC18 devices, the program space is byte-
wide, the compiler stores one character per byte location and values are read using the table read
instructions. All const-qualified data objects and string literals are placed in the const psect. The const
psect is placed at an address above the upper limit of RAM since RAM and const pointers use this
address to determine if an access to ROM or RAM is required. See section 5.4.11 on page 89.
5.6 Functions
5.6.1 Function Argument Passing
The method used to pass function arguments depends on the size of the argument or arguments.
If there is only one argument, and it is one byte in size, it is passed in the W register.
4 If there is only one argument, and it is greater than one byte in size, it is passed in the argument area of
the called function. If there are subsequent arguments, these arguments are also passed in the argument
area of the called function. The argument area is referenced by an offset from the symbol ?_function,
where function is the name of the function concerned.
If there is more than one argument, and the first argument is one byte in size, it is passed in the W
register, with subsequent arguments being passed in the argument area of the called function.
In the case of a variable argument list, which is defined by the ellipsis symbol ..., the calling function
builds up the variable argument list and passes a pointer to the variable part of the argument list in
btemp. Btemp is the label at the start of the temp psect (the psect used for temporary data).
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 argument block and a in the W register.
A call:
test( ’a’, 8);
would generate code similar to:
94
Functions
movlw 08h
movff wreg,?_test
movlw 0h
movff wreg,?_test+1
movlw 061h
call (_test)
In this example, the parameter b is held in the memory locations ?_test and ?_test+1.
If you need to determine, for assembler code for example, the exact entry or exit code within a function
or the code used to call a function, it is often helpful to write a dummy C function with the same
argument types as your assembler function, and compile to assembler code with the PICC -S option,
allowing you to examine the assembler code.
5.6.2 Function Return Values
Function return values are passed to the calling function as follows:
5.6.2.1 8-Bit Return Values
Eight-bit values are returned from a function in the W register. For example, the function: 4
char return_8(void)
{
return 0;
}
will exit with the following code:
movlw 0
return
5.6.2.2 16-Bit and 32-bit Return Values
16-bit and 32-bit values are returned in temporary memory locations, with the least significant word in
the lowest memory location. For example, the function:
int return_16(void)
{
return 0x1234;
}
will exit with the following code:
movlw 34h
movwf btemp
movlw 12h
movwf btemp+1
return
5.6.2.3 Structure Return Values
Composite return values (struct and union) of size 4 bytes or smaller are returned in memory as with
16-bit and 32-bit return values. For composite return values of greater than 4 bytes in size, the structure
or union is copied into the struct psect. Data is copied using the library routine structcopy which
uses FSR0 for the source address, FSR1 for the destination address and W for the structure size. For
example:
struct fred
{
int ace[4];
} ;
return wow;
}
will exit with the following code:
movlw low(?a_func+0)
movwf fsr0l
movlw high(?a_func+0)
movwf fsr0h
movlw structret
movwf fsr1l
clrf fsr1h
movlw 24
global structcopy
call structcopy
96
Register Usage
5.8 Operators
PICC-18 supports all the ANSI operators. The exact results of some of these are implementation
defined. The following sections illustrate code produced by the compiler.
98
Operators
The consequence of integral promotion as illustrated above is that operations are not performed with
char-type operands, but with int-type operands. However there are circumstances when the result of
an operation is identical regardless of whether the operands are of type char or int. In these cases,
PICC-18 will not perform the integral promotion so as to increase the code efficiency. Consider the
following example.
unsigned char a, b, c;
a = b + c;
Strictly speaking, this statement requires that the values of b and c should be promoted to unsigned
int, the addition performed, the result of the addition cast to the type of a, and then the assignment can
take place. Even if the result of the unsigned int addition of the promoted values of b and c was
different to the result of the unsigned char addition of these values without promotion, after the
unsigned int result was converted back to unsigned char, the final result would be the same. An
8-bit addition is more efficient than an a 16-bit addition and so the compiler will encode the former.
If, in the above example, the type of a was unsigned int, then integral promotion would have to be
performed to comply with the ANSI standard.
5.8.2 Shifts applied to integral types 4
The ANSI standard states that the result of right shifting (>> operator) signed integral types is
implementation defined when the operand is negative. Typically, the possible actions that can be taken
are that when an object is shifted right by one bit, the bit value shifted into the most significant bit of the
result can either be zero, or a copy of the most significant bit before the shift took place. The latter case
amounts to a sign extension of the number.
PICC-18 performs a sign extension of any signed integral type (for example signed char, signed
int or signed long). Thus an object with the signed int value 0124h shifted right one bit will yield
the value 0092h and the value 8024h shifted right one bit will yield the value C012h.
Right shifts of unsigned integral values always clear the most significant bit of the result.
Left shifts (<< operator), signed or unsigned, always clear the least significant bit of the result.
5.8.3 Division and modulus with integral types
The sign of the result of division with integers when either operand is negative is implementation
specific. Table 5 - 8 shows the expected sign of the result of the division of operand 1 with operand 2
when compiled with PICC-18.
In the case where the second operand is zero (division by zero), the result will always be zero.
5.9 Psects
The compiler splits code and data objects into a number of standard program sections referred to as
psects. The HI-TECH assembler allows an arbitrary number of named psects to be included in assembler
code. The linker will group all data for a particular psect into a single segment.
If you are using PICC18 to invoke the linker, you don’t need to worry about the information documented
here, except as background knowledge. If you want to run the linker manually (this is not
recommended), or write your own assembly language subroutines, you should read this section
carefully.
4 A psect can be created in assembler code by using the PSECT assembler directive, see. In C, user-defined
psects can be created by using the #pragma psect preprocessor directive.
5.9.1 Compiler-generated Psects
The code generator places code and data into psects with standard names which are subsequent
positioned by the default linker options. These psects are described below.
TThe compiler-generated psects which are placed in ROM are:
powerup Which contains executable code for the standard or user-supplied power-up routine.
idata These psects contain the ROM image of any initialised variables. These psects are copied into
the data psects at startup.
irdata These psects contain the ROM image of any initialised near variables. These psects are
copied into the rdata psects at startup.
ibigdata These psects contain the ROM image of initialised objects which at runtime reside in the
bigdata psect. This includes global or static local char objects or char arrays, and arrays
whose size exceeds the size of a RAM bank.
textn These psects (where n is a number) contain all executable code. Typically n is incremented
for each new C function compiled.
text Is a global psect used for executable code for some library functions.
100
Psects
const These psects hold objects that are declared const and string literals which are not modifiable.
config Used to store the configuration words.
idloc Used to store the ID location words.
eeprom_dataUse to store data to be programmed into the EEPROM data area.
intcode Is the psect which contains the executable code for the default or high-priority interrupt
service routine. This psect is linked to interrupt vector at address 08H.
intcodeloIs the psect which contains the executable code for the low-priority interrupt service routine.
This psect is linked to interrupt vector at address 018H.
init Used by initialisation code which, for example, clears RAM.
end_init Used by initialisation code which, for example, clears RAM.
clrtext Used by some startup routines for copying the data psects.
TThe compiler-generated psects which are placed in RAM are:
rbss These psects contain any uninitialized near variables. They reside in the access bank. 4
bigbss These psects contain any uninitialized global or static local char objects or char arrays,
and arrays whose size exceeds the size of a RAM bank. This psect is linked into a psect class
which does not have RAM bank boundaries. Accessing objects in this area may be less
efficient that accessing objects in the data psect.
farbss This psect contains any uninitialized objects which have been declared as far to be positioned
in external code space. By default this psect is linked after the top of program memory.
bss These psects contain any uninitialized variables not contained in the above psects.
rdata These psects contain any initialised near variables. They reside in the access bank.
bigdata These psects contain any initialized global or static local char objects or char arrays, and
arrays whose size exceeds the size of a RAM bank. This psect is linked into a psect class
which does not have RAM bank boundaries. Accessing objects in this area may be less
efficient that accessing objects in the data psect.
data These psects contain any initialised variables not contained in the above psects. These psects
will be wholly placed within a RAM bank and so can be accessed more efficiently.
nvrram This psect holds near persistent variables. It is not cleared or otherwise modified by the
runtime startup code.
nvbit This psect hold persistent bit objects. It is not cleared or otherwise modified at startup.
nvram This psect is used to store persistent variables. It is not cleared or otherwise modified at
startup.
rbit These psects are used to store all bit variables. All bit objects are near by default and are
placed in the access bank.
struct Contains any structure larger than 4 bytes in size which is returned from a function.
intsave_regsHolds the registers (including temporary locations) saved by the interrupt service routine.
temp Is used to store scratch variables used by the compiler. These include function return values
larger than a byte and values passed to and returned from library routines. This psect will be
positioned in the access bank.
102
Interrupt Handling in C
long tick_count;
registers. This minimizes code size and reduces the instruction cycles to access the high-priority service
routine. Note that fast interrupt save/restore is not available if compiling for the MPLAB ICD2
debugger.
The high-priority or compatibility-mode interrupt function places a small routine in a psect called
intcode which is linked directly to the interrupt vector. This code saves the STATUS (if fast interrupts
are not used) and PCLATH registers then jumps to code placed in a text psect. This code will save
further context if it is necessary and then jump to code directly related to the interrupt function. The
interrupt function code is also placed in a text psect.
All objects saved are done so to locations at an offset to a symbol called saved_regsh, except for the
BSR register. If fast interrupts are not used, BSR is saved to a location symbol called saved_bsrh.
The low-priority interrupt function places the code to save the STATUS and PCLATH registers in a psect
called intcodelo, which is directly linked to the low-priority interrupt vector. Operation is then similar
to the high-priority interrupt case, only with objects being saved offset to the symbol saved_regsl and
the BSR register saved to a location symbol called saved_bsrl.
5.10.3 Context Retrieval
4 Any objects saved by the compiler are automatically restored before the interrupt function returns. The
restoration code is placed into a text psect. The retfie instruction placed at the end of the interrupt
code will reload the program counter and the program will return to the location at which it was when
the interrupt occurred.
5.10.4 Interrupt Levels
Normally it is assumed by the compiler that any interrupt may occur at any time, and an error will be
issued by the linker if a function appears to be called by an interrupt function and by main-line code,
or another interrupt. Since it is often possible for the user to guarantee this will not happen for a specific
routine, the compiler supports an interrupt level feature to suppress the errors generated.
This is achieved with the #pragma interrupt_level directive. There are two interrupt levels
available, and any interrupt functions at the same level will be assumed by the compiler to be
mutually exclusive. This exclusion must be guaranteed by the user, i.e. the compiler is not able to control
interrupt priorities. Each interrupt function may be assigned a single level, either 0 or 1.
In addition, any non-interrupt functions that are called from an interrupt function and also from
main-line code may also use the #pragma interrupt_level directive to specify that they will never
be called by interrupts of one or more levels. This will prevent linker from issuing an error message
because the function was included in more than one call graph. Note that it is entirely up to the user to
ensure that the function is not called by both main-line and interrupt code at the same time. This will
normally be ensured by disabling interrupts before calling the function. It is not sufficient to disable
interrupts inside the function after it has been called.
104
Interrupt Handling in C
An example of using the interrupt levels is given below. Note that the #pragma directive applies to only
the immediately following function. Multiple #pragma interrupt_level directives may precede a
non-interrupt function to specify that it will be protected from multiple interrupt levels.
/* non-interrupt function called by interrupt and main-line code
*/
#pragma interrupt_level 1
void bill(){
int i;
i = 23;
}
#pragma interrupt_level 1
void interrupt joh()
{
bill();
}
main()
{
bill();
}
Both the low- and high-priority interrupt functions may use the interrupt level feature.
5.10.5 Interrupt Registers
It is up to the user how they want the interrupt source configured. All the registers and bits associated
with interrupts are defined in the specific header file which can be accessed by including <pic18.h>.
The following is an example of setting up the interrupts associated with the change-on-PORTB source.
Interrupt priorities are used and the interrupt source is made a low priority. See your PIC18 datasheet for
more information.
void main(void)
{
TRISB = 0x80; // only RB7 will interrupt on change
IPEN = 1; // interrupt priorities enabled
PEIE = 1; // enable peripheral interrupts
RBIP = 0; // make this a low priority interrupt
RBIE = 1; // enable PORTB change interrupts
RBIF = 0; // clear any pending events
GIEL = 1; //enable low-priority interrupts
while(1)
;
}
106
Mixing C and Assembler Code
PROCESSOR 18C452
PSECT text0,class=CODE,local,delta=1
GLOBAL _twice
SIGNAT _twice,4201
_twice:
; parameter is passed in the W register - assign it to
?a_twice.
movlb ?a_twice shr (8) ; select local bank
movwf ?a_twice & 0ffh ; assign it
addwf ?a_twice & 0ffh,w ; add value to itself
FNSIZE _twice,1,0
GLOBAL ?a_twice
END 4
The name of the assembly language function is the name declared in C, with an underscore prepended.
The GLOBAL pseudo-op is the assembler equivalent to the C extern keyword and the SIGNAT pseudo-
op is used to enforce link time calling convention checking. Signature checking and the SIGNAT pseudo-
op are discussed in more detail later in this chapter.
Note that in order for assembly language functions to work properly they must look in the right place
for any arguments passed and must correctly set up any return values. Local variable allocation (via the
FNSIZE directive), argument and return value passing mechanisms are discussed in the Section
Functions on page 94 and should be understood before attempting to write assembly language routines.
5.11.2 Accessing C objects from within assembler
Global C objects may be directly accessed from within assembly code using their name prepended with
an underscore character. For example, the object foo defined globally in a C module:
int foo;
may be access from assembler as follows.
GLOBAL _foo
movwf _foo
If the assembler is contained in a different module, then the GLOBAL assembler directive should be used
in the assembler code to make the symbol name available, as above. If the object is being accessed from
in-line assembly in another module, then an extern declaration for the object can be made in the C
code, for example:
extern int foo;
This declaration will only take effect in the module if the object is also accessed from within C code. If
this is not the case then, an in-line GLOBAL assembler directive should be used. Care should be taken in
the object is defined in a bank other than 0. The address of a C object includes the bank information
which must be stripped before the address can be used in most PIC18 instructions. The exceptions are
the movff and lsfr instructions. Failure to do this may result in fixup errors issued by the linker. If in
doubt as to writing assembler which access C objects, write code in C which performs a similar task to
what you intend to do and study the assembler listing file produced by the compiler.
5.11.3 #asm, #endasm and asm()
PIC18 instructions may also be directly embedded in C code using the directives #asm, #endasm and
the statement asm(). The #asm and #endasm directives are used to start and end a block of assembler
instructions which are to be embedded inside C code. The asm() statement is used to embed a single
assembler instruction in the code generated by the C compiler. The following example shows both
4 methods used to rotate a byte left through carry:
unsigned char var;
void main(void)
{
var = 1;
#asm // like this...
movlb (_var) >> 8
rlcf (_var)&0ffh,f
#endasm
// or like this
asm(“movlb (_var) >> 8”);
asm("rlcf (_var)&0ffh,f");
}
When using in-line assembler code, great care must be taken to avoid interacting with compiler-
generated code. If in doubt, compile your program with the PICC18 -S option and examine the
assembler code generated by the compiler.
IMPORTANT NOTE: the #asm and #endasm construct is not syntactically part of the C program, and
thus it does not obey normal C flow-of-control rules. For example, you cannot use a #asm block with an
if statement and expect it to work correctly. If you use in-line assembler around any C constructs such
108
Preprocessing
as if, while, do etc. they you should use only the asm("") form, which is a C statement and will
correctly interact with all C flow-of-control structures.
5.12 Preprocessing
All C source files are preprocessed before compilation. Assembler files can also be preprocessed if the
-p command-line option is issued.
5.12.1 Preprocessor Directives
PICC-18 accepts several specialised preprocessor directives in addition to the standard directives. These
are listed in Table 5 - 9 on page 110.
Macro expansion using arguments can use the # character to convert an argument to a string, and the ##
sequence to concatenate tokens.
5.12.2 Predefined Macros
The compiler drivers define certain symbols to the preprocessor (CPP), allowing conditional compilation
based on chip type etc. The symbols listed in Table 5 - 10 on page 111 show the more common symbols
defined by the drivers. Each symbol, if defined, is equated to 1 unless otherwise stated. 4
5.12.3 Pragma Directives
There are certain compile-time directives that can be used to modify the behaviour of the compiler.
These are implemented through the use of the ANSI standard #pragma facility. The format of a pragma
is:
#pragma keyword options
where keyword is one of a set of keywords, some of which are followed by certain options. A list of
the keywords is given in Table 5 - 11 on page 112. Those keywords not discussed elsewhere are detailed
below.
5.12.3.1 The #pragma jis and nojis Directives
If your code includes strings with two-byte characters in the JIS encoding for Japanese and other
national characters, the #pragma jis directive will enable proper handling of these characters,
specifically not interpreting a backslash “\” character when it appears as the second half of a two byte
character. The nojis directive disables this special handling. JIS character handling is disabled by
default.
5.12.3.2 The #pragma printf_check Directive
Certain library functions accept a format string followed by a variable number of arguments in the
manner of printf(). Although the format string is interpreted at run-time, it can be compile-time
110
Preprocessing
Placing code in a different psect is done by redirecting the text psect. To redirect code the following
preprocessor directive can be used.
#pragma psect text=othercode
where othercode is the name of the new psect to be created and filled.
This example will define the psect othercode0 for function()’s code, and othercode1 for
another()’s code.
Any given psect should only be redirected once in a particular source file, and all psect redirections for
a particular source file should be placed at the top of the file, below any #include statements and above
112
Preprocessing
any other declarations. For example, to declare a group of uninitialized variables which are all placed in
a psect called otherram, the following technique should be used:
--File OTHERRAM.C
#pragma psect bss=otherram
char buffer[5];
int var1, var2, var3;
Any files which need to access the variables defined in otherram.c should #include the following
header file:
--File OTHERRAM.H
extern char buffer[5];
extern int var1, var2, var3;
The #pragma psect directive allows code and data to be split into arbitrary memory areas. Definitions
of code or data for non-standard psects should be kept in separate source files as documented above.
When linking code which uses non-standard psect names, you will need to use the PICC18 -L option to
specify an extra linker option, drive the linker manually. If you want a nearly standard configuration with
the addition of only an extra psect like otherram, you can use the PICC18 -L option to add an extra -P
specification to the linker command. For example:
4
PICC18 -L-Potherram=200h -18C452 test.obj otherram.obj
will link test.obj and otherram.obj with a standard configuration, and the extra otherram psect at
200h in RAM.
5.12.3.4 The #pragma regsused Directive
PICC-18 will automatically save context when an interrupt occurs. The compiler will determine only
those registers and objects which need to be saved for the particular interrupt function defined. The
#pragma regsused directive allows the programmer to further limit the registers and objects that the
compiler might save and retrieve on interrupt.
Table 5 - 12 on page 114 shows registers names that would commonly be used with this directive. The
register names are not case sensitive and a warning will be produced if the register name is not
recognised.
This pragma affects the first interrupt function following in the source code. Code for High-End devices
which contains multiple interrupt functions should include one directive for each interrupt function.
For example, to limit the compiler to saving no registers other than the W register and FSR register for
an interrupt function, use:
#pragma regsused w fsr
114
Linking Programs
Program statistics:
C prototype used to call them. The simplest method of determining the correct signature for a function
is to write a dummy C function with the same prototype and compile it to assembly language using the
PICC18 -S option. For example, suppose you have an assembly language routine called _widget which
takes two int arguments and returns a char value. The prototype used to call this function from C
would be:
extern char widget(int, int);
Where a call to _widget is made in the C code, the signature for a function with two int arguments
and a char return value would be generated. In order to match the correct signature the source code for
widget needs to contain an ASPIC SIGNAT pseudo-op which defines the same signature value. To
determine the correct value, you would write the following code:
char widget(int arg1, int arg2)
{
}
and compile it to assembler code using
4 PICC18 -S x.c
The resultant assembler code includes the following line:
SIGNAT _widget,8297
The SIGNAT pseudo-op tells the assembler to include a record in the .obj file which associates the value
8297 with symbol _widget. The value 8297 is the correct signature for a function with two int
arguments and a char return value. If this line is copied into the .as file where _widget is defined, it
will associate the correct signature with the function and the linker will be able to check for correct
argument passing. For example, if another .c file contains the declaration:
extern char widget(long);
then a different signature will be generated and the linker will report a signature mis-match which will
alert you to the possible existence of incompatible calling conventions.
5.13.3 Linker-Defined Symbols
The link address of a psect can be obtained from the value of a global symbol with name __Lname where
name is the name of the psect. For example, __Lbss is the low bound of the bss psect. The highest
address of a psect (i.e. the link address plus the size) is symbol __Hname. If the psect has different load
and link addresses, as may be the case if the data psect is linked for RAM operation, the load address is
__Bname.
116
Standard I/O Functions and Serial I/O
Before any characters can be written or read using these functions, the putch() and getch() functions
must be written. Other routines which may be required include getche() and kbhit().
You will find samples of serial code which implements the putch() and getch() functions in the file
serial.c in the SAMPLES directory.
118
PICC-18 Macro Assembler
The HI-TECH PICC-18 Macro Assembler assembles source files for the Microchip PIC18 series of
microprocessors. This chapter describes the usage of the assembler and the directives (assembler
pseudo-ops and controls) accepted by the assembler.
The PICC-18 Macro Assembler package includes a linker, librarian, cross reference generator and an
object code converter.
120
PICC-18 Assembly Language
-H Particularly useful in conjunction with the -A option, this option specifies that output
constants should be shown as hexadecimal values rather than decimal values.
-I This option forces listing of macro expansions and unassembled conditionals which would
otherwise be suppressed by a NOLIST assembler control. The -L option is still necessary to
produce a listing.
-LlistfileThis option requests the generation of an assembly listing file. If listfile is specified
then the listing will be written to that file, otherwise it will be written to the standard output.
-O This requests the assembler to perform optimization on the assembly code. Note that the use
of this option slows the assembly down, as the assembler must make an additional pass over
the input code.
-OoutfileBy default the assembler determines the name of the object file to be created by stripping
any suffix or extension (i.e. the portion after the last dot) from the first source filename and
appending .obj. The -O option allows the user to override the default and specify and
explicit filename for the object file.
-S If a byte-size memory location is initialized with a value which is too large to fit in 8 bits, then
the assembler will generate a Size error message. Use of the -S option will suppress this
type of message.
-U Undefined symbols encountered during assembly are treated as external, however an error
message is issued for each undefined symbol unless the -U option is given. Use of this option
suppresses the error messages only, it does not change the generated code. 5
-V This option will include in the object file produced by the assembler, line number and file
name information for the use of a debugger. Note that the line numbers will be assembler code
lines - when assembling a file produced by the compiler, there will be line and file
directives inserted by the compiler so this option is not required.
-Wwidth This option allows specification of the listfile paper width, in characters. Width should be a
decimal number greater than 41. The default width is 80 characters.
-X The object file created by the assembler contains symbol information, including local
symbols, i.e. symbols that are neither public or external. The -X option will prevent the local
symbols from being included in the object file, thereby reducing the file size.
122
PICC-18 Assembly Language
The comment string ;volatile is used to indicate that the memory location being accessed is
associated with a variable that was declared as volatile in the C source code. Accesses to this location
which appear to be redundant will not be removed by the assembler optimizer if this string is present.
The comment string ;wreg free is placed on some CALL instructions. The string indicates that the
WREG was not loaded with a function parameter, i.e. it is not in use. If this string is present,
optimizations may be made to assembler instructions before the function call which load the WREG
redundantly.
6.3.2 Pre-defined Macros
The file sfr.h, contained in the SOURCES directory contains useful definitions for assembler
programming. In particular it contains an assembler macro called loadfsr, which can be used when
you require any of the FSR registers to be loaded. The two arguments to this macro are the FSR register
number and the value to be loaded. For example:
loadfsr 2,1FFh
which will load FSR2 with the value 1FFh. This macro should be used in preference to the lfsr
instruction.
6.3.3 Character Set
The character set used is standard 7 bit ASCII. Alphabetic case is significant for identifiers, but not
opcodes and reserved words. Tabs are treated as equivalent to spaces.
6.3.4 Constants 5
6.3.4.1 Numeric Constants
The assembler performs all arithmetic as signed 32 bit. Errors will be caused if a quantity is too large to
fit in a memory location. The default radix for all numbers is 10. Other radices may be specified by a
trailing base specifier as given in Table 6 - 2.
Radix Format
Binary digits 0 and 1 followed by B
Octal digits 0 to 7 followed by O, Q, o or q
Decimal digits 0 to 9 followed by D, d or nothing
Hexadecimal digits 0 to 9, A to F preceded by Ox or followed by H or h
Hexadecimal numbers must have a leading digit (e.g. 0ffffh) to differentiate them from identifiers.
Hexadecimal constants are accepted in either upper or lower case.
Note that a binary constant must have an upper case B following it, as a lower case b is used for
temporary (numeric) label backward references.
In expressions, real numbers are accepted in the usual format, and are interpreted as IEEE 32-bit format.
A real number may be converted into the truncated IEEE 24-bit format by using the float24 pseudo-
function. Here is an example of its use:
movlw low(float24(31.415926590000002))
6.3.4.2 Character Constants
A character constant is a single character enclosed in single quotes “'”. Multi-character constants may
be specified using double quotes ““”. See “Strings” on page 126.
6.3.5 Delimiters
All numbers and identifiers must be delimited by white space, non-alphanumeric characters or the end
of a line.
6.3.6 Special Characters
There are a few characters that are special in certain contexts. Within a macro body, the character “&” is
used for token concatenation. To use the bitwise & operator within a macro body, escape it by using &&
instead. In a macro argument list, the angle brackets “<” and “>” are used to quote macro arguments.
6.3.7 Identifiers
5 Identifiers are user-defined symbols representing memory locations or numbers. A symbol may contain
any number of characters drawn from the alphabetics, numerics and the special characters dollar, “$”,
question mark, “?” and underscore, “_”. The first character of an identifier may not be numeric. The
case of alpahabetics is significant, e.g. Fred is not the same symbol as fred. Some examples of
identifiers are shown here:
An_identifier
an_identifier
an_identifier1
$$$
?$_12345
6.3.7.1 Significance of Identifiers
Users of other assemblers that attempt to implement forms of data typing for identifiers should note that
this assembler attaches no significance to any symbol, and places no restrictions or expectations on the
usage of a symbol. The names of psects (program sections) and ordinary symbols occupy separate,
overlapping name spaces, but other than this, the assembler does not care whether a symbol is used to
represent bytes, words or chicken sheds. No special syntax is needed or provided to define the addresses
124
PICC-18 Assembly Language
of bits or any other data type, nor will the assembler issue any warnings if a symbol is used in more than
one context. The instruction and addressing mode syntax provide all the information necessary for the
assembler to generate correct code.
6.3.7.2 Assembler-Generated Identifiers
Where a LOCAL directive is used in a macro block, the assembler will generate a unique symbol to
replace each specified identifier in each expansion of that macro. These unique symbols will have the
form ??nnnn where nnnn is a 4 digit number. The user should avoid defining symbols with the same
form.
6.3.7.3 Location Counter
The current location within the active program section is accessible via the symbol “$”. This symbol
expands to the address of the currently executing instruction. The PIC18 PC register will contain the
address of the instruction following the currently executing instruction so $ will not be the same as the
PC register. Thus:
goto $
will represent code that will form an endless loop.
The address represented by $ is a byte address, the same as symbols used with the PIC18 compiler.
When determining an offset for this address, you must take into account the size of each instruction as
a byte quantity. Thus the goto in the following:
goto
movlw
$+6
55h
; size 4 bytes (2 words)
; size 2 bytes (1 word)
5
movwf _foo
will skip the movlw instruction.
6.3.7.4 Register Symbols
When the code generator compiles a C module, it includes a list of EQU directives for some of the more
commonly used SFRs. Thus any assembler that is placed in-line into a C module can uses these register
names. This list of registers can be seen when generating an assembler list file. If in-line assembler code
uses registers other than those listed, then an EQU definition for those registers must be included.
If writing true assembler modules, these SFR definitions will not be present since the code generator
does not process assembler files. Thus to use any SFRs, an EQU definition must be included into then
module.
Another way of using the SFRs is to linker in symbols with the C definitions for the SFRs that are
included in the chip specific header file. Whenever you include <pic18.H> into a C module, all the
available SFRs are defined as absolute C variables. This file cannot be included into an assembler
module, but assembler code can uses these definitions. To use a SFR in in-line assembler code from
within the same C module that includes <pic18.h>, simply use the symbol with an underscore
character prepended to the name. For example:
#include <pic18.h>}
void main(void)
{
asm(“movff wreg,_PORTC”);
To use these definitions from an assembler module you need to place a GLOBAL directive for the symbols
that are to be used in both the C module that includes <pic18.h> and in the assembler module that uses
the symbols. For example, in the C module you will need:
#include <pic18.h>
asm(“GLOBAL _PORTC”);
and in the assembler module you will need:
GLOBAL _PORTC
GLOBAL wreg
psect text,class=CODE,reloc=2
movff wreg,_PORTC
Note that wreg is an exception. Although a GLOBAL declaration is required in the assembler module, the
leading underscore character is not required and the declaration is not required at all in the C module.
5 It is not possible to equate a symbol to a register.
6.3.7.5 Symbolic Labels
A label is a name at the beginning of a statement which is assigned a value equal to the current offset
within the current psect (program section). A label is not the same as a macro name, which also appears
at the beginning of the line in a macro declaration.
A label may be any symbol followed by a colon, “:”. Here are two examples of legitimate labels:
frank:
simon44:
6.3.8 Strings
A string is a sequence of characters not including carriage return or newline characters, enclosed within
matching quotes. Either single quotes “’” or double quotes ““” maybe used, but the opening and closing
quotes must be the same. A string used as an operand to a DB directive may be any length, but a string
126
PICC-18 Assembly Language
used as operand to an instruction must not exceed 1 or 2 characters, depending on the size of the operand
required.
6.3.9 Expressions
Expressions are made up of numbers, symbols, strings and operators. Operators can be unary (one
operand, e.g. not) or binary (two operands, e.g. +). The operators allowable in expressions are listed in
Table 6 - 3. The usual rules governing the syntax of expressions apply.
Table 6 - 3 Operators
Operator Purpose
* Multiplication
+ Addition
- Subtraction
/ Division
= or eq Equality
> or gt Signed greater than
>= or ge Signed greater than or equal to
< or lt Signed less than
<= or le Signed less than or equal to
<> or ne Signed not equal to
low Low byte of operand 5
high High byte of operand
highword High 16 bits of operand
mod Modulus
& Bitwise AND
^ Bitwise XOR (exclusive or)
| Bitwise OR
not Bitwise complement
<< or shl Shift left
>> or shr Shift right
rol Rotate left
ror Rotate right
seg Segment (bank number) of address
float24 24-bit version of real operand
nul Tests if macro argument is null
The operators listed may all be freely combined in both constant and relocatable expressions. The
HI-TECH linker permits relocation of complex expressions, so the results of expressions involving
relocatable identifiers may not be resolved until link time.
6.3.10 Statement Format
Legal statement formats are shown in Table 6 - 4 on page 128. The second form is only legal with certain
directives, such as MACRO, SET and EQU. The label field is optional and if present should contain one
identifier. The name field is mandatory and should also contain one identifier. There is no limitation on
what column or part of the line any part of the statement should appear in.
PSECT text0,class=CODE,local,delta=1
adjust:
call clear_fred
movf flag
btfss 3,2
goto next
incf fred
goto clear_fred
next: decf fred
PSECT rbss,class=RAM,space=1
128
PICC-18 Assembly Language
flag:
ds 1
fred:
ds 1
PSECT text,class=CODE,local,reloc=2
clear_fred:
clrf fred
bcf status,5
return
Note that even though the two blocks of code in the text psect are separated by a block in the rbss psect,
the two text psect blocks will be contiguous when loaded by the linker. In other words, the decf fred
instruction will fall through to the label clear_fred: during execution. The actual location in memory
of the two psects will be determined by the linker. See the linker manual for information on how psect
addresses are determined.
A label defined in a psect is said to be relocatable, that is, its actual memory address is not determined
at assembly time. Note that this does not apply if the label is in the default (unnamed) psect, or in a psect
declared absolute (see the PSECT pseudo-op description below). Any labels declared in an absolute psect
will be absolute, that is their address will be determined by the assembler.
Relocatable expressions may be combined freely in expressions.
6.3.12 Assembler Directives
Assembler directives, or pseudo-ops, are used in a similar way to opcodes, but either do not generate
5
code, or generate non-executable code, i.e. data bytes. The directives are listed in Table 6 - 5 on page
130, and are detailed below.
6.3.12.1 GLOBAL
GLOBAL declares a list of symbols which, if defined within the current module, are made public. If the
symbols are not defined in the current module, it is a reference to symbols in external modules. Example:
GLOBAL lab1,lab2,lab3
6.3.12.2 END
END is optional, but if present should be at the very end of the program. It will terminate the assembly.
If an expression is supplied as an argument, that expression will be used to define the start address of
the program. Whether this is of any use will depend on the linker. Example:
END start_label
Directive Purpose
GLOBAL Make symbols accessible to other modules or allow reference to other modules’
symbols
END End assembly
PSECT Declare or resume program section
ORG Set location counter
EQU Define symbol value
SET Define or re-define symbol value
DB Define constant byte(s)
DW Define constant word(s)
DS Reserve storage
IF Conditional assembly
ELSIF Alternate conditional assembly
ELSE Alternate conditional assembly
ENDIF End conditional assembly
FNADDR Inform linker that a function may be indirectly called
FNARG Inform linker that evaluation of arguments for one function requires calling
another
5 FNBREAK
FNCALL
Break call graph links
Inform linker that one function calls another
FNCONF Supply call graph configuration info for linker
FNINDIR Inform linker that all functions with a particular signature may be indirectly called
FNROOT Inform linker that a function is the “root” of a call graph
FNSIZE Inform linker of argument and local variable sizes for a function
MACRO Macro definition
ENDM End macro definition
LOCAL Define local tabs
ALIGN Align output to the specified boundary
PAGESEL Generate set/reset instruction to set PCLATH for this page
PROCESSOR Define the particular chip for which this file is to be assembl.ed.
REPT Repeat a block of code n times
IRP Repeat a block of code with a list
IRPC Repeat a block of code with a character list
SIGNAT Define function signature
130
PICC-18 Assembly Language
6.3.12.3 PSECT
The PSECT directive declares or resumes a program section. It takes as arguments a name and optionally
a comma-separated list of flags. The allowed flags are listed in Table 6 - 6 below. Once a psect has been
declared it may be resumed later by simply giving its name as an argument to another psect directive;
the flags need not be repeated.
Flag Meaning
abs Psect is absolute
bit Psect holds bit objects
class Specify class name for psect
delta Size of an addressing unit
global Psect is global (default)
limit Upper address limit of psect
local Psect is not global
ovrld Psect will overlap same psect in other modules
pure Psect is to be read-only
reloc Start psect on specified boundary
size Maximum size of psect
space Represents area in which psect will reside
with Place psect in the same page as specified psect 5
T abs defines the current psect as being absolute, i.e. it is to start at location 0. This does not mean
that this module’s contribution to the psect will start at 0, since other modules may contribute
to the same psect.
T The bit flag specifies that a psect hold objects that are 1 bit long. Such psects have a scale
value of 8 to indicate that there are 8 addressable units to each byte of storage.
T The class flag specifies a class name for this psect. Class names are used to allow local psects
to be referred to by a class name at link time, since they cannot be referred to by their own name.
Class names are also useful where the linker address range feature is to be used.
T The delta flag defines the size of an addressing unit. In other words, the number of bytes
covered for an increment in the address. Since the PIC18 devices have both byte-wide ROM
and RAM memory spaces, the delta value associated with PIC18 psects is usually one (which
is the default delta value).
T A psect defined as global will be combined with other global psects of the same name from
132
PICC-18 Assembly Language
The argument to ORG must be either an absolute value, or a value referencing the current psect. In either
case the current location counter is set to the value of the argument. For example:
ORG 100h
will move the location counter to the beginning of the current psect plus 100h. The actual location will
not be known until link time. It is possible to move the location counter backwards.
In order to use the ORG directive to set the location counter to an absolute value, an absolute, overlaid
psect must be used:
PSECT absdata,abs,ovrld
ORG addr
where addr is an absolute address.
6.3.12.5 EQU
This pseudo-op defines a symbol and equates its value to an expression. For example
thomas EQU 123h
The identifier thomas will be given the value 123h. EQU is legal only when the symbol has not
previously been defined. See also SET on page 133
6.3.12.6 SET
This pseudo-op is equivalent to EQU except that allows a symbol to be re-defined. For example
5
thomas SET 0h
6.3.12.7 DB
DB is used to initialize storage as bytes. The argument is a list of expressions, each of which will be
assembled into one byte. Each character of the string will be assembled into one memory location.
An error will occur if the value of an expression is too big to fit into the memory location, e.g. if the
value 1020 is given as an argument to DB.
Examples:
alabel DB ’X’,1,2,3,4,
Note that because the size of an address unit in ROM is 2 bytes, the DB pseudo-op will initialise a word
with the upper byte set to zero.
6.3.12.8 DW
DW operates in a similar fashion to DB, except that it assembles expressions into words. An error will
occur if the value of an expression is too big to fit into a word.
Example:
DW -1, 3664h, ‘A’, 3777Q
6.3.12.9 DS
This directive reserves, but does not initialize, memory locations. The single argument is the number of
bytes to be reserved. Examples:
alabel: DS 23 ;Reserve 23 bytes of memory
xlabel: DS 2+3 ;Reserve 5 bytes of memory
6.3.12.10 FNADDR
This directive tells the linker that a function has its address taken, and thus could be called indirectly
through a function pointer. Such would be the case if a function pointer was assigned the address of a
function. For example, if the function func() had its address assigned to a pointer, the following would
be produced in the assembler code:
FNADDR _func1
which tells the linker that func1() has its address taken.
5 6.3.12.11 FNARG
The directive
FNARG fun1,fun2
tells the linker that evaluation of the arguments to function fun1() involves a call to fun2(), thus the
memory argument memory allocated for the two functions should not overlap. For example, the C
function calls
fred(var1, bill(), 2);
will generate the assembler directive
FNARG _fred,_bill
thereby telling the linker that bill() is called while evaluating the arguments for a call to fred().
6.3.12.12 FNBREAK
This directive is used to break links in the call graph information. The form of this directive is as follows:
134
PICC-18 Assembly Language
FNBREAK fun1,fun2
and is automatically generated when the interrupt_level pragma is used. It states that any calls to
fun1 in trees other than the one rooted at fun2 should not be considered when checking for functions
that appear in multiple call graphs. Fun2() is typically intlevel0 or intlevel1 in compiler-
generated code when the interrupt_level pragma is used. Memory for the auto/parameter area for
a fun1 will only be assigned in the tree rooted at fun2.
6.3.12.13 FNCALL
This directive takes the form:
FNCALL fun1,fun2
FNCALL is usually used in compiler generated code. It tells the linker that function fun1() calls function
fun2(). This information is used by the linker when performing call graph analysis. If you write
assembler code which calls a C function, use the FNCALL directive to ensure that your assembler
function is taken into account. For example, if you have an assembler routine called _fred which calls
a C routine called foo(), in your assembler code you should write:
FNCALL _fred,_foo
6.3.12.14 FNCONF
The FNCONF directive is used to supply the linker with configuration information for a call graph.
FNCONF is written as follows:
FNCONF psect,auto,args 5
where psect is the psect containing the call graph, auto is the prefix on all auto variable symbol
names and args is the prefix on all function argument symbol names. This directive normally appears
in only one place: the runtime startoff code used by compiler-generated code. For the HI-TECH PICC-
18 Compiler the picrt18x.as module should include the directive:
FNCONF bss,?a,?
telling the linker that the call graph is in the rbss psect, auto variable blocks start with ?a and function
argument blocks start with ?.
6.3.12.15 FNINDIR
This directive tells the linker that a function performs an indirect call to another function with a
particular signature (see the SIGNAT directive). The linker must assume worst case that the function
could call any other function which has the same signature and has had its address taken (see the FNADDR
directive). For example, if a function called fred() performs an indirect call to a function with
signature 8249, the compiler will produce the directive:
FNINDIR _fred,8249
6.3.12.16 FNSIZE
The FNSIZE directive informs the linker of the size of the local variable and argument area associated
with a function. These values are used by the linker when building the call graph and assigning addresses
to the variable and argument areas. This directive takes the form:
FNSIZE func,local,args
The named function has a local variable area and argument area as specified, for example
FNSIZE _fred,10,5
means the function fred() has 10 bytes of local variables and 5 bytes of arguments. The function name
arguments to any of the call graph associated directives may be local or global. Local functions are of
course defined in the current module, but most be used in the call graph construction in the same manner
as global names.
6.3.12.17 FNROOT
This directive tells the assembler that a function is a “root function” and thus forms the root of a call
graph. It could either be the C main() function or an interrupt function. For example, the C main
module produces the directive:
FNROOT _main
136
PICC-18 Assembly Language
not. If both ABS and DEF are zero, the third call will be assembled. Conditional assembly blocks may
be nested.
6.3.12.19 MACRO and ENDM
These directives provide for the definition of macros. The MACRO directive should be preceded by the
macro name and optionally followed by a comma-separated list of formal parameters. When the macro
is used, the macro name should be used in the same manner as a machine opcode, followed by a list of
arguments to be substituted for the formal parameters.
For example:
;macro: swap
;args: arg1, arg2 - the NUMBERS of the variables to swap
; arg3 - the NAME of the variable to use for temp storage;
;descr: Swaps two specified variables, where the variables
; are named:
; var_x
; and x is a number.
;uses: Uses the w register.
A point to note in the above example: the “&” character is used to permit the concatenation of macro
parameters with other text, but is removed in the actual expansion. The nul operator may be used within
a macro to test a macro argument, for example:
if nul arg3 ; argument was not supplied.
...
else ; argument was supplied
...
endif
A comment may be suppressed within the expansion of a macro (thus saving space in the macro storage)
by opening the comment with a double semicolon “;;”.
6.3.12.20 LOCAL
The LOCAL directive allows unique labels to be defined for each expansion of a given macro. Any
symbols listed after the LOCAL directive will have a unique assembler generated symbol substituted for
them when the macro is expanded. For example:
down MACRO count
LOCAL more
movlw count
5 more:
movwf
decfsz
tempvar
tempvar
goto more
ENDM
when expanded will include a unique assembler generated label in place of more. For example:
down 4
expands to:
movlw 4
movwf tempvar
??0001 decfsz tempvar
goto ??0001
if invoked a second time, the label more would expand to ??0002.
138
PICC-18 Assembly Language
6.3.12.21 ALIGN
The ALIGN directive aligns whatever is following, data storage or code etc., to the specified boundary
in the psect in which the directive is found. The boundary is specified by a number following the
directive and is a number of bytes. For example, to align output to a 2 byte (even) address within a psect,
the following could be used.
ALIGN 2
Note, however, that what follows will only begin on an even absolute address if the psect begins on an
even address. The ALIGN directive can also be used to ensure that a psect’s length is a multiple of a
certain number. For example, if the above ALIGN directive was placed at the end of a psect, the psect
would have a length that was always an even number of bytes long.
6.3.12.22 REPT
The REPT directive temporarily defines an unnamed macro then expands it a number of times as
determined by its argument. For example:
REPT 3
addwf fred, fred
andwf fred, w
ENDM
will expand to
addwf
andwf
fred,
fred,
fred
w
5
addwf fred, fred
andwf fred, w
addwf fred, fred
andwf fred, w
6.3.12.23 IRP and IRPC
The IRP and IRPC directives operate similarly to REPT. However, instead of repeating the block a fixed
number of times, it is repeated once for each member of an argument list. In the case of IRP the list is a
conventional macro argument list, in the case or IRPC it is each character in one argument. For each
repetition the argument is substituted for one formal parameter.
For example:
PSECT idata_0
IRP number,4865h,6C6Ch,6F00h
DW number
ENDM
PSECT text0
would expand to:
PSECT idata_0
DW 4865h
DW 6C6Ch
DW 6F00h
psect text0
Note that you can use local labels and angle brackets in the same manner as with conventional macros.
The IRPC directive is similar, except it substitutes one character at a time from a string of non-space
characters.
For example:
PSECT idata_0
5 IRPC
DW
char,ABC
‘char’
ENDM
PSECT text0
will expand to:
PSECT idata_0
DW ’A’
DW ’B’
DW ’C’
PSECT text0
6.3.12.24 PAGESEL
It’s sometimes necessary to set the current PCLATH bits so that a modify-PC type instruction will jump
to a location in the current page of ROM, e.g.
140
PICC-18 Assembly Language
PAGESEL $
6.3.12.25 PROCESSOR
The output of the assembler depends on which chip it is desired to assemble for. This can be set on the
command line, or with this directive, e.g.
PROCESSOR 18C452
6.3.12.26 SIGNAT
This directive is used to associate a 16-bit signature value with a label. At link time the linker checks
that all signatures defined for a particular label are the same and produces an error if they are not. The
SIGNAT directive is used by the HI-TECH C compiler to enforce link time checking of function
prototypes and calling conventions.
Use the SIGNAT directive if you want to write assembly language routines which are called from C. For
example:
SIGNAT _fred,8192
will associate the signature value 8192 with symbol _fred. If a different signature value for _fred is
present in any object file, the linker will report an error.
6.3.13 Macro Invocations
When invoking a macro, the argument list must be comma-separated. If it is desired to include a comma
(or other delimiter such as a space) in an argument then angle brackets “<“ and “>” may be used to quote
the argument. In addition the exclamation mark “!” may be used to quote a single character. The
5
character immediately following the exclamation mark will be passed into the macro argument even if
it is normally a comment indicator.
If an argument is preceded by a percent sign “%”, that argument will be evaluated as an expression and
passed as a decimal number, rather than as a string. This is useful if evaluation of the argument inside
the macro body would yield a different result.
6.3.14 Assembler Controls
Assembler controls may be included in the assembler source to control such things as listing format.
These keywords have no significance anywhere else in the program. The control is invoked by the
directive OPT followed by the control name. Some keywords are followed by one or more parameters.
For example:
OPT expand
A list of keywords is given in Table 6 - 7, and each is described further below.
6.3.14.1 COND
Any conditional code will be included in the listing output. See also the NOCOND control.
5 6.3.14.2 EXPAND
When EXPAND is in effect, the code generated by macro expansions will appear in the listing output. See
also the NOEXPAND control.
6.3.14.3 INCLUDE
This control causes the file specified by pathname to be textually included at that point in the assembly
file. The INCLUDE control must be the last control keyword on the line.
6.3.14.4 LIST
If the listing was previously turned off using the NOLIST control, the LIST control on its own will turn
the listing on.
Alternatively, the LIST control may includes options to control the assembly and the listing. The options
are listed in Table 6 - 8. See also the NOLIST control.
6.3.14.5 NOCOND
Any conditional code will not be included in the listing output. See also the COND control.
142
PICC-18 Assembly Language
144
Linker and Utilities Reference Manual
7.1 Introduction
HI-TECH C incorporates a relocating assembler and linker to permit separate compilation of C source
files. This means that a program may be divided into several source files, each of which may be kept to
a manageable size for ease of editing and compilation, then each source file may be compiled separately
and finally all the object files linked together into a single executable program.
This chapter describes the theory behind and the usage of the linker. Note however that in most instances
it will not be necessary to use the linker directly, as the compiler drivers (HI-TIDE, HPD or command
line) will automatically invoke the linker with all necessary arguments. Using the linker directly is not
simple, and should be attempted only by those with a sound knowledge of the compiler and linking in
general.
If it is absolutely necessary to use the linker directly, the best way to start is to copy the linker arguments
constructed by the compiler driver, and modify them as appropriate. This will ensure that the necessary
startup module and arguments are present.
Note also that the linker supplied with HI-TECH C is generic to a wide variety of compilers for several
different processors. Not all features described in this chapter are applicable to all compilers.
The difference between the data and bss psects may be illustrated by considering two external
variables; one is initialised to the value 1, and the other is not initialised. The first will be placed into the
data psect, and the second in the bss psect. The bss psect is always cleared to zeros on startup of the
program, thus the second variable will be initialised at run time to zero. The first will however occupy
space in the program file, and will maintain its initialised value of 1 at startup. It is quite possible to
modify the value of a variable in the data psect during execution, however it is better practice not to do
so, since this leads to more consistent use of variables, and allows for restartable and romable programs.
For more information on the particular psects used in a specific compiler, refer to the appropriate
machine-specific chapter.
146
Operation
The exact manner in which link and load addresses are used depends very much on the particular
compiler and memory model being used.
7.7 Operation
A command to the linker takes the following form:
Option Effect
-Aclass=low-high,... Specify address ranges for a class
-Cx Call graph options
-Cpsect=class Specify a class name for a global psect
-Cbaseaddr Produce binary output file based at baseaddr
-Dclass=delta Specify a class delta value
-Dsymfile Produce old-style symbol file
-Eerrfile Write error messages to errfile
-F Produce .obj file with only symbol records
-Gspec Specify calculation for segment selectors
-Hsymfile Generate symbol file
-H+symfile Generate enhanced symbol file
-I
-Jnum
Ignore undefined symbols
Set maximum number of errors before aborting
6
-K Prevent overlaying function parameter and auto areas
-L Preserve relocation items in .obj file
-LM Preserve segment relocation items in .obj file
-N Sort symbol table in map file by address order
-Nc Sort symbol table in map file by class address order
-Ns Sort symbol table in map file by space address order
-Mmapfile Generate a link map in the named file
Option Effect
-Ooutfile Specify name of output file
-Pspec Specify psect addresses and ordering
-Qprocessor Specify the processor type (for cosmetic reasons only)
-S Inhibit listing of symbols in symbol file
-Sclass=limit[,bound] Specify address limit, and start boundary for a class of psects
-Usymbol Pre-enter symbol in table as undefined
-Vavmap Use file avmap to generate an Avocet format symbol file
-Wwarnlev Set warning level (-10 to 10)
-Wwidth Set map file width (>10)
-X Remove any local symbols from the symbol file
-Z Remove trivial local symbols from symbol file
7.7.1 Numbers in linker options
Several linker options require memory addresses or sizes to be specified. The syntax for all these is
similar. By default, the number will be interpreted as a decimal value. To force interpretation as a hex
number, a trailing “H” should be added, e.g. 765FH will be treated as a hex number.
7.7.2 -Aclass=low-high,...
Normally psects are linked according to the information given to a -P option (see below) but sometimes
it is desired to have a class of psects linked into more than one non-contiguous address range. This option
allows a number of address ranges to be specified for a class. For example:
-ACODE=1020h-7FFEh,8000h-BFFEh
6 specifies that the class CODE is to be linked into the given address ranges. Note that a contribution to a
psect from one module cannot be split, but the linker will attempt to pack each block from each module
into the address ranges, starting with the first specified.
Where there are a number of identical, contiguous address ranges, they may be specified with a repeat
count, e.g.
-ACODE=0-FFFFhx16
specifies that there are 16 contiguous ranges each 64k bytes in size, starting from zero. Even though the
ranges are contiguous, no code will straddle a 64k boundary. The repeat count is specified as the
character “x” or “*” after a range, followed by a count.
148
Operation
7.7.3 -Cx
These options allow control over the call graph information which may be included in the map file
produced by the linker. The -CN option removes the call graph information from the map file. The -CC
option only include the critical paths of the call graph. A function call that is marked with a “*” in a full
call graph is on a critical path and only these calls are included when the -CC option is used. A call graph
is only produced for processors and memory models that use a compiled stack.
7.7.4 -Cpsect=class
This option will allow a psect to be associated with a specific class. Normally this is not required on the
command line since classes are specified in object files.
7.7.5 -Dclass=delta
This option allows the delta value for psects that are members of the specified class to be defined. The
delta value should be a number and represents the number of bytes per addressable unit of objects within
the psects. Most psects do not need this option as they are defined with a delta value.
7.7.6 -Dsymfile
Use this option to produce an old-style symbol file. An old-style symbol file is an ASCII file, where each
line has the link address of the symbol followed by the symbol name.
7.7.7 -Eerrfile
Error messages from the linker are written to standard error (file handle 2). Under DOS there is no
convenient way to redirect this to a file (the compiler drivers will redirect standard error if standard
output is redirected). This option will make the linker write all error messages to the specified file
instead of the screen, which is the default standard error destination.
7.7.8 -F 6
Normally the linker will produce an object file that contains both program code and data bytes, and
symbol information. Sometimes it is desired to produce a symbol-only object file that can be used again
in a subsequent linker run to supply symbol values. The -F option will suppress data and code bytes
from the output file, leaving only the symbol records.
This option can be used when producing more than one hex file for situations where the program is
contained in different memory devices located at different addresses. The files for one device are
compiled using this linker option to produce a symbol-only object file; this is then linked with the files
for the other device. The process can then be repeated for the other files and device.
7.7.9 -Gspec
When linking programs using segmented, or bank-switched psects, there are two ways the linker can
assign segment addresses, or selectors, to each segment. A segment is defined as a contiguous group of
psects where each psect in sequence has both its link and load address concatenated with the previous
psect in the group. The segment address or selector for the segment is the value derived when a segment
type relocation is processed by the linker.
By default the segment selector will be generated by dividing the base load address of the segment by
the relocation quantum of the segment, which is based on the reloc= directive value given to psects at
the assembler level. This is appropriate for 8086 real mode code, but not for protected mode or some
bank-switched arrangements. In this instance the -G option is used to specify a method for calculating
the segment selector. The argument to -G is a string similar to:
A/10h-4h
where A represents the load address of the segment and / represents division. This means "Take the load
address of the psect, divide by 10 hex, then subtract 4". This form can be modified by substituting N for
A, * for / (to represent multiplication), and adding rather than subtracting a constant. The token N is
replaced by the ordinal number of the segment, which is allocated by the linker. For example:
N*8+4
means "take the segment number, multiply by 8 then add 4". The result is the segment selector. This
particular example would allocate segment selectors in the sequence 4, 12, 20, ... for the number of
segments defined. This would be appropriate when compiling for 80286 protected mode, where these
selectors would represent LDT entries.
7.7.10 -Hsymfile
This option will instruct the linker to generate a symbol file. The optional argument symfile specifies
6 a file to receive the symbol file. The default file name is l.sym.
7.7.11 -H+symfile
This option will instruct the linker to generate an enhanced symbol file, which provides, in addition to
the standard symbol file, class names associated with each symbol and a segments section which lists
each class name and the range of memory it occupies. This format is recommended if the code is to be
run in conjunction with a debugger. The optional argument symfile specifies a file to receive the
symbol file. The default file name is l.sym.
7.7.12 -Jerrcount
The linker will stop processing object files after a certain number of errors (other than warnings). The
default number is 10, but the -J option allows this to be altered.
150
Operation
7.7.13 -K
For compilers that use a compiled stack, the linker will try and overlay function auto and parameter areas
in an attempt to reduce the total amount of RAM required. For debugging purposes, this feature can be
disabled with this option.
7.7.14 -I
Usually failure to resolve a reference to an undefined symbol is a fatal error. Use of this option will cause
undefined symbols to be treated as warnings instead.
7.7.15 -L
When the linker produces an output file it does not usually preserve any relocation information, since
the file is now absolute. In some circumstances a further "relocation" of the program will be done at load
time, e.g. when running a .exe file under DOS or a .prg file under TOS. This requires that some
information about what addresses require relocation is preserved in the object (and subsequently the
executable) file. The -L option will generate in the output file one null relocation record for each
relocation record in the input.
7.7.16 -LM
Similar to the above option, this preserves relocation records in the output file, but only segment
relocations. This is used particularly for generating .exe files to run under DOS.
7.7.17 -Mmapfile
This option causes the linker to generate a link map in the named file, or on the standard output if the
file name is omitted. The format of the map file is illustrated in Section 7.9 on page 155.
7.7.18 -N, -Ns and-Nc
By default the symbol table in the link map will be sorted by name. The -N option will cause it to be 6
sorted numerically, based on the value of the symbol. The -Ns and -Nc options work similarly except
that the symbols are grouped by either their space value, or class.
7.7.19 -Ooutfile
This option allows specification of an output file name for the linker. The default output file name is
l.obj. Use of this option will override the default.
7.7.20 -Pspec
Psects are linked together and assigned addresses based on information supplied to the linker via -P
options. The argument to the -P option consists basically of comma-separated sequences thus:
-Ppsect=lnkaddr+min/ldaddr+min,psect=lnkaddr/ldaddr, ...
There are several variations, but essentially each psect is listed with its desired link and load addresses,
and a minimum value. All values may be omitted, in which case a default will apply, depending on
previous values.
The minimum value, min, is preceded by a + sign, if present. It sets a minimum value for the link or load
address. The address will be calculated as described below, but if it is less than the minimum then it will
be set equal to the minimum.
The link and load addresses are either numbers as described above, or the names of other psects or
classes, or special tokens. If the link address is a negative number, the psect is linked in reverse order
with the top of the psect appearing at the specified address minus one. Psects following a negative
address will be placed before the first psect in memory. If a link address is omitted, the psect's link
address will be derived from the top of the previous psect, e.g.
-Ptext=100h,data,bss
In this example the text psect is linked at 100 hex (its load address defaults to the same). The data psect
will be linked (and loaded) at an address which is 100 hex plus the length of the text psect, rounded up
as necessary if the data psect has a reloc= value associated with it. Similarly, the bss psect will
concatenate with the data psect. Again:
-Ptext=-100h,data,bss
will link in assending order bss, data then text with the top of text appearing at address 0ffh.
If the load address is omitted entirely, it defaults to the same as the link address. If the slash “/” character
is supplied, but no address is supplied after it, the load address will concatenate with the previous psect,
e.g.
6 -Ptext=0,data=0/,bss
will cause both text and data to have a link address of zero, text will have a load address of 0, and data
will have a load address starting after the end of text. The bss psect will concatenate with data for both
link and load addresses.
The load address may be replaced with a dot “.” character. This tells the linker to set the load address
of this psect to the same as its link address. The link or load address may also be the name of another
(already linked) psect. This will explicitly concatenate the current psect with the previously specified
psect, e.g.
-Ptext=0,data=8000h/,bss/. -Pnvram=bss,heap
152
Operation
This example shows text at zero, data linked at 8000h but loaded after text, bss is linked and loaded
at 8000h plus the size of data, and nvram and heap are concatenated with bss. Note here the use of two
-P options. Multiple -P options are processed in order.
If -A options have been used to specify address ranges for a class then this class name may be used in
place of a link or load address, and space will be found in one of the address ranges. For example:
-ACODE=8000h-BFFEh,E000h-FFFEh
-Pdata=C000h/CODE
This will link data at C000h, but find space to load it in the address ranges associated with CODE. If no
sufficiently large space is available, an error will result. Note that in this case the data psect will still be
assembled into one contiguous block, whereas other psects in the class CODE will be distributed into the
address ranges wherever they will fit. This means that if there are two or more psects in class CODE, they
may be intermixed in the address ranges.
Any psects allocated by a -P option will have their load address range subtracted from any address
ranges specified with the -A option. This allows a range to be specified with the -A option without
knowing in advance how much of the lower part of the range, for example, will be required for other
psects.
7.7.21 -Qprocessor
This option allows a processor type to be specified. This is purely for information placed in the map file.
The argument to this option is a string describing the processor.
7.7.22 -S
This option prevents symbol information relating from being included in the symbol file produced by
the linker. Segment information is still included.
7.7.23 -Sclass=limit[, bound] 6
A class of psects may have an upper address limit associated with it. The following example places a
limit on the maximum address of the CODE class of psects to one less than 400h.
-SCODE=400h
Note that to set an upper limit to a psect, this must be set in assembler code (with a limit= flag on a
PSECT directive).
If the bound (boundary) argument is used, the class of psects will start on a multiple of the bound
address. This example places the FARCODE class of psects at a multiple of 1000h, but with an upper
address limit of 6000h:
-SFARCODE=6000h,1000h
7.7.24 -Usymbol
This option will enter the specified symbol into the linker's symbol table as an undefined symbol. This
is useful for linking entirely from libraries, or for linking a module from a library where the ordering has
been arranged so that by default a later module will be linked.
7.7.25 -Vavmap
To produce an Avocet format symbol file, the linker needs to be given a map file to allow it to map psect
names to Avocet memory identifiers. The avmap file will normally be supplied with the compiler, or
created automatically by the compiler driver as required.
7.7.26 -Wnum
The -W option can be used to set the warning level, in the range -9 to 9, or the width of the map file, for
values of num >= 10.
-W9 will suppress all warning messages. -W0 is the default. Setting the warning level to -9 (-W-9) will
give the most comprehensive warning messages.
7.7.27 -X
Local symbols can be suppressed from a symbol file with this option. Global symbols will always appear
in the symbol file.
7.7.28 -Z
Some local symbols are compiler generated and not of interest in debugging. This option will suppress
from the symbol file all local symbols that have the form of a single alphabetic character, followed by a
digit string. The set of letters that can start a trivial symbol is currently "klfLSu". The -Z option will
strip any local symbols starting with one of these letters, and followed by a digit string.
6 7.8 Invoking the Linker
The linker is called HLINK, and normally resides in the BIN subdirectory of the compiler installation
directory. It may be invoked with no arguments, in which case it will prompt for input from standard
input. If the standard input is a file, no prompts will be printed. This manner of invocation is generally
useful if the number of arguments to HLINK is large. Even if the list of files is too long to fit on one line,
continuation lines may be included by leaving a backslash “\” at the end of the preceding line. In this
fashion, HLINK commands of almost unlimited length may be issued. For example a link command file
called x.lnk and containing the following text:
-Z -OX.OBJ -MX.MAP \
-Ptext=0,data=0/,bss,nvram=bss/. \
X.OBJ Y.OBJ Z.OBJ C:\HT-Z80\LIB\Z80-SC.LIB
154
Map Files
C:\HT-Z80\LIB\z80-sc.lib
powerup.obj vectors 71 71 1
CLASS DATA
bss 8000 8000 24
156
Map Files
The graph shows the functions called and the memory usage (RAM) of the functions for their own local
objects. In the example above, the symbol _main is associated with the function main(). It is shown at
the far left of the call graph. This indicates that it is the root of a call tree. The run-time code has the
FNROOT assembler directive that specifies this. The size field after the name indicates the number of
parameters and auto variables, respectively. Here, main() takes no parameters and defines no auto
variables. The offset field is the offset at which the function’s parameters and auto variables have been
placed from the beginning of the area of memory used for this purpose. The run-time code contains a
FNCONF directive which tells the compiler in which psect parameters and auto variables should reside.
This memory will be shown in the map file under the name COMMON.
Main() calls a function called init(). This function uses a total of two bytes of parameters (it may be
two objects of type char or one int; that is not important) and has three bytes of auto variables. These
figures are the total of bytes of memory consumed by the function. If the function was passed a two-byte
int, but that was done via a register, then the two bytes would not be included in this total. Since
main() did not use any of the local object memory, the offset of init()’s memory is still at 0.
The function init() itself calls another function called ports(). This function uses two bytes of
parameters and another two bytes of auto variables. Since ports() is called by init(), its local
variables cannot be overlapped with those of init()’s, so the offset is 5, which means that ports()’s
local objects were placed immediately after those of init()’s.
The function main also calls sprintf(). Since the function sprintf is not active at the same time as
init() or ports(), their local objects can be overlapped and the offset is hence set to 0. Sprintf()
calls a function putch(), but this function uses no memory for parameters (the char passed as
argument is apparently done so via a register) or locals, so the size and offset are zero and are not printed.
Main() also calls another function indirectly using a function pointer. This is indicated by the two
INDIRECT entries in the graph. The number following is the signature value of functions that could
potentially be called by the indirect call. This number is calculated from the parameters and return type
of the functions the pointer can indirectly call. The names of any functions that have this signature value
are listed underneath the INDIRECT entries. Their inclusion does not mean that they were called (there
6
is no way to determine that), but that they could potentially be called.
The last line shows another function whose name is at the far left of the call graph. This implies that this
is the root of another call graph tree. This is an interrupt function which is not called by any code,
but which is automatically invoked when an enabled interrupt occurs. This interrupt routine calls the
function incr(), which is shown shorthand in the graph by the “->” symbol followed by the called
function’s name instead of having that function shown indented on the following line. This is done
whenever the calling function does not takes parameters, nor defines any variables.
Those lines in the graph which are starred “*” are those functions which are on a critical path in terms
of RAM usage. For example, in the above, (main() is a trivial example) consider the function
sprintf(). This uses a large amount of local memory and if you could somehow rewrite it so that it
used less local memory, it would reduce the entire program’s RAM usage. The functions init() and
ports() have had their local memory overlapped with that of sprintf(), so reducing the size of these
functions’ local memory will have no affect on the program’s RAM usage. Their memory usage could
be increased, as long as the total size of the memory used by these two functions did not exceed that of
sprintf(), with no additional memory used by the program. So if you have to reduce the amount of
RAM used by the program, look at those functions that are starred.
If, when searching a call graph, you notice that a function’s parameter and auto areas have been
overlapped (i.e. ?a_foo was placed at the same address as ?_foo, for example), then check to make
sure that you have actually called the function in your program. If the linker has not seen a function
actually called, then it overlaps these areas of memory since that are not needed. This is a consequence
of the linker’s ability to overlap the local memory areas of functions which are not active at the same
time. Once the function is called, unique addresses will be assigned to both the parameters and auto
objects.
If you are writing a routine that calls C code from assembler, you will need to include the appropriate
assembler directives to ensure that the linker sees the C function being called.
7.10 Librarian
The librarian program, LIBR, has the function of combining several object files into a single file known
as a library. The purposes of combining several such object modules are several.
T fewer files to link
T faster access
T uses less disk space
In order to make the library concept useful, it is necessary for the linker to treat modules in a library
differently from object files. If an object file is specified to the linker, it will be linked into the final
6 linked module. A module in a library, however, will only be linked in if it defines one or more symbols
previously known, but not defined, to the linker. Thus modules in a library will be linked only if
required. Since the choice of modules to link is made on the first pass of the linker, and the library is
searched in a linear fashion, it is possible to order the modules in a library to produce special effects
when linking. More will be said about this later.
7.10.1 The Library Format
The modules in a library are basically just concatenated, but at the beginning of a library is maintained
a directory of the modules and symbols in the library. Since this directory is smaller than the sum of the
modules, the linker is speeded up when searching a library since it need read only the directory and not
all the modules on the first pass. On the second pass it need read only those modules which are required,
seeking over the others. This all minimises disk I/O when linking.
158
Librarian
It should be noted that the library format is geared exclusively toward object modules, and is not a
general purpose archiving mechanism as is used by some other compiler systems. This has the advantage
that the format may be optimized toward speeding up the linkage process.
7.10.2 Using the Librarian
The librarian program is called LIBR, and the format of commands to it is as follows:
libr options k file.lib file.obj ...
Interpreting this, LIBR is the name of the program, options is zero or more librarian options which
affect the output of the program. k is a key letter denoting the function requested of the librarian
(replacing, extracting or deleting modules, listing modules or symbols), file.lib is the name of the
library file to be operated on, and file.obj is zero or more object file names.
The librarian options are listed in Table 7 - 2.
Option Effect
-Pwidth specify page width
-W suppress non-fatal errors
Key Meaning
r Replace modules
d
x
Delete modules
Extract modules
6
m List modules
s List modules with symbols
When replacing or extracting modules, the file.obj arguments are the names of the modules to be
replaced or extracted. If no such arguments are supplied, all the modules in the library will be replaced
or extracted respectively. Adding a file to a library is performed by requesting the librarian to replace it
in the library. Since it is not present, the module will be appended to the library. If the r key is used and
the library does not exist, it will be created.
Under the d key letter, the named object files will be deleted from the library. In this instance, it is an
error not to give any object file names.
The m and s key letters will list the named modules and, in the case of the s keyletter, the symbols
defined or referenced within (global symbols only are handled by the librarian). As with the r and x key
letters, an empty list of modules means all the modules in the library.
7.10.3 Examples
Here are some examples of usage of the librarian. The following lists the global symbols in the modules
a.obj, b.obj and c.obj:
libr s file.lib a.obj b.obj c.obj
This command deletes the object modules a.obj, b.obj and 2.obj from the library file.lib:
libr d file.lib a.obj b.obj 2.obj
7.10.4 Supplying Arguments
Since it is often necessary to supply many object file arguments to LIBR, and command lines are
restricted to 127 characters by CP/M and MS-DOS, LIBR will accept commands from standard input if
no command line arguments are given. If the standard input is attached to the console, LIBR will prompt
for input. Multiple line input may be given by using a backslash as a continuation character on the end
of a line. If standard input is redirected from a file, LIBR will take input from the file, without prompting.
For example:
libr
libr> r file.lib 1.obj 2.obj 3.obj \
libr> 4.obj 5.obj 6.obj
will perform much the same as if the object files had been typed on the command line. The libr>
prompts were printed by LIBR itself, the remainder of the text was typed as input.
6 libr <lib.cmd
LIBR will read input from lib.cmd, and execute the command found therein. This allows a virtually
unlimited length command to be given to LIBR.
7.10.5 Listing Format
A request to LIBR to list module names will simply produce a list of names, one per line, on standard
output. The s keyletter will produce the same, with a list of symbols after each module name. Each
symbol will be preceded by the letter D or U, representing a definition or reference to the symbol
respectively. The -P option may be used to determine the width of the paper for this operation. For
example:
LIBR -P80 s file.lib
160
Objtohex
will list all modules in file.lib with their global symbols, with the output formatted for an 80 column
printer or display.
7.10.6 Ordering of Libraries
The librarian creates libraries with the modules in the order in which they were given on the command
line. When updating a library the order of the modules is preserved. Any new modules added to a library
after it has been created will be appended to the end.
The ordering of the modules in a library is significant to the linker. If a library contains a module which
references a symbol defined in another module in the same library, the module defining the symbol
should come after the module referencing the symbol.
7.10.7 Error Messages
LIBR issues various error messages, most of which represent a fatal error, while some represent a
harmless occurrence which will nonetheless be reported unless the -W option was used. In this case all
warning messages will be suppressed.
7.11 Objtohex
The HI-TECH linker is capable of producing simple binary files, or object files as output. Any other
format required must be produced by running the utility program OBJTOHEX. This allows conversion of
object files as produced by the linker into a variety of different formats, including various hex formats.
The program is invoked thus:
objtohex options inputfile outputfile
All of the arguments are optional. If outputfile is omitted it defaults to l.hex or l.bin depending
on whether the -b option is used. The inputfile defaults to l.obj.
The options for OBJTOHEX are listed in Table 7 - 4 on page 162. Where an address is required, the format
is the same as for HLINK:.
6
7.11.1 Checksum Specifications
The checksum specification allows automated checksum calculation. The checksum specification takes
the form of several lines, each line describing one checksum. The syntax of a checksum line is:
addr1-addr2 where1-where2 +offset
All of addr1, addr2, where1, where2 and offset are hex numbers, without the usual H suffix. Such
a specification says that the bytes at addr1 through to addr2 inclusive should be summed and the sum
placed in the locations where1 through where2 inclusive. For an 8 bit checksum these two addresses
should be the same. For a checksum stored low byte first, where1 should be less than where2, and vice
Option Meaning
-A Produce an ATDOS .atx output file
-Bbase Produce a binary file with offset of base. Default file name is l.obj
-Cckfile Read a list of checksum specifications from ckfile or standard input
-D Produce a COD file
-E Produce an MS-DOS .exe file
-Ffill Fill unused memory with words of value fill - default value is 0FFh
-I Produce an Intel HEX file with linear addressed extended records.
-L Pass relocation information into the output file (used with .exe files)
-M Produce a Motorola HEX file (S19, S28 or S37 format)
-N Produce an output file for Minix
-Pstk Produce an output file for an Atari ST, with optional stack size
-R Include relocation information in the output file
-Sfile Write a symbol file into file
-T Produce a Tektronix HEX file. -TE produces an extended TekHEX file.
-U Produce a COFF output file
-UB Produce a UBROF format file
-V Reverse the order of words and long words in the output file
-x Create an x.out format file
-n,m Format either Motorola or Intel HEX file where n is the max number of
bytes per record and m specifies the multiple to which the record size is
rounded
6 versa. The +offset is optional, but if supplied, the value offset will be used to initialise the checksum.
Otherwise it is initialised to zero. For example:
0005-1FFF 3-4 +1FFF
This will sum the bytes in 5 through 1FFFH inclusive, then add 1FFFH to the sum. The 16 bit checksum
will be placed in locations 3 and 4, low byte in 3. The checksum is initialised with 1FFFH to provide
protection against an all zero ROM, or a ROM misplaced in memory. A run time check of this checksum
would add the last address of the ROM being checksummed into the checksum. For the ROM in
question, this should be 1FFFH. The initialization value may, however, be used in any desired fashion.
162
Cref
7.12 Cref
The cross reference list utility CREF is used to format raw cross-reference information produced by the
compiler or the assembler into a sorted listing. A raw cross-reference file is produced with the -CR
option to the compiler. The assembler will generate a raw cross-reference file with a -C option (most
assemblers) or by using an OPT CRE directive (6800 series assemblers) or a XREF control line (PIC
assembler). The general form of the CREF command is:
cref options files
where options is zero or more options as described below and files is one or more raw cross-
reference files. CREF takes the options listed in Table 7 - 5 on page 163. Each option is described in more
Option Meaning
-Fprefix Exclude symbols from files with a pathname or
filename starting with prefix
-Hheading Specify a heading for the listing file
-Llen Specify the page length for the listing file
-Ooutfile Specify the name of the listing file
-Pwidth Set the listing width
-Sstoplist Read file stoplist and ignore any symbols listed.
-Xprefix Exclude any symbols starting with the given prefix
7.12.4 -Ooutfile
Allows specification of the output file name. By default the listing will be written to the standard output
and may be redirected in the usual manner. Alternatively outfile may be specified as the output file
name.
7.12.5 -Pwidth
This option allows the specification of the width to which the listing is to be formatted, e.g. -P132 will
format the listing for a 132 column printer. The default is 80 columns.
7.12.6 -Sstoplist
The -S option should have as its argument the name of a file containing a list of symbols not to be listed
in the cross-reference. Multiple stoplists may be supplied with multiple -S options.
7.12.7 -Xprefix
The -X option allows the exclusion of symbols from the listing, based on a prefix given as argument to
-X. For example if it was desired to exclude all symbols starting with the character sequence xyz then
the option -Xxyz would be used. If a digit appears in the character sequence then this will match any
digit in the symbol, e.g. -XX0 would exclude any symbols starting with the letter X followed by a digit.
CREF will accept wildcard filenames and I/O redirection. Long command lines may be supplied by
invoking CREF with no arguments and typing the command line in response to the cref> prompt. A
backslash at the end of the line will be interpreted to mean that more command lines follow.
7.13 Cromwell
The CROMWELL utility converts code and symbol files into different formats. The formats available are
shown in Table 7 - 6.
6 The general form of the CROMWELL command is:
cromwell options input_files -okey output_file
where options can be any of the options shown in Table 7 - 7. Output_file (optional) is the name
of the output file. The input_files are typically the HEX and SYM file. CROMWELL automatically
searches for the SDB files and reads those if they are found. The options are further described in the
following paragraphs.
7.13.1 -Pname
The -P options takes a string which is the name of the processor used. CROMWELL may use this in the
generation of the output format selected.
164
Cromwell
Key Format
cod Bytecraft COD file
coff COFF file format
elf ELF/DWARF file
eomf51 Extended OMF-51 format
hitech HI-TECH Software format
icoff ICOFF file format
ihex Intel HEX file format
omf51 OMF-51 file format
pe P&E file format
s19 Motorola HEX file format
7.13.2 -D
The -D option is used to display to the screen details about the named input file in a readable format.
Option Description
-Pname Processor name
-D Dump input file
-C Identify input files only
-F Fake local symbols as globals
-Okey Set the output format
-Ikey
-L
Set the input format
List the available formats
6
-E Strip file extensions
-B Specify big-endian byte ordering
-M Strip underscore character
-V Verbose mode
The input file can be one of the file types as shown in Table 7 - 6.
7.13.3 -C
This option will attempt to identify if the specified input files are one of the formats as shown in Table
7 - 6. If the file is recognised, a confirmation of its type will be displayed.
7.13.4 -F
When generating a COD file, this option can be used to force all local symbols to be represented as
global symbols. The may be useful where an emulator cannot read local symbol information from the
COD file.
7.13.5 -Okey
This option specifies the format of the output file. The key can be any of the types listed in Table 7 - 6.
7.13.6 -Ikey
This option can be used to specify the default input file format. The key can be any of the types listed
in Table 7 - 6.
7.13.7 -L
Use this option to show what file format types are supported. A list similar to that given in Table 7 - 6
will be shown.
7.13.8 -E
Use this option to tell CROMWELL to ignore any filename extensions that were given. The default
extension will be used instead.
7.13.9 -B
In formats that support different endian types, use this option to specify big-endian byte ordering.
7.13.10 -M
When generating COD files this option will remove the preceeding underscore character from symbols.
6 7.13.11 -V
Turns on verbose mode which will display information about operations CROMWELL is performing.
7.14 Memmap
MEMMAP has been individualized for each processor. The MEMMAP program that appears in your BIN
directory will conform with the following criteria; XXmap.exe where XX stands for the processor type.
From here on, we will be referring to this application as MEMMAP, as to cover all processors.
At the end of compilation and linking, HPD and the command line compiler produce a summary of
memory usage. If, however, the compilation is performed in separate stages and the linker is invoked
explicitly, this memory information is not displayed. The MEMMAP program reads the information stored
166
Memmap
in the map file and produces either a summary of psect address allocation or a memory map of program
sections similar to that shown by HPD and the command line compiler.
7.14.1 Using MEMMAP
A command to the memory usage program takes the form:
memmap options file
Options is zero or more MEMMAP options which are listed in Table 7 - 8 on page 167. File is the name
Option Effect
-P Print psect usage map
-Wwid Specifies width to which address are printed
168
Error Messages
This chapter lists all possible error messages from the HI-TECH C compiler, with an explanation of each
one. The name of the applications that could have produced the error are listed in brackets opposite the
error message. The tutorial chapter describes the function of each application.
170
#if ... sizeof() syntax error(Preprocessor)
The preprocessor found a syntax error in the argument to sizeof, in a #if expression. Probable causes are
mismatched parentheses and similar things.
#if ... sizeof: bug, unknown type code *(Preprocessor)
The preprocessor has made an internal error in evaluating a sizeof() expression. Check for a malformed
type specifier.
#if ... sizeof: illegal type combination(Preprocessor)
The preprocessor found an illegal type combination in the argument to sizeof() in a #if expression.
Illegal combinations include such things as "short long int".
#if bug, operand = *(Preprocessor)
The preprocessor has tried to evaluate an expression with an operator it does not understand. This is an
internal error.
#if sizeof() error, no type specified(Preprocessor)
Sizeof() was used in a preprocessor #if expression, but no type was specified. The argument to sizeof()
in a preprocessor expression must be a valid simple type, or pointer to a simple type.
#if sizeof, unknown type *(Preprocessor)
An unknown type was used in a preprocessor sizeof(). The preprocessor can only evaluate sizeof() with
basic types, or pointers to basic types.
#if value stack overflow(Preprocessor)
The preprocessor filled up its expression evaluation stack in a #if expression. Simplify the expression -
it probably contains too many parenthesized subexpressions.
#if, #ifdef, or #ifndef without an argument(Preprocessor)
The preprocessor directives #if, #ifdef and #ifndef must have an argument. The argument to #if should
be an expression, while the argument to #ifdef or #ifndef should be a single name.
#include syntax error(Preprocessor)
The syntax of the filename argument to #include is invalid. The argument to #include must be a valid
file name, either enclosed in double quotes ("") or angle brackets (< >). For example:
#include "afile.h"
#include <otherfile.h>
7
Spaces should not be included, and the closing quote or bracket must be present. There should be
nothing else on the line.
#included file * was converted to lower case(Preprocessor)
The #include file name had to be converted to lowercase before it could be opened.
] expected(Parser)
A closing square bracket was expected in an array declaration or an expression using an array index.
{ expected(Parser)
An opening brace was expected here.
} expected(Parser)
A closing brace was expected here.
a macro name cannot also be a label(Assembler)
A label has been found with the same name as a macro. This is not allowed.
a maximum of * reserved areas are allowed. remainder of -RES* ignored(Driver)
Too many address ranges were specified with either the -RESROM or -RESRAM option.
A maximum of * ROM banks are allowed. Remainder of -ROM option ignored(Driver)
Too many ranges were specified with the -ROM option.
a parameter may not be a function(Parser)
A function parameter may not be a function. It may be a pointer to a function, so perhaps a "*" has been
omitted from the declaration.
a psect may only be in one class(Assembler)
You cannot assign a psect to more than one class. The psect was defined differently at this point than
when it was defined elsewhere.
a psect may only have one ’with’ option(Assembler)
A psect can only be placed ’with’ one other psect.
absolute expression required(Assembler)
An absolute expression is required in this context.
add_reloc - bad size(Assembler)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
ambiguous chip type * -> * or *(Driver)
The chip type specified on the command line is not complete and could refer to more than one chip.
7 Specify the full name of the chip type.
ambiguous format name ’*’(Cromwell)
The output format specified to Cromwell is ambiguous.
argument * conflicts with prototype(Parser)
The argument specified (argument 1 is the left most argument) of this function declaration does not agree
with a previous prototype for this function.
172
argument -w* ignored(Linker)
The argument to the linker option -w is out of range. For warning levels, the range is -9 to 9. For the map
file width, the range is greater than or equal to 10.
argument list conflicts with prototype(Parser)
The argument list in a function definition is not the same as a previous prototype for that function. Check
that the number and types of the arguments are all the same.
argument redeclared: *(Parser)
The specified argument is declared more than once in the same argument list.
argument too long(Preprocessor, Parser)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
arithmetic overflow in constant expression(Code Generator)
A constant expression has been evaluated by the code generator that has resulted in a value that is too
big for the type of the expression, e.g. trying to store the value 256 in a "char".
array dimension on * ignored(Preprocessor)
An array dimension on a function parameter has been ignored because the argument is actually
converted to a pointer when passed. Thus arrays of any size may be passed.
array dimension redeclared(Parser)
An array dimension has been declared as a different non-zero value from its previous declaration. It is
acceptable to redeclare the size of an array that was previously declared with a zero dimension, but not
otherwise.
array index out of bounds(Parser)
An array is being indexed with a constant value that is less than zero, or greater than or equal to the
number of elements in the array.
assertion(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
assertion failed: *(Linker)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
attempt to modify const object(Parser) 7
Objects declared "const" may not be assigned to or modified in any other way.
auto variable * should not be qualified(Parser)
An auto variable should not have qualifiers such as "near" or "far" associated with it. Its storage class is
implicitly defined by the stack organization.
174
bad bconfloat - *(Code Generator)
This is an internal code generator error. Contact HI-TECH technical support with full details of the code
that caused this error.
bad bit number(Assembler, Optimiser)
A bit number must be an absolute expression in the range 0-7.
bad bitfield type(Parser)
A bitfield may only have a type of int.
bad character const(Parser, Assembler, Optimiser)
This character constant is badly formed.
bad character constant in expression(Assembler)
The character constant was expected to consist of only one character, but was found to be greater than
one character.
bad character in extended tekhex line *(Objtohex)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
bad checksum specification(Linker)
A checksum list supplied to the linker is syntatically incorrect.
bad combination of flags(Objtohex)
The combination of options supplied to objtohex is invalid.
bad common spec in -p option(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
bad complex range check(Linker)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
bad complex relocation(Linker)
The linker has been asked to perform complex relocation that is not syntactically correct. Probably
means a corrupted object file.
bad confloat - *(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details. 7
bad conval - *(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
bad dimensions(Code Generator)
The code generator has been passed a declaration that results in an array having a zero dimension.
176
bad non-zero node in call graph(Linker)
The linker has encountered a top level node in the call graph that is referenced from lower down in the
call graph. This probably means the program has indirect recursion, which is not allowed when using a
compiled stack.
bad object code format(Linker)
The object code format of this object file is invalid. This probably means it is either truncated, corrupted,
or not a HI-TECH object file.
bad op * to revlog(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
bad op * to swaplog(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
bad op: "*"(Code Generator)
This is caused by an error in the intermediate code file. You may have run out of disk space for temporary
files.
bad operand(Optimiser)
This operand is invalid. Check the syntax.
bad origin format in spec(Linker)
The origin format in a -p option is not a validly formed decimal, octal or hex number. A hex number
must have a trailing H.
bad overrun address in -a spec(Linker)
The overrun address given in a -A specification is invalid: it should be a valid number, in decimal, octal
or hexadecimal radix. The radix is specified by a trailing O (for octal) or H for hex. Decimal is default.
bad popreg: *(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
bad pragma *(Code Generator)
The code generator has been passed a "pragma" directive that it does not understand.
bad pushreg: *(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details. 7
bad putwsize(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
bad record type *(Linker)
This indicates that the object file is not a valid HI-TECH object file.
178
bad string * in psect pragma(Code Generator)
The code generator has been passed a "pragma psect" directive that has a badly formed string. "Pragma
psect" should be followed by something of the form "oldname=newname".
bad switch size *(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
bad sx (Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
bad u usage(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
bad uconval - *(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
bad variable syntax(Code Generator)
There is an error in the intermediate code file. This could be caused by running out of disk space for
temporary files.
bad which * after i(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
banked/common conflict(Assembler)
The assembler has found conflicting information that suggests that a symbol is located in the access
bank, but also in the banked RAM area.
binary digit expected(Parser)
A binary digit was expected. The format for a binary number is 0Bxxx where xxx is a string containing
zeroes and/or ones, e.g.
0B0110
bit address overflow(Assembler)
The bit address supplied is outside the XA bit space. The bit address should be in the bit space which
has a range 0h to 3FFh.
bit field too large (* bits)(Code Generator) 7
The maximum number of bits in a bit field is the same as the number of bits in an "int".
bit range check failed *(Linker)
The bit addressing was out of range.
180
can’t create cross reference file *(Assembler)
The cross reference file could not be created. Check that all directories are present. This can also be
caused by the assembler running out of memory.
can’t create temp file(Linker)
The compiler was unable to create a temporary file. Check the DOS Environment variable TEMP (and
TMP) and verify it points to a directory that exists, and that there is space available on that drive. For
example, AUTOEXEC.BAT should have something like:
SET TEMP=C:\TEMP
where the directory C:\TEMP exists.
can’t create temp file *(Code Generator)
The compiler could not create the temporary file named. Check that all the directories in the file path
exist.
can’t enter abs psect(Assembler)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
can’t find op(Assembler, Optimiser)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
can’t find space for psect * in segment *(Linker)
The named psect cannot be placed in the specified segment. This either means that the memory
associated with the segment has been filled, or that the psect cannot be positioned in any of the available
gaps in the memory. Split large functions (for CODE segements) in several smaller functions and ensure
that the optimizers are being used.
can’t generate code for this expression(Code Generator)
This expression is too difficult for the code generator to handle. Try simplifying the expression, e.g.
using a temporary variable to hold an intermediate result.
can’t have ’port’ variable: *(Code Generator)
The qualifier "port" can be used only with pointers or absolute variables. You cannot define a port
variable as the compiler does not allocate space for port variables. You can declare an external port
variable. 7
can’t have ’signed’ and ’unsigned’ together(Parser)
The type modifiers signed and unsigned cannot be used together in the same declaration, as they have
opposite meaning.
can’t have an array of bits or a pointer to bit(Parser)
It is not legal to have an array of bits, or a pointer to bit.
182
can’t open include file *(Assembler)
The named include file could not be opened. Check spelling. This can also be caused by running out of
memory, or running out of file handles.
can’t open input file *(Preprocessor, Assembler)
The specified input file could not be opened. Check the spelling of the file name.
can’t open output file *(Preprocessor, Assembler)
The specified output file could not be created. This could be because a directory in the path name does
not exist.
can’t reopen *(Parser)
The compiler could not reopen a temporary file it had just created.
can’t seek in *(Linker)
The linker can’t seek in the specified file. Make sure the output file is a valid filename.
can’t take address of register variable(Parser)
A variable declared "register" may not have storage allocated for it in memory, and thus it is illegal to
attempt to take the address of it by applying the "&" operator.
can’t take sizeof func(Parser)
Functions don’t have sizes, so you can’t take use the "sizeof" operator on a function.
can’t take sizeof(bit)(Parser)
You can’t take sizeof a bit value, since it is smaller than a byte.
can’t take this address(Parser)
The expression which was the object of the "&" operator is not one that denotes memory storage ("an
lvalue") and therefore its address can not be defined.
can’t use a string in an #if(Preprocessor)
The preprocessor does not allow the use of strings in #if expressions.
cannot get memory(Linker)
The linker is out of memory! This is unlikely to happen, but removing TSR’s etc. is the cure.
cannot open(Linker) 7
A file cannot be opened - check spelling.
cannot open include file *(Preprocessor)
The named include file could not be opened for reading by the preprocessor. Check the spelling of the
filename. If it is a standard header file, not in the current directory, then the name should be enclosed in
angle brackets (<>) not quotes.
184
complex relocation not supported for -r or -l options yet(Linker)
The linker was given a -R or -L option with file that contain complex relocation. This is not yet
supported.
conflicting fnconf records(Linker)
This is probably caused by multiple run-time startoff module. Check the linker arguments, or "Object
Files..." in HPD.
constant conditional branch(Code Generator)
A conditional branch (generated by an "if" statement etc.) always follows the same path. This may
indicate an expression with missing or badly placed parentheses, causing the evaluation to yield a value
different to what you expected, or it may be because you have written something like "while(1)". To
produce an infinite loop, use "for(;;)".
constant conditional branch: possible use of = instead of == (Code Generator)
There is an expression inside an if or other conditional construct, where a constant is being assigned to
a variable. This may mean you have inadvertently used an assignment (=) instead of a compare (==).
constant expression required(Parser)
In this context an expression is required that can be evaluated to a constant at compile time.
constant left operand to ?(Code Generator)
The left operand to a conditional operator (?) is constant, thus the result of the tertiary operator ?: will
always be the same.
constant operand to || or &&(Code Generator)
One operand to the logical operators || or && is a constant. Check the expression for missing or badly
placed parentheses.
constant relational expression(Code Generator)
There is a relational expression that will always be true or false. This may be because e.g. you are
comparing an unsigned number with a negative value, or comparing a variable with a value greater than
the largest number it can represent.
control line * within macro expansion(Preprocessor)
A preprocessor control line (one starting with a #) has been encountered while expanding a macro. This
should not happen. 7
conversion to shorter data type(Code Generator)
Truncation may occur in this expression as the lvalue is of shorter type than the rvalue.
copyexpr: can’t handle v_rtype = *(Assembler)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
186
did not recognize format of input file(Cromwell)
The input file to Cromwell is required to be COD, Intel HEX, Motorola HEX, COFF, OMF51, P&E or
HI-TECH.
digit out of range(Parser, Assembler, Optimiser)
A digit in this number is out of range of the radix for the number, e.g. using the digit 8 in an octal number,
or hex digits A-F in a decimal number. An octal number is denoted by the digit string commencing with
a zero, while a hex number starts with "0X" or "0x".
dimension required(Parser)
Only the most significant (i.e. the first) dimension in a multi-dimension array may not be assigned a
value. All succeeding dimensions must be present.
direct range check failed *(Linker)
The direct addressing was out of range.
divide by zero in #if, zero result assumed(Preprocessor)
Inside a #if expression, there is a division by zero which has been treated as yielding zero.
division by zero(Code Generator)
A constant expression that was being evaluated involved a division by zero.
double float argument required(Parser)
The printf format specifier corresponding to this argument is %f or similar, and requires a floating point
expression. Check for missing or extra format specifiers or arguments to printf.
ds argument must be a positive constant(Assembler)
The argument to the DS assembler directive must be a positive constant.
duplicate * for * in chipinfo file at line *(Assembler, Driver)
The chipinfo file has a processor section with multiple values for a field. Only one value is allowed per
chip.
duplicate -d or -h flag(Linker)
The symbol file name has been specified to the linker for a second time.
duplicate -m flag(Linker)
The linker only likes to see one -m flag, unless one of them does not specify a file name. Two map file 7
names are more than it can handle!
duplicate arch for * in chipinfo file at line *(Assembler, Driver)
The chipinfo file has a processor section with multiple ARCH values. Only one ARCH value is allowed.
duplicate case label(Code Generator)
There are two case labels with the same value in this switch statement.
188
end of string in format specifier(Parser)
The format specifier for the printf() style function is malformed.
end statement inside include file or macro(Assembler)
An END statement was found inside an include file or a macro.
entry point multiply defined(Linker)
There is more than one entry point defined in the object files given the linker.
enum tag or { expected(Parser)
After the keyword "enum" must come either an identifier that is or will be defined as an enum tag, or an
opening brace.
eof in #asm(Preprocessor)
An end of file has been encountered inside a #asm block. This probably means the #endasm is missing
or misspelt.
eof in comment(Preprocessor)
End of file was encountered inside a comment. Check for a missing closing comment flag.
eof inside conditional(Assembler)
END-of-FILE was encountered while scanning for an "endif" to match a previous "if".
eof inside macro def’n(Assembler)
End-of-file was encountered while processing a macro definition. This means there is a missing "endm"
directive.
eof on string file(Parser)
P1 has encountered an unexpected end-of-file while re-reading its file used to store constant strings
before sorting and merging. This is most probably due to running out of disk space. Check free disk
space.
error closing output file(Code Generator, Optimiser)
The compiler detected an error when closing a file. This most probably means there is insufficient disk
space.
error dumping *(Cromwell)
Either the input file to Cromwell is of an unsupported type or that file cannot be dumped to the screen. 7
error in format string(Parser)
There is an error in the format string here. The string has been interpreted as a printf() style format string,
and it is not syntactically correct. If not corrected, this will cause unexpected behaviour at run time.
evaluation period has expired(Driver)
The evaluation period for this compiler has expired. Contact HI-TECH to purchase a full licence.
190
file locking not enabled on network drive(Driver)
The driver has attempted to modify the lock file located in the LIB directory butwas unable to do so.
This has probably resulted from the network drive used to hold the compiler being read only.
file name index out of range in line no. record(Cromwell)
The .COD file has an invalid format in the specified record.
filename work buffer overflow(Preprocessor)
A filename constructed while looking for an include file has exceeded the length of an internal buffer.
Since this buffer is 4096 bytes long, this is unlikely to happen.
fixup overflow in expression *(Linker)
The linker was asked to relocate (fixup) an item that would not fit back into the space after relocation.
For example this will occur if a byte size object is initialized with an address that is bigger than 255. This
error occurred in a complex expression.
fixup overflow referencing *(Linker)
The linker was asked to relocate (fixup) an item that would not fit back into the space after relocation.
For example this will occur if a byte size object is initialized with an address that is bigger than 255.
float param coerced to double(Parser)
Where a non-prototyped function has a parameter declared as "float", the compiler converts this into a
"double float". This is because the default C type conversion conventions provide that when a floating
point number is passed to a non-prototyped function, it will be converted to double. It is important that
the function declaration be consistent with this convention.
form length must be >= 15(Assembler)
The form length specified using the -Flength option must be at least 15 lines. Setting this length to zero
turns off paging. Default value is zero.
formal parameter expected after #(Preprocessor)
The stringization operator # (not to be confused with the leading # used for preprocessor control lines)
must be followed by a formal macro parameter. If you need to stringize a token, you will need to define
a special macro to do it, e.g.
#define __mkstr__(x) #x
then use __mkstr__(token) wherever you need to convert a token into a string.
7
function * appears in multiple call graphs: rooted at *(Linker)
This function can be called from both main line code and interrupt code. Use the reentrant keyword, if
this compiler supports it, or recode to avoid using local variables or parameters, or duplicate the
function.
192
functions can’t return functions(Parser)
A function cannot return a function. It can return a function pointer. A function returning a pointer to a
function could be declared like this: int (* (name()))(). Note the many parentheses that are necessary to
make the parts of the declaration bind correctly.
functions nested too deep(Code Generator)
This error is unlikely to happen with C code, since C cannot have nested functions!
hex digit expected(Parser)
After "0x" should follow at least one of the hex digits 0-9 and A-F or a-f.
I/O error reading symbol table(Cromwell)
Cromwell could not read the symbol table. This could be because the file was truncated or there was
some other problem reading the file.
ident records do not match(Linker)
The object files passed to the linker do not have matching ident records. This means they are for different
processor types.
identifier expected(Parser)
Inside the braces of an "enum" declaration should be a comma-separated list of identifiers.
identifier redefined: *(Parser)
This identifier has already been defined. It cannot be defined again.
identifier redefined: * (from line *)(Parser)
This identifier has been defined twice. The ’from line’ value is the line number of the first declaration.
illegal # command *(Preprocessor)
The preprocessor has encountered a line starting with #, but which is not followed by a recognized
control keyword. This probably means the keyword has been misspelt. Legal control keywords are:
assert, asm, define, elif, else, endasm, endif, error, if, ifdef, ifndef, include, line, pragma, undef.
illegal #if line(Preprocessor)
There is a syntax error in the expression following #if. Check the expression to ensure it is properly
constructed.
illegal #undef argument(Preprocessor) 7
The argument to #undef must be a valid name. It must start with a letter.
illegal ’#’ directive(Preprocessor, Parser)
The compiler does not understand the "#" directive. It is probably a misspelling of a pre-processor "#"
directive.
194
illegal operation on a bit variable(Parser)
Not all operations on bit variables are supported. This operation is one of those.
illegal operator in #if(Preprocessor)
A #if expression has an illegal operator. Check for correct syntax.
illegal or too many -g flags(Linker)
There has been more than one -g option, or the -g option did not have any arguments following. The
arguments specify how the segment addresses are calculated.
illegal or too many -o flags(Linker)
This -o flag is illegal, or another -o option has been encountered. A -o option to the linker must have a
filename. There should be no space between the filename and the -o, e.g. -ofile.obj
illegal or too many -p flags(Linker)
There have been too many -p options passed to the linker, or a -p option was not followed by any
arguments. The arguments of separate -p options may be combined and separated by commas.
illegal record type(Linker)
There is an error in an object file. This is either an invalid object file, or an internal error in the linker.
Try recreating the object file.
illegal register indirection
The register does not exist.
illegal relocation size: *(Linker)
There is an error in the object code format read by the linker. This either means you are using a linker
that is out of date, or that there is an internal error in the assembler or linker.
illegal relocation type: *(Linker)
An object file contained a relocation record with an illegal relocation type. This probably means the file
is corrupted or not an object file.
illegal switch *(Code Generator, Assembler, Optimiser)
This command line option was not understood.
illegal type for array dimension(Parser)
An array dimension must be either an integral type or an enumerated value. 7
illegal type for index expression(Parser)
An index expression must be either integral or an enumerated value.
illegal type for switch expression(Parser)
A "switch" operation must have an expression that is either an integral type or an enumerated value.
196
directory, or a temporary file error has occurred leading to corruption of a temporary file. Check the
setting of the TEMP environment variable. If it refers to a long path name, change it to something
shorter.
incomplete * record body: length = *(Linker)
An object file contained a record with an illegal size. This probably means the file is truncated or not an
object file.
incomplete ident record(Libr)
The IDENT record in the object file was incomplete.
incomplete record(Objtohex, Libr)
The object file passed to objtohex or the librarian is corrupted.
incomplete record: *(Linker)
An object code record is incomplete. This is probably due to a corrupted or invalid object module. Re-
compile the source file, watching for out of disk space errors etc.
incomplete record: type = * length = *
This message is produced by the DUMP or XSTRIP utilities and indicates that the object file is not a
valid HI-TECH object file, or that it has been truncated, possibly due to running out of disk or RAMdisk
space.
incomplete symbol record(Libr)
The SYM record in the object file was incomplete.
inconsistent lineno tables(Cromwell)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
inconsistent storage class(Parser)
A declaration has conflicting storage classes. Only one storage class should appear in a declaration.
inconsistent symbol tables(Cromwell)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
inconsistent type(Parser)
Only one basic type may appear in a declaration, thus combinations like "int float" are illegal.
7
initialisation syntax(Parser)
The initialisation of this object is syntactically incorrect. Check for the correct placement and number
of braces and commas.
initializer in ’extern’ declaration(Parser)
A declaration containing the keyword "extern" has an initialiser. This overrides the "extern" storage
class, since to initialise an object it is necessary to define (i.e. allocate storage for ) it.
198
invalid format specifier or type modifier(Parser)
The format specifier or modifier in the printf() style string is illegal for this particular format.
invalid hex file: *, line *(Cromwell)
The specified Hex file contains an invalid line.
invalid number syntax(Assembler, Optimiser)
The syntax of a number is invalid. This can be, e.g. use of 8 or 9 in an octal number, or other malformed
numbers.
invalid size for fnsize directive(Assembler)
The assembler FNSIZE assembler directive arguments must be positive constants.
inverted common bank in chipinfo file at line *(Assembler, Driver)
The second hex number specified in the COMMON field in the chipinfo file (libpicinfo.ini by default)
must be greater in value than the first.
inverted ICD ROM address in chipinfo file at line *(Driver)
The second hex number specified in the ICD ROM address field in the chipinfo file (libpicinfo.ini by
default) must be greater in value than the first.
inverted ram bank in chipinfo file at line *(Assembler, Driver)
The second hex number specified in the RAM field in the chipinfo file (libpicinfo.ini by default) must
be greater in value than the first.
label identifier expected(Parser)
An identifier denoting a label must appear after "goto".
lexical error(Assembler, Optimiser)
An unrecognized character or token has been seen in the input.
library * is badly ordered(Linker)
This library is badly ordered. It will still link correctly, but it will link faster if better ordered.
library file names should have .lib extension: *(Libr)
Use the .lib extension when specifying a library.
line does not have a newline on the end(Parser) 7
The last line in the file is missing the newline (linefeed, hex 0A) from the end. Some editors will create
such files, which can cause problems for include files. The ANSI C standard requires all source files to
consist of complete lines only.
line too long(Optimiser)
This line is too long. It will not fit into the compiler’s internal buffers. It would require a line over 1000
characters long to do this, so it would normally only occur as a result of macro expansion.
200
mismatched comparision(Code Generator)
A comparison is being made between a variable or expression and a constant value which is not in the
range of possible values for that expression, e.g. if you compare an unsigned character to the constant
value 300, the result will always be false (not equal) since an unsigned character can NEVER equal 300.
As an 8 bit value it can represent only 0-255.
misplaced ’?’ or ’:’, previous operator is *(Preprocessor)
A colon operator has been encountered in a #if expression that does not match up with a corresponding
? operator. Check parentheses etc.
misplaced constant in #if(Preprocessor)
A constant in a #if expression should only occur in syntactically correct places. This error is most
probably caused by omission of an operator.
missing ’)’(Parser)
A closing parenthesis was missing from this expression.
missing ’=’ in class spec(Linker)
A class spec needs an = sign, e.g. -Ctext=ROM
missing ’]’(Parser)
A closing square bracket was missing from this expression.
missing arch specification for * in chipinfo file(Assembler)
The chipinfo file (libpicinfo.ini by default) has a processor section without an ARCH values. The
architecture of the processor must be specified.
missing arg to -a(Parser)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
missing arg to -e(Linker)
The error file name must be specified following the -e linker option.
missing arg to -i(Parser)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
missing arg to -j(Linker)
The maximum number of errors before aborting must be specified following the -j linker option. 7
missing arg to -q(Linker)
The -Q linker option requires the machine type for an argument.
missing arg to -u(Linker)
The -U (undefine) option needs an argument, e.g. -U_symbol
202
will prevent the compiler aligning structure members onto anything other than one byte boundaries. Use
this with caution as some processors enforce alignment and will not operate correctly if word fetches are
made on odd boundaries (e.g. 68000, 8096).
missing number after pragma interrupt_level(Parser)
Pragma ’interrupt_level’ requires an argument from 0 to 7.
missing processor spec after -p(Cromwell)
The -p option to cromwell must specify a processor.
mod by zero in #if, zero result assumed(Preprocessor)
A modulus operation in a #if expression has a zero divisor. The result has been assumed to be zero.
module * defines no symbols(Libr)
No symbols were found in the module’s object file.
module has code below file base of *(Linker)
This module has code below the address given, but the -C option has been used to specify that a binary
output file is to be created that is mapped to this address. This would mean code from this module would
have to be placed before the beginning of the file! Check for missing psect directives in assembler files.
multi-byte constant * isn’t portable(Preprocessor)
Multi-byte constants are not portable, and in fact will be rejected by later passes of the compiler.
multiple free: *(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
multiply defined symbol *(Assembler, Linker)
This symbol has been defined in more than one place in this module.
n= must specify a positive constant(Assembler)
The parameter to the LIST assembler control’s ’N’ option (which sets the page length for the listing
output) must be a positive constant number.
nested #asm directive(Preprocessor)
It is not legal to nest #asm directives. Check for a missing or misspelt #endasm directive.
nested comments(Preprocessor) 7
This warning is issued when nested comments are found. A nested comment may indicate that a
previous closing comment marker is missing or malformed.
no #asm before #endasm(Preprocessor)
A #endasm operator has been encountered, but there was no previous matching #asm.
204
no ROM range covering address 0 encountered(Driver)
None of the on-chip memory or memory specified with -ROM was found to include address 0. This may
have been deliberate.
no room for arguments(Preprocessor, Parser, Code Generator, Linker, Objtohex)
The code generator could not allocate any more memory. Try increasing the size of available memory.
no space for macro def’n(Assembler)
The assembler has run out of memory.
no start record: entry point defaults to zero(Linker)
None of the object files passed to the linker contained a start record. The start address of the program
has been set to zero. This may be harmless, but it is recommended that you define a start address in your
startup module by using the "END" directive.
no valid entries in chipinfo file(Assembler)
The chipinfo file (libpicinfo.ini by default) contains no valid processor descriptions.
no. of arguments redeclared(Parser)
The number of arguments in this function declaration does not agree with a previous declaration of the
same function.
nodecount = *(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
non-constant case label(Code Generator)
A case label in this switch statement has a value which is not a constant.
non-prototyped function declaration: *(Parser)
A function has been declared using old-style (K&R) arguments. It is preferable to use prototype
declarations for all functions. If the function has no arguments, declare it as e.g. "int func(void)".
non-scalar types can’t be converted(Parser)
You can’t convert a structure, union or array to anything else. You can convert a pointer to one of those
things, so perhaps you left out an ampersand ("&").
non-void function returns no value(Parser)
A function that is declared as returning a value has a "return" statement that does not specify a return 7
value.
not a member of the struct/union *(Parser)
This identifier is not a member of the structure or union type with which it used here.
not a variable identifier: *(Parser)
This identifier is not a variable; it may be some other kind of object, e.g. a label.
206
operands of * not same type(Parser)
The operands of this operator are of different pointer. This probably means you have used the wrong
variable, but if the code is actually what you intended, use a typecast to suppress the error message.
operator * in incorrect context(Preprocessor)
An operator has been encountered in a #if expression that is incorrectly placed, e.g. two binary operators
are not separated by a value.
org argument must be a positive constant(Assembler)
An argument to the ORG assembler directive must be a positive constant or a symbol which has been
equated to a positive constant.
out of far memory(Code Generator)
The compiler has run out of far memory. Try removing TSR’s etc. If your system supports EMS memory,
the compiler will be able to use up to 64K of this, so if it is not enable, try enabling EMS.
out of memory(Code Generator, Assembler, Optimiser)
The compiler has run out of memory. If you have unnecessary TSRs loaded, remove them. If you are
running the compiler from inside another program, try running it directly from the command prompt.
Similarly, if you are using HPD, try using the command line compiler driver instead.
out of memory allocating * blocks of *(Linker)
Memory was required to extend an array but was unavailable.
out of near memory(Code Generator)
The compiler has run out of near memory. This is probably due to too many symbol names. Try splitting
the program up, or reducing the number of unused symbols in header files etc.
out of space in macro * arg expansion(Preprocessor)
A macro argument has exceeded the length of an internal buffer. This buffer is normally 4096 bytes long.
out-of-range case label *(Code Generator)
This case label is not a value that the controlling expression can yield, and thus this label will never be
selected.
output file cannot be also an input file(Linker)
The linker has detected an attempt to write its output file over one of its input files. This cannot be done,
because it needs to simultaneously read and write input and output files.
7
overfreed(Assembler)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
page width must be >= *(Assembler)
The listing page width must be at least * characters. Any less will not allow a properly formatted listing
to be produced.
phase error(Assembler)
The assembler has calculated a different value for a symbol on two different passes. This is probably due
to bizarre use of macros or conditional assembly.
pointer required(Parser)
A pointer is required here. This often means you have used "->" with a structure rather than a structure
pointer.
pointer to * argument required(Parser)
A pointer argument is required for this format specifier. Check the number and order of format specifiers
and corresponding arguments.
pointer to non-static object returned(Parser)
This function returns a pointer to a non-static (e.g. automatic) variable. This is likely to be an error, since
the storage associated with automatic variables becomes invalid when the function returns.
popreg: bad reg (*)(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
portion of expression has no effect(Code Generator)
Part of this expression has no side effects, and no effect on the value of the expression.
possible pointer truncation(Parser)
A pointer qualified "far" has been assigned to a default pointer or a pointer qualified "near", or a default
pointer has been assigned to a pointer qualified "near". This may result in truncation of the pointer and
loss of information, depending on the memory model in use.
preprocessor assertion failure(Preprocessor)
The argument to a preprocessor #assert directive has evaluated to zero. This is a programmer induced
error.
probable missing ’}’ in previous block(Parser)
The compiler has encountered what looks like a function or other declaration, but the preceding function
has not been ended with a closing brace. This probably means that a closing brace has been omitted from
somewhere in the previous function, although it may well not be the last one.
208
psect * memory delta redefined: */*(Linker)
A global psect has been defined with two different deltas.
psect * memory space redefined: */*(Linker)
A global psect has been defined in two different memory spaces. Either rename one of the psects or, if
they are the same psect, place them in the same memory space using the SPACE psect flag.
psect * not loaded on * boundary(Linker)
This psect has a relocatability requirement that is not met by the load address given in a -P option. For
example if a psect must be on a 4K byte boundary, you could not start it at 100H.
psect * not relocated on * boundary(Linker)
This psect is not relocated on the required boundary. Check the relocatability of the psect and correct the
-p option. if necessary.
psect * not specified in -p option(Linker)
This psect was not specified in a -P or -A option to the linker. It has been linked at the end of the program,
which is probably not where you wanted it.
psect * re-orged(Linker)
This psect has had its start address specified more than once.
psect * selector value redefined(Linker)
The selector value for this psect has been defined more than once.
psect * type redefined: *(Linker)
This psect has had its type defined differently by different modules. This probably means you are trying
to link incompatible object modules, e.g. linking 386 flat model code with 8086 real mode code.
psect alignment redefined(Assembler)
The psect alignment has already been defined using the psect ALIGN flag elsewhere.
psect delta redefined(Assembler)
The DELTA parameter to the PSECT assembler directive’s is different from a previous PSECT directive.
psect exceeds address limit: *(Linker)
The maximum address of the psect exceeds the limit placed on it using the LIMIT psect flag.
7
psect exceeds max size: *(Linker)
The psect has more bytes in it than the maximum allowed as specified using the SIZE psect flag.
psect is absolute: *(Linker)
This psect is absolute and should not have an address specified in a -P option.
210
record too long: *(Linker)
An object file contained a record with an illegal size. This probably means the file is corrupted or not an
object file.
recursive function calls: (Linker)
These functions (or function) call each other recursively. One or more of these functions has statically
allocated local variables (compiled stack). Either use the reentrant keyword (if supported with this
compiler) or recode to avoid recursion.
recursive macro definition of *(Preprocessor)
The named macro has been defined in such a manner that expanding it causes a recursive expansion of
itself!
redefining macro *(Preprocessor)
The macro specified is being redefined, to something different to the original definition. If you want to
deliberately redefine a macro, use #undef first to remove the original definition.
redundant & applied to array(Parser)
The address operator "&" has been applied to an array. Since using the name of an array gives its address
anyway, this is unnecessary and has been ignored.
refc == 0(Assembler, Optimiser)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
regused - bad arg to g(Code Generator)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
reloc= must specify a positive constant(Assembler)
The parameter to the PSECT assembler directive’s ’RELOC’ option must be a positive constant number.
relocation error(Assembler, Optimiser)
It is not possible to add together two relocatable quantities. A constant may be added to a relocatable
value, and two relocatable addresses in the same psect may be subtracted. An absolute value must be
used in various places where the assembler must know a value at assembly time.
relocation offset * out of range *(Linker)
An object file contained a relocation record with a relocation offset outside the range of the preceding
text record. This means the object file is probably corrupted.
7
relocation too complex(Assembler)
The complex relocation in this expression is too big to be inserted into the object file.
remsym error(Assembler)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
212
simple integer expression required(Parser)
A simple integral expression is required after the operator "@", used to associate an absolute address
with a variable.
simple type required for *(Parser)
A simple type (i.e. not an array or structure)is required as an operand to this operator.
size= must specify a positive constant(Assembler)
The parameter to the PSECT assembler directive’s ’SIZE’ option must be a positive constant number.
sizeof external array * is zero(Parser)
The sizeof an external array evaluates to zero. This is probably due to the array not having an explicit
dimension in the extern declaration.
sizeof yields 0(Code Generator)
The code generator has taken the size of an object and found it to be zero. This almost certainly indicates
an error in your declaration of a pointer, e.g. you may have declared a pointer to a zero length array. In
general, pointers to arrays are of little use. If you require a pointer to an array of objects of unknown
length, you only need a pointer to a single object that can then be indexed or incremented.
sizer required after dot(Assembler)
The size of the operand is required. For example, MOV.w indicates that data of word size is to be moved.
’w’ is the ’sizer’.
space= must specify a positive constant(Assembler)
The parameter to the PSECT assembler directive’s ’SPACE’ option must be a positive constant number.
static object has zero size: *(Code Generator)
A static object has been declared, but has a size of zero.
storage class illegal(Parser)
A structure or union member may not be given a storage class. Its storage class is determined by the
storage class of the structure.
storage class redeclared(Parser)
A variable or function has been re-declared with a different storage class. This can occur where there are
two conflicting declarations, or where an implicit declaration is followed by an actual declaration. 7
strange character * after ##(Preprocessor)
A character has been seen after the token catenation operator ## that is neither a letter nor a digit. Since
the result of this operator must be a legal token, the operands must be tokens containing only letters and
digits.
strange character after # *(Preprocessor)
There is an unexpected character after #.
214
syntax error(Assembler, Optimiser)
A syntax error has been detected. This could be caused a number of things.
syntax error in -a spec(Linker)
The -A spec is invalid. A valid -A spec should be something like:
-AROM=1000h-1FFFh
syntax error in checksum list(Linker)
There is a syntax error in a checksum list read by the linker. The checksum list is read from standard
input by the linker, in response to an option. Re-read the manual on checksum list.
syntax error in chipinfo file at line *(Assembler)
The chipinfo file contains non-standard syntax at the specified line.
syntax error in local argument(Assembler)
There is a syntax error in a local argument.
text does not start at 0(Linker)
Code in some things must start at zero. Here it doesn’t.
text offset too low(Linker)
You aren’t likely to see this error. Rhubarb!
text record has bad length: *(Linker)
There is an error in an object file. This is either an invalid object file, or an internal error in the linker.
Try recreating the object file.
text record has length too small: *(Linker)
This indicates that the object file is not a valid HI-TECH object file.
this function too large - try reducing level of optimization(Code Generator)
A large function has been encountered when using a -Og (global optimization) switch. Try re-compiling
without the global optimization, or reduce the size of the function.
this is a struct(Parser)
This identifier following a "union" or "enum" keyword is already the tag for a structure, and thus should
only follow the keyword "struct". 7
this is a union(Parser)
This identifier following a "struct" or "enum" keyword is already the tag for a union, and thus should
only follow the keyword "union".
this is an enum(Parser)
This identifier following a "struct" or "union" keyword is already the tag for an enumerated type, and
thus should only follow the keyword "enum".
too few arguments(Parser)
This function requires more arguments than are provided in this call.
too few arguments for format string(Parser)
There are too few arguments for this format string. This would result in a garbage value being printed
or converted at run time.
too many (*) enumeration constants(Parser)
There are too many enumeration constants in an enumerated type. The maximum number of enumerated
constants allowed in an enumerated type is 512.
too many (*) structure members(Parser)
There are too many members in a structure or union. The maximum number of members allowed in one
structure or union is 512.
too many address spaces - space * ignored(Linker)
The limit to the number of address spaces is currently 16.
too many arguments(Parser)
This function does not accept as many arguments as there are here.
too many arguments for format string(Parser)
There are too many arguments for this format string. This is harmless, but may represent an incorrect
format string.
too many arguments for macro(Preprocessor)
A macro may only have up to 31 parameters, as per the C Standard.
too many arguments in macro expansion(Preprocessor)
There were too many arguments supplied in a macro invocation. The maximum number allowed is 31.
too many cases in switch(Code Generator)
7 There are too many case labels in this switch statement. The maximum allowable number of case labels
in any one switch statement is 511.
too many common lines in chipinfo file for *(Assembler, Driver)
The chipinfo file (libpicinfo.ini by default) contains a processor section with too many COMMON
fields. Only one COMMON field is allowed.
216
too many errors(Preprocessor, Parser, Code Generator, Assembler, Linker)
There were so many errors that the compiler has given up. Correct the first few errors and many of the
later ones will probably go away.
too many file arguments. usage: cpp [input [output]](Preprocessor)
CPP should be invoked with at most two file arguments.
too many files in coff file(Cromwell)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
too many include directories(Preprocessor)
A maximum of 7 directories may be specified for the preprocessor to search for include files.
too many initializers(Parser)
There are too many initializers for this object. Check the number of initializers against the object
definition (array or structure).
too many input files(Cromwell)
To many input files have been specified to be converted by Cromwell.
too many macro parameters(Assembler)
There are too many macro parameters on this macro definition.
too many nested #* statements(Preprocessor)
#if, #ifdef etc. blocks may only be nested to a maximum of 32.
too many nested #if statements(Preprocessor)
#if, #ifdef etc. blocks may only be nested to a maximum of 32.
too many object files(Driver)
A maximum of 128 object files may be passed to the linker. The driver exceeded this amount when
generating the command line for the linker.
too many output files(Cromwell)
To many output file formats have been specified to Cromwell.
too many psect class specifications(Linker)
There are too many psect class specifications (-C options) 7
too many psect pragmas(Code Generator)
Too many "pragma psect" directives have been used.
too many psects(Assembler)
There are too many psects! Boy, what a program!
218
type redeclared(Parser)
The type of this function or object has been redeclared. This can occur because of two incompatible
declarations, or because an implicit declaration is followed by an incompatible declaration.
type specifier reqd. for proto arg(Parser)
A type specifier is required for a prototyped argument. It is not acceptable to just have an identifier.
unable to open list file *(Linker)
The named list file could not be opened.
unbalanced paren’s, op is *(Preprocessor)
The evaluation of a #if expression found mismatched parentheses. Check the expression for correct
parenthesisation.
undefined *: *(Parser)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
undefined enum tag: *(Parser)
This enum tag has not been defined.
undefined identifier: *(Parser)
This symbol has been used in the program, but has not been defined or declared. Check for spelling
errors.
undefined shift (* bits)(Code Generator)
An attempt has been made to shift a value by a number of bits equal to or greater than the number of bits
in the data type, e.g. shifting a long by 32 bits. This will produce an undefined result on many processors.
This is non-portable code and is flagged as having undefined results by the C Standard.
undefined struct/union(Parser)
This structure or union tag is undefined. Check spelling etc.
undefined struct/union: *(Parser)
The specified structure or union tag is undefined. Check spelling etc.
undefined symbol *(Assembler)
The named symbol is not defined, and has not been specified "GLOBAL".
7
undefined symbol * in #if, 0 used(Preprocessor)
A symbol on a #if expression was not a defined preprocessor macro. For the purposes of this expression,
its value has been taken as zero.
undefined symbol in fnaddr record: *(Linker)
The linker has found an undefined symbol in the fnaddr record for a non-reentrant function.
220
unexpected \ in #if(Preprocessor)
The backslash is incorrect in the #if statement.
unknown ’with’ psect referenced by psect *(Linker)
The specified psect has been placed with a psect using the psect ’with’ flag. The psect it has been placed
with does not exist.
unknown addressing mode *(Assembler, Optimiser)
An unknown addressing mode was used in the assembly file.
unknown architecture in chipinfo file at line *(Assembler, Driver)
An chip architecture (family) that is unknown was encountered when reading the chip INI file. Valid
architectures are: PIC12, PIC14 and PIC16, representing baseline, midrange and highend devices,
respectively.
unknown argument to ’pragma switch’: *(Code Generator)
The ’#pragma switch’ directive has been used with an invalid switch code generation method. Possible
arguments are: auto, simple and direct.
unknown complex operator *(Linker)
There is an error in an object file. This is either an invalid object file, or an internal error in the linker.
Try recreating the object file.
unknown fnrec type *(Linker)
This indicates that the object file is not a valid HI-TECH object file.
unknown format name ’*’(Cromwell)
The output format specified to Cromwell is unknown.
unknown op * in emobj(Assembler)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
unknown op * in size_psect(Assembler)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
unknown op in emasm(): *(Assembler)
This is an internal compiler error. Contact HI-TECH Software technical support with details.
7
unknown option *(Preprocessor)
This option to the preprocessor is not recognized.
unknown pragma *(Parser)
An unknown pragma directive was encountered.
222
would add const and code to the current string qualifiers. If no qualifiers are specified, all qualification
will be removed from subsequent strings. The qualifier names must be recognized by the compiler.
unterminated #if[n][def] block from line *(Preprocessor)
A #if or similar block was not terminated with a matching #endif. The line number is the line on which
the #if block began.
unterminated comment in included file(Preprocessor)
Comments begun inside an included file must end inside the included file.
unterminated macro arg(Assembler)
An argument to a macro is not terminated. Note that angle brackets ("< >") are used to quote macro
arguments.
unterminated string(Assembler, Optimiser)
A string constant appears not to have a closing quote missing.
unterminated string in macro body(Preprocessor, Assembler)
A macro definition contains a string that lacks a closing quote.
unused constant: *(Parser)
This enumerated constant is never used. Maybe it isn’t needed at all.
unused enum: *(Parser)
This enumerated type is never used. Maybe it isn’t needed at all.
unused label: *(Parser)
This label is never used. Maybe it isn’t needed at all.
unused member: *(Parser)
This structure member is never used. Maybe it isn’t needed at all.
unused structure: *(Parser)
This structure tag is never used. Maybe it isn’t needed at all.
unused typedef: *(Parser)
This typedef is never used. Maybe it isn’t needed at all.
unused union: *(Parser)
7
This union type is never used. Maybe it isn’t needed at all.
unused variable declaration: *(Parser)
This variable is never used. Maybe it isn’t needed at all.
unused variable definition: *(Parser)
This variable is never used. Maybe it isn’t needed at all.
224
zero size ROM bank * defined(Driver)
An additional memory bank has been defined which has a size of zero.
226
Library Functions
The functions within the PICC-18 C compiler library are listed in this chapter. Each entry begins with
the name of the function. This is followed by information analysed into the following headings.
Synopsis
This is the C definition of the function, and the header file in which it is declared.
Description
This is a narrative description of the function and its purpose.
Example
This is an example of the use of the function. It is usually a complete small program that illustrates the
function.
Data types
If any special data types (structures etc.) are defined for use with the function, they are listed here with
their C definition. These data types will be defined in the header file given under heading - Synopsis.
See also
This refers you to any allied functions.
Return value
The type and nature of the return value of the function, if any, is given. Information on error returns is
also included
Only those headings which are relevant to each function are used.
ABS
Synopsis
#include <stdlib.h>
Description
The abs() function returns the absolute value of j.
Example
#include <stdio.h>
#include <stdlib.h>
void
main (void)
{
int a = -5;
228
ACOS
Synopsis
#include <math.h>
Description
The acos() function implements the converse of cos(), i.e. it is passed a value in the range -1 to +1,
and returns an angle in radians whose cosine is equal to that value.
Example
#include <math.h>
#include <stdio.h>
void
main (void)
{
float i, a;
ASCTIME
Synopsis
#include <time.h>
Description
The asctime() function takes the time broken down into the struct tm structure, pointed to by its
argument, and returns a 26 character string describing the current date and time in the format:
Sun Sep 16 01:03:52 1973\n\0
Note the newline at the end of the string. The width of each field in the string is fixed. The example gets
the current time, converts it to a struct tm pointer with localtime(), it then converts this to
ASCII and prints it. The time() function will need to be provided by the user (see time() for
details).
Example
#include <stdio.h>
#include <time.h>
void
main (void)
{
time_t clock;
struct tm * tp;
time(&clock);
tp = localtime(&clock);
printf("%s", asctime(tp));
}
See Also
ctime(), gmtime(), localtime(), time()
Return Value
A pointer to the string.
8 Note
The example will require the user to provide the time() routine as it cannot be supplied with the
compiler. See time() for more details.
230
Data Types
struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
ASIN
Synopsis
#include <math.h>
Description
The asin() function implements the converse of sin(), i.e. it is passed a value in the range -1 to +1,
and returns an angle in radians whose sine is equal to that value.
Example
#include <math.h>
#include <stdio.h>
void
main (void)
{
float i, a;
232
ATAN
Synopsis
#include <math.h>
Description
This function returns the arc tangent of its argument, i.e. it returns an angle e in the range -π/2 to π/2
such that tan(e) == x.
Example
#include <stdio.h>
#include <math.h>
void
main (void)
{
printf("%f\n", atan(1.5));
}
See Also
sin(), cos(), tan(), asin(), acos(), atan2()
Return Value
The arc tangent of its argument.
ATAN2
Synopsis
#include <math.h>
Description
This function returns the arc tangent of y/x, using the sign of both arguments to determine the quadrant
of the return value.
Example
#include <stdio.h>
#include <math.h>
void
main (void)
{
printf("%f\n", atan2(1.5, 1));
}
See Also
sin(), cos(), tan(), asin(), acos(), atan()
Return Value
The arc tangent of y/x in the range -π to +π radians. If both y and x are zero, a domain error occurs and
zero is returned.
234
ATOF
Synopsis
#include <stdlib.h>
Description
The atof() function scans the character string passed to it, skipping leading blanks. It then converts
an ASCII representation of a number to a double. The number may be in decimal, normal floating point
or scientific notation.
Example
#include <stdlib.h>
#include <stdio.h>
void
main (void)
{
char buf[80];
double i;
gets(buf);
i = atof(buf);
printf("Read %s: converted to %f\n", buf, i);
}
See Also
atoi(), atol()
Return Value
A double precision floating point number. If no number is found in the string, 0.0 will be returned.
ATOI
Synopsis
#include <stdlib.h>
Description
The atoi() function scans the character string passed to it, skipping leading blanks and reading an
optional sign. It then converts an ASCII representation of a decimal number to an integer.
Example
#include <stdlib.h>
#include <stdio.h>
void
main (void)
{
char buf[80];
int i;
gets(buf);
i = atoi(buf);
printf("Read %s: converted to %d\n", buf, i);
}
See Also
xtoi(), atof(), atol()
Return Value
A signed integer. If no number is found in the string, 0 will be returned.
236
ATOL
Synopsis
#include <stdlib.h>
Description
The atol() function scans the character string passed to it, skipping leading blanks. It then converts
an ASCII representation of a decimal number to a long integer.
Example
#include <stdlib.h>
#include <stdio.h>
void
main (void)
{
char buf[80];
long i;
gets(buf);
i = atol(buf);
printf("Read %s: converted to %ld\n", buf, i);
}
See Also
atoi(), atof()
Return Value
A long integer. If no number is found in the string, 0 will be returned.
CEIL
Synopsis
#include <math.h>
Description
This routine returns the smallest whole number not less than f.
Example
#include <stdio.h>
#include <math.h>
void
main (void)
{
double j;
scanf("%lf", &j);
printf("The ceiling of %lf is %lf\n", j, ceil(j));
}
238
CLRWDT
Synopsis
#include <pic18.h>
CLRWDT();
Description
This macro is used to clear the device’s internal watchdog timer.
Example
#include <pic18.h>
void
main (void)
{
WDTCON=1; /* enable the WDT */
CLRWDT();
}
config_read(),config_write()
Synopsis
#include <pic18.h>
Description
These functions allow access to the device configuration registers which determine many of the
behavioural aspects of the device itself.
config_read() accepts a single parameter to determine which config word will be read. The 16-Bit
value contained in the register is returned.
config_write() doesn’t return any value. It accepts a second parameter which is a 16-Bit value to
be written to the selected register.
Example
#include <pic18.h>
void
main (void)
{
unsigned int value;
}
See Also
device_id_read(),idloc_read(),idloc_write()
Return Value
config_read() returns the 16-Bit value contained in the nominated configuration register.
8 Note
The functions config_read() config_write() are only applicable to such devices that support
this feature.
240
Note also that not all bits in the device configuration registers can be modified at runtime. If unsure
consult the device datasheet.
COS
Synopsis
#include <math.h>
Description
This function yields the cosine of its argument, which is an angle in radians. The cosine is calculated by
expansion of a polynomial series approximation.
Example
#include <math.h>
#include <stdio.h>
#define C 3.141592/180.0
void
main (void)
{
double i;
242
COSH, SINH, TANH
Synopsis
#include <math.h>
Description
These functions are the hyperbolic implementations of the trigonometric functions; cos(), sin() and
tan().
Example
#include <stdio.h>
#include <math.h>
void
main (void)
{
printf("%f\n", cosh(1.5));
printf("%f\n", sinh(1.5));
printf("%f\n", tanh(1.5));
}
Return Value
The function cosh() returns the hyperbolic cosine value.
The function sinh() returns the hyperbolic sine value.
The function tanh() returns the hyperbolic tangent value.
CTIME
Synopsis
#include <time.h>
Description
The ctime() function converts the time in seconds pointed to by its argument to a string of the same
form as described for asctime(). Thus the example program prints the current time and date.
Example
#include <stdio.h>
#include <time.h>
void
main (void)
{
time_t clock;
time(&clock);
printf("%s", ctime(&clock));
}
See Also
gmtime(), localtime(), asctime(), time()
Return Value
A pointer to the string.
Note
The example will require the user to provide the time() routine as one cannot be supplied with the
compiler. See time() for more detail.
Data Types
typedef long time_t;
244
device_id_read()
Synopsis
#include <pic18.h>
Description
This function returns the device ID code that is factory-programmed into the chip. This code can be used
to identify the device and its revision number.
Example
#include <pic18.h>
void
main (void)
{
unsigned int id_value;
unsigned int device_code;
unsigned char revision_no;
id_value = device_id_read();
/* lower 5 bits represent revision number
* upper 11 bits identify device */
device_code = (id_value >> 5);
revision_no = (unsigned char)(id_value & 0x1F);
}
See Also
flash_read(), config_read()
Return Value
device_id_read() returns the 16-Bit factory-programmed device id code used to identify the de-
vice type and its revision number.
Note
The device_id_read() is applicable only to those devices which are capable of reading their own
program memory. 8
DI, EI
Synopsis
#include <pic18.h>
void ei(void)
void di(void)
Description
ei and di enable and disable interrupts respectively. These are implemented as macros defined in pic.h.
They will expand to an in-line assembler instruction that sets or clears the interrupt enable bit.
The example shows the use of ei and di around access to a long variable that is modified during an
interrupt. If this was not done, it would be possible to return an incorrect value, if the interrupt occurred
between accesses to successive words of the count value.
Example
#include <pic18.h>
long count;
long getticks(void)
{
long val; /* Disable interrupts around access
to count, to ensure consistency.*/
di();
val = count;
ei();
return val;
}
246
EEPROM_READ, EEPROM_WRITE
Synopsis
#include <pic18.h>
Description
These function allow access to the on-board eeprom (when present). The eeprom is not in the directly-
accessible memory space and a special byte sequence is loaded to the eeprom control registers to access
this memory. Writing a value to the eeprom is a slow process and the eeprom_write() function polls
the appropriate registers to ensure that any previous writes have completed before writing the next
datum.
Reading data is completed in the one cycle and no polling is necessary to check for a read completion.
Example
#include <pic18.h>
void
main (void)
{
unsigned char data;
unsigned int address = 0x0010;
data=eeprom_read(address);
eeprom_write(address, data);
}
See Also
FLASH_READ, FLASH_WRITE, FLASH_ERASE
Note
The high and low priority interrupt are disabled during sensitive sequnces required to access EEPROM.
Interrupts are restored after the sequence has completed. eeprom_write() will clear the EEIF hardware
flag before returning.
Both eeprom_read() and eeprom_write() are available in a macro form.
8
EVAL_POLY
Synopsis
#include <math.h>
Description
The eval_poly() function evaluates a polynomial, whose coefficients are contained in the array d,
at x, for example:
y = x*x*d2 + x*d1 + d0.
The order of the polynomial is passed in n.
Example
#include <stdio.h>
#include <math.h>
void
main (void)
{
double x, y;
double d[3] = {1.1, 3.5, 2.7};
x = 2.2;
y = eval_poly(x, d, 2);
printf("The polynomial evaluated at %f is %f\n", x, y);
}
Return Value
A double value, being the polynomial evaluated at x.
248
EXP
Synopsis
#include <math.h>
Description
The exp() routine returns the exponential function of its argument, i.e. e to the power of f.
Example
#include <math.h>
#include <stdio.h>
void
main (void)
{
double f;
FABS
Synopsis
#include <math.h>
Description
This routine returns the absolute value of its double argument.
Example
#include <stdio.h>
#include <math.h>
void
main (void)
{
printf("%f %f\n", fabs(1.5), fabs(-1.5));
}
See Also
abs()
250
flash_erase, flash_read, flash_write
Synopsis
#include <pic18.h>
Description
These functions allow access to the flash memory of the microcontroller (when present).
Reading from the flash memory can be done one byte at a time with use of the flash_read()
function. flash_read() returns the data value found at the specified address in flash memory.
Entire sectors of 64 bytes can be restored to an unprogrammed state (value=FF) with use of the
flash_erase() function. Specifying an address to the flash_erase() funtion, will erase all 64
bytes of the sector that contains the given address.
flash_write() copies blocks of data/code from RAM/flash to a new destination in flash memory.
flash_read() requires a pointer to the data that will be copied, the length of data to copy (in bytes)
and a pointer to the destination address in flash memory. This function can be used to update values of
variables declared as const. Lengths of data up to 256 bytes may be copied at a time.
Example
#include <pic18.h>
void
main (void)
{
const unsigned char old_text[]="insert text here";
unsigned char new_text[]="HI-TECH Software";
flash_erase(address);
flash_write(source, length,destination);
}
Return Value
flash_read() returns the data found at the given address, as an unsigned char.
Note
The flash_write() function can be used to update anywhere from 1 to 256 bytes of data at a time,
however it is optimal to write in data lengths that are multiples of 64.
252
FLOOR
Synopsis
#include <math.h>
Description
This routine returns the largest whole number not greater than f.
Example
#include <stdio.h>
#include <math.h>
void
main (void)
{
printf("%f\n", floor( 1.5 ));
printf("%f\n", floor( -1.5));
}
FREXP
Synopsis
#include <math.h>
Description
The frexp() function breaks a floating point number into a normalized fraction and an integral power
of 2. The integer is stored into the int object pointed to by p. Its return value x is in the interval (0.5,
1.0) or zero, and f equals x times 2 raised to the power stored in *p. If f is zero, both parts of the result
are zero.
Example
#include <math.h>
#include <stdio.h>
void
main (void)
{
double f;
int i;
f = frexp(23456.34, &i);
printf("23456.34 = %f * 2^%d\n", f, i);
}
See Also
ldexp()
254
GMTIME
Synopsis
#include <time.h>
Description
This function converts the time pointed to by t which is in seconds since 00:00:00 on Jan 1, 1970, into
a broken down time stored in a structure as defined in time.h. The structure is defined in the ’Data
Types’ section.
Example
#include <stdio.h>
#include <time.h>
void
main (void)
{
time_t clock;
struct tm * tp;
time(&clock);
tp = gmtime(&clock);
printf("It’s %d in London\n", tp->tm_year+1900);
}
See Also
ctime(), asctime(), time(), localtime()
Return Value
Returns a structure of type tm.
Note 8
The example will require the user to provide the time() routine as one cannot be supplied with the
256
idloc_read(),idloc_write()
Synopsis
#include <pic18.h>
Description
These functions allow access to the user ID register which can be used to store small ammounts of
information such as serial numbers, checksums etc.
idloc_read() accepts a single parameter to determine which user ID register to read. The value
contained in the register is returned.
idloc_write() doesn’t return any value. It accepts a second parameter which is a value to be written
to the selected register. Note that only the lower nibble is significant. The upper nibble of the value
written will always be 0xF as per Microchip’s documentation.
Example
#include <pic18.h>
void
main (void)
{
unsigned char value;
}
See Also
device_id_read(),config_read(),config_write()
Return Value
idloc_read() returns the value contained in the nominated user ID register.
8
Note
The functions idloc_read() idloc_write() are only applicable to such devices that support
this feature.
Note also that ICD2 breakboints should not be set within the idloc_write() function. Doing so can
result in disrupting the operation of the debugger.
258
ISALNUM, ISALPHA, ISDIGIT, ISLOWER et. al.
Synopsis
#include <ctype.h>
Description
These macros, defined in ctype.h, test the supplied character for membership in one of several
overlapping groups of characters. Note that all except isascii() are defined for c, if isascii(c)
is true or if c = EOF.
Example 8
#include <ctype.h>
#include <stdio.h>
void
main (void)
{
char buf[80];
int i;
gets(buf);
i = 0;
while(isalnum(buf[i]))
i++;
buf[i] = 0;
printf("’%s’ is the word\n", buf);
}
See Also
toupper(), tolower(), toascii()
260
KBHIT
Synopsis
#include <conio.h>
Description
This function returns 1 if a character has been pressed on the console keyboard, 0 otherwise. Normally
the character would then be read via getch().
Example
#include <conio.h>
void
main (void)
{
int i;
while(!kbhit()) {
cputs("I’m waiting..");
for(i = 0 ; i != 1000 ; i++)
continue;
}
}
See Also
getch(), getche()
Return Value
Returns one if a character has been pressed on the console keyboard, zero otherwise. Note: the return
value is a bit.
Note
The body of the routine will need to be implemented by the user. The skeleton function will be found in
the sources direstory.
LDEXP
Synopsis
#include <math.h>
Description
The ldexp() function performs the inverse of frexp() operation; the integer i is added to the
exponent of the floating point f and the resultant returned.
Example
#include <math.h>
#include <stdio.h>
void
main (void)
{
double f;
f = ldexp(1.0, 10);
printf("1.0 * 2^10 = %f\n", f);
}
See Also
frexp()
Return Value
The return value is the integer i added to the exponent of the floating point value f.
262
LDIV
Synopsis
#include <stdlib.h>
Description
The ldiv() routine divides the numerator by the denominator, computing the quotient and the
remainder. The sign of the quotient is the same as that of the mathematical quotient. Its absolute value
is the largest integer which is less than the absolute value of the mathematical quotient.
The ldiv() function is similar to the div() function, the difference being that the arguments and the
members of the returned structure are all of type long int.
Example
#include <stdlib.h>
#include <stdio.h>
void
main (void)
{
ldiv_t lt;
lt = ldiv(1234567, 12345);
printf("Quotient = %ld, remainder = %ld\n", lt.quot, lt.rem);
}
See Also
div()
Return Value
Returns a structure of type ldiv_t
Data Types
typedef struct {
long quot; /* quotient */
long rem; /* remainder */
} ldiv_t; 8
LOCALTIME
Synopsis
#include <time.h>
Description
The localtime() function converts the time pointed to by t which is in seconds since 00:00:00 on
Jan 1, 1970, into a broken down time stored in a structure as defined in time.h. The routine
localtime() takes into account the contents of the global integer time_zone. This should contain
the number of minutes that the local time zone is westward of Greenwich. Since there is no way under
MS-DOS of actually predetermining this value, by default localtime() will return the same result
as gmtime().
Example
#include <stdio.h>
#include <time.h>
char * wday[] = {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
};
void
main (void)
{
time_t clock;
struct tm * tp;
time(&clock);
tp = localtime(&clock);
printf("Today is %s\n", wday[tp->tm_wday]);
}
See Also
ctime(), asctime(), time()
8 Return Value
Returns a structure of type tm.
264
Note
The example will require the user to provide the time() routine as one cannot be supplied with the
compiler. See time() for more detail.
Data Types
typedef long time_t;
struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
LOG, LOG10
Synopsis
#include <math.h>
Description
The log() function returns the natural logarithm of f. The function log10() returns the logarithm
to base 10 of f.
Example
#include <math.h>
#include <stdio.h>
void
main (void)
{
double f;
266
MEMCHR
Synopsis
#include <string.h>
void * memchr (const void * block, int val, size_t length)
Description
The memchr() function is similar to strchr() except that instead of searching null terminated
strings, it searches a block of memory specified by length for a particular byte. Its arguments are a
pointer to the memory to be searched, the value of the byte to be searched for, and the length of the block.
A pointer to the first occurrence of that byte in the block is returned.
Example
#include <string.h>
#include <stdio.h>
void
main (void)
{
char * cp;
MEMCMP
Synopsis
#include <string.h>
Description
The memcmp() function compares two blocks of memory, of length n, and returns a signed value
similar to strncmp(). Unlike strncmp() the comparison does not stop on a null character. The
ASCII collating sequence is used for the comparison, but the effect of including non-ASCII characters
in the memory blocks on the sense of the return value is indeterminate. Testing for equality is always
reliable.
Example
#include <stdio.h>
#include <string.h>
void
main (void)
{
int buf[10], cow[10], i;
buf[0] = 1;
buf[2] = 4;
cow[0] = 1;
cow[2] = 5;
buf[1] = 3;
cow[1] = 3;
i = memcmp(buf, cow, 3*sizeof(int));
if(i < 0)
printf("less than\n");
else if(i > 0)
printf("Greater than\n");
else
printf("Equal\n");
}
8 See Also
strncpy(), strncmp(), strchr(), memset(), memchr()
268
Return Value
Returns negative one, zero or one, depending on whether s1 points to string which is less than, equal to
or greater than the string pointed to by s2 in the collating sequence.
MEMCPY
Synopsis
#include <string.h>
Description
The memcpy() function copies n bytes of memory starting from the location pointed to by s to the
block of memory pointed to by d. The result of copying overlapping blocks is undefined. The
memcpy() function differs from strcpy() in that it copies a specified number of bytes, rather than
all bytes up to a null terminator.
Example
#include <string.h>
#include <stdio.h>
void
main (void)
{
char buf[80];
270
MEMMOVE
Synopsis
#include <string.h>
Description
The memmove() function is similar to the function memcpy() except copying of overlapping blocks
is handled correctly. That is, it will copy forwards or backwards as appropriate to correctly copy one
block to another that overlaps it.
See Also
strncpy(), strncmp(), strchr(), memcpy()
Return Value
The function memmove() returns its first argument.
MEMSET
Synopsis
#include <string.h>
Description
The memset() function fills n bytes of memory starting at the location pointed to by s with the byte c.
Example
#include <string.h>
#include <stdio.h>
void
main (void)
{
char abuf[20];
272
MODF
Synopsis
#include <math.h>
Description
The modf() function splits the argument value into integral and fractional parts, each having the
same sign as value. For example, -3.17 would be split into the intergral part (-3) and the fractional part
(-0.17).
The integral part is stored as a double in the object pointed to by iptr.
Example
#include <math.h>
#include <stdio.h>
void
main (void)
{
double i_val, f_val;
PERSIST_CHECK, PERSIST_VALIDATE
Synopsis
#include <sys.h>
Description
The persist_check() function is used with non-volatile RAM variables, declared with the
persistent qualifier. It tests the nvram area, using a magic number stored in a hidden variable by a
previous call to persist_validate() and a checksum also calculated by
persist_validate(). If the magic number and checksum are correct, it returns true (non-zero). If
either are incorrect, it returns zero. In this case it will optionally zero out and re-validate the non-volatile
RAM area (by calling persist_validate()). This is done if the flag argument is true.
The persist_validate() routine should be called after each change to a persistent variable. It will
set up the magic number and recalculate the checksum.
Example
#include <sys.h>
#include <stdio.h>
void
main (void)
{
if(!persist_check(1))
printf("Reset count invalid - zeroed\n");
else
printf("Reset number %ld\n", reset_count);
reset_count++; /* update count */
persist_validate(); /* and checksum */
for(;;)
continue; /* sleep until next reset */
}
8 Return Value
FALSE (zero) if the NV-RAM area is invalid; TRUE (non-zero) if the NVRAM area is valid.
274
POW
Synopsis
#include <math.h>
Description
The pow() function raises its first argument, f, to the power p.
Example
#include <math.h>
#include <stdio.h>
void
main (void)
{
double f;
PRINTF
Synopsis
#include <stdio.h>
Description
The printf() function is a formatted output routine, operating on stdout. There are corresponding
routines operating into a string buffer (sprintf()). The printf() routine is passed a format string,
followed by a list of zero or more arguments. In the format string are conversion specifications, each of
which is used to print out one of the argument list values.
Each conversion specification is of the form %m.nc where the percent symbol % introduces a
conversion, followed by an optional width specification m. The n specification is an optional precision
specification (introduced by the dot) and c is a letter specifying the type of the conversion.
If the character * is used in place of a decimal constant, e.g. in the format %*d, then one integer
argument will be taken from the list to provide that value. The types of conversion are:
o x X u d
Integer conversion - in radices 8, 16, 16, 10 and 10 respectively. The conversion is signed in the case of
d, unsigned otherwise. The precision value is the total number of digits to print, and may be used to force
leading zeroes. E.g. %8.4x will print at least 4 hex digits in an 8 wide field. The letter X prints out
hexadecimal numbers using the upper case letters A-F rather than a-f as would be printed when using x.
When the alternate format is specified, a leading zero will be supplied for the octal format, and a leading
0x or 0X for the hex format.
s
Print a string - the value argument is assumed to be a character pointer. At most n characters from the
string will be printed, in a field m characters wide.
c
The argument is assumed to be a single character and is printed literally.
Any other characters used as conversion specifications will be printed. Thus %% will produce a single
percent sign.
l
Long integer conversion - Preceding the integer conversion key letter with an l indicates that the
8 argument list is long.
f
Floating point - m is the total width and n is the number of digits after the decimal point. If n is omitted
276
it defaults to 6. If the precision is zero, the decimal point will be omitted unless the alternate format is
specified.
Example
printf("Total = %4d%%", 23)
yields ’Total = 23%’
printf("xx%*d", 3, 4)
yields ’xx 4’
/* vprintf example */
#include <stdio.h>
int
error (char * s, ...)
{
va_list ap;
va_start(ap, s);
printf("Error: ");
vprintf(s, ap);
putchar(’\n’);
va_end(ap);
}
void
main (void)
{
int i;
i = 3;
error("testing 1 2 %d", i);
}
See Also 8
sprintf()
Return Value
The printf() routine returns the number of characters written to stdout.
NB The return value is a char, NOT an int.
Note
Certain features of printf are only available when linking in alternative libraries. Printing floating point
numbers requires that the float to be printed be no larger than the largest possible long integer. In order
to use long or float formats, the appropriate supplemental library must be included. See the description
on the PICC18 -L library scan option for more details.
278
RAND
Synopsis
#include <stdlib.h>
Description
The rand() function is a pseudo-random number generator. It returns an integer in the range 0 to
32767, which changes in a pseudo-random fashion on each call. The algorithm will produce a
deterministic sequence if started from the same point. The starting point is set using the srand() call.
The example shows use of the time() function to generate a different starting point for the sequence
each time.
Example
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
void
main (void)
{
time_t toc;
int i;
time(&toc);
srand((int)toc);
for(i = 0 ; i != 10 ; i++)
printf("%d\t", rand());
putchar(’\n’);
}
See Also
srand()
Note
The example will require the user to provide the time() routine as one cannot be supplied with the
compiler. See time() for more detail.
8
SIN
Synopsis
#include <math.h>
Description
This function returns the sine function of its argument.
Example
#include <math.h>
#include <stdio.h>
#define C 3.141592/180.0
void
main (void)
{
double i;
280
SPRINTF
Synopsis
#include <stdio.h>
Description
The sprintf() function operates in a similar fashion to printf(), except that instead of placing
the converted output on the stdout stream, the characters are placed in the buffer at buf. The resultant
string will be null terminated, and the number of characters in the buffer will be returned.
See Also
printf()
Return Value
The sprintf() routine returns the number of characters placed into the buffer.
NB: The return value is a char not an int.
SQRT
Synopsis
#include <math.h>
Description
The function sqrt(), implements a square root routine using Newton’s approximation.
Example
#include <math.h>
#include <stdio.h>
void
main (void)
{
double i;
282
SRAND
Synopsis
#include <stdlib.h>
Description
The srand() function initializes the random number generator accessed by rand() with the given
seed. This provides a mechanism for varying the starting point of the pseudo-random sequence yielded
by rand(). On the z80, a good place to get a truly random seed is from the refresh register. Otherwise
timing a response from the console will do, or just using the system time.
Example
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
void
main (void)
{
time_t toc;
int i;
time(&toc);
srand((int)toc);
for(i = 0 ; i != 10 ; i++)
printf("%d\t", rand());
putchar(’\n’);
}
See Also
rand()
STRCAT
Synopsis
#include <string.h>
Description
This function appends (catenates) string s2 to the end of string s1. The result will be null terminated.
The argument s1 must point to a character array big enough to hold the resultant string.
Example
#include <string.h>
#include <stdio.h>
void
main (void)
{
char buffer[256];
char * s1, * s2;
284
STRCHR, STRICHR
Synopsis
#include <string.h>
Description
The strchr() function searches the string s for an occurrence of the character c. If one is found, a
pointer to that character is returned, otherwise NULL is returned.
The strichr() function is the case-insensitive version of this function.
Example
#include <strings.h>
#include <stdio.h>
void
main (void)
{
static char temp[] = "Here it is...";
char c = ’s’;
if(strchr(temp, c))
printf("Character %c was found in string\n", c);
else
printf("No character was found in string");
}
See Also
strrchr(), strlen(), strcmp()
Return Value
A pointer to the first match found, or NULL if the character does not exist in the string.
Note
Although the function takes an integer argument for the character, only the lower 8 bits of the value are
used. 8
STRCMP, STRICMP
Synopsis
#include <string.h>
Description
The strcmp() function compares its two, null terminated, string arguments and returns a signed
integer to indicate whether s1 is less than, equal to or greater than s2. The comparison is done with the
standard collating sequence, which is that of the ASCII character set.
The stricmp() function is the case-insensitive version of this function.
Example
#include <string.h>
#include <stdio.h>
void
main (void)
{
int i;
286
STRCPY
Synopsis
#include <string.h>
Description
This function copies a null terminated string s2 to a character array pointed to by s1. The destination
array must be large enough to hold the entire string, including the null terminator.
Example
#include <string.h>
#include <stdio.h>
void
main (void)
{
char buffer[256];
char * s1, * s2;
STRCSPN
Synopsis
#include <string.h>
Description
The strcspn() function returns the length of the initial segment of the string pointed to by s1 which
consists of characters NOT from the string pointed to by s2.
Example
#include <stdio.h>
#include <string.h>
void
main (void)
{
static char set[] = "xyz";
288
STRLEN
Synopsis
#include <string.h>
Description
The strlen() function returns the number of characters in the string s, not including the null
terminator.
Example
#include <string.h>
#include <stdio.h>
void
main (void)
{
char buffer[256];
char * s1, * s2;
STRNCAT
Synopsis
#include <string.h>
Description
This function appends (catenates) string s2 to the end of string s1. At most n characters will be copied,
and the result will be null terminated. s1 must point to a character array big enough to hold the resultant
string.
Example
#include <string.h>
#include <stdio.h>
void
main (void)
{
char buffer[256];
char * s1, * s2;
290
STRNCMP, STRNICMP
Synopsis
#include <string.h>
Description
The strcmp() function compares its two, null terminated, string arguments, up to a maximum of n
characters, and returns a signed integer to indicate whether s1 is less than, equal to or greater than s2.
The comparison is done with the standard collating sequence, which is that of the ASCII character set.
The stricmp() function is the case-insensitive version of this function.
Example
#include <stdio.h>
#include <string.h>
void
main (void)
{
int i;
i = strcmp("abcxyz", "abcxyz");
if(i == 0)
printf("Both strings are equal\n");
else if(i > 0)
printf("String 2 less than string 1\n");
else
printf("String 2 is greater than string 1\n");
}
See Also
strlen(), strcmp(), strcpy(), strcat()
Return Value
A signed integer less than, equal to or greater than zero.
Note 8
Other C implementations may use a different collating sequence; the return value is negative, zero or
positive, i.e. do not test explicitly for negative one (-1) or one (1).
292
STRNCPY
Synopsis
#include <string.h>
Description
This function copies a null terminated string s2 to a character array pointed to by s1. At most n
characters are copied. If string s2 is longer than n then the destination string will not be null terminated.
The destination array must be large enough to hold the entire string, including the null terminator.
Example
#include <string.h>
#include <stdio.h>
void
main (void)
{
char buffer[256];
char * s1, * s2;
STRPBRK
Synopsis
#include <string.h>
Description
The strpbrk() function returns a pointer to the first occurrence in string s1 of any character from
string s2, or a null pointer if no character from s2 exists in s1.
Example
#include <stdio.h>
#include <string.h>
void
main (void)
{
char * str = "This is a string.";
while(str != NULL) {
printf( "%s\n", str );
str = strpbrk( str+1, "aeiou" );
}
}
Return Value
Pointer to the first matching character, or NULL if no character found.
294
STRRCHR, STRRICHR
Synopsis
#include <string.h>
Description
The strrchr() function is similar to the strchr() function, but searches from the end of the string
rather than the beginning, i.e. it locates the last occurrence of the character c in the null terminated string
s. If successful it returns a pointer to that occurrence, otherwise it returns NULL.
The strrichr() function is the case-insensitive version of this function.
Example
#include <stdio.h>
#include <string.h>
void
main (void)
{
char * str = "This is a string.";
while(str != NULL) {
printf( "%s\n", str );
str = strrchr( str+1, ’s’);
}
}
See Also
strchr(), strlen(), strcmp(), strcpy(), strcat()
Return Value
A pointer to the character, or NULL if none is found.
STRSPN
Synopsis
#include <string.h>
Description
The strspn() function returns the length of the initial segment of the string pointed to by s1 which
consists entirely of characters from the string pointed to by s2.
Example
#include <stdio.h>
#include <string.h>
void
main (void)
{
printf("%d\n", strspn("This is a string", "This"));
printf("%d\n", strspn("This is a string", "this"));
}
See Also
strcspn()
Return Value
The length of the segment.
296
STRSTR, STRISTR
Synopsis
#include <string.h>
Description
The strstr() function locates the first occurrence of the sequence of characters in the string pointed
to by s2 in the string pointed to by s1.
The stristr() routine is the case-insensitive version of this function.
Example
#include <stdio.h>
#include <string.h>
void
main (void)
{
printf("%d\n", strstr("This is a string", "str"));
}
Return Value
Pointer to the located string or a null pointer if the string was not found.
STRTOK
Synopsis
#include <string.h>
Description
A number of calls to strtok() breaks the string s1 (which consists of a sequence of zero or more text
tokens separated by one or more characters from the separator string s2) into its separate tokens.
The first call must have the string s1. This call returns a pointer to the first character of the first token,
or NULL if no tokens were found. The inter-token separator character is overwritten by a null character,
which terminates the current token.
For subsequent calls to strtok(), s1 should be set to a null pointer. These calls start searching from
the end of the last token found, and again return a pointer to the first character of the next token, or NULL
if no further tokens were found.
Example
#include <stdio.h>
#include <string.h>
void
main (void)
{
char * ptr;
char * buf = "This is a string of words.";
char * sep_tok = ".,?! ";
298
TAN
Synopsis
#include <math.h>
Description
The tan() function calculates the tangent of f.
Example
#include <math.h>
#include <stdio.h>
#define C 3.141592/180.0
void
main (void)
{
double i;
TIME
Synopsis
#include <time.h>
Description
This function is not provided as it is dependant on the target system supplying the current time. This
function will be user implemented. When implemented, this function should return the current time in
seconds since 00:00:00 on Jan 1, 1970. If the argument t is not equal to NULL, the same value is stored
into the object pointed to by t.
Example
#include <stdio.h>
#include <time.h>
void
main (void)
{
time_t clock;
time(&clock);
printf("%s", ctime(&clock));
}
See Also
ctime(), gmtime(), localtime(), asctime()
Return Value
This routine when implemented will return the current time in seconds since 00:00:00 on Jan 1, 1970.
Note
The time() routine is not supplied, if required the user will have to implement this routine to the
specifications outlined above.
300
TOLOWER, TOUPPER, TOASCII
Synopsis
#include <ctype.h>
Description
The toupper() function converts its lower case alphabetic argument to upper case, the tolower()
routine performs the reverse conversion and the toascii() macro returns a result that is guaranteed
in the range 0-0177. The functions toupper() and tolower() return their arguments if it is not an
alphabetic character.
Example
#include <stdio.h>
#include <ctype.h>
#include <string.h>
void
main (void)
{
char * array1 = "aBcDE";
int i;
#include <stdarg.h>
Description
These macros are provided to give access in a portable way to parameters to a function represented in a
prototype by the ellipsis symbol (...), where type and number of arguments supplied to the function are
not known at compile time.
The rightmost parameter to the function (shown as parmN) plays an important role in these macros, as
it is the starting point for access to further parameters. In a function taking variable numbers of
arguments, a variable of type va_list should be declared, then the macro va_start() invoked with
that variable and the name of parmN. This will initialize the variable to allow subsequent calls of the
macro va_arg() to access successive parameters.
Each call to va_arg() requires two arguments; the variable previously defined and a type name which
is the type that the next parameter is expected to be. Note that any arguments thus accessed will have
been widened by the default conventions to int, unsigned int or double. For example if a character
argument has been passed, it should be accessed by va_arg(ap, int) since the char will have been
widened to int.
An example is given below of a function taking one integer parameter, followed by a number of other
parameters. In this example the function expects the subsequent parameters to be pointers to char, but
note that the compiler is not aware of this, and it is the programmers responsibility to ensure that correct
arguments are supplied.
Example
#include <stdio.h>
#include <stdarg.h>
void
pf (int a, ...)
{
8 va_list ap;
va_start(ap, a);
302
while(a--)
puts(va_arg(ap, char *));
va_end(ap);
}
void
main (void)
{
pf(3, "Line 1", "line 2", "line 3");
}
XTOI
Synopsis
#include <stdlib.h>
Description
The xtoi() function scans the character string passed to it, skipping leading blanks reading an optional
sign, and converts an ASCII representation of a hexadecimal number to an integer.
Example
#include <stdlib.h>
#include <stdio.h>
void
main (void)
{
char buf[80];
int i;
gets(buf);
i = xtoi(buf);
printf("Read %s: converted to %x\n", buf, i);
}
See Also
atoi()
Return Value
A signed integer. If no number is found in the string, zero will be returned.
304
__CONFIG
Synopsis
#include <pic18.h>
__CONFIG(n, data)
Description
This macro is used to program the configuration fuses that set the device into various modes of
operation.
The macro accepts the number corresponding to the configuration register it is to program, then the 16-
Bit value it is to update it with.
16-Bit masks have been defined to describe each programmable attribute available on each device.
These attribute masks can be found tabulated in this manual in the Features and Runtime Environment
section.
Multiple attributes can be selected by ANDing them together
Example
#include <pic18.h>
void
main (void)
{
}
__EEPROM_DATA
Synopsis
#include <pic18.h>
__EEPROM_DATA(a,b,c,d,e,f,g,h)
Description
This macro is used to store initial values into the device’s EEPROM registers at the time of
programming.
The macro must be given blocks of 8 bytes to write each time it is called, and can be called repeatedly
to store multiple blocks.
__EEPROM_DATA() wil begin writing to EEPROM address zero, and will auto-increment the address
written to by 8, each time it is used.
Example
#include <pic18.h>
__EEPROM_DATA(0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07)
__EEPROM_DATA(0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F)
void
main (void)
{
}
306
__IDLOC
Synopsis
#include <pic18.h>
__IDLOC(x)
Description
This macro places data into the device’s special locations outside of addressable memory reseved for ID.
This would be useful for storage of serial numbers etc.
The macro will attempt to write 5 nibbles of data to the 5 locations reserved for ID purposes.
Example
#include <pic18.h>
__IDLOC(15F01);
/* will store 1, 5, F, 0 and 1 in the ID registers*/
void
main (void)
{
}
308
Index
310
Index
312
Index
314
Index
EEPROM_WRITE macro 72 F
EI 246
ellipsis symbol 94 FABS 250
else directive 136 far keyword 89, 90
elsif directive 136 far pointers 90
end directive 129 far variables 89
end_init psect 101 farbss 101
endif directive 136 farbss psect 101
endm directive 137 fast interrupt save/restore 103
enhanced S-Record 49 fast interrupts 103
enhanced symbol files 150 file formats 17
environment variable American Automation hex 49
HTC_ERR_FORMAT 54 assembler listing 49
HTC_WARN_FORMAT 54 binary 51
equ directive 128, 133 command 48, 160
equating symbols 133 creating with cromwell 164
errata fixes 60 cross reference 119, 163
error files 55 cross reference listings 53
creating 149 DOS executable 151
error messages 55 enhanced Motorola S-Record 49
formatting 54 enhanced symbol 150
formatting for MPLAB 56 Intel hex 58
LIBR 161 library 75, 158, 160
redirecting 55 link 154
used by HPDPIC 120 map 155
EVAL_POLY 248 Motorola hex 60
EXP 249 object 52, 151, 160
expand assembler control 141 optimizer 119
exponent 84 preprocessor 61
expressions prototype 61
assembler 127 specifying 60, 121
relocatable 128 S-Record files 60
extern keyword 106 symbol 150
external program space 89 symbol files 75
external variables 89 symbolic debug 25
Tektronix hex 65
TOS executable 151
UBROF 65
316
Index
318
Index
320
Index
322
Index
324
Index
326
Index
W
W register 97, 126
warning level 65
setting 154
warnings
level displayed 65
stopping on 66
suppressing 154
with psect flag 132
word boundaries 132
X
xref assembler control 143
XTOI 304
328
1 Introduction
2 Tutorials
7 Error Messages
8 Library Functions
3
PICC18 Options
Option Meaning
-processor Define the processor
-AAHEX Generate an American Automation symbolic HEX file
-ASMLIST Generate assembler .LST file for each compilation
-Aaddress Specify offset for ROM code
-A-option Specify -option to be passed directly to the assembler
-BIN Generate a Binary output file
-Bl Selects large memory model
-Bs Selects small memory model
-C Compile to object files only
-CKfile Make OBJTOHEX use a checksum file
-CP16 Use 16-bit wide pointers to program space
-CP24 Use 24-bit wide pointers to program space
-CRfile Generate cross-reference listing
-D24 Use truncated 24-bit floating point format for doubles
-D32 Use IEEE754 32-bit floating point format for doubles
-Dmacro Define pre-processor macro
-E Use “editor” format for compiler errors
-Efile Redirect compiler errors to a file
-E+file Append errors to a file
-FAKELOCAL Produce MPLAB-specific debug information
-FDOUBLE Use Fast 32-bit floating point libraries
-Gfile Generate enhanced source level symbol table
-HELP Print summary of options
-ICD Compile code for MPLAB-ICD
-Ipath Specify a directory pathname for include files
-INTEL Generate an Intel HEX format output file (default)
-Llibrary Specify a library to be scanned by the linker
-L-option Specify -option to be passed directly to the linker
-Mfile Request generation of a MAP file
-MOT Generate a Motorola S1/S9 HEX format output file
-MPLAB Specify compilation and debugging under MPLAB IDE
-Nsize Specify identifier length
-NODEL Do not remove temporary/intermediate files.
PICC18 Options
Option Meaning
-NOERRATA Disable errata-fix modifications of the output code
-NORT Do not link standard runtime module
-O Enable post-pass optimization
-Ofile Specify output filename
3 -O-option
-P
Specify -option to be passed directly to objtohex
Preprocess assembler files
-PRE Produce preprocessed source files only
-PROTO Generate function prototype information
-PSECTMAP Display complete memory segment usage after linking
-q Specify quiet mode
-RESRAMranges Reserve the specified RAM address ranges.
-RESROMranges Reserve the specified ROM address ranges.
-ROMranges Specify program space memory for ROM-less devices
-S Compile to assembler source files only
-SIGNED_CHAR Make the default char signed.
-STRICT Enable strict ANSI keyword conformance
-TEK Generate a Tektronix HEX format output file
-Usymbol Undefine a predefined pre-processor symbol
-UBROF Generate an UBROF format output file
-V Verbose: display compiler pass command lines
-Wlevel Set compiler warning level
-Wlevel! Set compiler warning level and stop on warnings
-X Eliminate local symbols from symbol table
-XDATArange Specify an address range for external data memory
-Zg[level] Enable global optimization in the code generator