cc7800 implements a subset of C compiler for Atari 7800. The main goal of cc7800 is to enable making games for the Atari 7800 using C language, including fast filling of Maria display lists, not to provide full C support for the 6502 (have a look at cc65 if this is what you are looking for). Any code written for cc7800 can be compiled with gcc, but not the other way round... The 6502 processor is famous for being an inefficient target for C compilers, due to its poor stack support, its infamous indexing modes and its lack of registers. cc7800 tries to cope with these limitations by not strictly implementing all C features but mapping C syntax to the specifics of 6502, in particular 6502 X/Y indexing modes.
cc7800 is implemented in the Rust programming language, a touch of modernity for an almost 40 years old console... The C language grammar has been handwritten from scratch as a PEG grammar, so don't expect any ANSI or ISO C compliance.
#include "other_file.c"
to cope with this.Installing from source is quite straightforward when Rust Cargo is available on your platform. If this is not the case, please
use rustup to install it, then use cargo install --path .
in the root
directory to compile and install cc7800 locally.
You can install the binary directly using Cargo by typing cargo install cc7800
If you definitely don't want to install Rust (quite a shame), a Windows installer will be provided in the coming years.
A few examples are available in the examples
directory. To compile HelloWorld, please type in the root directory :
cc7800 -Iheaders examples/test_helloworld.c
This will produce out.a
, which is a DASM compatible source code.
Type dasm out.a -f3 -v4 -oout.bin -lout.lst -sout.sym
to make out.bin
.
Now use a7800header
to create a .a78
file from out.bin
.
The out.a78
can be executed with the a7800
emulator by typing a7800 a7800 -cart out.a78
, or copied directly on a
Concerto cart.
cc7800 supports a few intrinsics to help making ASM-like tuned code :
load(expr)
loads the 6502 accumulator with expr
. It implements a LDA
isntruction.
store(expr)
stores expr
into the accumulator. It implements a STA
isntruction.
strobe(pointer)
implements a STA
instruction also. It's just the same as store, but accepts only pointers. Typically used for your numerous strobe(WSYNC)
instructions in your kernel...
asm(string)
inlines the given assembler instruction into the C code. Particularly useful to call ASM function (use asm("jsr asm_function")
).
csleep(int)
stands for cycle sleep. Helps to insert nops in the code. Implemented for 2 to 10 cycles.
16-bits arithmetics is supported, BUT beware to use only simple expressions (like a simple addition, or +=
, not multiple additions on the same line of code), since carry propagation is not ensured (maybe will it be in the future). In particular 16-bits operations are not supported in comparisons. Use short
to declare a 16-bits variable. char *
are also 16-bits variables, since address space on 6502 is 16-bits wide.
In order to convert from 16-bits to 8-bits, use the >> 8
special operation to get the higher byte of a short
, and use nothing to get the lower byte.
X and Y are unsigned char
typed, BUT in order to optimize the loops, they are considered signed char
when compared to 0. Hence the code do { something; Y-- } while (Y >= 0);
will be implemented with a BPL
(branch if plus) instruction, just like you would do in assembler. Beware then that if Y > 128, due to the complement-to-2 binary representation, it will be considered negative number and the loop will exit immediately.