Tuesday 5 September 2017

VIC20 Tape to Disk Conversion

This is an old post, preserved for reference.
The products and services mentioned within are no longer available.

Even though the VIC20 computer is a popular machine, there are still a lot of software titles which are not easily available.
Tornado is an example of this, as pointed out on twitter, quite a decent little game for the Unexpanded VIC20. Currently, there isn't a .prg file to allow it to be loaded more easily via an SD2IEC, so it's load from tape each time.
The tap file version of this game is essentially a digital copy of the data on the tape. This tap file can be used by emulators, but on real hardware it's not as easy, ideally there would be a prg file that could be loaded. I like a challenge, so I got the tap file for that game and tried it out.
It has a two stage loading process. The first program that loads shows a title screen, then the game itself loaded, you selected the number of players and away you go.
This was probably done to get around the limitations of the machine, the game was designed for an unexpanded VIC20, so there was only 4K to play with, so the code is split in two.
The first part is in BASIC, which shows the introduction screen and loads the second part from tape, with is 4K of machine code.
This code at the start checks where the screen RAM is located (address 0x288 or 648 in decimal), and moves it if necessary. The next few pokes are in the 0x900x range, which is writing directly to the VIC video chip. Writing those values to 36866 (0x9002) and 36869 (0x9005) puts it in 22 column mode, sets the video memory address to to 0x1E00 and selects character set 1. The Poke to 36875 (0x900F) sets the border and background colours to white.
The data at the end of the loader is a small bit of machine code which does the actual loading. The loop at line 200 pokes this into a spare location in RAM and then line 300 runs it.
(C:$0301) d 2a1
.C:02a1  A9 01       LDA #$01
.C:02a3  A2 01       LDX #$01
.C:02a5  A0 01       LDY #$01
.C:02a7  20 BA FF    JSR $FFBA
.C:02aa  A9 00       LDA #$00
.C:02ac  A2 00       LDX #$00
.C:02ae  A0 1E       LDY #$1E
.C:02b0  20 BD FF    JSR $FFBD
.C:02b3  A9 00       LDA #$00
.C:02b5  A2 CE       LDX #$CE
.C:02b7  A0 1F       LDY #$1F
.C:02b9  20 D5 FF    JSR $FFD5
.C:02bc  4C 00 12    JMP $1200
.C:02bf  00          BRK
This is from the monitor in the Vice emulator, which is handy for this sort of thing. The command 'd 2a1' disassembled code starting a 0x02A1 (or 673 in decimal as it was in the BASIC program). The code can be broken into three calls to kernal routines and a jump to run the start of code memory.
.C:02a1  A9 01       LDA #$01
.C:02a3  A2 01       LDX #$01
.C:02a5  A0 01       LDY #$01
.C:02a7  20 BA FF    JSR $FFBA
The three calls all follow the same pattern, load the three registers, A, X and Y with values then call a kernal routine. In this case, this is setting up a load operation, and sets the logical, first and second addresses. Here, they are all set to 1, so loads from cassette.
.C:02b2  A9 07       LDA #$07
.C:02b4  A2 A1       LDX #$A1
.C:02b6  A0 02       LDY #$02
.C:02b8  20 BD FF    JSR $FFBD
The second kernal routine is setting the load filename, in this case, A is 0, so X and Y are ignored, no filename is used, so just LOAD "".
.C:02b3  A9 00       LDA #$00
.C:02b5  A2 CE       LDX #$CE
.C:02b7  A0 1F       LDY #$1F
.C:02b9  20 D5 FF    JSR $FFD5
The third routine uses the previously set information to load from file into RAM. A load address is actually specified (0x1FCE) but it isn't used as the secondary address is set to 1 above, so it uses the load address from the file (just like LOAD"*",8,1 does).
.C:02bc  4C 00 12    JMP $1200
Finally, with the game code loaded, it jumps to the start of that code and runs the game. The address of 0x1200 is the start of the 4K internal RAM of the VIC20, so loading there has actually overwritten the BASIC loader program, which is why these few instructions were placed in a spare area of RAM (02A1-02FF) outside of the main 4K to avoid being overwritten.
That area of RAM is available in all states of the VIC20, unexpanded, 3K, 8K and more. This does run fine on an unexpanded VIC20, and there is no mention on the case or cassette, but the instruction leaflet says it needs +3K RAM. I'm here to tell you it doesn't need +3K RAM, but will run fine if you happen to have it.
The next step was to extract the loaded information. I went back to the .tap file and loaded the loader program into the Vice emulator. This time, I changed the number of bytes to poke from 29 to 26, so it didn't have the JMP $1200 instruction at the end. That meant it loaded the file, then stopped. I then entered the Vice monitor and saved the block of memory it had loaded.
s "tornado" 0 1000 1DFF
This writes the file "tornado" to device 0 (the host file system - use 8 for a disk image if you prefer), from 0x1000 to 0x1DFF, a 3.5K block that is available on both unexpanded and expanded VIC20s, all of which appears to be used. The s command adds the load address prefix to the file in the correct way, so to test this, you can use the following.
That appears to work, but ideally I want to modify the loader to load the data from disk. The loader code specifies device 1, so will still try to load from tape, even if the loader itself is loaded from disk. It's not just a case of changing the device number, a file name needs to be specified, so I have added that to the start of the data statements, so it appears at 0x02A1.
(C:$02d2) m 02a1>C:02a1  54 4f 52 4e  41 44 4f 00   TORNADO.
The code then starts at 0x02A9.
(C:$02d9) d 2a9
.C:02a9  A9 01       LDA #$01
.C:02ab  A2 08       LDX #$08
.C:02ad  A0 01       LDY #$01
.C:02af  20 BA FF    JSR $FFBA
The first function parameters need to be altered, this time to device 8.
.C:02b2  A9 07       LDA #$07
.C:02b4  A2 A1       LDX #$A1
.C:02b6  A0 02       LDY #$02
.C:02b8  20 BD FF    JSR $FFBD
The second function now sets the filename length as 7 characters, and says it is stored at 0x02A1.
.C:02bb  A9 00       LDA #$00
.C:02bd  A2 CE       LDX #$CE
.C:02bf  A0 1F       LDY #$1F
.C:02c1  20 D5 FF    JSR $FFD5
The third function is unchanged, and still has the unused load address.
.C:02c4  A9 01       LDA #$01
.C:02c6  20 C3 FF    JSR $FFC3
I have added a forth function call, this one closes the file after it has been loaded. Those four together are the equivalent of LOAD "TORNADO",8,1.
This modified code was hand assembled and converted into decimal and added to the data statements. What fun I had doing that. The final change was line 300 now runs the code at 0x02A9 (681 in decimal).
The final step was to make a D64 image. I used CBM Transfer to create a new empty D64 and copied the files in, loader first so it would be the default file, then the tornado program itself.
I tested that out in Vice and all was well, so time to try it on some real hardware, loading from an SD2IEC (via the Penultimate Cartridge menu).
I have sent the file to The Future Was 8 Bit (who originally requested it), here is his video of playing it on a real VIC20.