Sunday 25 June 2023

Converting a multipart game for the VIC20 - Galaxia

During the process of selecting games to add to the new Penultimate +2 cartridge, we have come across a lot of games that load in multiple parts.

These are games where a program loads from tape (or disk), and then goes on to load one or more additional parts. This was quite common in the day due to the memory limitations of the VIC20.

The first part would display a title screen or some instructions or define some graphics characters (or various combinations of the above). Once those have been displayed, the game itself could then be loaded, overwriting all of the previous data and leaving as much room as possible for the game.

Multipart games need to be converted to cartridge images, and that often needs a custom approach for each one due to the ingenuity (or craftiness) of the original designers.

Over on my Patreon, I have written quite a few posts about some of the different techniques found during converting mulitpart tape games for the VIC20. This is one of the later posts, but seems a good place to start as it covers one of the easier conversions in more detail.

Today's game conversion is Galaxia, I wonder if you can work out what sort of game it is based on the cunningly disguised name?

Step one is to check out the source, here a D64 disk image. I load it on Vice, in this case with the following command line

xvic -memory none "Galaxia (1983)(Romik).d64" 

This means start the VIC20 emulator, set the memory to unexpanded and auto-load the Galaxia disk image. Note this is "none" and not 0. xvic -memory 0 sets the VIC20 to have RAM in block 0, the 3K expansion. The number of times I have typed memory 0 by mistake these last few months......

This is to check the source file works. To check if the game is any good, and to check it isn't just the same game with a different name (looking at you, Jeff Minter).

In some cases, the tap or d64 files will contain only a single PRG file, this is not going to be one of those, but that's fine. If it was a single PRG, that would probably be the version that had been archived.

The title screen loads up, press fire and you are into the game, a sort of pillar-boxed version of Galaxians. (did you guess right?)

No time for playing games now, but nice that it is wishing me luck with the conversion.

This all seemed to happen in one go, so is it just a single file then?

There is a common trick of using one program to display a title, maybe some instructions, and sometimes pre-load graphics, and then to load a second part which will overwrite the first, it's job now having been done. This is to cope with the lack of memory in the unexpanded VIC20.

The first of these is very small, so presumably a loader, and the second would be the game itself.

It is always worth loading these individually to see what they are.

In the simplest case, the first will be a title screen and maybe instructions, and the second will the just the game itself, and often if the gameplay is obvious enough, you can ignore the loader and just use the second part, the game itself.

Here, loading the first part, it shows just a SYS command, so it is assembly language program.

Incidentally, in case anyone isn't familiar with it, I used a trick to load the file. The spacing of the directory listing is such that you can cursor up and overwrite the start with the line with LOAD, and add ,8,1 after then name and overwrite the PRG with spaces, then press enter to load the file.

That is sometimes useful if the name is long or difficult to retype due to unusual characters.

Loading the program is fast as there is not much too it. Listing it shows a single line

10 SYS 4112

Normally what happens here is there is a large machine code program directly following the one line BASIC program in memory, all loaded in a single go.

To view what is loaded, I use the monitor in the Vice emulator, and the command

m 1000

To display the memory from 1000 (4096 in decimal) onwards.

There is something that looks like code there, so I use the command

d 1010

To disassemble the code from address 1010 (4112 in decimal) onwards. This is the last one I will mention in decimal, just because it comes from the SYS(4112) command, which is in decimal and stored as the characters '4', '1', '1' and '2' in the file. The rest of the time, assume everything is in hexadecimal.

That is all the code that runs from there. Interrupts are disabled, then 20 bytes are copies from 1020 to 0140. 1020 is immediately after this code, and 0140 is in the processor stack, but in an area not required for the moment.

The tape buffer from 033C-03FB is also a common place to hide code so that it won't get overwritten by the second part loading (unless you are loading from tape, obviously....).

Once the code is copied, the last line jumps to 0140 and runs the code from there.

In vice, I set a breakpoint on address 0140 with the break command.

break 0140

Once that was set, x was used to exit back to BASIC and RUN the program.

Once it had copied the code to 0140, it did the jump and them the monitor kicked in and I was able to disassemble the code at 0140 with the command 

d 0140

This code uses three kernal ROM calls to load a file. The first is to FFBA, SETLFS. This sets the three zero page parameters based on the values in registers A, X and Y, which are loaded before the call.

  • A, LA = 8, logical device (i.e. the channel in the open command)
  • X, FA = 8, device ID = 8, hard coded to the usual default drive, but not always. (trap for young players, this won't work from drive 9)
  • Y, SA = FF, secondary address = FF (tested for 0 or not 0, but normally it is set to 1 rather than FF, but it doesn't really matter)

The second call is to FFBD, setnam. This sets the namename for the load command based on registers A, X and Y. A contains the length of the filename, and X and Y are pointers to the filename

  • A, FNLEN = 8, 8 character filename
  • X, FNADDR (LSB) = 3A
  • Y, FNADDR (MSB) = 10

Together this sets the filename to be 8 characters from address 103A, which is at the end of the area of program memory.

That can be checked with another M command in Vice M 103A

m 103A

The 8 characters are ZZGALAXI, the name of the second file

The final command is FFD5, LOADSP. This loads RAM using the parameters previously set, from device ID 8, with filename ZZGALAXI. Here only register A is used. X and Y hold the load address, which is ignored when SA above is not 0, and the load address from the file is used instead.

  • A, VERCK = 0, 0 means load, anything else means verify.

The final line of code is a jump to 1A26, presumably the initialisation function somewhere in the code.

So, all in all, that does the equivalent of LOAD "ZZGALAXI",8,1 then jumps into the code to launch the game.

The file is 0E00 bytes long, so exactly fills the available space between 1000 and 1E00 where the screen starts. Things must have been so tight that there was not enough space for the usual 10 SYS xyz command at the start.

Normally with these conversions there is a bit more to do in replicating what the first parts of the loaders do. In this case it is just copying a block of code into RAM and running it.

I have created various loader cartridges over the years, and I just pick something similar and adapt it. The cartridge code need to call various initialisation functions from the kernal to setup the VIC and IO chips and initialise the memory etc. 

Looking at the VIC20 KERNAL, pretty much the first thing it does is check for a cartridge.

Unfortunately, for some reason it does this with JSR, rather than just having those few lines of code directly in place.

This wastes a few cycles and a few bytes unnecessarily, but more importantly, it means you need working RAM in the first 1K in order to boot a cartridge, which is a shame for things like Dead Test as nothing can run on a system with bad RAM in the first 1K. (JSR pushes a return address onto the stack at 01FF, and RTS pops that back off to go back there. If the RAM is faulty, the address popped off could be anything).

The cartridge borrows the initialisation code that runs when a cartridge is not present.

Next is a simple block copy routine to copy the code into the appropriate location in RAM.

Normally BASIC is also setup and the newly loaded code relinked and then run as if the user had typed run. However, here, that is not necessary as it just need a jump into the copied code as the original loader did.

I will cover that in more detail in a future post on a game that needs it.

The loader and the block of code together are less than 4K, which is ideal as this now fits into a 4K slot in the Penultimate+2 ROM.

That loads and plays nicely. Although, sadly only in PAL. On an NTSC VIC, the screen is positioned off to the side with no adjustment as far as I can see (some games use arrow keys or joystick to reposition the screen).

I will look at a possible NTSC conversion in future.


Penultimate +2 Cartridge

The Penultimate +2 Cartridge with the Galaxia and a host of other games is available to pre-order from The Future Was 8 bit:

More info in a previous post:


You can support me via Patreon, and get access to advance previews of posts like this and behind the scenes updates. These are often in more detail than I can fit in here. This also includes access to my Patreon only Discord server for even more regular updates.