[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The GNU Compiler Collection integrates several compilers for many architectures. The C and C++ compilers have been ported for 68HC11 and 68HC12 micro-controllers.
This chapter gives some information about how the compiler implements several aspects of the C and C++ languages. The reader is supposed to be familiar with these programming languages (see section 7. References to obtain information or books about those languages).
The complete GCC documentation is available in The GNU Compiler Collection.
The compiler supports all the data types defined in ISO/IEC 9899:1990. The table below gives information about each type and how they are passed in function calls.
char
type can be signed when you use the `-fsigned-char'
option.
They are passed as parameters on the stack as 16-bit values.
They may be stored in the B
, X
, Y
registers.
Most arithmetic operations are generated inline.
A function returns it in register B
.
7 0 +------+ | 0 | +------+ |
D
, X
, Y
registers. Most arithmetic operations
are generated inline (except mult, div and mod).
A function returns it in register D
.
15 7 0 +------+------+ | 1 | 0 | +------+------+ |
A pointer can point to a far function (that is a function which is in a paged memory). The pointer will in fact point to a trampoline code whose address is not in paged memory.
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.
A function returns it in register D
and X
.
long 31 23 15 7 0 +------+------+------+------+ | 3 | 2 | 1 | 0 | +------+------+------+------+ |
A function returns it in register D
and X
.
63 55 47 39 31 23 15 7 0 +------+------+------+------+------+------+------+------+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +------+------+------+------+------+------+------+------+ |
long long
for copies and parameter passing rule.
All operations are made by a library call.
The `-fshort-double' option changes the definition of `double' to use IEEE 32-bit floating point numbers.
The 68HC11/68HC12 registers (D
, X
and Y
) are
completely managed by GCC. Local
variables can be stored in these registers. However, since the 68HC11
and 68HC12 have
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:
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...).
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.
The table below indicates how the compiler uses the given registers.
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. This register is also 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 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
.
GCC for 68HC11 and 68HC12 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__'.
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/68HC12 vectors table.
Bugs: You can define an interrupt handler which has parameters and which returns some value. Such invalid specification will be forbidden later.
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 or 68HC12 vector's table. If you define several trap handlers, you must be careful in switching the <b>swi</b> 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.
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. For the binary installations (RPMs and Windows) the default CPU is the 68HC11.
When compiling, assembling and linking you must make sure to 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.
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |