Miniboard Toolchain

Obtain GCC for M68HC11. For Debian users, this means apt-get install gcc-m68hc1x. Others will need to visit the GCC for 68HC11 home page.

At time of writing, this is GCC version 3.3.4 and it only supports the C language.

Let's take a sample file, test.c:

int main(void)
{
  while(1)
    ;
}

You can run “m68hc11-gcc test.c -o test.elf” from the prompt. And it works.

$ m68hc11-gcc test.c -o test.elf
$ ls -l test.elf
-rwxr-xr-x  1 nicholas users 11777 Oct  9 12:45 test.elf*
$ 

Or not. See a small problem? Yep, it's that file size. Let's optimize it. “-Os” means optimize for size while “-s” strips the symbol table.

$ m68hc11-gcc -Os -s test.c -o test.elf
$ ls -l test.elf
-rwxr-xr-x  1 nicholas users 8904 Oct  9 12:46 test.elf*
$ 

Much better, but still more than double our mark. But why? What's inside? Use “objdump -d” for disassembly:

$ m68hc11-objdump -d test.elf

/home/nicholas/test.elf:     file format elf32-m68hc11

Disassembly of section .text:

00008000 <.text>:
    8000:       8e 7f ff        lds     #0x7fff
    8003:       8d 37           bsr     0x803c
    8005:       ce 80 43        ldx     #0x8043
    8008:       18 ce 11 00     ldy     #0x1100
[... 20+ lines of text removed ...]
    803c:       4f              clra
    803d:       06              tap
    803e:       39              rts
    803f:       0e              cli
    8040:       3e              wai
    8041:       20 fc           bra     0x803f 

There's two things wrong with the above output. Let's start with the easy one. It's trying to build this program as if it were an executable that would run on a UNIX system. It's using main, returning an exit code, etc. That's not what we had in mind. Let's write test2.c:

void _start(void)
{
  while (1)
    ;
} 

What's _start? It's the real entry point for a program. In Linux, you'll find that there's start.S which is written in assembly. It implements _start by initializing the C library, calling main(), then calling _exit() with the return value from your main. We don't need that. Let's see how well this works:

$ m68hc11-gcc -Os -s test2.c -o test2.elf
/usr/lib/gcc-lib/m68hc11/3.3.4-m68hc1x-20040829/crt1.o(.install4+0x1): undefined reference to `main'

Not well at all. Our _start is conflicting with start.S from crt1.o. Fortunately, the gcc manpage tells us what to do. I won't make you read it though; the option to add is “-nostartfiles”.

$ m68hc11-gcc -nostartfiles -Os -s test2.c -o test2.elf
/usr/lib/gcc-lib/m68hc11/3.3.4-m68hc1x-20040829/../../../../m68hc11/bin/ld: warning: cannot find entry symbol _start; defaulting to 00008000
$ ls -l test2
-rwxr-xr-x  1 nicholas users 8904 Oct  9 12:53 test2.elf*
$ m68hc11-objdump -d ~/test2.elf

/home/nicholas/test2.elf:     file format elf32-m68hc11

Disassembly of section .text:

00008000 <.text>:
    8000:       de 00           ldx     *0x0
    8002:       3c              pshx
    8003:       9f 00           sts     *0x0
    8005:       20 fe           bra     0x8005 

That's the complete assembly, all seven bytes. This is promising! There's no OS to deal with. But there's an obvious problem; it's still far too big. Why is _start at 0x8000? We need it to be at address 0xf800. Further, notice the last instruction “bra 0x8005”. Even if we just moved the code to the right address, it still wouldn't work since that instruction points to the wrong place.

How do we tell the linker where to put our code? The answer is a linker script, “miniboard.lds”. We can tell gcc to use it with the -T option, as in “-T miniboard.lds”. Let's try it out:

$ m68hc11-gcc -nostartfiles -Os -s test2.c -o test2.elf -T miniboard.lds
$ ls -l sample.elf
-rwxr-xr-x  1 nicholas users 8520 Dec 20 23:26 test2.elf*
$ m68hc11-objdump -d ~/test2.elf

/home/nicholas/test2.elf:     file format elf32-m68hc11

Disassembly of section .text:

0000f800 <.text>:
    f800:       de 00           ldx     *0x0
    f802:       3c              pshx
    f803:       9f 00           sts     *0x0
    f805:       20 fe           bra     0xf805

Improvement! The code is at the right spot. Unfortunately, the file is still too large. What's taking up the file?

The answer is its file format. objdump told us at the top, “file format elf32-m68hc11”. ELF32 is the Executable Linker Format. What does ELF store? Let's ask the “readelf” program:

# m68hc11-readelf -a test2.elf
ELF Header:
  Magic:   7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, big endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Motorola MC68HC11 Microcontroller
  Version:                           0x1
  Entry point address:               0xf800
  Start of program headers:          52 (bytes into file)
  Start of section headers:          8240 (bytes into file)
  Flags:                             0x3
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         2
  Size of section headers:           40 (bytes)
  Number of section headers:         7
  Section header string table index: 6

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .data             PROGBITS        00000000 001000 000000 00  WA  0   0  1
  [ 2] .bss              NOBITS          00000000 001000 000000 00  WA  0   0  1
  [ 3] .softregs         NOBITS          00000000 001000 000002 00  WA  0   0  1
  [ 4] .text             PROGBITS        0000f800 001800 000007 00  AX  0   0  1
  [ 5] irq_table         PROGBITS        0000ffd6 001fd6 00002a 00  WA  0   0  1
  [ 6] .shstrtab         STRTAB          00000000 002000 000030 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x001000 0x00000000 0x00000000 0x00000 0x00002 RW  0x1000
  LOAD           0x001800 0x0000f800 0x0000f800 0x00800 0x00800 RWE 0x1000

 Section to Segment mapping:
  Segment Sections...
   00     .softregs 
   01     .text irq_table 

There is no dynamic segment in this file.

There are no relocations in this file.

There are no unwind sections in this file.

No version information found in this file.

ELF is for the operating system's dynamic loader to load up the program into the right virtual address, link it with the runtime libraries, etc. But on a miniboard there is no operating system. We need raw instructions and data in memory. These instructions and data are there, but just not in the format the miniboard needs.

The standard for M68HC11 is the S19 format. We have an ELF file but need an S19.

 It may be possible to do this step with the objcopy program.
I wrote a tool to convert one to the other, discarding all the unwanted bits. It uses libelf, which is already installed on most systems. Debian users install the libelfg0-dev package. To build elftos19:

$ g++ elftos19.cpp -o elftos19 -lelf
$ ./elftos19 test2.elf test2.s19
$ cat sample.s19
S123F800DE003C9F0020FE000000000000000000000000000000000000000000000000000D
[64 lines skipped]

That's it. See the part DE00...20FE? That matches the opcodes given to us when we last run objdump.

That S19 will load 9 bytes of data into the EEPROM of your miniboard, and one interrupt vector to point at the start of the code. In order to upload, use the MBUtil package. For some reason, the DOS dlm program will not upload these files properly.

The supplied Makefile has all of the definitions you need to build your work. Just run “make program.s19”. The file hardware.h contains definitions for all of the registers. The names match those in the Motorola 6811 reference manual.

All of the function defined by IC11 are not available. This includes the pulse width modulated motor functions. You are, however, provided with enough tools to write replacements for yourself. The only functions provided in this package are serial I/O functions in serial.h.

Since IRQ handlers are special, there is some specific support for them. See the IRQ documentation for how to declare your own IRQ handlers.


Nick Lewycky
Last modified: Tue Dec 21 00:29:21 EST 2004