C Programming with cc65
Programming in c is generally easier than assembly, so if I want to make anything complicated it would be nice to have. First, I have to mention Dirk Grappendorf’s 6502 Home Computer, which I referred to a lot. I also use the Makefile and cfg files that he used.
To start, there is a startup routine that is written in assembly. This works the same as a regular assembly program, where there is a segment for the vectors that point to the startup routine, like this:
The only thing strange about this program is the _main
routine that appears
out of nowhere. This routine is actually our int main()
from our main C
program. All of the methods in C get converted to assembly routines with an
underscore.
The main program, in C, looks like this:
This looks a lot nicer than assembly but I can’t just use printf();
, since
there is no console or anything to write it to. Instead, I have a header with
all of the routines to control the FPGA Graphics Adapter
which lets me use a VGA monitor. Right now I’m only using the monochrome text
mode, so using the existing assembly code was easy. Here’s what that header
looks like:
and the related assembly source starts like this:
io.inc65 and zeropage.inc65 are definitions for io locations and zeropage
locations. popa is a macro that’s made by the compiler. The way parameters
are passed from a C program to the assembly program is through a stack.
The parameter stack is different than the CPU stack though, so you can’t use
pla
or anything like that. When I call something like fga_putc_m(c);
, the
character c is loaded into the parameter stack and that stack pointer points
to it.
For __fastcall__
functions though, the right-most parameter is passed
through the “primary register” instead of the stack. The primary register
changes depending on the width of the data-type, being A, A/X, and A/X/sreg
for 1, 2, and 4 byte variables, respectively. sreg is just a 2 byte zeropage
location. For functions with only one parameter, this means you don’t need
to worry about the stack at all, it just comes in the registers. To return
a value, you leave it in the registers. The wiki says all return values should
be in X, but it seems like it’s actually A, so I’m not sure. For 16 bit values
you would put it in A/X then.
Being able to program in C should make it easier to make higher level software, and eventually a real operating system. Low level stuff would still have to written in assembly, but either way I look forward to seeing what new things I can do.
Here are some relevant wiki articles:
Parameter-passing-and-calling-conventions
And here’s the code I wrote: c_programs