Turn a BASIC Program into a Cartridge for the C64

The other day, I was researching the Jason-Ranheim Capture cartridges and browsing the DDI Projects on that topic. The capture cartridge is a freezer module for the C64 that allows the internal state of the machine to be saved or to be burned directly into an EPROM. These ROMs can then be installed in a cartridge that will run the frozen state. An interesting concept that I’ll probably explore closer at a later date.

In one of the archives I downloaded I came across a PDF document containing scans of a text, also by Jason-Ranheim Company, that caught my eye. The document is titled “Auto-Start BASIC Programs” and describes a simple process to convert a Commodore BASIC program into an auto-starting cartridge for the C64 or the VIC20. I got curious and decided to try this — but first I wanted to explore how it works in detail.

The Bootloader

The instructions for the C64 first require you to type in and execute a small BASIC program.

The BASIC loader for the bootloader.

The loop in lines 3 to 6 is a simple loader that POKEs the data and machine coded given by the DATA lines into memory starting at $8000 (32768). This address also happens to be the memory location where the contents of 8k ROM cartridges will appear when inserted in the expansion port. The instructions then describe how to load your actual BASIC program directly behind this 67 bytes “bootloader” and how to burn the data between $8000 and $9FFF into an EPROM. But let’s have a closer look at our bootloader first.

After a reset, the C64 Kernal checks for a signature — “CBM80”, PETSCII encoded — at memory location $8004, and if found it will jump to the address stored at location $8000/$8001 (little-endian, of course). This is how the cartridge boot-up works. After running the BASIC program above, our bootloader is located at $8000 as mentioned. So we can use a monitor program to examine it. Here I’m using the monitor of the Final Cartridge III.

The CBM80 signature and our cold-start vector.

The vector found at location $8000 points to $8009 which is right behind the CBM80 signature. This is where the Kernal will start executing our bootloader code, so we’ll use the monitor again to disassemble that code. Also, some reference of the Kernal routines and a memory map of the C64 will be helpful.

The bootloader code disassembled.

So, what is happening here? The first three lines are just some basic initialization of the VIC (stx $d016), CIA (jsr $fda3), and RAM (jsr $fd50) that the Kernal would also perform as part of its RESET routine if no cartridge was found. The next instructions from $8012 to $8026 form a loop that will copy 2 bytes of data from $802c to $281 and three bytes from $802e to $277.

The target areas are both located in the so-called extended zero page. The two bytes copied to $281 set the beginning of the BASIC area to $8031 which is again within our bootloader.

The three bytes copied from $802e contain the letter “r” followed by a capital “U” and the code for RETURN ($0d). This corresponds to the abbreviated RUN command followed by a press on the RETURN key, and those bytes are written to $277 which happens to be the keyboard buffer of the C64!

When the loop is finished at position $8027, the X register will contain the value 3 which is then store to $c6 in the zero page — the current length of the keyboard buffer. Finally, our bootloader exits with a jmp $fcf8 into the “middle” of the Kernal’s RESET routine. So, let’s have a look at the entire RESET routing now which is located at $fce2.

The Kernal RESET routine.

Immediately after setup, the routine checks for the presence of the CBM80 cartridge signature (A). If found, a jump is performed to the address stored at $8000 (B). If no cartridge was found, three lines of initialization follow that we’ve already seen duplicated in our bootloader. After that, the routine continues with some more initializing at $fcf8 (C) which also happens to be the address that our bootloader was jumping back to! Finally, a jump is performed to the BASIC cold start vector stored at $a000 (D).

This will of course prepare the BASIC interpreter and start the well known input mode that is usually greeting us with a blinking cursor after we power on the C64. And the input mode will find three keystrokes already contained in the keyboard buffer…

Remember that the booloader set the beginning of the BASIC area to $8031, so the first line of BASIC is not expected at $0801 as usual but at $8032 instead. So the BASIC contained in the cartridge ROM will be run “in place”. Let’s see what BASIC code is included in the bootloader using this explanation of the BASIC tokens.

One line of BASIC prepended by the bootloader.

We find that a single BASIC line with number 0 (B) is prepended to the actual program. The next BASIC line is located at $8043 (A) which is where the “object program” is loaded to. This first line contains 3 tokens (C, D, E) separated by colons and it is terminated by a 0-byte (F). So, in the end this is equivalent of the following BASIC line being prepended:

After that, the BASIC interpreted will continue execution at the first line of the actual program we were preparing for our cartridge. Address 46 ($2e) in the zero page is the high byte of the pointer indicating the beginning of the BASIC variable area. Usually, this area is located behind the program but in this case there is no more room behind it so it is moved to the (unused) beginning of the regular BASIC area. I’m not sure why only the high byte is set, though.

Let’s try it!

I typed in a variant of the Hello-World classic to be converted into a cartridge. Since I would have to program the EPROM on my PC in the end anyway, I cheated a little and used the Vice emulator instead of my real hardware.

The BASIC program to be made into a cartridge.

I created a d64 disk image and saved this “payload” program alongside the bootloader-loader from above. Then, the actual process is quite simple.

First, the bootloader program is loaded (A) and executed. Next, the beginning of the BASIC area it set to right behind the installed bootloader (B). Then, the Hello-World program is loaded to that position in memory (C). I used the monitor program again to save the entire section of memory to a new file.

Finally, the first two bytes of the “hello.bin” file we’ve just created have to be removed because they contain the memory address that the file originated from. The resulting binary image is then ready to be burned into an EPROM or just to be converted into a .crt file for testing in the Vice emulator.

cartconv.exe -t normal -i hello.bin -o hello.crt -n "hello hackup.net" -p

To conclude the proof of concept, I used my TL866CS programmer to burn the truncated hello.bin file into an EEPROM and inserted this into a generic OpenC64Cart PCB.

Our EEPROM on an OpenC64Cart.

The result is a working HelloWorld cartridge that starts printing its message immediately when the C64 is powered on. Not much practical use but an interesting exercise for sure!

The HelloWorld cartridge in action.

Of course, this method will only work for BASIC programs that fit into the 8k of ROM provided by such cartridges. Also, the program runs right where it is, mapped into memory at an unusual location far removed from its normal position. This will not work for all programs, e.g. some might contain embedded machine language routines that expect to be run at a specific location in memory. There is a second document titled “Auto-Start BASIC Programs With Machine Language” that addresses this issue with a different bootloader. I might examine that one at a later point.


1 thought on “Turn a BASIC Program into a Cartridge for the C64

  1. Hello, pretty good projects
    I am mainly interested in CNC and then in steppers motors,
    I wonder if is it possible make an hybrid with Arduino + Commodore
    to manage CNC or Lathe… In the past i tried with turbo c++ to make an app for ms-dos that interface LPT1 , but still interpolations are missing 😀

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.