The GNU Compiler Collection integrates several compilers for many architectures. The C and C++ compilers have been ported for 68HC11 and 68HC12 micro-controllers.

1. Description of the Port

This section gives some information about the port itself.

1.1 Supported Data Types

Type Description Return
char
unsigned char
signed char
Characters are 8-bit entities. They are unsigned by default. They are passed as parameters on the stack as 8-bit values. They may be stored in the D, X, Y registers. All arithmetic operations are generated inline. B
short
unsigned short
Shorts are 16-bit entities. They are passed as parameters on the stack as 16-bit values. They may be stored in the D, X, Y registers. All arithmetic operations are generated inline (except mult, div and mod). D
int
unsigned int
Integers are either 16 or 32-bit entities. They are 32-bit by default. The -mshort option turns on the 16-bit integer mode. The parameter passing rule applies to either short or long depending on its size. cf short
cf long
long
unsigned long
Long integers are 32-bit entities. They are passed as parameters on the stack as 32-bit values. They may be stored in the D+X register: the low-part in D and the high-part in X. Logical operations are generated inline, some addition and subtraction are inline. Other arithmetic operations are made by a library call. Comparison are inline. Low: D
High: X
float Floats are IEEE 32-bit floating point numbers. They are treated like longs for copies, parameter passing rule and register allocation. Most/all of the operations are made by a library call. Low: D
High: X
long long
unsigned long long
Long long are 64-bit entities. They are never allocated in a hard register. They are passed on the stack for operations. They are returned like a struct (in memory). Logical operations (and, or, xor) are generated inline. Other operations are made by a library call. Some shift operations are generated inline. Ptr D
double Double are IEEE 64-bit floating point numbers. They are treated like long long for copies and parameter passing rule. Most/all of the operations are made by a library call. Ptr D

1.2 Register Allocation

The M68HC11 registers (D, X and Y) are completely managed by GCC. Local variables can be stored in these registers. However, since the M68HC11 has so few registers, soft-registers are used to simulate hard registers for GCC. This avoids register spill failure in the reload pass. There are two kinds of soft-registers:
  • A set of soft registers which are replaced during the machine specific reorganization into a real hard register. At present, this set of soft registers is only composed of a single register called 'Z'. This register is treated like X or Y register by GCC. It is replaced by X or Y and sometimes D. When it is replaced, the old X/Y/D value is saved and restored if this is necessary. In general, the 'Z' register is allocated during the reload pass for the reload of an address. This 'Z' register helps significantly the reload pass and contributes in the generation of better code. It is possible to disable the 'Z' register by using the -ffixed-z option (in general and depending on the program, this will result in register spill failure...).
  • A set of general soft registers which are located in .softregs section (mapped in page0 or data memory bank). These registers are not replaced during the machine reorganization. There are 4 soft registers by default for 68HC11 and 2 for 68HC12. This is configured by using the -msoft-reg-count=cnt. The minimum depends on your program, and the maximum is 32.
    Note: It is not clear whether having a lot of soft-registers produces optimal code. For small functions, reducing the number of soft registers is a good idea. For large functions, increasing to 8 or 12 helps in generating a smaller and faster code.

1.3 Register Usage

Register Description
A
B
The A and B register are not directly used by GCC. Some patterns generate instructions that use these registers to generate specific instructions such as add with a carry (used in 32-bit add).
D This register is used for most arithmetic operation. It holds 8 or 16-bit values. It is also used to hold the low-part of a 32-bit value (either a long or a float).
In a function call, it is used to pass the first argument of that function. If the function returns a struct or a 64-bit value, the result will be returned in memory. The caller passes a pointer to that memory location in this register.
It is used for the result of functions.
X This register is used for indexed addressing. It holds 8 or 16-bit values. It is also used to hold the high-part of a 32-bit value (either a long or a float).
When the first function parameter is 32-bit wide, it is used to pass the high-part of the 32-bit value.
It is used for the result of functions when the result does not fit in a 16-bit value. This includes long, float as well as small structures (-fno-pcc-struct-return).
Y This register is used for indexed addressing. It holds 8 or 16-bit values.
Z This register is used for indexed addressing. It is treated like X or Y by GCC. It is replaced by either X or Y during the machine reorganization. When it must be saved, it is saved in _.z (see below).
_.z This is a 16-bit soft-register in .page0 that is used during the Z register replacement to save the Z register. It is possible to eliminate the use of that register by passing the -ffixed-z option. The program may however not compile in some cases.
_.xy This is a 16-bit soft-register in .page0 that is used during the Z register replacement to save the old content of either X or Y. It is possible to eliminate the use of that register by passing the -ffixed-z option. The program may however not compile in some cases.
_.frame This is a 16-bit soft-register in .page0 that represents the frame pointer. It is possible to eliminate the use of that register by passing the -fomit-frame-pointer flag. But in that case, debugging becomes difficult.
_.tmp This is a 16-bit soft-register in .page0 that is used internally by the machine description. It is not available to GCC. It is used in some cases where the machine description needs a memory location to copy some hard registers (reg <-> reg copy). It is not possible to eliminate the use of that register.
_.d1
_.d32
These are the 32 soft-registers in .page0. Each of them is 16-bit wide. Consecutive registers may be allocated to store long as well as long long values. The use of these registers is controlled by the -msoft-reg-count=n option.

GCC assumes that the following registers are clobbered by any function call:

      D, X, Y, Z

The soft registers in .page0 have now a name which cannot be specified in C and C++. No conflict can therefore arise with a program global variable or function. However, if you want to access those registers from a C or C++ function, do the following declaration:

extern unsigned short d1 __asm__("_.d1");

Such declaration tells GCC that the external variable d1 corresponds to the assembly symbol _.d1.

1.4 Traps and Interrupts

GCC for 68HC11 supports the generation of trap and interrupt handlers. The trap handler correspond to either the swi exception handler or to the invalid opcode handler. The difference between the trap and interrupt handlers are that the former is a synchronous exception while the later is asynchronous. Trap and interrupt handlers are specified by using the GNU extension __attribute__.

1.4.1 Interrupts

To define an interrupt handler, you must declare the prototype of the interrupt handler as follows:
   void my_interrupt_handler(void) __attribute__((interrupt));
Then, you must define your interrupt handler as follows:
   void my_interrupt_handler(void)
   {
     ...
   }
The prologue of the interrupt handler saves the GCC soft registers _.tmp, _.xy and _.z. Other soft registers need not to be saved (unless they are used in the interrupt handler). The epilogue restores these registers and uses rti to return from interrupt.

Note: You are responsible for installing the interrupt handler in the 68HC11 vectors table.

Bugs: You can define an interrupt handler which has parameters and which returns some value. Such invalid specification will be forbidden later.

1.4.3 Traps

The trap handler is defined in the same manner except that you can pass parameters and it can return a value. The trap handler follows exactly the same parameter passing rules as a normal function. To define some generic system call handler, you can define for example:
   int syscall(int op, ...) __attribute__((trap));
   int syscall(int op, ...)
   {
     int result;
     ...
     return result;
   }
The prologue of the trap does not save the GCC soft registers _.tmp, _.xy and _.x as the interrupt handler does. This is because the trap is synchronous and its invocation is treated like a function call by GCC.

The epilogue saves the result on the stack so that the rti instruction used to return pops the correct result.

To invoke a trap handler, just call the trap handler, for example:

   int result = syscall(1, "Hello World!", 12);
A swi instruction will be generated instead of a bsr.

Note: You are responsible for installing the trap handler in the 68HC11 vector's table. If you define several trap handlers, you must be careful in switching the 68HC11 swi vector.

Limitation: You can define a trap handler for the illegal opcode but there is no way (yet) to tell GCC to generate the illegal opcode to invoke the trap handler.

1.5 Branches

It is the responsibility of the assembler to turn relative branches into absolute branches when the displacement is out of bounds. The GNU Assembler knows how to do that for function calls (bsr) as well as all the conditional branches (for example beq).

1.6 GCC CPU Target

GCC has three options -m68hc11, -m68hc12 and -m68hcs12 which control what is the target micro-controller. These options are passed to the assembler and they also control the linker. When none of these options are specified, GCC uses the default CPU target that you specified during the configuration.

When compiling, assembling and linking you must make sure you pass the same cpu target option. If you fail, you will get a linker error since 68HC11 and 68HC12 object files have different ELF magic numbers.

1.7 68HC12 memory banks

For 68HC12, the -mlong-calls option can be used to tell the compiler to use call and rtc for function calls and returns.

1.8 Generated Code Size Comparison

The table below gives the size of the text section for the same C source file string.c compiled with different options.
Note:
  • this file comes from GNU/Linux kernel, it was modified a little to compile outside of Linux kernel.
  • the assembly file corresponds to the result of the latest development version (Alpha 1.9b).
  • Release 1.1 is based on GCC 2.95.3.
  • Alpha 1.9b is based on GCC 3.0.3.

Target 68HC11
Compilation Apr 24, 2000 Aug 5, 2000 Release 1.1 Alpha 1.9b Asm
gcc -O 1648 1497 1531 1589 string-32-O.s
gcc -O -fomit-frame-pointer 1437 1287 1315 1379 string-32-Of.s
gcc -O -mshort 1453 1334 1371 1391 string-16-O.s
gcc -O -mshort -fomit-frame-pointer 1253 1131 1157 1190 string-16-Of.s

Target 68HC12
Compilation Release 1.1 Alpha 1.9b Asm
gcc -O 1811 1597 string-32-O-h12.s
gcc -O -fomit-frame-pointer 1362 914 string-32-Of-h12.s
gcc -O -mshort 1616 1412 string-16-O-h12.s
gcc -O -mshort -fomit-frame-pointer 1176 762 string-16-Of-h12.s
gcc -O -mshort -fomit-frame-pointer -mauto-incdec
-msoft-reg-count=0
911 738 string-16-Ofm-h12.s