Defender (1981) by Eugene Jarvis and Sam Dicker
This is the source code for the Williams arcade game Defender.
The source code can be assembled into the 11 rom files that would have been loaded onto the arcade cabinet's ROM board. Today, these rom files can be used to play the game in an emulator such as MAME.
- Build Instructions
- Play Defender
- Notes on the Source Code, ROM Files, and the Physical Circuit Boards
sudo apt install build-essentials wine python3 flex
Build the assembler toolchain
First you must run the following to set up the git submodules containing the assembler toolchain:
git submodule init git submodule update
Now you can build the toolchain, as follows:
cd asm6809 ./autogen.sh ./configure make cd .. cd vasm-mirror make CPU=6800 SYNTAX=oldstyle cd ..
To build the Red Label rom files (see below for more information on what these are), do:
These will get written to a directory called
Once you've built defender you can now use the rom files in the
redlabel directory to play defender on MAME. If you're on Ubuntu you can also install MAME with apt:
sudo apt install mame
Notes on the Source Code, ROM Files, and the Physical Circuit Boards
About the source code
The game source code for Defender in src was originally retrieved from https://github.com/historicalsource/defender. It is the Motorola 6809 assembly language source code for the 'Red Label' version of the game.
The source code for the sound module was retrieved from https://github.com/historicalsource/williams-soundroms.
There were four versions of the game released: White Label, Blue Label, Green Label, and Red Label, in that order. Each release was a circuit board with the assembled code split across a number of different ROM chips, also referred to as 'ICs'. This image of the Red Label ROM board from Scott Tunstall's site gives you an idea of what such a board looks like:
If you compare this image to the file listing for the Red Label
roms you'll notice that the missing chip on the board
corresponds to a missing file
[robert@mwenge-desktop defender-redlabel (master)]$ ls -al total 64 drwxrwxr-x 2 robert robert 4096 Jul 11 10:32 . drwxrwxr-x 8 robert robert 4096 Jul 11 11:02 .. -rw-rw-r-- 1 robert robert 2048 Dec 24 1996 defend.1 -rw-rw-r-- 1 robert robert 2048 Dec 24 1996 defend.10 -rw-rw-r-- 1 robert robert 2048 Dec 24 1996 defend.11 -rw-rw-r-- 1 robert robert 2048 Dec 24 1996 defend.12 -rw-rw-r-- 1 robert robert 4096 Dec 24 1996 defend.2 -rw-rw-r-- 1 robert robert 4096 Dec 24 1996 defend.3 -rw-rw-r-- 1 robert robert 2048 Dec 24 1996 defend.4 -rw-rw-r-- 1 robert robert 2048 Dec 24 1996 defend.6 -rw-rw-r-- 1 robert robert 2048 Dec 24 1996 defend.7 -rw-rw-r-- 1 robert robert 2048 Dec 24 1996 defend.8 -rw-rw-r-- 1 robert robert 2048 Dec 24 1996 defend.9
So each file
defend.[x] corresponds to a matching chip on the board and
because there is a missing chip in slot 5 we have no
defend.5 in the rom dump
In the Defender product documentation a
chart lists the part numbers for each chip and confirms that IC5 (i.e.
defend.5) is unused:
When we assemble the Defender source with
make redlabel we create a bunch of
object files and then split them across the 11 files to match the 11 in the Red
Label ROM dump listing above.
Eugene Jarvis left a slightly cryptic note to how we assemble the source in
TO ASSEMBLE THE DEFENDER MESS RASM PHR2,DEFA2,DEFB2,AMODE0;-X (ELSE CREF SYMBOL OVERFLOW) RASM PHR2,SAMEXPA7 RASM PHR2,DEFA2,DEFB2 TO GET THE DIAGS, CHAIN ALL.CF LOAD IT ALL AND THEN PRAY IT WORKS (NOTE: BEWARE OF ORDER OF LOADING LOOK OUT FOR THE SELECTED BLOCK SHIT DR J. 1/21/81
RASM assembler is no longer available to us we use
asm6809 instead. Fortunately this
does a good job of assembling the source faithfully and only very minor modifications to the source files are
required to produce binaries. We recreate the steps in Eugene's notes as follows:
# Build amode1 # The equivalent of: RASM PHR2,DEFA2,DEFB2,AMODE0;-X (ELSE CREF SYMBOL OVERFLOW) ./asm6809/src/asm6809 -B src/phr6.src src/defa7.src src/defb6.src src/amode1.src\ -l bin/defa7-defb6-amode1.lst -o bin/defa7-defb6-amode1.o
This is the main game code with the attract mode module assembled in.
phr6.src is a file containing definitions,
defb6/src contain the main game code;
amode1.src contains the attract mode code.
We also have to build a version of this game code without the attract mode module:
# Build defa7 and defb6 # The equivalent of: RASM PHR2,DEFA2,DEFB2 ./asm6809/src/asm6809 -B src/phr6.src src/defa7.src src/defb6.src\ -l bin/defa7-defb6.lst -o bin/defa7-defb6.o
The final module the notes mention is
samexpa7 a bunch of explosion routines added by Sam Dicker:
# Build samexamp # The equivalent of: RASM PHR2,SAMEXPA7 ./asm6809/src/asm6809 -B src/phr6.src src/samexap7.src\ -l bin/samexap7.lst -o bin/samexap7.o
Eugene's notes are much less clear on how we go about assembling the reamining source files:
mess0.src blk71.src romf8.src romc0.src romc8.src
However the way he lists them in the notes turns out to provide a clue to the order in which they shoud be assembled:
PHR6.SRC 636 LINES DEFA7.SRC 3,375 LINES DEFB6.SRC 2,252 LINES AMODE1.SRC 1,310 LINES BLK71.SRC 723 LINES SAMEXAP7.SRC 382 LINES MESS0.SRC 955 LINES ROMF8.SRC 692 LINES ROMC0.SRC 925 LINES ROMC8.SRC 839 LINES
We assemble the last four together in the order that they appear above:
# Build roms ./asm6809/src/asm6809 -B src/mess0.src src/romf8.src src/romc0.src src/romc8.src\ -l bin/roms.lst -o bin/roms.o
And we assemble
blk71.src by itself:
# Build blk71 ./asm6809/src/asm6809 -B src/blk71.src -l bin/blk71.lst -o bin/blk71.o
ROM Part Table with Corresponding Assembled Object Files
This table shows how the contents of each ROM chip relates back to the assembled code.
|ROM Chip||Part Number||File Name||Build Binary||Start Position in Build Binary||End Position in Build Binary|
Replicating this arrangement of the binaries is achieved by
ChainFilesToRom.py in the
project's Makefile. It's a simple python script that extracts the relevant segments from each of the
binaries built in the
bin folder when you run
Changes required for the source to assemble
There were a few modifications to the source required along the way to get this to work.
- Replacing macro arguments to make them compatible
-NAPP MACRO \0,\1 - LDA #\0 - LDX #\1 +NAPP MACRO \1,\2 + LDA #\1 + LDX #\2 JMP SLEEPP ENDM
- Replacing the use of
$in label names, e.g.:
-INIT$V EQU HOFV+2 -HALLDV EQU INIT$V+2 +INITSSV EQU HOFV+2 +HALLDV EQU INITSSV+2
- Replacing the use of '.' in label names, e.g.:
- LDY #.P1SCR ;START WITH PLAYER 1 + LDY #P1SCR ;START WITH PLAYER 1
- Work around the fact that
RASMseems to have allowed you to assemble code sections into overlapping memory segments. For example both
defa7.srcwant to assemble into position
$C000in the ROM, meaning that one will overwrite the other. This explains why the main game files get assembled twice, once with attract mode (
amode1.src) and once without: they wanted a binary with some segments overwritten with attract mode features and one without. We achieve this ourselves by modifying the source to assemble and place the attract mode code to position
$2000in memory, and when we later split the object files into the
defend.xfiles pick the chunk of code we're interested in:
@@ -111,6 +111,8 @@ YSHIP EQU $5000 AMTYPE EQU 0 ORG $C000 + PUT $2000 + JMP HALLOF ;VECTORS JMP SCNR
amode1.srcI had to replace a few hard-coded constant values to match what was in the binary:
diff --git a/src/amode1.src b/src/amode1.src index 543cb7e..cdaeb1e 100755 --- a/src/amode1.src +++ b/src/amode1.src @@ -145,19 +147,19 @@ HALL1 STA PNUMB ;PLAYER NUMBER HALL1A JSR P2SW HALL1B LDB #$85 ;LIGHT BLUE LETTERS STB PCRAM+1 - LDA #$FE ;TODAYS SOUND - PHANTOM + LDA #$3E ;TODAYS SOUND - PHANTOM LDX #THSTAB ;TODAYS TOP SCORE JSR CPXCY ;COMPARE BHS HALL2 ;NOT THE BEST? - LDA #$FD ;HIGH SCORE SOUND - TOCCATA + LDA #$3D ;HIGH SCORE SOUND - TOCCATA HALL2 LDX #$CC02 ;SOUND PIAS - LDB #$FF ;CLEAR LINES + LDB #$3F ;CLEAR LINES JSR STBXBV - LDB #$E4 ;SELECT ORGAN + LDB #$24 ;SELECT ORGAN JSR STBXBV -HALL3 DECB DELAY +HALL3 DECB ;DELAY BNE HALL3 - LDB #$FF ;CLEAR LINES + LDB #$3F ;CLEAR LINES JSR STBXBV TFR A,B JSR STBXBV ;PLAY SOUND
- Update the bit-shift and bit-wise comparison notation to be compatible with syntax expected by
- LDX #WCURS!.$C35A ;CONFUSION - LDD #WCURS!.$3CA5 ;MORE CONFUSION + LDX #WCURS&$C35A ;CONFUSION + LDD #WCURS&$3CA5 ;MORE CONFUSION @@ -1206,7 +1208,7 @@ MT1 LDD BGL ;CALC SCANL LDA STATUS BITA #2 ;NO TERRAIN??? BNE MTX ;NONE - LDA #SCANER!>8 + LDA #SCANER>>8 LDY #STETAB ;ERASE TABLE
- Comment out some directives not used by
index d2d7212..86d4bf0 100755 --- a/src/blk71.src +++ b/src/blk71.src @@ -1,6 +1,6 @@ - TTL D E F E N D E R 1.0 - NMLIST - NOGEN +* TTL D E F E N D E R 1.0 +* NMLIST +* NOGEN
- This one may be worth investingating further. The code references
P1LASwhen it needs to be
P1LATto match the Red Label binaries. Would be interesting to know if this sheds any light on the version of the source code as it looks very like a typo that was bug-fixed.
@@ -831,14 +831,14 @@ CSCX RTS *DISPLAY LASERS * LDISP PSHS D,X,Y,U ;PLAYER 1 - LDX #P1LAS - LDA .P1LAS + LDX #P1LAT ;Fixme was: #P1LAS + LDA P1LAS BSR LDSP LDA PLRCNT DECA BEQ LDPX - LDX #P2LAS - LDA .P2LAS + LDX #P2LAT + LDA P2LAS BSR LDSP
- I needed to modify the
KILOmacros to assembly properly with
asm6809. Removing the addition of
$to the argument in the macro itself and instead adding it before passing the argument:
-KILP MACRO \0,\1 +KILP MACRO \1,\2 JSR KILPOS - FDB $\0 FDB \1 + FDB \2 ENDM -KILO MACRO \0,\1 +KILO MACRO \1,\2 JSR KILOS - FDB $\0 FDB \1 + FDB \2 ENDM index 33665f5..3f4c5ac 100755 --- a/src/defb6.src +++ b/src/defb6.src @@ -73,13 +73,13 @@ UFONV4 ADDA #10 CLRB LDA XTEMP+1 ADDD PLAYV - ASRA DIVIDE ;BY 2 + ASRA ; DIVIDE BY 2 RORB STD OYV,X UFONVX RTS *UFOKILL UFOKIL DEC UFOCNT - KILP 0120,UFHSND + KILP $0120,UFHSND RTS *
- Replace some single-quotes with double-quotes for compatibility with
@@ -780,21 +780,21 @@ A28 FCC "SPECIAL ;FUNCTION/" * DEFAULT HERE FOR NOW * DEFALT FCB $02,$12,$70 ;CRHSTD - FCC 'DRJ' + FCC "DRJ" FCB $01,$83,$15 ;CRHST1 - FCC 'SAM' + FCC "SAM" FCB $01,$59,$20 ;THSTD2 - FCC 'LED' + FCC "LED"
blk71.srchas to be assembled with 6309 extensions and
STX [,Y]has to be changed to the `COMF' instruction in order to get it to match with the ROM binaries.
./src/blk71.src:191: TTER03 COMF ;Was: STX [,Y] ;FAST OUTPUT
- In a few cases I've had to replace the values of constants in the code to match the ROM binaries. These are:
./src/romc0.src:78: FDB $D9FF ;Fixme was: $FFFF ./src/defb6.src:2170: FDB $0000,$00FE,$C300 ; Fixme: $C300 was $6600 ./src/mess0.src:68: FDB $5BFF ;Fixme was: $FFFF ./src/mess0.src:654: FDB $84FF ;Fixme was: $FFFF