A Decent Disassembly
The first step in any porting project is to get a good listing of the program to work on. Since this is a reverse engineering exercise this involves acquiring a copy of the original program and disassemling it.
These are the tools I used. I'm doing this under Windows, not sure if all these or equivalent tools are availeble under other OSes.
- trsread - reads TRS80 disk images
- z80disasm - disassembler for TRS80 .CMD file
- readcmd - dumps records from TRS80 .CMD files
- YAZD - My Z80 disassembler
- binhex - Tool to convert binary to hex and vice versa
- bin2tap - Tool to generate Microbee .tap files from a .bin file
- z80asm and Original Here - An open source Z80 cross-assembler
- Cygwin - for make and gawk
- Sublime Text - awesome text editor
Getting a Copy of the Original Program
So first of all I needed a copy of the program which I found on a TRS80 disk image here:
Next I created a directory for all the working files placed a copy the dsk image there. TRS80 disk based programs are stored as .CMD files, so I needed to extract that from the downloaded disk image. The easiest way to do that is with trsread
. First I listed the disk's directory:
Z:\retro\meteor>trsread -v favourites1_80sssd_jv1.DSK
ATTACKFC/CMD 13,824
GAMMON/BAS 8,448
HISCORES/ATK 256
METEOR/CMD 12,057
PENETRAT/CMD 20,836
SCARFMAN/CMD 7,296
SEADRAG/CMD 14,720
STRTREK2/BAS 13,696
SWAMP/CMD 5,376
TAIPAN/BAS 13,440
Free: 84,480 bytes
And then extracted it:
Z:\retro\meteor>trsread -e favourites1_80sssd_jv1.DSK meteor.cmd
Extracting the Raw Program from the CMD file
CMD files are not a raw binary image of the program - they have an internal format that splits the image up into pages defined by a set of records. I needed a way to extract the raw content of the image. There's probably a few ways to do this, but the easiest way I've found is with z80disasm
which understands the CMD file format.
Z:\retro\meteor>z80disasm meteor.cmd > meteor.disasm
The other thing I needed was the entry point (ie: where it starts executing) which I found with readcmd
:
Z:\retro\meteor>readcmd meteor.cmd
which reports the entry point as:
Reading 02 block length = 2.
Entry point is 36366 8e0e
8e0e is the hex address of the program's entry point. (I've since realized this is also available in the last line of meteor.disasm)
I now had a disassemabled listing of the program and knew the entry point. Unfortunately z80disasm isn't that great of a disassembler. YAZD does a much better job of creating cross references and following code paths to isolate code from data. Unforunately YAZD doesn't understand CMD files, so I needed to jump through a few hoops - namely extracting the hex column of the disasseambly, converting that back to a raw binary file and the re-disassemling with YAZD:
Here's what part of the disassbly from z80disasm looked like:
00392 M5FEE EQU 5FEEH
00393 M5FF5 EQU 5FF5H
00394 M5FFB EQU 5FFBH
00395 ORG 6000H
6000 F3 00396 DI s
6001 AF 00397 XOR A /
6002 D3FF 00398 OUT 0FFH,A S.
6004 3E38 00399 LD A,'8' >8
6006 D3EC 00400 OUT 0ECH,A Sl
6008 310080 00401 LD SP,M8000 1..
600B AF 00402 XOR A /
600C 32F74C 00403 LD (M4CF7),A 2wL
600F CD7C4B 00404 CALL M4B7C M.K
What I needed was the second column of hex digits - the raw binary code of the program. I used a text editor to trim everything from the top/bottom of the file then ran it through gawk
:
Z:\retro\meteor2>gawk '{print substr($0,6,8)}' meteor.disasm > meteor.hex
Then converted it back to a raw binary
Z:\retro\meteor2>binhex meteor.hex meteor.bin
Disassembly with YAZD
Finally we can run the raw binary through YAZD:
Z:\retro\meteor2>yazd meteor.bin --addr:0x6000 --entry:0x8e0e --xref --lst --mwr > meteor.yazd.disasm
To explain these options:
- meteor.bin - the file we want to disassamble in raw binary form
- --addr:0x6000 - the file is intended to be loaded at 0x6000 so YAZD needs to know this
- --entry:0x8e0e - the entry point to the program. YAZD will follow all code paths from here to isolate code from data.
- --xref - create cross references (useful for working things out)
- --lst - create full listing including original raw binary data
- --mwr - mark word references (handy for locating literal values that might be addresses and may need to be manually fixed up later)
At this point I should have had a complete, cross-referenced disassembly of the program but looking at the file showed almost all of it as raw data DB bytes. After a little investigation I noticed a tiny bit of code at the end of the file that was relocating the whole thing to a different memory address and jumping to a new entry point. I suspect this was the result of the TRS80 LMOFFSET program that's used to relocate cassette programs to make them work under LDOS. (There was other code in this stub at the end, but I'm assuming it's all related to relocating the program and I decided to ignore it for now)
; --- START PROC L8E00 ---
8E00: 21 00 60 L8E00: LD HL,6000h ; address or value?
8E03: 11 00 45 LD DE,4500h ; address or value?
8E06: 01 00 2E LD BC,2E00h ; address or value?
8E09: ED B0 LDIR
8E0B: C3 00 45 JP 4500h
Given that 0x6000h was getting shoved down to 0x4500, I just re-disassembled with the new memory location. Note the new entry point too - to match the jump above.
Z:\retro\meteor2>yazd meteor.bin --addr:0x4500 --entry:0x4500 --xref --lst --mwr > meteor.yazd.disasm
Finally, a decent disassembly and now the real work can begin.