For a while I have been thinking about creating or obtaining a cartridge of one of my favorite games, Wizball. Now I know what most people will suggest, which is to get an EasyFlash cartridge and put a CRT version on there.
However that is not how I roll, for two reasons:
A: Its too easy! Which means it is no fun and you don’t learn anything.
2: I like to have an authentic 1 cartridge = 1 game feel when I use them.
And D: I have had this Super Games cartridge in my drawer for years and was sure I could use it for this!
(trying to be funny there with the numbering… ah nevermind!)
So a while back I created a mix tape for the C64 which contained a ton of my favorite games on a C90 tape. To create it I ripped the games from their original cassettes using the Action Replay machine code monitor. I also demonstrated it in the Vice emulator extracting Wizball from the cassette, see my video here:
Once a dump of the RAM is made as the game program is about to start, this can be loaded back into memory by any method you like as long as all the data goes back where it originally came from. Then it’s just a case of jumping to the program start address, which in this case (using the UK version of the Wizball cassette) is $6389.
So instead of loading from cassette or disk, I will be loading from cartridge into RAM then executing the start address. I need to get all the Wizball data into the space the Super Games used to take up, along with a bit of code I will need to write which will read the data from the cartridge chips and put in the correct RAM location then start the game.
How do cartridges work on the Commodore 64?
Ok, now there is a lot of information around about cartridges and one of the best documents to study is one written about the CRT emulator format: https://ist.uwaterloo.ca/~schepers/formats/CRT.TXT
Put simply, when a cartridge is plugged in, part of the C64s RAM (8k or 16k depending on the cartridge) is replaced with the contents of the cartridge. For smaller games, this is enough to load and play the game without any issue, but with a large game or cartridges with multiple games, 16k is not enough. This is overcome with a technique called BANK SWITCHING, and since my Wizball dump is close to 64k, I certainly need to do some of it!
A cartridge chip can be split into chunks, or BANKS and the active bank can be changed while the computer is running, hence the name “Bank Switching”. This allows code from one part of the cartridge to be used, then when different data is required it can switch to a different bank and load something else.
This Super Games cartridge has two ROM chips but the principle is the same, the collective size of the chips is split into four banks. As stated in the document above, whichever bank is active takes the place of the C64s memory in location $8000-BFFF.
Hacking the CRT version of Super Games
I decided before messing with the physical cartridge that I would see if I could modify the CRT version of Super Games to run my version of Wizball, and if that worked then move onto the hardware side.
So armed with this information I first downloaded the CRT of Super Games and got to work. The CRT files have a header which is for the emulator to identify the type of cartridge and know what to do with it. Viewing the file in a hex editor will show this:
Although full details can be found in the CRT format documentation, I will briefly explain what each section means.
RED – $0000-$003F – This is the CRT header which identifies the file to the emulator with details of name, file size, version etc. I dont need to change anything here.
BLUE – $0040-$004F – This provides details of the bank the following data belongs in, nothing to change here either. There is a line like this for each bank, as such they can be found in the Super Games CRT at $0040, $4050, $8060, $C070.
GREEN – $0050-$0058 – The first four bytes indicate the address the C64 should go to when the cartridge starts (in Low/High order), first one is cold start, second is warm start (both the same 800A in this case). The following 5 bytes (C3 C2 CD 38 30) is the text “CBM80” in PETSCII, this is what the C64 searches for to see if the cartridge wants to autostart (i.e. instead of going to BASIC as normal). This will need to be changed to reflect my new start address.
YELLOW – $005A-404F – This is the first bank of data.
It is important to note that the header is not needed for anything involving a REAL C64! It is ONLY useful to the emulator. Therefore all our addresses so far are offset by $50, and means the GREEN section above will start at location $8000 and the bank will end at $BFFF.
Each commercial cartridge is different and they have their own methods of bank switching, details can be found in the CRT document. The Super Games cartridge makes it very easy for us, all we need to do to select which bank we want is write the required number to memory address $DF00. So to select the first bank we would write $00 to $DF00, for the second bank it would be $01, followed by $02 then $03 for the last bank.
So in addition to adding my Wizball data, I also need to write some code to perform a few tasks, it will go something like:
- Initialise System (vaguely following the Kernal routine to do this)
- Select first bank ($00) > Copy all bank data to RAM
- Select second bank ($01) > Copy all bank data to RAM
- Select third bank ($02) > Copy all bank data to RAM
- Select last bank ($03) > Copy all bank data to RAM
- Disable the cartridge
- Jump to Wizball start address
Since the cartridge has four banks, I need to split my dumped Wizball data into four parts. Starting at the end of the file I removed the last $4000 bytes and saved it as its own file, this would be my Bank 3 and it must be 16,384 bytes exactly. I repeated the procedure to create Bank 2 and 1, then the remainder of the file was my Bank 0.
This first bank also had the file load address in the first two bytes (this is normal) so these need to be removed but we need to note what the address was, my Wizball dump begins at $0460 (yep, right in the screen RAM, the data must go back there or the game wont run). After removing the start address my bank zero was 15,010 bytes which leaves me over 1k to write a startup routine! Looooads of room! The resulting file however must be the full size of 16,384 in order to fit both the Super Games CRT and any EPROM thereafter, so any unused space will need padding out with zeros.
Next was to copy the data into the CRT file and I again started with bank 3, which can be pasted directly into the CRT at $C080 and should reach the end of the file at $1007F (the file size is larger than you may expect due to the 79 byte header and four 16 byte bank headers). Bank 2 fits neatly between $8070-$C06F, bank 1 at $4060-$805F. Since my bank 0 is only $3A9F bytes, I used the hex editor to add 1377 zero bytes to the START of the file (I will later use this space for the startup routine) which takes the file to the 16,384 size needed. This bank can then be added at $0050-$404F.
Lets attach the CRT in VICE and see what happens!
Nothing?!! Actually, something isn’t quite right is it. 30719 bytes free is clearly incorrect so what happened? Well, what did we expect? After filling the start of bank zero with erm, zeros – the cartridge start address was removed along with the autostart signature “CBM80”. So nothing was supposed to happen, but why the wrong number of bytes free? Well since there is technically a cartridge connected but it did not autostart, the C64 performed a RAM test as usual. The usual 38911 BASIC BYTES FREE is the tested RAM from $0800-$9FFF, but since our cartridge is taking space after $8000 means basic has lost $2000, or 8,192 in decimal bytes, and if my calculations are correct, 38911 – 8192 = 30719.
Lets open the VICE Monitor and have a look around the RAM.
I first need to make sure the monitor is showing me what the CPU is currently seeing (The C64 itself uses bank switching in order to use it’s several ROMS while still having 64k of RAM). To do this we enter ‘bank cpu’.
Now, I know the cartridge should be at $8000 so lets type ‘m 8000’ to view the contents.
Just a bunch of zeros right? Well that is actually correct since the start of bank zero was padded with zeros. Entering ‘m’ several more times progresses the memory view, and by the time we get to $8550 onward I begin seeing familiar hex values from the Wizball dump!
The layout of VICE’s monitor is a bit crappy and doesn’t show 16 bytes per line so it never seems to line up the bytes evenly like the hex editor (or even the Action Replay on the C64) does. Regardless, the contents of the CRT bank 0 are in the C64s RAM as expected!
Now to try switching to the next bank!
Easy! In the VICE monitor just enter ‘F DF00 DF00 01’ and voila! List again the RAM contents at $8000 and we are now looking at bank 1!
The command was telling VICE to fill from $DF00 to $DF00 with $01, that’s all we need to do to switch bank! If we wanted to do this in our assembler code it would be something like:
> LDA #$01
> STA $DF00
To access all of the banks is exactly the same, we just store the required number at $DF00. Storing a $0C at that location will turn off the cartridge completely.
Now that I can access all the dumped Wizball data from the cartridge banks, I can set about writing my code to copy it to RAM and start the game.
I am probably NOT going to post the code here since it will only work with with my dumped version of the game and it really shouldn’t be too hard if you know a little assembly. In an almost pseudo code listing it will be like this:
Follow kernal routine from $FCF2
(initialize SID, CIA and IRQ, perform RAM test and reset IO vectors)
Clear screen (shouldn’t be anything on but you never know)
Copy this code into cassette buffer at $033c
(needed so Wizball doesn’t overwrite it, and also wont be here when bank switched)
Jump to code now copied to cassette buffer
Bank switch to bank zero (should be already there but to be safe)
Copy from $8560 (start of Wizball in bank 0) to $0460 in RAM
Copy till entire bank copied
Switch to bank 1 > Copy till entire bank copied
Switch to bank 2 > Copy till entire bank copied
Switch to bank 3 > Copy till entire bank copied
Jump to Wizball start address $6389
Now that I have written the code and pasted it into the empty space in bank zero, I can add the cartridge autostart signature to the start of the bank “0A 08 0A 08 C3 C2 CD 38 30” – yes I kept the same start address as Super Games, although I could have put the code anywhere. My startup code takes up 86 bytes and begins at $800A.
Attaching the CRT in VICE, a mere 2 and a half seconds later I get this:
Now onto the physical cartridge! As previously mentioned, the Super Games cartridge has two ROM chips which as luck would have it are pin compatible with standard 27C256 PROM/EPROMS. If you try this with another cartridge make sure you check them as a different chip may fry the EPROM or your C64!
Sadly these two ROMs are soldered to the PCB so time to get de-soldering!
Fortunately I had a two 28 pin DIP sockets in my box of parts. Sadly they were different types but no matter.
Just to make sure I hadn’t damaged anything during solder work, I inserted the original ROMs into the sockets and plugged it in.
Everything worked fine!
I always use EPROMS for experimenting, that way I can erase them if something goes wrong. Turns out I have used a couple of these before as they had data on them. To be sure I put them all in the UV eraser anyway.
While they were erasing I set about removing the CRT and BANK headers from the CRT file ready for programming onto the EPROMs. I removed the following from the CRT file:
$C070-$C07F – Bank 3 header
$8060-$806F – Bank 2 header
$4050-$405F – Bank 1 header
$0000-$004F – CRT header and first bank header
The file is now exactly 65,536 bytes long and needs splitting for the two ROM chips. I logically (or illogically) assumed (I know, right) that bank 0 and 1 would be on the first ROM and bank 2 and 3 on the second. After all, the Super Games CRT file has them in that order, and since the new Wizball CRT worked, I was sure that was the right way. So I split the file into two 32k (32,768 or $7FFF bytes) files and programmed them onto the now blank EPROMs.
With bated breath I plugged it into the C64 and powered it up… after a few seconds I got a blank basic screen with the READY prompt at the top. Something was wrong. I did a few PEEKs starting with 32768 and upwards ($8000, the cartridge space) and there was data… no, in fact the correct and expected data was showing (the cart autostart header was in the first several bytes). A few more PEEKs showed the entire RAM was filled with data, just seemingly not the correct data or in the wrong places! Strange, the CRT worked but this didnt 🙁
I decided to look closer at the data read from the original Super Games (remember I said it wasn’t quite the same as the CRT files?) and compare it chunk by chunk to the CRT.
Long story short, I was wrong to assume the chips had two banks each! Turns out the data is kind of interleaved in $2000 (8192) bytes between them. ROM 1 has the first $2000 of each bank, and ROM 2 has the second $2000 of each. I need to modify my Wizball binary to match this layout, hopefully then it will work.
Rather than go back to the drawing board, as I already had a working CRT of Wizball, I figured I just needed to reorganise where the data is stored on the EPROM binaries.
Using the Super Games CRT and binaries I read from the ROM chips, I created this reference:
I moved all the data to the corresponding slots in the binaries and programmed the EPROMS again. This time on plugging it in, after a short pause we have SUCCESS!!!
Almost instant on to the game screen – I had to make sure that the game data was all present, and the only way to do that is to play the game to the end from the cartridge:
Now that I know it works I can program it onto standard PROMS and reuse the EPROMS in the future. And just a final note for anyone wondering. I intend to keep the original ROM chips from the Super Games cartridge, I never ever destroy working hardware (not intentionally anyway 🙂 ), regardless of the quality of the original product.