Skip to content

cc

native C compiler (runs on the P8X)

NAME
    cc - native C compiler (runs on the P8X)

SYNOPSIS
    cc src.c >out.asm
    asm out.asm PROG.BIN      (then: run PROG.BIN)

DESCRIPTION
    A small C compiler written directly in assembly, compact enough to run
    ON the machine. It reads a C source (streamed through the BIOS read
    stream) and writes P8X assembly to stdout, which the native assembler
    (asm) turns into a RUNnable binary. So a C program can be compiled,
    assembled, and run entirely on the P8X:

        cc hello.c >hello.asm
        asm hello.asm HELLO.BIN
        run HELLO.BIN

    cc does the whole job on-target -- preprocess (splicing /lib/lib_NAME.c for
    each //#use), parse, and code-generate -- so nothing off the machine is
    needed. Variable storage is 16-bit-addressed (a program may use thousands of
    slots), so cc compiles the large shared-code commands -- dir, grep, sed, vi
    and the like, each pulling in several //#use libraries -- entirely on the
    machine.

    Supported C subset  (`int` is 16-bit; `char` is a 1-byte element type — see
    below.)

        program : (func | global | struct)*
        struct: struct TAG { (type [*]member ;)* } ;   member access: v.m , p->m
        global: type [*] NAME [ [ N ] ] ;   (file-scope variable, zero-init)
        func  : type NAME ( [type [*]P (, type [*]P)*] ) { <stmt>* }
        stmt  : type NAME [ = <expr> ] ;  (declare a variable)
              | type * NAME ;             (pointer variable)
              | type NAME [ N ] ;         (array of N ints)
              | NAME = <expr> ;           (assign)
              | NAME++ ; | NAME-- ; | NAME += e ; | NAME -= e ; (scalar)
              | NAME [ <expr> ] = <expr> ; (array/pointer element store)
              | * <expr> = <expr> ;       (store through a pointer)
              | <expr> ;                  (expression stmt, e.g. a call)
              | if ( <expr> ) <stmt> [ else <stmt> ]
              | while ( <expr> ) <stmt>
              | for ( [asg] ; [<expr>] ; [asg] ) <stmt>
              | break ; | continue ;      (innermost loop)
              | { <stmt>* }               (block)
              | putchar( <expr> ) ;
              | return [<expr>] ;
        expr  : cond ? expr : expr        (ternary) | land ('||' land)*
        land  : bor ('&&' bor)*           (short-circuit, yields 0/1)
        bor   : bxor ('|' bxor)*          (bitwise or)
        bxor  : band ('^' band)*          (bitwise xor)
        band  : rel ('&' rel)*            (bitwise and)
        rel   : sh [ relop sh ]           relop: <  <=  >  >=  ==  !=
        sh    : add (('<<' | '>>') add)*  (shifts, variable count)
        add   : term (('+' | '-') term)*
        term  : unary (('*' | '/' | '%') unary)*
        unary : ('-'|'!'|'*'|'&'|'++'|'--') unary | factor | NAME++ | NAME--
        factor: NUMBER | 0xHEX | 'c' | "string" | NAME | NAME[ <expr> ]
              | bios( ADDR, p1, a ) | NAME( args ) | '(' <expr> ')'

    Multiple functions with parameters; execution starts at main(). Functions
    return a value via `return`. Parameters and locals use STATIC per-function
    storage, but a function saves its own live slots around a direct recursive
    call, so (direct) RECURSION works — fact(), fib(), etc. POINTERS are 16-bit:
    `&x` takes a variable's address, `*p` dereferences (read), `*p = e` stores;
    this gives pass-by-reference — set(&x), swap(&a,&b). ARRAYS: `int a[N]`
    reserves N ints; `a[i]` reads/writes an element; an array name doesn't decay
    on its own, but `&a[0]` gives a pointer you can index (`p[i]`) and pass to a
    function. A bare array name now DECAYS to &a[0] (so `puts(buf)` and
    passing an array work). BUILTINS (like p8cc): putchar(c), puts(s) [+newline],
    getchar() [->char or 0xFFFF at EOF], peek(addr), poke(addr,val), argstr()
    [->the program's argument string], and bios(ADDR,p1,a) [a raw BIOS/OS syscall,
    ADDR constant, -> A|carry<<8]. STRINGS: a char literal 'A' is a number; a string literal "abc"
    is emitted inline (jumped over) as NUL-terminated data padded with an extra
    zero, and evaluates to its address. So a classic
        char *s; s = "hi"; while (*s) { putchar(*s); s = s + 1; }
    loop and helpers like puts()/strlen() work. CHAR is a real 1-byte element:
    `char a[N]` packs N bytes (2 per word slot), `char *p`/`char a[]` deref and
    index (`*p`, `p[i]`, `a[i]`) read/write ONE byte and pointer arithmetic on
    them steps by 1 — so a char buffer strcpy(&d[0], s) works. (A scalar `char c`
    is still stored in a word; the byte width matters for arrays and pointers.)
    Escape sequences \n \t \r \0 \\ \' \" work in char and string literals, and
    // line and /* block */ comments are skipped; a `//#define NAME value`
    directive defines an object-like macro (value = a decimal or 0x-hex number)
    that is substituted wherever NAME appears -- e.g. `//#use abi` brings in the
    BIOS/OS address names so a program writes bios(FOPEN, RDBUF, 0). And a `//#use NAME`
    directive splices in /lib/lib_NAME.c on-target (recursive, deduped -- the
    native counterpart of the host clib.py). Arguments pass on a runtime arg
    stack (the callee pops them into its own slots), so FORWARD CALLS and MUTUAL
    RECURSION work when the callee has a forward prototype `int f(int);` before
    its use. NOTE: within one call's argument list a later
    argument must not read a parameter an earlier one overwrote; and taking the
    address of a local across a DIRECT recursive call is undefined (that local is
    saved/restored). Comparisons yield 0/1. 16-bit
    `int` — arithmetic runs through a memory accumulator (__ax) and runtime
    helpers (__add/__sub/__mul/__cmp/__div); integer division, and '/' or '%' by
    0 gives 0. putchar takes the low byte.
    It grows one tested feature at a time (functions, char/pointers, ...) toward
    the p8cc subset. Redirect the output to a RELATIVE filename
    (cc src.c >out.asm) — an absolute redirect target is not yet supported by
    the shell.

EXAMPLES
    cc t.c >t.asm            compile; then  asm t.asm T.BIN ;  run T.BIN

SEE ALSO
    asm, edit, run

See also: asm, edit, run

The text of man cc on P8X (os/man/cc in the repository). All commands