
Way back in 1985, I created games on the ZX Spectrum/Timex and CBM-64. A friend and I set up a small software house, and in addition to creating our own games, we also (and more lucratively) converted games for other publishers from CBM-64 to other formats. During this period, I wrote several original games in Z80 and 6502 assembler. I kept their sources on 5 1/4" floppy disks, but after a few years I lost the floppy drive that could read the disks, and they were shoved in a cupboard. Somewhere between house moves, I lost the disks for all time. Fast-forward to this past December. In a store, I spotted a cheap game console for roughly $100 (get a look at this absolute unit, sold under the brand name "RetroPi"). It came with 18,000 games for various old computers and consoles, including SNES, ZX Spectrum and CBM-64. The hardware was a Raspberry PI clone in a case, and included Nintendo-type game controllers, along with HDMI and USB power cables. Given my history, this device interested me a great deal, so I explored further. The games were on a 16 GB SDHC card; the system was a RetrOrangePi (you can read the full specs of the hardware and emulators) running Armbian, a Debian derivative. (The entire software stack is on Github, if you’re curious.) Browsing through the CBM-64 and ZX Spectrum menus, I discovered three of my Spectrum games and one for CBM-64. Not knowing much about the hardware or OS, I thought I'd investigate and see if I could fetch the game binaries and take a stab at disassembling them. Now, I've used Raspberry Pi before. I also know that SD/SDHC cards are quite easy to corrupt and render non-bootable. So before taking any further steps, I made a backup copy. The open-source Win32DiskImager is very good for that purpose, and with a hardware adaptor to read the card, it took 20 minutes to read and create a 15 GB file on disk, then another 20 minutes to burn a backup card from the image (a backup SDHC card costs less than $5 and comes with a SD card adapter). At this point, I put on my amateur computer forensic hat. Sure, the games were somewhere in the 15 GB disk image—but what was the format? The disk was neither encrypted nor compressed. The Linux .img format was a file/folder dump in a file, and a bit of searching on the web revealed that Disk Internals would do the trick of reading it. Disk Internals is a Windows GUI freeware program that can read many Linux disk formats; it's also rather good at reading Windows disks, so use with care. Ten minutes of delving uncovered a folder with the various ROM collections in it (home\pi\RetroPie\roms\), and I not only found my four files, but I was able to export them as binary files. These files were between 27 KB and 45 KB, smaller than all but the smallest .jpg files! (Compare that to the huge games of today; a title like “Red Dead Redemption 2” might hit 100 GB in size.)
Disassembling
Both 6502 and Z80 CPUs are still popular, and there are a considerable number of assemblers and disassemblers available (many open-source, but also a few commercial ones). 6502.org is a good place to start, and I also found a half-decent free Z80 disassembler. One of my Z80 games disassembled to over 21,000 lines; it will take a while to manually add labels and make the code readable. Currently, the disassembly looks like this (Z80):0240 21c4fa ld hl,0fac4h 0243 0632 ld b,32h 0245 110300 ld de,0003h 0248 7e ld a,(hl) 0249 e620 and 20h 024b 77 ld (hl),a 024c 23 inc hl 024d 3600 ld (hl),00h 024f 19 add hl,de 0250 10f6 djnz 0248h 0252 c9 ret ...The 6502 disassembly isn't much easier, though:
dex txs ldy #$00 L086b dec $fd dec $0874 L0870 lda ($fc),y sta $ffff,y dey bne L0870 ...Younger developers, remember: We used to write this stuff by hand! Many of the disassembled lines contain graphics, text or data tables. After 1,500 lines of Z80, there are 30 Z80 jumps (jp) like this. Why is that?
17a0 c31299 jp 9912h 17a3 c3ee99 jp 99eeh 17a6 c3d79d jp 9dd7h 17a9 c3c39e jp 9ec3h 17ac c36e9f jp 9f6eh 17af c3809f jp 9f80h ...That was something I did because my original development system (A Tatung Einstein running CP/M) only had about 48K RAM. The crude editor we used maxed out at a couple of thousand lines at most (Typically 40KB RAM limit!), so the source was split into several files, each starting with a jump table. There was no linker back then, so each file’s code was loaded into memory at a fixed address. To call a routine in File B from File A, you had a list of routine addresses in File A, each pointing to a jump table entry in File B. That table had 30 jumps to the actual routines. That way, you could change the code in File B without needing any other changes. No matter how the address of the routines moved in File B, the jump table stayed at the same address.