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 |