Frequently Asked Questions


(with answers!)

You may also want to check out our on-line forums for the PICC compiler and the Pacific C compiler.

These forums allow you to post and answer questions, or just browse what other people have asked or replied. If none of these questions are helpful, you may send email to


6. Convert 2500AD assembler to HI-TECH Z80 format
12. Printf does not work with a getch()
13. HC11 code space with hole for EEPROM
16. Producing a relocatable load file with the 68000 compiler
19. How do I redefine register symbols with the XA assembler?
21. MPC bit usage conversion on the PIC
22. Const problem with pointers or arrays
23. How to initialize 6805 configuration byte at compile time?
24. Why don't I get source level debugging information in my emulator/debugger?
25. Using %f in printf does not work
26. My interrupt service routine is too slow
27. Code works with lucifer, but doesn't work when I program an EPROM
28. XA Interrupt Service Routine won't run
29. RAM usage on the PIC
30. Why do I get an "Arithmetic overflow" message?
34. How do I locate "Fixup error" messages?
38. Converting Code from ByteCraft or MPLABC to HI-TECH PICC
39. Saving variables to EEPROM as a block
41. #asm/endasm near an if statement ends up in the wrong place
42. Allocating memory into other banks on a PIC
44. "Compiler not installed properly" error message comes up.
45. "Compiler already in use - try again later" message comes up - help!
46. Why is the compiler DOS based, not Windows?
47. What symbol lengths are supported?
48. Can functions be defined to compile inline rather then as calls?
49. How do I use interrupts on the PIC?
50. Installing the compiler breaks cc:Mail
51. How can I print from the editor in HPD?
53. Reserving ROM space on a PIC for serial no. etc.
54. How do I access C variables from assembler?
55. Error message "incompatible intermediate code version"
56. Meaning of Cross-Grade
57. "Out of environment space" error
58. How to map bits onto a RAM variable?
59. What does the call graph in the map file mean?
60. Integrating MPLAB with PICC, PICC Lite or PICC-18
61. Loading the OSCCAL value for PIC devices
62. Are the run-time libraries re-entrant?
63. Setting ID Locations
64. Why is the debugger called Lucifer?
67. Intergrating PICC with ICEPIC-2
68. Adapting printf for use with serial ports
69. How do I predefine the contents of eeprom data using PICC?
70. Adding Codewrite Error Parser for HI-TECH Compilers
71. How do I write graphics programs with Pacific C?
72. PICC compiler upgrade installation fails
73. Getting started programming microcontrollers in C
74. Make fails when using RICE emulator
75. RAM and ROM integrity test on a PIC?
76. How do I ensure declarations are consistent across files?
77. How can I access external RAM on the PIC17 devices?
78. Error handling in MPLAB isn`t working
80. Leading 0`s with printf for PICC
81. Updating/Upgrading my compiler
82. Compiler can't find files when not installed in default location
83. I get the error: Cant find XXX words/bytes in psect XXX
84. I get the error - "Too many file arguments. Usage: cpp [input [output]]"
85. The PICC-18 compiler produces code twice as big as the PICC compiler
86. Compiling programs for use with the MPLAB-ICD
87. I get the error: function * appears in multiple call graphs: rooted at *
88. No XOR(^) for bit variables on the PIC an PIC18
89. Positioning code, constant or variable at a specifc address in ROM or RAM.
90. How to get MPLAB to display compile errors
91. How do I view local variables in MPLAB?
92. printf() ouput
93. How do I use scanf() and gets() functions with the PICC/PICC18 compiler ?
94. How do I write assembler functions which can be called from C?
95. How do I create a new PICC project in MPLAB?
96. How do I put a program to a specific address for the PIC17C*** series of chips ?
97. I keep running out of ram on the PICC-18, yet there should be enough space
98. Extended support option
99. I recently purchased the compiler and it has suddenly stopped working
100. Why is the compiler slow at compiling?
101. My compiler is running in demo mode. How do I activate it?


6. Convert 2500AD assembler to HI-TECH Z80 format
These are some of the changes that must
be made to convert 2500AD assembler code files to
be used with the HI-TECH Z80 assembler

1)      *INCLUDE must start in column 1 (no white space before it)
2)      Filenames to *INCLUDE should not have <> around them. If the included file
        is not in the current directory you must supply a pathname.
3)      the EXTERN directive is not used - use GLOBAL instead (it serves both to
        declare external symbols and export public symbols)
4)      Conditional assembly is done with the IF directive. It should be followed
        by an expression which, if it is non-zero, the code will be assembled, if
        it is zero, it will not. So '.ifnz pdt' is replaced by either 'if pdt' or
        'if pdt<>0'.
5)      The ENDC directive does not have a dot.
Back to top

12. Printf does not work with a getch()

Q: If I use printf to display a prompt, then do a getch() call to wait for a key
press, the prompt does not appear unless it includes a newline at the end. 

A: Printf is a stdio function, and by default is line buffered, i.e. only complete
lines are output. Special provisions are made to automatically flush the buffer
when any stdio input function is called, but getch() is not a stdio function, but
a low-level (CONIO) function. The solution is to explicitly fliush the stdout
buffer before calling getch, e.g. 

          printf("This is a prompt - press a key");



For more information see the manual entry for fflush(). 
Back to top

13. HC11 code space with hole for EEPROM

> I am using the 68HC11 version that has an unmovable EEPROM at B600. The
> designer of the board located the code space in 8000-FFFF. Short of turning
> off the EEPROM feature, how can I make a 'hole' in the text segment through
> which I can get to the EEPROM.

> Is there a way to create a TEXT fragment that is not relocatable but the
> other segments will move around. I have tried all kinds of tricks with the

Yes, there is. We will support this more directly in the next version, but
for now what you do is this:

If your program is not already broken into more than one source file, do
so now.
Then choose one or more modules and add this line to the beginning of each:

#pragma psect text=hirom

The name 'hirom' is not anything special, you can call it what you like.
Now add to the linker options (either add to the Make/Linker arguments list
in HPD, or use a -l option on the command line) this option:


where 0C000h is the address that ROM continues at after the EEPROM. I don't know
how big your EEPROM is so you work that out. On the command line to C68 you would


Now remake everything. IT sounds like you might have been driving the linker
directly in which case you just add -phirom=0c000h to your linker command

That should solve you problem. Be sure to check the link map afterwards
to make sure everything is kosher.
Back to top

16. Producing a relocatable load file with the 68000 compiler

Q: I want to produce a file from the 68000 compiler that I can load and
relocate to any arbritrary address. How do I do this?

A: There's an easy solution; you can ask
objtohex to generate a file that has enough information to relocate
jumps when it is loaded. When linking, use the options

	-ptext=0,data,bss -l

and to objtohex use 

	-l -p

You will get a file with a header on it like this:

struct  prg
        ushort  p_magic;        /* magic number - 0x601A */
        uchar   p_text[4];      /* length of text segment */
        uchar   p_data[4];      /* length of data segment */
        uchar   p_bss[4];       /* length of bss segment */
        uchar   p_syms[4];      /* length of symbol table */
        uchar   p_fill[4];      /* unused */
        uchar   p_txtbegin[4];  /* beginning of text segment */
        ushort  p_reloc;        /* relocation flag */

Followed by the text and data psects (the bss psect is not physically present in
the file), symbol table if present, then a relocation table. The relocation
table is one 4 byte value, which represents the first location requiring
relocation (all relocatable locations are 4 bytes - one longword) then a sequence
of single bytes, representing offsets from the last relocatable location to the
next one. An offset of 255 means you update the address, but do not relocate
the location (this is used where the gap is > 254). The end of the table is
flagged by a zero byte. At each relocatable location you add the base address
of the memory you are loading the code into, to what is already there. Note
that any bss and data addresses will also be affected, though since you can
tell where the bss and data begin and end, you could use this to determine
if the reference is to one of those psects and relocate it to a different

Back to top

19. How do I redefine register symbols with the XA assembler?

Q: I want to define symbols as register names when writing XA assembler code,
but once I've used equ to define a new register name, I can't later use
set to redefine that name to something else.

A: What happens is this; once you have defined a symbol as a register, it
then becomes identical with that register, i.e. this code:

areg    equ     r0
areg    set     0

is equivalent to

areg    equ     r0
r0      set     0

which is syntactically incorrect.

The way I would do it is to use the C preprocessor, and do

#define areg    r0


#undef  areg
#define areg    0

You can get the XAC driver to automatically run the assembler file
through CPP by using the -P option, e.g.

xac -p -c

will preprocess then assemble, leaving yourfile.obj.
Back to top

21. MPC bit usage conversion on the PIC

Q: I want to convert some Bytecraft MPC source code to the HI-TECH C PIC
compiler. The bit access notation has confused me. In MPC, the
Convention is FILE.BIT, where the file and bit are separated by a period.
Will HI-TECH C accept this notation, or is there a "slick" way to convert the
source code to compile with the HI-TECH C Compiler ?
A: This notation is not accepted by our compiler - it's non-ANSI. What I'd
suggest is this: do a global change (or several thereof) to convert
everything that looks like

and then add to the program (or a header file) declarations like:

/* this line once only, of course */
#define PB(port,bit)    ((unsigned)&(port)*8+(bit))

/* as many like this as required */
static bit      FILE_BIT        @ PB(FILE,BIT);

I'm not sure if you are using symbols for the bit number, or an actual
number, either way it doesn't really matter.

How easy the global changes are depends on whether you are using the dot
character anywhere else, and how powerful your editor is.

The absolute variable notation (@ address) is non-ANSI too, but this is only used
in declarations, not in the code itself, so it's a lot easier to port than
the Bytecraft dot notation, where you have to go and change all your code instead
of just your declarations.
Back to top

22. Const problem with pointers or arrays

Q: If I declare an array of pointers and I want to make the array itself qualified
const, so that it goes into ROM not RAM, it doesn't work. 

A: This is usually because you have put the 'const' qualifier before the '*' n the 
array type. This makes the const bind to the type pointed to rather than to the array
itself. Put the const keyword immediately before the array name, for example: 

/* an array of pointers to a structure */

struct xyz * const aname[10];

/* an array of pointers to functions */

int (* const fname[10])(void);

In some situations you may want to have a const array pointing to const
data, in which case you would have 'const' both before and after the '*', e.g.

const char * const array[] = { "string 1", "string 2" };

Back to top

23. How to initialize 6805 configuration byte at compile time?

>I'm using a 68HC705C9A. I must somehow generate at hexcode which writes the registry
> at programming time. I'm looking for a instruction like:
> const unsigned char PORTB_MOR @ 0x3ff0 = 0x00;  

Something like this should do it:

#define ___mkstr(x)     #x
#define __CONFIG(x)     asm("\tpsect config,abs,ovrld");\
                        asm("\torg\t3FF0h"); \
                        asm("\tfcb "___mkstr(x))

Then just use


or whatever. Just make sure the argument is ok as an assembler
expression - you can use #define'd constants because the __mkstr
hack makes sure they get expanded.
Back to top

24. Why don't I get source level debugging information in my emulator/debugger?

Q: When I compile my program and load it into the debugger/emulator/whatever,
I can get global symbols, but no source line numbers. Why is this?

A: Probably because you did not use the HPD "Options/Source level debug info"
setting (in some versions this is moved into the "Symbol and map file options"
dialog) or from the command line you did not use the -g option to the command
line driver. Note that if you use the command line driver separately to
compile to .obj files and then link, you must use -g both times. At link time
you may need to specify a file name, e.g. -gfile.sym.

If you're using MPLAB to run the compiler, make sure "Generate Debug Info" is
turned on in the Node Properties for the Hex file and all the source files.
Also add in the "Additional command line options" area for the hex file's
Node properties the option


if this is not already provided by MPLAB. This will ensure that local symbols are
visible as global symbols when debugging with MPLAB (which has no local variable
support itself).

Back to top

25. Using %f in printf does not work

Q: I have a printf call like:


but the output is 'number=(non-float printf)'. What am I doing wrong?

A: In HPD you must set "Options/Float formats in printf" or from the command
line use a -LF option to include the floating point library. The default
library does not include floating point support for printf, to reduce the
code size.
Back to top

26. My interrupt service routine is too slow

> I have an interrupt service routine (ISR) written in C that is too slow.
> I looked at the code produced for the ISR
> by the compiler and I detected that very much time is used to save and restore
> the internal RAM and memory scratchpads. 
> Is there a way to avoid this?

Yes, do as little as possible in the ISR - in particular do not call
any other functions, or if you must, ensure that you only call other
functions that are defined in the same source module before the ISR,
so that the compiler will know exactly what registers are used
by the function.

If you call a function that is unknown to the compiler at the
time, it must make a worst-case assumption about register use.
Back to top

27. Code works with lucifer, but doesn't work when I program an EPROM

Code runs on the XA board with the Lucifer debugger, but when an EPROM is
programmed, the code no longer works.

Don't forget the watchdog!  You either have to disable it in the powerup
routine or keep feeding it.

Use the following code to disable the watchdog:

#ifdef  _XA_
psect   text,global,reloc=2,align=2
global  powerup,start
        clr     0x02fa          ;clear watchdog enable bit (ie. disable)
        mov.b   0x45d,#0xa5     ;watchdog1
        mov.b   0x45e,#0x5a     ;watchdog2
        jmp     start
Back to top

28. XA Interrupt Service Routine won't run


I have set up an ISR but it never runs.


You may be having a problem with priorities.

Attached is a program which should give you an idea
of what to do.

Among other tasks, the ROM_VECTOR command sets up
the priority of the Interrupt Service Routine (ISR)

The priority of the currently running code (ie. the
main program in this case) is reduced to allow the
ISRs code to run.

Finally, the priority of the interrupts themselves
are set up.

Here is a step-by-step explanation.

1. This sets up the vector, and priority for the Timer 0 ISR.
The priority is set to 14:


2. This reduces the priority of the currently executing program
to the lowest priority.

  PSWH &= 0xF0;

3. This sets up the priority of the Timer 0 INTERRUPT (not the
ISR) to 14:

  IPA0 |= 0x60;

#include        < xa.h >
#include        < intrpt.h >

static long     i, k;

void banked interrupt

        ROM_VECTOR(IV_T0, timer_tick, IV_SYSTEM+IV_PRI14+IV_BANK1);     // set vector
        PSWH &= 0xF0;                           // reduce processor priority
        IPA0 |= 0x60;                           // set timer0 priority

        TMOD = 0x22;                            // timer mode
        RTL0 = 0x20;                            // reload value
        TR0 = 1;                                // enable timer
        ET0 = 1;                                // enable timer interrupts
        ei();                                   // enable global interrupts
        for(;;)                                 // loop forever
Back to top

29. RAM usage on the PIC

> (1)I get this error when I'm compiling my program for a PIC:

::Can't find space for psect rbss_0 in segment BANK0 (error)

> What does this mean exactly?

It means that you have run out of space in RAM bank 0 (hence the rbss_0
name). It might also mention the class COMBANK, depending on the circumstances.
What you probably need to do is move some variables into banks 1, 2 or 3.
You do this by prefixing some global variable declarations with a qualifier
like bank1, e.g.

bank1 int fred;

Naturally you should group variables often accessed together in the same
bank. You can't use the bank qualifiers with local variables, functions or
parameters. You can use them with bit variables. Beware of pointers, e.g.

bank2 char * p;

is a pointer to a char in bank 2, but the pointer itself is still in bank 0.
This declaration

bank2 char * bank1 p;

is a pointer to a char in bank 2, but the pointer is located in bank 1.
> (2)  Why doesn't the compiler automatically start using Bank1, etc.?

Because you can link together object files that are independently compiled,
it's not possible to be sure until link time that things will fit, by which
time it's too late to reassign variables into another bank. If the code
generator were to produce code that made no assumptions about what bank
a variable was in, it would have to produce bank bit select set and reset
instructions before *every* RAM access - a totally unacceptable

Even trying to make a good guess about whether bank 0 is going to overflow
is nigh impossible when one module is being compiled - a small change to
the function calling graph can change the allocation requirements significantly.

For these reasons, we concluded that there is no viable alternative to
requiring the user to specify tbe bank.

> (3)  I'm not sure from reading the manual exactly what goes where.
> Could you please enlighten me on how RAM is organized

Bank 0 is used for any non-const variables that are:

        1) local variables or
        2) static or global and not specified as being in another bank
        3) interrupt register saving
        4) temporary locations (these are placed in common RAM if available) 

Anything that is explicitly given a bank goes in that bank. Local variables
(i.e. variables inside a function that are not static) cannot be allocated
in any other bank.
Back to top

30. Why do I get an "Arithmetic overflow" message?

Q1: If I assign 0xFFFF to an integer, I get the message "arithmetic overflow
in constant expression. Why doesn't it just treat it as -1?

A1: As an int, this variable can only assume values of -32768 thru to 32767.
The value 0xFFFF is equal to 65535, and is thus too big. If you really
don't want signed values, use an unsigned int, or use -1, or use ~0.

In general, if you want a value that has all bits set, and you don't
want to be specific about how many bits that is, ~0 is good. e.g.

        OnLimit = ~0;

will assign a value to the variable that has all bits set, for a 16 bit int
this will be 0xFFFF (or -1).

A macro like

#define ALL_BITS_SET    (~0)

can be useful too.

Q2: What about something like

#define XTAL 8000000
#define BEEP_FREQ 3200
#define BEEP_TIME XTAL/(64*BEEP_FREQ)-1   // = 38.0625

this also gives me "arithmetic overflow" when I assign BEEP_TIME to
an unsigned char. But the final value easily fits a char. What's
going on?

A2: You need to ensure that long arithmetic is used for the whole
expression. 8000000 is automatically a long number, but 3200 and 64
aren't, so 64*3200 is evaluated in int length, and overflows. Use
an 'L' suffix to force long arithmetic, e.g.

#define BEEP_TIME XTAL/(64L*BEEP_FREQ)-1   // = 38.0625

Note the 'L' appended to 64 - this makes it a long number, and
the evaluation of 64L*3200 will be done in 32 bit length.
Back to top

34. How do I locate "Fixup error" messages?

Q: I get the message "Fixup error referencing ..."; or "Fixup error in expression ..."
What does this mean and what can I do about it?

A: Briefly, it means the linker was instructed to adjust (fixup) a reference
to a memory location, and the address that it calculated would not fit in the
space available, e.g. a byte (8 bit) reference was asked to contain a value
larger than 0xFF. 

Typically this occurs with the PICC compiler where pointers have not
correctly been declared. For example:

bank2 char ch;
char *ptr;

ptr = &ch;  // fixup error

This is a fixup error because "ptr" is a bank0 pointer - it needs to
be bank2. ie:

bank2 char *ptr;

To locate the cause of the fixup error, look at the error message. 
Here's an example:

gmain6.obj:113:Fixup overflow referencing psect rbss_1 (loc 0x8C8 (0x8C2+6), size 1, value 0xA8)

This is from the PIC compiler. The "size" tells us it's a one byte reference -
in fact it's a 7 bit data reference, hence the value 0xA8 is too big to fit.
The fact that it references rbss_1, which is a bank1 address, suggests that
a reference was being made to bank 1 without truncating the full address to
the 7 bits that actually fit in the instruction.

Note the location - 0x8C8. This is the absolute address of the offending instruction;
make sure you have generated an assembler list file, and edit it. The address given
is a byte address, but because this is on a PIC which has 14 bit wide
program memory, divide by two to get a word address - 8C8/2 = 0x464.
On other processors the address would be used directly.

In the list file, simply locate location 0x464:

   993  0464  00A8                       movwf _TempChar ;#
        ^^^^ location

The value given in the error message (0xA8) is the address of TempChar - you can also
check the map or symbol file to find a symbol with this address (but the list file
method is probably better).

If you're using a compiler that doesn't give you an absolute list file, it's a
little harder. In this case use the dump utility to dump the object file. The
number after the file name is the record number - 113. Do something like this:

dump gmain6.obj >afile

Now edit afile and look for 113:

        113     RELOC   506
                0       RELBITS COMPLEX         11
                6       RELBITS RPSECT  rbss_1  7

Here is the info we need - note the offset of 6 from the start of this
record - that corresponds to the +6 in the location of the error. To
relate this to an address, look at the immediately preceding text record
(112 in this case, sometimes there will be two reloc records for one
text record):

        112     TEXT    98
                text0   1478    88

The "text 0 1478" says this is the text0 psect, and this block starts
at offset 1478 (decimal) from the start of the text0 psect in this module.
Add the 6 byte in-block offset to get 1484, convert to hex, 0x5CC, now
divide by two for a PIC (use as is for other chips) to get 2E6, and check
the (relocatable) list file:

   993  02E6' 0088'                      movwf _TempChar ;#

There it is. The same line as in the absolute list file, but unrelocated.
In this case, this is embedded assembler code (the ;# indicates this) and
it should read something like this:

                  bsf 3,5            ;set RP0 to access bank 1
                  movwf _TempChar^0x80  ; invert top bit of address

Back to top

38. Converting Code from ByteCraft or MPLABC to HI-TECH PICC

Converting Source Code from ByteCraft or MPLABC to HI-TECH PIC
by Dave Korpi, Sierra Instruments Inc.

1. Introduction

This application note will assist you in converting your source code from
ByteCraft to the more powerful HI-TECH PIC C compiler.

2. Assumptions

This assumes you have installed the HI-TECH PIC C compiler properly.

3. A Few Things to Note

- HI-TECH calls their assembly files .AS not .ASM.

- In order to use the HPDPIC integrated environment to produce a HEX file,
make sure you first create a PROJECT. This is done by calling up
Make/NewProject and following the prompts. Refer to 12.5.6 in the manual
for complete details. Choose "Full Optimisation" to allow the compiler to
optimise the code.

- If MPLAB is to be used, select the output file type as "ByteCraft .COD
file" type. A .HEX file is produced as well. Both file types are required
to run in the MPLAB environment. Also, for debugging purposes, it is
desirable to "Generate assembly listing" which is an option under the
"Compile" heading. An assembler listing has variable and function names
and allows you to search for and find where breakpoints should be set.

- Once you have the project SAVE it.

- Test your program by running MAKE (F5) and check if you have the desired

file. You can see where the files will go by looking at the options in the

pull down.

4. File Differences In Your .h File

4.1 Types

Below we study the differences in how you define your TYPES. Notice how
HI-TECH suggests "assigning" real names to the variable types. When you
see UINT32 you know it is a 32-bit unsigned long.


// Types
#define SINT32 signed long
#define UINT32 unsigned long
#define UINT16 unsigned short
#define SINT16 signed short
#define WORD unsigned short
#define SINT8 signed char
#define UINT8 unsigned char
#define BYTE unsigned char


// Types
#define SLONG signed long
#define ULONG unsigned long
#define WORD unsigned long
#define SINT signed int
#define UINT unsigned int
#define BYTE unsigned char


4.2 In-line Assembler

Here is an example of how you call assembler directly from C:


#define WatchDogTimerClear() asm( "clrwdt")


#define WatchDogTimerClear() #asm "clrwdt"

Note that to use Special Function Registers in in-line assembler, include
pic.h and prepend an underscore.

#include <>

void main(void)
asm("movf _FSR");

4.3 Defining Static Bits

Notice how defining PORTBIT(p,b) makes this process simpler.


#define PORTBIT(p,b) ((unsigned)&(p)*8+(b))
static bit ALARM_HI @ PORTBIT(PORTA,2); // Out 1=active (HI_ALM)
static bit ALARM_LO @ PORTBIT(PORTA,3); // Out 1=active (LO_ALM)


#define ALARM_HI PORTA.2 // Out 1=active (HI_ALM)
#define ALARM_LO PORTA.3 // Out 1=active (LO_ALM)

4.4 Defineing Simple Functions

Note how we define a simple and yet handy function:


#define EEPROMClearDataLine() { TRISC &= ~(1 << 4); PORTC &= ~(1 << 4); }


#define EEPROMClearDataLine() { TRISC.4 =0; PORTC.4 =0; }

4.5 The NOP Instruction

Note in order to call the asm NOP you first must define it


#define NOP() asm("nop")
#define DELAY_IT() { NOP(); NOP(); NOP();}


#define DELAY_IT() { NOP(); NOP(); NOP();} // NOP() is defined in the

// ByteCraft header file

4. File Differences In Your .c Code

4.1 Defining Crystal Frequency

In the top of your HI-TECH file define the crystal frequency.

#define XTAL_FREQ 10MHZ

4.2 Defining Variables' Bank

Define in which banks your variables are to be placed.. (This implies that
you keep track of how many bytes you're using for global variables and how
they can/should be grouped). In the ByteCraft version, you can define the
address via the "@" symbol where the variables are, or accept wherever
the compiler places them. (Just don't expect correct data transfers between
the upper two banks and the lower two banks of data memory without some

static bank2 UINT16 gnTimeMicroSecTickToken;
static bank2 UINT8 gnTimeMicroSecTickRollover;

Note how we define variables in the c file:


static UINT16 gnA2DInputRawFlow;
static bank2 UINT8 gnLEDRaceTrackIndex;
static bank2 UINT8 gnLEDDisplayNumber;


ULONG gnA2DInputRawFlow;
UINT gnLEDRaceTrackIndex;
UINT gnLEDDisplayNumber;

4.3 Special Function Registers

Note how HI-TECH allows you to address Special Function Register bits
directly once they are defined.


CREN = 0;
CREN = 1;



Note how we deal with Special Function Register (SFR) CCP2CON.X with


CCPR2L = (UINT8)(gnD2APWM4_20maOutputValue >>2);
CCP2X = (gnD2APWM4_20maOutputValue &0x02) ?1 :0;
CCP2Y = (gnD2APWM4_20maOutputValue &0x01) ?1 :0;


CCPR2L = (UINT)(gnD2APWM4_20maOutputValue >>2);
CCP2CON.5 = (gnD2APWM4_20maOutputValue &0x02) ?1 :0;
CCP2CON.4 = (gnD2APWM4_20maOutputValue &0x01) ?1 :0;

Here is how we can set the bits of TRISC (an SFR). Note the | character.


TRISC |= 1 << 4; // or TRISC |= 0x10;


TRISC.4 =1;

4.4 Pointers


16 bit pointers are OK! You can point anywhere, although it's not polite to
do so.


8 bit pointers only! (Can be a problem if you're not careful.)
Back to top

39. Saving variables to EEPROM as a block

> 1)  I need to save all my runtime variables to EEPROM on power failure.  I
>       What tells the compiler to make a block of memory contiguous?
There are two ways you could tackle this; I'm assuming that there are only
certain variables you need to save. The best way is to declare these
as "persistent", e.g.

persistent int  fred;

This will prevent this variable from being cleared at startup, AND it will
put it in a special psect (on the PIC, you can use bank 0 only, at this time -
don't use bank1 with persistent).

Thus all the 'persistent' variables will be kept together, and you can
get the bounds of this psect from the following declarations:

extern char     _Lnvram[], _Hnvram[];

and use this in the following manner:

        write_to_eeprom(_Lnvram, _Hnvram-_Lnvram);

which will pass the starting address and the length to the function
write_to_eeprom, which should be declared something like:

void write_to_eeprom(char * start, unsigned char length)

You'll need to provide this function, configured to write to your particular
EEPROM. On the PIC16C84 you can use the EEPROM_WRITE macro in this function.

Back to top

41. #asm/endasm near an if statement ends up in the wrong place

Q: I have a block of assembler code embedded in C using #asm and #endasm, and
it follows an if statement. But even though it's not part of the if statement,
it's affected by it.

A: The #asm construct is not syntactically part of the C program - it's
handled at the pre-processing level. So if you have something like

       if(cond) do_stuff();
;      assembler code here

what happens is that the compiler sees the if and the statement after it,
then skips ahead looking for the corresponding else - which is absent, but
it doesn't find that out until after the #asm/endasm block, by which time it
has been output - but as part of the code conditional on the if statement.

The solution is to place a dummy statement before the #asm - this terminates
the scan for the else keyword, e.g.

       if(cond) do_stuff();
       /* null statement here */ ;
;      assembler code here

Alternatively, use the asm("") construct - it is like a C statement, so it
interacts correctly with C control structures.
Back to top

42. Allocating memory into other banks on a PIC

Q:   Will the PIC compiler allocate memory into other banks all by itself or does it
have to be forced? 
A: You need to use a bank qualifier to allocate memory in other banks.
For example:

bank1   char    fred;

Will define a char variable in bank1.

An ordinary pointer can point into either bank 0 or bank 1, but
not into banks 2 or 3. You can have a bank2 pointer, e.g.

bank2 char *    b2p;

which cannot point to banks 0 or 1.

Other examples:

bank2 char * bank2 ptr1;  // a pointer in bank2 that can point to bank2 char's
char * bank3 ptr2; // a pointer in bank3 that can point to bank0 char's

It's not practical to have bank1 allocation done automatically,
at least not without a large overhead in code size.
Back to top

44. "Compiler not installed properly" error message comes up.

Q: I moved my C compiler from my C: to E: recently,
and now when I try to compile it tells me:

"Compiler has not been installed properly - reinstall and try

A: Firstly, to move from one drive to another, or even from
one directory to another, you must reinstall. You cannot copy the installed
compiler (even backing up and restoring will not work unless you simply
restore over the existing files). If you have reinstalled, then
I suspect you're still running the old version. Check
your PATH to make sure you're running what you think you are - make sure
your PATH specifies the newly installed compiler, and make sure you have
updates the HTC_xxx environment variable or variables (note that most
compilers e.g. V7.44 and later don't actually need these set.)
Back to top

45. "Compiler already in use - try again later" message comes up - help!

Q: I'm trying to run the compiler, and it tells me 

"Compiler already in use - try again later"

Help! What do I do?

A: If you are running on a network, then there may well be someone else
using the compiler, and you only have a single user licence. If however
you're running on a stand-alone machine then it is most likely that the
compiler has left a lock file lying around and failed to detect that this
is the same machine as was used before. The fix is easy; run

HPDPIC clear_locks

(substitute the appropriate HPDxx command for your compiler.) The argument
must be in lower case.
Back to top

46. Why is the compiler DOS based, not Windows?

Well, firstly we are developing a Windows user interface. This will be Win-32,
i.e. it will run under Windows 95 and Windows NT. But since the compiler is a
non-graphical application, and the DOS interface runs quite happily under all
flavours of Windows already, it has not been as high a priority as making sure
that the basic functionality is top-notch.

In other words, we believe an excellent compiler with a DOS front-end is better
than an average compiler with a Windows front end. In the not too distant future
we'll have an excellent compiler with a Windows front end. Until then, the
compiler does what it's supposed to do very well, even if it's not quite as
pretty as some.
Back to top

47. What symbol lengths are supported?

Our assemblers have no specific limit on symbol lengths - all characters are
significant, and the line length is the only limitation (typically 256 characters
or more).

Our compilers default to 31 characters in symbols (this is the minimum value
permitted by the ANSI standard). This is adequate for most purposes, but if
more characters are needed, most of our compilers will allow the length to
be set anywhere from 31 to 255 characters.

Whatever the length of a symbol, the linker preserves the full name. Symbols
are also case-sensitive in all situations.
Back to top

48. Can functions be defined to compile inline rather then as calls?

Q: Can I defined a function as 'inline' so that the body of the function
gets expanded wherever I use it?

A: Inline functions are not an ANSI feature, but the same functionality is
available by using macros, e.g. here is a function defined as a macro to
write a byte to the EEPROM on a 16C84: when using this the only thing to
be careful of is that if you use it near an if, else or other flow-of-control
construct, it would be necessary to use braces around the function "call". Note
the backslashes on the end of each line except the last - these are required
since the macro definition is technically only one line.

#define EEPROM_WRITE(addr, value) \
             while(WR != 0)   /* wait till EEPROM idle */ \
                 continue; \
             EEADR=(addr); \
                 GIE = 0;        /* disable interrupts */ \
             while(GIE != 0);    /* make sure it worked */ \
             WREN=1;             /* enable writes */ \
             EECON2=0x55;        /* enter password */ \
             EECON2=0xAA; \
             WR=1;               /* start write cycle */ \
             GIE = 1;            /* re-enable interrupts */ \
             WREN=0              /* disable writes */
Back to top

49. How do I use interrupts on the PIC?

Q: How do I set up an interrupt routine on the PIC microcontroller?

A: Just declare a function with the 'interrupt' keyword. The compiler will
place it in the right place, and take care of all register saving and restoring.
The high-end (17Cxx) devices need a vector address as well.
Here's an example of a program for a midrange PIC that uses interrupts;

 *      Interrupt demo for PIC; wait for button press on RB0/INT,
 *      turn on a relay on another port bit for a period of time.
 *      For simplicity here, literal constants are used, usually these
 *      should be calculated with compile-time arithmetic.
static bit              RELAY @ (unsigned)&PORTB*8+7;   // use this bit to drive relay
static unsigned int     relay_timer;                    // timer value for relay driver

        RELAY = 1;              // ensure relay is off before enabling output
        TRISB = 0x3F;           // Port B bits 7 and 6 are output
        T0CS = 0;               // Timer increments on instruction clock
        T0IE = 1;               // Enable interrupt on TMR0 overflow
        INTEDG = 0;             // falling edge trigger the interrupt
        INTE = 1;               // enable the external interrupt
        GIE = 1;                // Global interrupt enable
                CLRWDT();       // Idly kick the dog

static void interrupt
isr(void)                       // Here be interrupt function - the name is
                                // unimportant.
        if(T0IF) {                              // timer interrupt
                TMR0 -= 250;                    // reload the timer - 250uS per interrupt
                T0IF = 0;                       // clear the interrupt flag
                if(relay_timer != 0)            // is the relay timer running?
                        relay_timer--;          // decrement it
                if(relay_timer == 0)            // if it has time out
                        RELAY = 1;              // turn the relay off
                PORTB ^= 0x40;                  // toggle a bit to say we're alive
        if(INTF) {                              // did we see a button press?
                RELAY = 0;                      // turn the relay on
                relay_timer = 4000;             // start the timer - 4000 ticks = 1 second
                INTF = 0;                       // clear the interrupt

For the high-end devices, the vector address is specified e.g. the timer0
interrupt is usually at location 0x10 (see the data sheet for vector
addresses). Here's the example:

void interrupt timer0_isr(void) @ 0x10
   /* your code here */
Back to top

50. Installing the compiler breaks cc:Mail

> After I installed PIC C, I tried to execute cc:Mail v6.0 but it failed
cc:Mail has a problem with the temp file path; change the TEMP variable
so that the file name does NOT have a trailing backslash, i.e. it should
be something like
This will not bother the compiler. We have changed the installer
to do this differently (since we can't fix cc:Mail).
Regards, Clyde
Back to top

51. How can I print from the editor in HPD?

Q: I would like to print a file from within the editor in HPD, but
there is no facility for this. What can I do?

A: Set up a user defined command: go to the Utilities menu, select
"Define user commands...". A dialog box will open up, add a new command
with "Print" in the Menu entry column, and "print $(EDIT)" in the
Command string column (don't include the quotes).

Now you can use this command, which will appear in the Utilities menu,
to print the currently loaded edit file. If you are using a network
printer under Windows, or want to print to a printer other than
the default, you can add a /d:printername option to the command

Under some versions of Windows, you may need to set up your printer to
specifically allow printing from DOS applications.
Back to top

53. Reserving ROM space on a PIC for serial no. etc.

Q: I'm going to set aside the highest 128 bytes in Page 1 ROM
to simulate an EEPROM that we have removed now that we are
using the PIC14C000.  The bytes will be written once by
external software (I'm going to serially program the part in
situ) and from then on, obviously, they will be read only.

Is there a recommended way of setting aside this ROM?

A: I'd recommend the following; in your C module put this code:

extern const char   myarray[];       // Don't declare the size here
        psect   reserved,class=CODE,delta=2
        global     _myarray
        ds      128           ;reserve 128 words

Now add to the linker options, in HPDPIC add a line to the
Make/Linker options list like this:


or add to the PICC command line the option:


Now you will get your 128 words at F80,
and this space will be avoided by
the linker when allocating code space.

If you need to pre-initialize this to FF or whatever, then it
should look like: 
        psect   reserved,class=CODE,delta=2
        global     _myarray
      retlw   0xFF
      retlw   0xFF
      ; etc. repeat as many times as required.

Back to top

54. How do I access C variables from assembler?

Q: I want to access a C variable from assembler code. How do I do this?

A: Firstly, it depends on what kind of variable you want to access. If the
variable is a parameter or local variable, then basically you can't. There
is no easy way of determining where this is stored (it can be different
places at different times).

If it's a global variable, it's pretty easy. In your assembler code, refer
to the variable by it's name, with an underscore prepended, e.g. the variable

int    fred;

becomes _fred in assembler. If the variable is defined in another module,
you'll need to declare it global, e.g.

       global      _fred
Back to top

55. Error message "incompatible intermediate code version"

This error can occur if the TEMP environment variable is set to a long
path name, which when combined with a file name, can result in a command
line to a compiler pass being too long. Often you will find TEMP is set

Change the TEMP setting to


This is usually done in the AUTOEXEC.BAT file.

You may also need to create the directory C:\TEMP

This will fix the problem.
Back to top

56. Meaning of Cross-Grade

Cross-Grade means crossing over to our product from a comparable competitors
product for the same chip/family type. This must be verified and validated by 
HI-TECH Software prior to any confirmation of any order approval.
Back to top

57. "Out of environment space" error

Q: When I run the compiler I get the error message "out of environment space".
How do I fix this?

A: If you're using Win95, edit C:\CONFIG.SYS and look for a line like:


(the details may vary - the SHELL= part is what's important). If this line
is present, remove it or comment it out - this will allow Win95 to automatically
set the environment space available to a DOS program.

If you're using Win3.1 or DOS, leave the line in, but increase the number after
the /e: option - e.g. from 1024 to 2048 should solve the problem.
Back to top

58. How to map bits onto a RAM variable?

Q: I want to be able to access single bits in a byte, but if I try to define
   a bit variable using the absolute variable construct, e.g.

static bit bitvar @ ((unsigned)&bytevar)*8+0;

   I get a compiler error. How can I do this?

A: The short answer is "you can't do this". The absolute variable construct using
@ requires an address known at compile time.

The long (and more useful) answer will depend on what you're actually trying
to do. You may find all you need is some simple macros like:

#define  testbit(var, bit)   ((var) & (1 <<(bit)))
#define  setbit(var, bit)    ((var) |= (1 << (bit)))
#define  clrbit(var, bit)    ((var) &= ~(1 << (bit)))

or you may like to define a union, e.g.

union both {
       unsigned char byte;
       struct {
           unsigned     bit0:1;  // etc.
       } bits;
}     var;

Now you can refer to var.byte or var.bits.bit0 for example.

Taking this a bit further, you can use unions to map bit "variables"
onto an existing byte, which is getting close to your original question.          

First up, define a structure type with bits in it:

typedef struct {
        unsigned        b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1;
}       bitv;                                                          

Now given a char variable, e.g.

char    myvar;

define a "variable" to be a single bit in that char like this:

#define mybit   (((bitv *)&myvar)->b0)

Now you can refer to mybit and it will access bit 0 of myvar. You can
use this just like you would use a bit variable defined any other way.
The code generated will be just as good as any other way. Example of

         mybit = 1;

To streamline the process a little, you can define a helper macro like

#define _paste(a,b)     a##b
#define bitof(var,num)  (((bitv *)&(var))->_paste(b,num))
Now defining a bit "variable" is done like this:         

#define x4      bitof(myvar, 4)
So now x4 represents bit 4 of myvar.
Back to top

59. What does the call graph in the map file mean?

> I can not find any explanation in the manual on the call graph section
> of the map file generated by the linker. 

> - Some functions are marked with a star.

These are functions on the critical path - i.e. if you want to reduce
the amount of RAM used for arguments and locals, you can ignore those
functions NOT marked with a star, because they do not influence the total.
Of course if you change a critical function, and reduce the RAM needed, other
functions may then become critical.

> - "size #,#". I guess it has something to do with arguments.

The first number is the number of bytes of locals, and the second is the number
of bytes of arguments.

> - "offset #".

This is the offset at which this function's RAM allocation starts. The offset
is from the base of the local RAM block, which you will see in the COMMON
section of the link map, after the file listing.
> - INDIRECT ####.

This represents all those functions called indirectly with a signature of ####.
Since it is based on signatures, then if you have two groups of functions that
are called indirectly in two different places, but have the same signature,
then the functions in both groups will appear to be called from both places.

> What about the "->" symbol in the call graph?

This appears when there is a function in the call graph that has no local
variables or arguments, but is calling other functions. It's just a shorthand
for a call, saves the call graph listing getting too wide with extra levels
of indentation.

Back to top

60. Integrating MPLAB with PICC, PICC Lite or PICC-18

Choose "Install Language Tool" from the "Project menu."
For PICC, select "HI-TECH PICC"
For PICC Lite, select "HI-TECH PICC Lite"
For PICC-18, select "HI-TECH PICC-18"

If these options are not available, make sure you are
using the latest version of MPLAB.

For each of the three entries in the "Tool Name",
specify the path to compiler in the "Executable" box:

For PICC (which is typically installed in C:\HT-PIC),

For PICC Lite (which is typically installed in C:\PICCLITE),

For PICC-18 (which is typically install in C:\HT-PIC18),
PICC-18 Compiler:  C:\HT-PIC18\BIN\PICC18.EXE
PICC-18 Assembler: C:\HT-PIC18\BIN\PICC18.EXE
PICC-18 Linker:    C:\HT-PIC18\BIN\PICC18.EXE

PICC.EXE, PICL.EXE and PICC18.EXE are the only front-end to
the compiler/assembler/linker and it automatically
runs the appropriate utility which it determines
from the filename extension. Thus you do not need
to call the assembler and linker executables directly. 
Set all three (compiler, assembler and linker) to be
either PICC.EXE, PICL.EXE or PICC18.EXE as shown above.

In the sections which says "Command line" or "Windowed":

For the full version of PICC and PICC Lite, set them all to be command line.
For PICC-18 or the demo version of PICC, set them all to be windowed.

Back to top

61. Loading the OSCCAL value for PIC devices

With the latest versions of the PIC compiler the calibration constant is
automatically handled by the compiler for the 12C50X and 16C505 devices.
These PIC devices load the W register with the value on reset and this is
then loaded into the OSCCAL register immediately after reset by the
startup code.

For 12C67X chips the oscallation constant is not loaded on reset, but
can be read using the macro _READ_OSCCAL_DATA(). To be able to use this
macro, make sure that  is included into the relevant modules
of your program.


If you are using a windowed device, the calibration constant must
be saved from the last ROM location before erasing, then reprogrammed
after erasing the device.

If you are using an ICE, the retlw instruction will not be present and
calling the _READ_OSCCAL_DATA() macro will not work. This macro will
work when running code on a part.
Back to top

62. Are the run-time libraries re-entrant?

Q: Are the compiler's run-time libraries re-entrant? Can I call library
functions from an interrupt?

A: Depending on which compiler you're using, yes. If the compiler implements
reentrant functions at all, then the library is fully reentrant except for
the rand() function, which uses a static variable.

The compilers that don't support reentrancy are those with a compiled stack,
i.e. 6805, PIC, V8 and 8051 in small and medium models. The 8051 in
large model is reentrant.

The other compilers - 8086, 68K, 6809, 68HC11, Z80, H8/300 and XA are fully
Back to top

63. Setting ID Locations

Some PIC devices have locations outside the addressable memory area that can be
used for storing program information, such as an ID number. The __IDLOC macro
(defined in pic.h) may be used to place data into these locations. The macro
is used in a manner similar to:


where x is a list of nibbles which are to be positioned in to the ID locations.
Only the lower four bits of each ID location is programmed, so the following:


will attempt to fill four ID locations with the decimal values: 1, 5, 15 and 0.
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

In versions of the compiler prior to v7.80, it may be defined as __IDLOCATIONS
and operates differently. In this case, you can use the following as a basis to
making modifications.
Create the macro;

#define __IDLOCATIONS(x) asm("\tpsect IDLOC,class=CODE,delta=2");\
                         asm("\tglobal\tIDLOC_word"); \
                         asm("IDLOC_word"); \
                         asm("\tdw "___mkstr(x))

then in the linker options link in this new psect eg;
-PIDLOC=2000h, (Note check the address of the locations before proceeding)
or for picc.exe the linker command will be;
The above will write one location, for more just add more variable to the
definition eg. __IDLOCATIONS(x, y, ..), for ID locations up to four variables.
then replicate the __mkstr() macro for each, eg;

#define __IDLOCATIONS(x, y) asm("\tpsect IDLOC,class=CODE,delta=2");\
                            asm("\tglobal\tIDLOC_word"); \
                            asm("IDLOC_word"); \
                            asm("\tdw "___mkstr(x)) \
                            asm("\tdw "___mkstr(y))
Back to top

64. Why is the debugger called Lucifer?

The name "Lucifer" is a Latin name for the morning star (Venus) and means
"light-bringer". It was a common proper name during the Middle Ages. The debugger
is intended to shed light inside the "black box" of an embedded system.

The name has no religious significance.
Back to top

67. Intergrating PICC with ICEPIC-2

To run the Hi-Tech PICC compiler from within the ICEPIC enviornment you
need to write a batch file which contains all the commands and options
for the compiler and place a link to execute this file from the
Options>Tools menu. You will need a seperate batch file for each project
you do.
The batch file must specify the format of the output files from the
compiler and all the source files, along with any options that are used
in the project. Also the correct options must be selected in the
Options>Code Download dialogue box. i.e. under Source/Listing make sure
the ' open all source files' box is checked and the 'open listing file'
box is checked.  You will also need under Auto Download to ensure the
'if no errors on assemble/compile' box is checked.
When the compiler PICC.EXE is invoked it will automatically invoke the
linker if it needs to.
An example of the file to compile the program lcd.c would be -
@echo off
set HTC_WARN_FORMAT=MESSAGE %%f %%l : %%s
set HTC_ERROR_FORMAT=ERROR %%f %%l : %%s
c:\Ht-pic\Bin\picc.exe -q -G -O -P -ASMLIST -CRref.crf
-Ic:\Ht-pic\Include -Ic:\Ht-pic\Samples -16c74a -Elcd.err lcd.c
@echo on
Back to top

68. Adapting printf for use with serial ports

question: How can I adapt the printf- function for using the serial interface?

Please refer to the file sci.c in your sample directory, you will have to alter
the names of some of the functions, sci_PutByte -> putch, sci_GetByte -> getch.
Then use this file when compiling your code, so these functions override the
library versions and direct input and output to the ports as desired.
Back to top

69. How do I predefine the contents of eeprom data using PICC?

If you are using v7.87 or higher, there is a macro __EEPROM_DATA()
which can be used. The macro allows for 8 bytes to be defined, but can
be called more than once. For example:



If you are using a version of the compiler prior to v7.87, some inline
assembly like the following can be used:

        psect   eedata,class=CODE,delta=2
        db      1,2,3,4,5,6,7,8

Then add the additional linker option:-

This relies on Microchip's spec. that EEPROM data should be embedded in the HEX
file at location 2100h, one byte per word. The "db" line represents the data (8
bytes in this case). 
Back to top

70. Adding Codewrite Error Parser for HI-TECH Compilers

Codewright ships with several error parsers built-in for you.  These are listed
in the drop down boxes in the dialog Project|Properties|Errors.  If you find 
that none of the listed parsers will do the job, there may be a parser in 
ERRPARSE.DLL that is designed for your compiler or utility.  If it still does 
not exist, you can create your own using the Custom Error Parser dialog found 
in the same tab.  You can install ERRPARSE from the Tools|Libraries dialog.
Once installed you then select the Error Parser _AvocetHiTechErrorInfo from the
list, this will do the job.

Back to top

71. How do I write graphics programs with Pacific C?

Q. I want to do some graphics programming with Pacific C, but I can't find
graphics.h? Where is it?

A. Pacific C is our freeware native DOS compiler, and it does not have any
graphics libraries, hence there is no graphics.h. You will need to locate
some suitable graphics library functions from somewhere else. There is
a simple library located at
but we can't provide any help using it.
Back to top

72. PICC compiler upgrade installation fails

Q. An error message occurred that the replacement of a file in ..\lib
   is not possible. What is the reason? 

A. On installation of a previous version, a file in the LIB directory became
   read only.
   To be able to install the latest version of the compiler we
   suggest deleting the older version of the compiler including the compiler 
   directory structure and then installing the newer version of the compiler.

   If deleting the entire HC-PIC directory tree is not desired (e.g. if you
   have other files in there) then just remove the LIB directory and all its
   contents and sub-directories.
Back to top

73. Getting started programming microcontrollers in C

How do I get started programming a PIC or other microntroller in C?

1) Do you already know how to program in C? If not, then you need to learn
this before you try to program a microcontroller in C. If you don't have a C compiler
for DOS/Windows, you can get a free one that is very similar to the cross
compiler from our web site -

There are numerous books you can buy from any good bookshop on C programming.
Two we recommend are "A Book on C" by Kelley and Pohl, and "Programming in
ANSI C" by Kochan.

There are also numerous resources on the Internet to help learn C. Here's
one tutorial:
You can find many others by searching online.

2) Once you know how to program in C, you can move onto programming a microcontroller
in C. The main resource you need is the datasheet for the particular
chip. The best way to start is to take one of the sample programs provided
with the compiler, e.g. LED.C and make it work on your hardware. The sample
LED.C supplied with the PIC compiler just flashes leds attached to port B,
changing the pattern as it sees an input bit changing (e.g. from a pushbutton).

There will be similar examples with the other compilers -the following applies
to the PIC specifically, but the other compilers are similar:

Points to note are:

a) PIC hardware registers (aka Special Function Registers or SFRs) can be
addressed as C variables by #including . This means e.g. that you
can write a byte to the PORTB data latch just by writing something like

    PORTB = 0;

b) All the registers listed in the data sheet are defined in the header file,
and most of the named single bits as well, e.g. bit 0 of PORTB can be
addresses as RB0, e.g.

    RB0 = 1;

would set bit 0 in port B.

Once you've got that program working, move onto some of the other example
code, or just add code to it to implement your particular needs. As long
as you take one thing at a time, you should not go too far wrong.

I can't emphasise more that the very first thing to do is get one program
working, and the sample code is the best way to do that. Once you have one
program working, you will know that you can do much more.
Back to top

74. Make fails when using RICE emulator

Q: If I'm using HI-TECH C, compiling with HPDPIC, and emulating with
   an Advanced Transdata RICE emulator, a "Make" command in HPDPIC fails -
   I have to to "Re-make all" to successfully compile.

A: The RICE software creates a file called "xxx.obj" where xxx is the basename
   of the hex file. IF this conflicts with an object file created by the
   compiler, it causes problems.

Solution: name the compiler output file something different to any of
   the source files. This will avoid the conflict.
Back to top

75. RAM and ROM integrity test on a PIC?

> I need to implement a RAM/ROM integrity checksum on a pic 16c76
> pic16c76 ,do you have any suggestion ?,

A RAM test is possible, a ROM checksum is not - the PIC has no
way of reading program memory. If you have to have a ROM integrity
check, you need to use a different processor (17Cxx PIC chips can do it).

The RAM test would go something like this (obviously you can embellish
it to make the test more comprehensive) - the numbers involved are specific
to the 16C76, you will need to check the data sheet to find out what memory
ranges to use for other chips.


volatile unsigned char INDF @ 0x0; // not defined in pic.h

        IRP = 0;                /* select bank 0/1 */

        FSR = 0x20;             /* check bank 0 */
        do {
                INDF = 0x55;
        } while(++FSR != 0x80);

        FSR = 0x20;
        do {
                if(INDF != 0x55)
        } while(++FSR != 0x80);

        FSR = 0xA0;                // check bank 1
        do {
                INDF = 0x55;
        } while(++FSR != 0xF0);    // avoid the common area, already checked

        FSR = 0xA0;
        do {
                if(INDF != 0x55)
        } while(++FSR != 0xF0);

        IRP = 1;        /* select bank 2/3 */

        FSR = 0x10;     /* bank 2 RAM starts at 110 */
        do {
                INDF = 0x55;
        } while(++FSR != 0x70);  // last address is 170 (common is 170-17F)

        FSR = 0x10;
        do {
                if(INDF != 0x55)
        } while(++FSR != 0x70);   

        FSR = 0x90;                 // bank 3 RAM, 190-1EF
        do {
                INDF = 0x55;
        } while(++FSR != 0xF0);

        FSR = 0x90;
        do {
                if(INDF != 0x55)
        } while(++FSR != 0xF0);
Back to top

76. How do I ensure declarations are consistent across files?

Q: I have a variable (or structure, or whatever) that I want to define in one
source file and access via an extern declaration in another source file. How do
I make sure that the extern declaration matches the definition?

A: USe a header file (which you may be already) but include the header file
in all C source files that reference the variable, including the one that
defines it. For example, in header file x.h you might have:

extern bank3 int my_var;

This header file should be #included in any C source file that needs to
reference this variable. As well, it should be included in the C source file
that defines it, i.e. file x.c might look like this:

#include    "x.h"

bank3 int my_var;

This allows the compiler to check that the extern declaration is consistent with
the actual definition. You can re-declare a variable as many times as you like
in the same module, providing all the declarations are consistent. You can
define it only once (the definition reserves space as well as declaring it).
So this line:

extern bank3 int my_var;

is a declaration only; this line:

bank3 int my_var;

is both a declaration (making the variable known to the compiler) and a
definition (reserving space for it).
Back to top

77. How can I access external RAM on the PIC17 devices?

Q. I want to read and write some external RAM memory on a PIC 17C series
device, but the compiler doesn't support it.

A. Here are some sample routines that will do this. These read and write
16 bit words from external memory. Usage is something like:

#define ARRAY_BASE      0x8000            
        WriteMem(ARRAY_BASE+offset, value);                            

Here are the functions:

void WriteMem(unsigned short Adr, unsigned short Val)
        movfp   (?_WriteMem+0),tblptrl          ; Load the table address lo byte
        movfp   (?_WriteMem+1),tblptrh          ; Load the table address hi byte

        tlwt    l,?_WriteMem+2
        tablwt  h,,?_WriteMem+3

unsigned short ReadMem(unsigned short Adr)
        GLOBAL  btemp
        movfp   (?_ReadMem+0),tblptrl                   ; Load the table address lo byte
        movfp   (?_ReadMem+1),tblptrh                   ; Load the table address hi byte

        tablrd  l,,btemp                                ; Read memory, dummy transfer
        tlrd    l,btemp                                 ; Transfer lo byte to RAM
        tlrd    h,btemp+1                               ; Transfer hi byte to RAM

If you compile this and get an error about btemp not being defined, then
add the following:

    psect   temp,ovrld,class=BANK0,space=1
  ds      2
    global  used_btemp0
    global  used_btemp1

The temporary space "btemp" is normally used in calls to library routines,
etc... but if you haven't done this in any of your code within this C file,
then it won't be defined. You may find that as you add more code, you can
remove this definition.

Back to top

78. Error handling in MPLAB isn`t working

Q: When I compile a PIC program using MPLAB, the error messages from PICC
appear the in MPLAB build results window, but if I double click on the
message, MPLAB doesn't jump to the error location. How do I fix this?

A: The default error format from PICC is not what MPLAB wants. You can alter
this by setting some environment variables. These are:

HTC_ERR_FORMAT=Error[000] %f %l : %s
HTC_WARN_FORMAT=Warning[000] %f %l : %s

These should be set in one of

Windows NT:     Control Panel/System/Environment
Windows 9x:     C:\autoexec.bat or via control panel
DOS:            C:\autoexec.bat

You may need to restart your computer after setting these for the settings
to take effect.

Note also that if setting these variables in a batch file like autoexec.bat
it is necessary to double the % symbols since % is treated specially, e.g.

set HTC_ERR_FORMAT=Error[000] %%f %%l : %%s

Each %% in this line will be translated back to % when the SET
command is executed.

Now when you run MPLAB the error messages should be in a format like:

Error[000] c:\xx\yourfile.c 2 : Error text here

which MPLAB will understand so you can double click on the error line and be
transported to the right file and line number.

Note: since MPLAB doesn't correct for altered line numbers, it is often useful
to deal with the last errors first, so that the line numbers for earlier
errors remain correct - otherwise added or deleted lines early on will make
the later line numbers incorrect.
Back to top

80. Leading 0`s with printf for PICC

Q: In printf, the specifier with leading zeros does not work, at 
least not with hex specifier.  Example: printf("%04X", tmpint);

A: The standard printf supplied with PICC does not implement
leading zero's, so the above pritnf would not produce the
desired result.

In the source directory of version 7.86 onwards appears the file
"newmprnt.c".  If this file is included as part of the project
it will override the existing printf function and replace it
with a new version which will produce the leading 0's.  Note
that apart from including the file in the project no other 
changes have to be made.

The newmprnt.c file is not the default implementation because it
takes up even more ROM than the standard printf.
Back to top

81. Updating/Upgrading my compiler

What is the best method to updating/upgrading my compiler to the latest 
version? What is the cost?

To update your compiler to the latest version you will need to purchase either:
1. Compiler update/upgrade, cost US$200.00 plus shipping (US$30.00). You would 
then receive the current version of the compiler, with NO technical support and 
with NO ability to update from our web site.

2. Extended Support Option, cost US$250.00. You will then also receive the 
latest version of the compiler along with 12 months technical support and be 
able to update your compiler from our web site in that 12 month period.
Back to top

82. Compiler can't find files when not installed in default location

If you have installed the compiler is a directory other than the default,
you will have to set an environment variable to point to the new location.
The environment variable is: 

Where 'XXX' is the compiler's name. For example, if you were using the
PICC-18 compiler and it was installed in the directory C:\compiler\ht-pic18
you would set the environment variable:

Back to top

83. I get the error: Cant find XXX words/bytes in psect XXX

> I get the error: Can't find XXX words/bytes in psect XXX
> What does this mean??

This error message simply means that the linker could not find enough
space in ROM or RAM to fit your program.

If you are using the PICC compiler and the error says that it can't find
space in segment CODE, then this can mean one of two things.
Either your program is simply too big to fit into the available ROM space
of the processor you are compiling for; OR
You have a large function which won't fit into a single ROM page. There is
a restriction that functions must be able to fit within a ROM page. To overcome
this, split the large function into two or more smaller functions. You can
determine which function is causing the problem by generating an assembler
listing file (-asmlist), and locating the psect that was given in the error

If the error message says that it was segment BANKx that it couldn't find space,
then this means you have run out of RAM in that particular bank. FAQ number 29
has details of this.

Back to top

84. I get the error - "Too many file arguments. Usage: cpp [input [output]]"

Q: I get the error 

cpp.exe -DHI_TECH_C -D_MPC_ -SP1,1,1,1,1,1,1 -IC:\HT-PIC \INCLUDE 
-S1,2,2,4,3,3 -D_16F877 -D_PIC14 -D_COMMON_=1 -D_HTC_VER_MAJOR_=7 
-D_HTC_VER_MINOR_=87 -D_BANKBITS_=2 main.c C:\WIN2000\TEMP\$$028594.000
Too many file arguments.  Usage: cpp [input [output]]

A: This error usually occurs when there is whitespace in the path of the
environment setting. 

If you have installed your compiler to any directory other than the
default "\ht-pic", make sure that there are no whitepspaces in the names
of the subdirectories. The names of these sub-directories must NOT contain

Also, when the environment variable, HTC_PIC=C:\..., is set, ensure that 
this does not contain whitespace either, especially at the end of the 
command. As DOS will store the whitespace and include this into the path,
as seen above in the output message ( -IC:\HT-PIC \INCLUDE ). This 
causes the CPP.EXE to read this in as two files, rather than the one file.
Back to top

85. The PICC-18 compiler produces code twice as big as the PICC compiler

Q. Why when I compile my project on the PICC-18 compiler it appears to be
   twice as large as when I compile it on the PICC compiler?

A. If you look at the memory summary in both cases you'll notice that the
   PICC compiler is measured in WORDS and the PICC-18 compiler is measured
   in BYTES. To compare them, multiply the total from the PICC compiler by 2.
Back to top

86. Compiling programs for use with the MPLAB-ICD

Q. I am using the MPLAB-ICD for the 16F87x range and it requires certain
   ROM and RAM locations to be reserved. How do I get the compiler to reserve
   this space?

A. The compiler directly supports the ICD via a command line switch. If you
   download the latest version of MPLAB you can select this in the project
   linker settings. This will add the -ICD option to the command line.

Q. Ok, I've done that but it doesn't seem to put a NOP instruction at address
   0x0 - what's wrong?

A. The compiler will reserve the necessary ROM and RAM locations, but it will
   not place a NOP at address 0x0. You can safely ignore this because the
   NOP instruction is not needed. Microchip suggest placing a NOP at that
   address simply because when using the ICD the instruction at that location
   will not be executed - it doesn't necessarily have to be a NOP. The
   instruction which the compiler puts there can be ignored without causing
   any problems.

Back to top

87. I get the error: function * appears in multiple call graphs: rooted at *

Q. I get the error: function * appears in multiple call graphs: rooted at *
   What does this mean?

A. It means that there is a function which is being called from an interrupt
   and from main code and reentrancy is not supported by the compiler.
   Because the function is not reentrant, it leaves open the possibility
   of it being called from both locations at the same time. There are several
   possible ways to work around this:

1. If the compiler supports the "reentrant" qualifer, then define the function
   with this.

2. ROM space permitting, make two copies of the function but give them different
   names. One is only called by the interrupt, the other only by main code.

3. Rewrite the function such that it doesn't have any local variables or 
   parameters. If the function doesn't have these, then it can be called from
   the interrupt and main code at the same time.

4. If you can guarantee that the function will not be called simultaneously, then
   you can use the #pragma interrupt_level directive. This is detailed in the
   user manuals, but in brief it is used like the following:

#pragma interrupt_level 1
void common_func(void)
   /* local variable definitions */

   /* code */

#pragma interrupt_level 1
void interrupt isr(void)
    /* more code */

void main(void)
   common_func();  /* gets called before interrupts are turned on */
   EI();    /* enable interrupts */
   /* more code */

Back to top

88. No XOR(^) for bit variables on the PIC an PIC18

Q. The compiler gives me a "Can't generate code for this expression (error)"
when I am trying to XOR two bits ?

A. PICC and PICC18 don't support the XOR operator(^) between two bit 
   variables when used in case statements, for example:
   bit a;
   bit b;
   if( a ^ b){

A work around is to use the logical eqivilent, for example:

  if( ((!a) & b) | (a & (!b)) ){
OR assign the result to bit variable, for example:

   if( a = a ^ b){
Back to top

89. Positioning code, constant or variable at a specifc address in ROM or RAM.

Q: How do I position code, constant or variable at a specific address ?


Using the '#pragma psect' directive you can place code, constants or variables into 
a custom psect. You can then use linker options to place the new custom psect at 
the desired address. The syntax of pragma psect is:

	#pragma psect original_psect=new_psect

where 'original_psect' is the name of the standard psect the code, constants or variables 
were orginally in. The orignal psect can be found by looking at the symbol table
in the map file. 

		 Example symbol table
__Lstrings     strings     0008  __Lstringtable stringtable 0008
__Lstruct      struct      0020  __Ltemp        temp        0070
__Ltext        text        0190  __Lvectors     vectors     000B
__S0           (abs)       0800  __S1           (abs)       0190
_exit          init        0004  _main          text0       07FD
_serial        const1      0009  intlevel0      intentry    0004

_serial        const1      0009  ; The variable declared as 'const int serial = 5621;'

   ^ variable name with underscore prepended
                 ^ psect name

Because there is a number at the end of 'const', 'const1', it can have any number on the end,
that is, it won't necessarly be 1. To compat this replace the number with '%%u'. This is
the case for all psects with a number on the end except rbss_x and rdata_x. The rbss_x 
and rdata_x are the RAM variables psect for each bank, rbss_0 is bank 0, rbss_1 is bank 1, etc...

'new_psect' is a meaningful name for the custom psect, it can not be the same as any of the 
standard psect names. 

An example declaration of a placing 'serial' into a new psect would be:

#pragma psect const%%u=fixed_serial
const int serial = 5621;

Each '#pragma psect' derective should be declared in a separate file, that is, all variables
declared below the "#pragma psect' derective will also be placed in the new custom psect.
// --File serial.c 

#pragma psect const%%u=fixed_serial
const int serial = 5621;

// --End file

// --File serial.h

extern int serial;

// --End file

To position the new custom psect you will need to add an additional command line option 


Where '-L' is passing an option to the linker, '-P' is positioning a psect at particular address,
'custom_psect' is the name of the psect you wish to position, 'addressh' is the address where
you want the psect is places in hex. For example:

PICC -16f877 main.c serial.c  -L-Pfixed_serial=50h

There are some special cases for 'text' psects. A directive such as
'#pragma psect text%%u=myPsect' will put all text psects into a single 
custom psect. The optimizer relies on the fact that the each psect 
contains a single function. This may produce bank switching errors.

To get around this use a custom psect with '%%u' apended, for example:

#pragma psect text%%u=myPsect%%u

A number of custom psects will be produced. 

For example:

//file test.c
#pragma psect text%%u=myPsect%%u
void func()
void func2()
void main()
//end file

The following information was gained from the asmlist and map files.

'func()' is in psect 'myPsect0'
'func1()' is in psect 'myPsect1'
'main()' is in psect 'myPsect2'

You will then need to explicity set the address for each psect with the 
'-L-P' option.

So to put these functions a 500h you would use a linker option as below:


Please note that no ROM page bounds check is done with the -L-P option so 
you will have to make sure that a function(psect) does not cross any
page boundries, that is, each function(psect) must be wholly contained
in a page of ROM.

See the manual on '#pragma psect' for more information.
Back to top

90. How to get MPLAB to display compile errors

Q: When I compile an MPLAB project, I get the message:

"MPLAB is unable to find output file "XXXX.OBJ". This may be due to a compile,
assemble, or link process failure.

Build failed.",

but no errors are displayed. How do I know what is producing the error?

A: MPLAB displays any errors after it attempts to compile. These messages are
read from error files that the compiler must produce. You need to turn on the
"Error file" option for each source node in the project and the HEX link
node. In the data field for this option enter the name of the source file
with the extension ".err". So if you have a source file called "main.c",
then in the node properties for the node, you should specify an error
file of main.err. If the HEX node has the same name as one of the source
modules, then turn on the "Append Errors to file" option and enter the
error file as indicated above.
Back to top

91. How do I view local variables in MPLAB?

To be able to view local varibles in MPLAB, be sure to compile
your project with the option "Generate Debug Info" for each node.
Also add to the HEX file node the additional command line 

Local variables will then be seen in the format: function_name.local_var
So for example, if you had a local variable called "number" and it
was within a function called "testing", then it would appear in the
symbol list as "testing.number"
Back to top

92. printf() ouput

Q. What does printf output to ?

A. The printf() function calls another function putch() to output 
   the character after formatting. By default putch() is defined 
   as an empty function. To allow printf() to write to a specific
   output the putch() function needs to be redefined. In most cases
   output is directed to the serial port. A sample definition can 
   be found in the serial.c file in the samples directory of the 
   compiler distribution for the PICC and PICC18 compilers.
Back to top

93. How do I use scanf() and gets() functions with the PICC/PICC18 compiler ?

Q. How do I use scanf() and gets() functions with the PICC/PICC-18 compiler?

A. The gets() function is not supplied by the compiler. Becuase gets() will 
get a string from a programer specified input, such as the serial port,
it is easier for the programer to write this function then for it to be
supplied in a library. There is a console get string function, cgets(),
that will read string from an input using console formatting. The 
cgets() function is available in the cgets.c file in the sources 
directory. To use the cgets() function you will need to inlcude the 
cgets.c file in you project and define the getch() and getche() to
read a character from the specified input.

The scanf() function is available in the sources directory of the 
compiler. The following files will be needed to be included in your

The getch() and getche() functions will also have to be defined.

The scanf() function is very large, that is why it is not included
the the library code. It is often more efficent to read the string
from input and extract any numeric values using atof(), atoi(), atol()

Example difinitions of getch() and getche() functions can be found in
the file serial.c in the samples directory of the PICC and PICC18 
Back to top

94. How do I write assembler functions which can be called from C?

Q: How do I write assembler functions which can be called from C?

A: The easiest way to learn how to do anything in assembler, is
to write a simple version of it in C, then use the compiler to compile
it to assembly. You can then use this output as a template to your routine.
For example:

Say you wanted to write an assembler routine which takes a "long" type
as a parameter. Firstly, write the function in C:

void MyAsmFunc(unsigned long parameter)

Now compile it to assembly. The -S option will do this.
For this example we will use the PICC compiler:

picc -16f877 -S file.c

This command will generate "" and it will look something
like the following:

;       param _parameter assigned to ?_MyAsmFunc+0
_MyAsmFunc$parameter    set     ?_MyAsmFunc+0
        psect   text1,local,class=CODE,delta=2
        global  _MyAsmFunc
        signat  _MyAsmFunc,4216
        psect   text1
        bcf     3,6
        bcf     3,5

With the PICC compiler functions must leave the ram bank set to bank0.
So here we see it set the bank to bank0, then return.
From here you can expand it with your asm code. The parameter
can be accessed by using the symbol _MyAsmFunc$paramter
This will access the first byte, to access the remainder of the parameter,
just add to the symbol. For example:


This will refer to the third byte.

Back to top

95. How do I create a new PICC project in MPLAB?

Q: How do I create a new PICC project in MPLAB?

A: Create a "New Project" from the "Project" menu
giving a name such as test.pjt.
Select "HI-TECH" or "HI-TECH PICC" from the "Language Tool Suite"
category. There should be one node already
present. This node will be called, e.g. test[.hex].
Select this node and click the "Node Properties"
button. Select "PICC Linker" from the "Language Tool"
category. Select those options which you require from
the list provided. You should see the equivalent
command-line arguments being assembled in the "Command
Line" box below. Click "OK" when you have finished.

Click the "Add Node" button. Add the path to one of
your source files in the "New Node" box. Click OK
to create the node.

Edit this new node's properties. Select "PICC Compiler"
from the "Language Tool" category. Again, choose the
appropriate options from the list. The "Compile to
object file" option (-C) is automatically turned on.
Click "OK" when finished.

Select and copy this source node so that you have
one node for each source file in your project.
Edit these new nodes if their options should be
different from the first source node entered.

Select "Make Project" from the "Project" menu.
This should compile each source file independently,
link the object files, and finally download
the resulting hex file (if appropriate).
Back to top

96. How do I put a program to a specific address for the PIC17C*** series of chips ?

Q. How do I put a program to a specific address for the PIC17C*** 
   series of chips ?

A. To put a program at a specific address there is a compiler option available
   -A. To tell the compiler that there is extra rom available the 
   -ROM switch. To place the program at address 0x2000, which is external
   memory on th -17c44, the command line will look like below:

   picc main.c -17c44 -a2000h -rom2000-fffc

   You will also need to include the file in your project. The file is in the samples directory of the PICC compiler. There
   are instructions on how to use the file in the file as comments.
   The file redirects interrupts and the startup vectors to the
   relocated program.
Back to top

97. I keep running out of ram on the PICC-18, yet there should be enough space

Q. I keep running out of ram on the PICC-18, yet there should be enough space.
   What am I doing wrong?

Each module is able to define single bss and data psect. 
Each bss and data psect must be wholly contained in a 
RAM bank. The data psects hold the initialized variables;
the bss, the unitialized. 
For example:

int i = 1;

defines an initialised variable.

A variable is placed in the bss and data psect if the
following 3 cases are true:

1. The variable is declared global.

2. The variable is not of type char.

3. The size of the variable is smaller than the size of a single bank.

An out of space error is produced when either a bss or a data psect
is larger than the size of a RAM bank.

Non-near variables that are not placed into the bss or data psects
are placed in the bigbss and bigdata psects. Variables are separated
like this to allow the compiler to perform various code optimizations.

The following examples apply to variables in the bss psect.
The examples below will apply to data psect variables if
the variables are initialised.

//case 1
//both variables are placed into the bss psect

int array1[128]; //one bank in size, 128 * 2 bytes
int array2[1];   //plus 2 bytes will give an out of space error

The following cases will not cause an out of space error:

//case 2
//array1 is placed in the BIGBSS psect, array2 is placed in the BSS psect 

int array1[129]; //single variable larger than one bank in size
int array2[1];   //plus 2 bytes will NOT give an out of space error

//case 3
//Declared in one module. Is place in the modules bss psect.
int array1[128];

//Declared in another module. Is place in the modules bss psect.
int array2[1];

The above 3 cases will also apply to structures and other non-char type
variables. Arrays are used in the examples so that size and type can 
easily be shown.

A work around is to split the declaration of the variables into different
modules. Below is an example:

extern int array1[128];
//end var1.h

int array1[128];
//end var1.c

extern int array2[1];
//end var2.h

int array2[a];
//end var2.c
Back to top

98. Extended support option

Q.  What is an Extended Support Option ?

The Extended Support Option will allow existing compiler owners to receieve
the latest version of the compiler plus receive 12 months technical support plus
receive any updates specifically released for that compiler in that 12 month 
Back to top

99. I recently purchased the compiler and it has suddenly stopped working

Q: I recently purchased the compiler and it has suddenly stopped working.
   What's going on?

A: When you install the compiler it will operate in demo mode for 21 days.
To activate the compiler you must register it. Registration can be done online
from the HI-TECH web site. 
Go to:

If you have problems or require further information about registration,
please contact us:

Back to top

100. Why is the compiler slow at compiling?

Q: The compiler is slow at compiling my program, why?

A: When you install the compiler it will operate in demo mode for 21 days.
When the compiler is in demo mode some compiler options are disabled and
there is a delay compiling files. To active the compiler you must register it.
You can register on-line from the HI-TECH web site.
Go to:

If you have any problems registering or require more information about
registration, please contact
Back to top

101. My compiler is running in demo mode. How do I activate it?

Q: My compiler is running in demo mode. How do I activate it?

A: To activate your compiler you must first register. You can register
on-line from the HI-TECH web site.
Go to:

After you have registered you will receive an activation key.
Run "register.exe" which can be found in the compiler's installation 
directory and follow the instructions on screen.

If you have problems registering or require further information about
registration, please contact

Back to top