Close

Preparing the driver

A project log for Targeting SDCC to the 8080

Writing a code generator for the 8080 microprocessor for Small Device C Compiler (SDCC)

ken-yapKen Yap 08/30/2019 at 12:140 Comments

As aficionados of Unix know, the classic C compiler is actually a chain of programs that take C source and generate various files, in the longest case, to an executable, classically a.out format, but for a long time now, usually ELF format. Here is the chain in block diagram form, taken from Wikibooks:

By Agpires - Own work, CC BY-SA 3.0, Link

SDCC is no different. However I only need to concern myself with the box labelled Compiler in the above diagram. For the 8080, as for the Z80, aslink generates Intel Hex format.

SDCC calls each distinct processor target a model, which is passed to the compiler driver sdcc as an argument, for example:

sdcc -mz80 hello.c -o hello.ihx

All related models share a backend, for example all the 8051 targets use the same backend.  Since the 8080 is closely related to the Z80, this is the backend I'm modifying.

Each variant processor is included in SDCC by including a pointer to a PORT structure. It's a large structure which describes many aspects of that port, including, but not limited to, the characteristics of the C implementation such as the sizes of various data types, the names of various sections in the generated assembler code, and the programs and libraries that are used in the chain.  Here is an excerpt from it:

PORT i80_port =
{
  TARGET_ID_I80,
  "i80",
  "Intel 8080",           /* Target name */
  NULL,
  {
    glue,
    FALSE,
    NO_MODEL,
    NO_MODEL,
    NULL,                       /* model == target */
  },
  {                             /* Assembler */
    _z80AsmCmd,
    NULL,
    "-plosgffwy",               /* Options with debug */
    "-plosgffw",                /* Options without debug */
    0,
    ".asm",
    NULL                        /* no do_assemble function */
  },
  {                             /* Linker */
    _z80LinkCmd,                //NULL,
    NULL,                       //LINKCMD,
    NULL,
    ".rel",
    1,
    _crt,                       /* crt */
    _libs_i80,                  /* libs */
  },
  {                             /* Peephole optimizer */
    _i80_defaultRules,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    z80symmParmStack,
  },
  /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */
  { 1, 2, 2, 4, 8, 2, 2, 2, 2, 2, 1, 4 },

Fortunately I could copy a lot of the fields from the Z80 port. But some parameters, such as for optimisation, may need tweaking later.

The structure refers to a mapping.i file which is used as a sort of macro expansion mechanism to generate many lines of code from a single line of pseudo-code. For example this entry:

{ "enterx", "add sp, #-%d" },

generates an instruction to adjust the stack from a single mnemonic. 

Also referred to are the peephole rules for optimising short sequences of assembler lines. For example:

replace restart {
        ld      %1, %1
} by {
        ; peephole 1 removed redundant load.
} if notVolatile(%1)

removes an unneeded assembler instruction. I took the Z80 rules and removed those which did not apply to the 8080. 

Finally we need to spit out the .8080 directive to the assembler output at the beginning. This is done in the file SDCCglue.c:

else if (TARGET_IS_I80)
    fprintf (asmFile, "\t.8080\n");

All in all about half a dozen files were modified to prepare the framework to handle the 8080 backend. The real work is yet to begin.

Discussions