Sunday, April 22, 2012

C Programming Tutorial with cc65 on the Replica 1

The cc65 cross-development tools include a C compiler. This allows you to write code for 6502 systems in a high level language. I'll present here a little tutorial on how to get C programs compiled and running on the Replica 1.

Where assembly language is fast, compact and efficient, but hard to program and BASIC is easy program but much slower, C provides an alternative that has most of the speed of assembly language while being easier to program.

To use it, you need to build the tools and cross-compile your code on a desktop system and then download it to your target system, i.e. a Replica 1. The documentation is quite good. See the web site for information or check the on-line html documents when it is installed (typically at /usr/local/share/doc/cc65/index.html on a Linux system).

You'll want to get my patched version of cc65 that supports the Replica 1 from here.

For this tutorial I assume you are on a Linux system but it should work much the same way on a Windows or Mac OS X desktop.

I assume you now have the cc65 tools built and installed. Let's build and run a simple example C program (note that the source code for the examples I discuss can all be found here).

Let's start with the standard simple "hello world" program. Here is the listing for it (I'll refer to the file as hello1.c)

#include <stdio.h>
int main (void)
    printf("\nHELLO, WORLD!\n");
    return 0;

This is standard and the same as can be found in a book on C except maybe for only using upper case characters. The Replica 1 supports output in lower case but some other Apple 1 and compatible systems do not, so I will only use upper case in these examples.

To compile it we can use the "cl65" program. In the simplest case we can just do:

  cl65 hello1.c

which will produce a binary file hello1.

Typically we want to get a little fancier and specify some additional options. I typically use:

  cl65 -O -l -vm -m -t replica1 hello1.c

Where the options specified the following:

-O   -> optimize the code
-l     -> produce a listing (.lst) file
-vm -> produce a detailed map file
-m   -> specify the map file name
-t     -> compile for Replica 1 (default is to compile for a Commodore 64).

Now to get the code onto the Replica 1 we want a Woz monitor binary format file. The bintomon program can do this. This program is included in my cc65 patches but you will need to compile and run it. You'll find it in the source under /util/apple1. You need to compile it for your desktop system and you probably want to install it in a standard location as well. Commands like this will do the trick on a Linux system:

cd util/apple1
gcc -o bintomon bintomon.c
sudo cp bintomon /usr/local/bin

To generate the Woz monitor version of our compiled hello1 program we can run:

bintomon -v -f hello1 >hello1.mon

and see the output:

Load address: $0280
Run address: $0280
Length: $0A07 (2567 bytes)
Last address: $0C87

With compiled C programs the start address and program length are stored in the binary and bintomon can use these when generating the min file. Note that it does not work this way for assembly programs, you need to specify the load address unless it is the default 0x280.

Now load the monitor file into your Replica 1 machine using a serial terminal program. I use minicom on Linux. I also use high speed serial mod which speeds up the loading process greatly.

After loading the program will run and you should see output like this:


Getting this first program running is usually the hardest step. Congratulations if you were successful.

Now for a little more complex program, let's try hello2.c, the listing for which is below. It uses some Apple 1 specific routines from apple1.h, specifically keypressed() and readkey().

The program illustrates some math by outputting a table of numbers and their squares and cubes. It also shows the length in bytes of the various C data types. Then it gets and displays a key press.

#include <stdio.h>
#include <stdlib.h>
#include <apple1.h>
int main (void)
    int i   = 0;
    short x = 1;
    char c  = 2;
    long l  = 3;
    for (i = 0; i < 10; i++) {
        printf("%d %d %d\n", i, i*i, i*i*i);
    printf("SIZEOF(CHAR) = %d\n",sizeof(char));
    printf("SIZEOF(SHORT) = %d\n",sizeof(short));
    printf("SIZEOF(INT) = %d\n",sizeof(int));
    printf("SIZEOF(LONG) = %d\n",sizeof(long));
    printf("PRESS A KEY TO CONTINUE\n");
    while (!keypressed())
    i = readkey();
    printf("KEY WAS: %c\n", i);
    return 0;

When run, the output looks like:

1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729

Note that all these examples as well as a Makefile for building them is provided on the site at github.

Included there are a couple of other examples. The file nqueens.c is a little program I write years ago for solving a chess program called the "N Queens Problem". The program sieve.c calculates prime numbers. Both of these are very CPU-intensive programs and will give you an idea of how fast code runs on a Replica 1 versus a modern desktop system.

Feel free to try building and running the examples and perhaps modifying them. Some other things to try:

  • Look at the generated assembly code (.lst file) for the programs and trying to understand it.
  • Compare the code generated with and without the -O (optimize) option.
  • Find or write some more small C programs and get them running on the Replica 1.

In summary. cc65 is a full C compiler that supports almost all language features except for floating point math. I've used it for writing a reasonably large adventure program, about 880 lines of source code. The code was much easier to write than in assembly, and more pleasant to write in than BASIC.
I was also able to compile and test it on a desktop system before running it on the Replica 1.


Unknown said...
This comment has been removed by the author.
Unknown said...

Very good and helpful work.
Visit here for more programming examples.

Sreenath said...

This is from The C Programming Language - 5.12
1. int (*(*x[3])( ))[5];
What is The meaning of this declaration??
Understanding the above declaration is easy once we follow this order.. the inner most loop to the outer most one.
a. consider *x[3];
which means we are creating three array of pointers
As these are pointers, to say the type to which they point to we made the outer declaration.
b. in general , int (*p())[5];
means p is a function which returns pointer to array with column size as 5.
on Combining a,b
we can say above declaration as
the array of pointers, each storing the address of the function returning pointer to an array of column size as 5.

For further queries visit

Anonymous said...

nice one eh!

Unknown said...

Appreciation for nice Updates, I found something new and folks can get useful info about BEST ONLINE TRAINING