loading...
All Defender articles | Back to top

Defender Sound ROM Disassembly

Defender Production Line (picture from http://www.robotron2084guidebook.com/)

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.

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.

I disassembled 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 X and 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[0] == 'X' or operands[0] == 'L' then
            # unless branch
            unless mnemonic =~ /^b[^i]/ then
                operands[0] = '$'
            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 defend.snd.

Reassembly

Defender Production Line (picture from http://www.robotron2084guidebook.com/)

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:

    ldaa #7

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:

86 07

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.

References

Motorola 6800:

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.