Real-Time Systems Inc.

Specializing in Embedded Controller Design

GCC Toolchain for Motorola 68HC11, 68HC12, and 68HC16


In about 1993, we at Real-Time Systems began to use the C and C++ languages for some of our projects. While good compilers were available for the major desktop processors (i386, m68k, and so forth), we found the existing cross-compilers for the smaller micros either expensive, buggy, or non-existent.

At the time, our hottest machines were 486-66's with 8 Mbytes of ram and 250 Mbyte hard disks. We were doing all of our development under MS-DOS (with the occasional switch to Windows 3.1 to use the file manager).

I had heard, ten years earlier, about the beginning of the GNU project, but had lost track of it until it popped up in a article in Dr. Dobbs. I thought about trying to port it to our embedded micros, so I downloaded it using my shiny new internet account. It took 13 hours at 2400 baud, only to find that I'd have to port it to DOS first, or find a Unix machine to work on.

Fortunately, a little more poking around revealed that D.J. Delorie had done all of the heavy lifting of porting GCC, binutils, and GNU make to MS-DOS. The toolchain could even build itself under DOS (after a fashion). Delorie had not yet ported bash to DOS, so that the configure system did not run -- the configure script had to be run on a Unix machine, then the resulting makefile could be used under DOS. As a result, it was quite a while before our port used the configure system.

Design Choices

Our desires for an compiler targeting these small embedded processors were, in order:

Some things we did not care about were:

These desires are reflected in certain design choices we made.

Handling register shortages

GCC really works best on machines with many general-purpose registers, but the HC1x series have only three or four registers, depending on the processor. Several approaches are possible here:

We chose the last option, since it would give us the simplest context switching, and possibly the fastest code. Fortunately, the GCC code generator already uses stack slots, even on larger processors, when it runs out of hardware registers.

The biggest problem was coercing the code generator to keep longs and floats entirely on the stack. We did this by telling the compiler that it should keep longs and floats only in 32-bit registers, then telling it that there were never any such registers free. This trick caused all long and float variables and intermediates to end up on the stack.

This trick did, however, cause us another small problem:

The GCC design allows that some operations on the target machine (such as long or float arithmetic) may be too expensive to open-code, so it provides for calling support subroutines to handle these operations. By default, GCC assumes these subroutines follow the same calling convention as normal functions. While our trick got the longs and floats onto the stack, it forced us into a strange calling convention for these internal support routines. This wasn't a serious problem, it just meant some extra coding to connect the compiler-generated code with the support routines.

Wide numbers

While it would be straightforward, we never bothered handling 64-bit ints and double-precision floats. We haven't yet needed these data types in our projects.

Stack frame limitations

The HC11 has no stack-relative addressing mode, so we used the X register to point to the stack frame where temporaries, local variables, and function arguments are stored by the compiler. Unfortunately, the X-indexed addressing mode of the HC11 is limited to a 0 to 256 bytes from the value of X, which limits the total size of the stack frame to 256 bytes.

We haven't found this to be a limitation in our embedded controller work (we even have a TCP/IP protocol stack, written in C++, which builds and runs on the HC11), but there are probably some routines in glibc or newlib that won't compile.

This limitation doesn't apply to the HC12 and HC16 ports, which have 16-bit indexed addressing modes.

Memory models

The HC11 port supports a single "small" memory model -- a 64 kbyte flat space containing any combination of text and data. We never built any HC11 systems with banked memory, so we never bothered supporting banking in the compiler.

The HC12 port supports two models:

The HC16 port supports two models:

Both the small and medium models use 16-bit pointers for efficiency. In order to have 16-bit function pointers in the medium models, we generate small "thunk" functions in the data space, one for each real function in the larger text space.

These thunk functions each contain a long call instruction to the real function out in the text space, followed by a short return instruction. The address of the thunk function then gives us something we can point to with a 16-bit pointer.

Run-time library (libgcc.a)

Mostly for historical reasons (the way we started out under MS-DOS), the support routines for non-inlined arithmetic ended up separate from the compiler source tree. In the normal GCC way of doing things, these routines should be in a library called 'libgcc.a', which the compiler would be configured to find automagically.

In our work, we just build these routines into a library in our project tree and link them into the project manually. Though the published source for our ports doesn't include these routines, we would probably be willing to make them available if there is some interest in them. Please contact us to enquire.

Standard libraries (libc.a, libm.a, and so forth)

Due to the stack frame limitations of the HC11 (see above), we never bothered trying to build standard libraries such as glibc using our ports. We instead just re-wrote the few routines we needed, which also allowed us to squeeze out unnecessary features. This doesn't mean that it's not worth trying to compile these libraries, just that we haven't tried it ourselves.


You can find the source for our port here. It consists of patch files to apply to a stock distribution of gcc-2.8.1 and binutils-2.9.1. You will also find a script called `linux-targ' which shows how we build the ports.

How to build the ports

Untar these files in your gnu directory.

(where 'N' is the current revision number of the patch files and scripts). Put all of these files also in the gnu directory.

When this script completes, you should have a complete cross-assembler and cross-compiler installed in the prefix directory you have specified.


We offer this project in the hopes that you will find it useful. We welcome any comments, questions, requests, or bug reports you may have regarding this project. Please feel free to contact us via email.

Copyright 2014 Real-Time Systems Inc. All Rights Reserved.