In the previous article, the audio hardware of the Defender arcade machine was described in detail. In this article, we will look into the audio software of Defender, 2 KB of MC6800 machine code located in a ROM chip. This code generates all the different sounds heard in the game.
We will disassemble the sound ROM so we can inspect it in assembly code form. The disassembled code with some annotation is available for download.
After disassembling the ROM, we will reassemble it and check if it is exactly the same as the original, verifying the correctness of our disassembly.
The 'Defender' sound ROM (
defend.snd) is 2 KB of MC6800 machine code. It looks like this in hexidecimal form.
Not very readable.
In order to gain some insight into the algorithms contained within it, we need to disassemble it, meaning converting the machine code to equivalent assembly code.
defend.snd using Peter Clare's DASMx.
dasmx.exe -c6800 -o0xf800 -a defend.snd
DASMx produced disassembly output looking like this:
org $F800 ; stx X0F8E ; db $00 ; XF804: clr XCE04 ; db $00 ; clr $01,x clr $03,x ldaa #$FF staa $00,x ...
DASMx has a few syntactic quirks in its output that makes it incompatible with our preferred assembler, 'vasm', in particular the use of the prefixes
L for constants. I had to fix these before I could reassemble it. I wrote this Ruby script to postprocess the code:
lines = File.new("defend.asm").readlines lines.each do |l| match = /^\t(?<mnemonic>[a-z]*)\t(?<operands>.*)/.match(l) if match then mnemonic = match[:mnemonic] operands = match[:operands] if operands == 'X' or operands == 'L' then # unless branch unless mnemonic =~ /^b[^i]/ then operands = '$' end end puts "\t" + mnemonic + "\t" + operands else puts l end end
After running the postprocessing script, I also replaced the data declaration statement
db with the syntax
dc.b using a simple search-and-replace.
After all postprocessing, the assembly code now looked like this:
org $F800 ; stx $0F8E ; dc.b $00 ; XF804: clr $CE04 ; dc.b $00 ; clr $01,x clr $03,x ldaa #$FF staa $00,x ...
Looking more closely at the output, it looked a bit weird. The two
dc.b declarations in the middle of the code didn't really make sense.
However, if the code started with an offset of 1 byte, it looks more reasonable.
So, after some hand editing, I had output that looked like this - starting with a mystery byte
$ff, and then some reasonable startup code:
dc.b $ff ; ??? mystery byte ??? ; F801 RESET, NON-MASKABLE INTERRUPT HANDLER ================= sei ; disable interrupts lds #$007f ; SP = 007f ldx #$0400 clr $01,x ; 0401 = 0 clr $03,x ; 0403 = 0 ldaa #$FF staa $00,x ; 0400 = FF ...
The full hand-edited version is available here.
Now we have a full disassembly, we can try reassembling it to see if the resulting binary is the same as the original
Recreating the ROM and thus verifying our disassembled code requires a MC6800 assembler. This also allows experimenting with modifying the code or creating entirely new replacement ROMs. The open source assembler 'vasm' built with Motorola-style syntax works well for this purpose.
Currently, there isn't a Windows binary download of vasm publicly available, so it is necessary to build it from source. It is distributed with a Makefile, which can be built with Visual Studio's nmake tool. To build it, start the Visual Studio 'Developer Command Prompt' and enter the directory containing the vasm source, and then execute these commands:
makedir obj_win32 nmake -f Makefile.Win32 CPU=6800 SYNTAX=mot
This generates the executable
vasm6800_mot_win32.exe, which can be used to create a ROM file.
We can test the assembler with a simple program:
A single instruction that sets the accumulator register to the value 7.
Assembling it using this command:
vasm6800_mot_win32 -Fbin test.asm -o test.bin
results in a 2 byte file test.bin:
It works. Armed with a MC6800 assembler, we can now try to reassemble our disassembled ROM:
vasm6800_mot_win32 -Fbin defend-hand_edited.asm -o defend-reassembled.snd
The result is what we hope for, a binary that has exactly the same content as the original ROM, which can be verified within reasonable doubt using an MD5 sum:
md5sum defend.snd defend-reassembled.snd ec5b36f80f7bd93ba9e6269f0376efd6 *defend.snd ec5b36f80f7bd93ba9e6269f0376efd6 *defend-reassembled.snd
Now that we have verified our disassembled code, we can start analyzing the sound algorithms in detail.
Before I found DASMx, I tried using Sean Riddle's 6800dasm (mirrored here), but the output of DASMx was easier to convert to code compatible with vasm.