| annotations | ||
| disassembly | ||
| docs | ||
| extracted | ||
| survey | ||
| swiftlink | ||
| tools | ||
| .gitattributes | ||
| .gitignore | ||
| Modem_Wars.pdf | ||
| Modem_Wars_1988_Electronic_Arts_b4.d64 | ||
| Modem_Wars_SwiftLink.d64 | ||
| README.md | ||
Modem Wars (Commodore 64, 1988) - annotated disassembly
A complete, reassemblable, commented disassembly of Dan Bunten's Modem Wars (Ozark Softscape /
Electronic Arts, 1988) produced from the disk image Modem_Wars_1988_Electronic_Arts_b4.d64.
docs/overview.md- how the disk is organised, how the game boots, the fast-loader protocol, the sector cipher, the run-time memory map and the overlay system. Start here.docs/knowledgeBase.md- every routine and data block with its purpose (generated from the survey pass of the annotation work).docs/titleScreen.png,docs/screenshots/- the loader's title picture decoded straight from the disk bytes, and screenshots taken by driving the game in VICE (extracted/vice/playSession.pyboots it on a private X display and feeds it real key events).docs/track1_DanBuntenLetter.txt- the letter to pirates that is hidden in plain text on track 1.disassembly/- the sources:boot/- the two directory files (ea,load), the C128 boot sector and its CBM80 stub, and the title-picture data;drive/- the code that runs inside the 1541 (bootstrap sector and fast loader / cipher);game/- the game itself: main program, every overlay variant, data blocks;c64.inc,kernal.inc,drive1541.inc,zeropage.inc,drive_zp.inc- symbol files;XREF.txt- cross reference (callers, readers, writers) for every label and zero-page location;INDEX.txt- one line per source file with its address range;verify.sh- reassembles every file withca65/ld65and compares it byte for byte with the bytes extracted from the disk.
extracted/- raw and decrypted sectors, the directory files, the reconstructed memory images and the VICE automation used to validate them.annotations/- the comment/label database (JSON) thattools/build.pymerges into the sources.tools/- the disassembler pipeline:build.py- rebuilds the whole ofdisassembly/from the decrypted sectors plusannotations/;m6502.py/discover.py- instruction decoder, recursive-descent tracer, and the discovery pass that follows interrupt vectors and self-modified jump tables into code the tracer alone cannot reach;render.py- turns a traced memory image into annotated ca65 source (bit art for graphics, grids for tables, per-file table of contents);findStrings.py- finds the bit-7-terminated message strings still sitting in data areas;chunks.py,mergeSurvey.py,extractAssets.py,hwSymbols.py- supporting tools;phase2_annotate.js,phase3_finish.js- the multi-agent passes that produced the comments.
The listing is never edited by hand. Everything a human contributed lives in annotations/*.json
(labels, comments, data types, zero-page names) and tools/build.py regenerates disassembly/ from
the decrypted sectors plus those annotations, so the sources can always be reproduced from the disk.
What is in the disassembly
24 source files, 56,000 lines, covering 74,741 bytes: the C128 boot sector and its C64 cartridge stub, both directory files, the code that runs inside the 1541 drive, the main program, every overlay variant, and all of the data. About 65% of those bytes are code and the rest data.
- Every one of the 21,627 instructions carries a comment explaining what it is for, not what the mnemonic does.
- Every routine has a header giving its purpose, its inputs, its outputs and its callers, and every source file opens with a table of contents.
- All 164 zero-page locations the game uses are named and described, as are the work-RAM variables, the unit record arrays and the hardware registers.
- Data is rendered as what it is: character and sprite graphics as bit art, tables as rows of the right width, strings as text, jump tables as address lists.
verify.shreassembles all 24 files withca65/ld65and compares each against the bytes taken off the disk. All 24 match exactly, so nothing in the listing is guesswork about the bytes.
Rebuilding and verifying
python3 tools/build.py # regenerate disassembly/ from extracted/ + annotations/
./disassembly/verify.sh # reassemble everything and compare with the original bytes
Requires Python 3 and the cc65 tool chain (ca65, ld65).
Conventions used in the listings
- Every instruction line ends with
; XXXX- its address - followed by the comment. - Labels are camelCase;
sub_XXXX/L_XXXX/D_XXXXare auto-generated names for code and data that has not (yet) been given a meaningful name. - Labels are scoped to a source file. The
$6F00-$87FFand$E000-$EFFFregions exist in two variants each; when code in one file calls into such a region the name of the variant that really contains code at that address is used, and the other variant's name is mentioned in the comment. - Strings end with a character whose bit 7 is set and are written as
.byte "TEXT",'T'|$80. - Hardware registers use the names from
c64.inc(VIC_*,SID_*,CIA1_*,CIA2_*).
SwiftLink driver
swiftlink/ holds an alternate opponent module that drives a SwiftLink cartridge (a 6551 ACIA on
the expansion port) instead of the shipped module's bit-banged user-port UART, so the game can play
over a modem or a null-modem cable at up to 38400 baud instead of 300/1200. The shipped module is
layered and only its bottom layer is hardware specific, so the packet layer, the ARQ frame layer, the
ring buffers and the link state machine are left byte-for-byte alone: 434 of the module's 4096 bytes
changed, every routine kept its address, and a SwiftLink player and a stock-driver player can still
play each other at a shared rate.
./swiftlink/build.sh assembles the module and writes Modem_Wars_SwiftLink.d64, a copy of the game
disk that differs from the original in four sectors. See swiftlink/README.md for the hardware it
expects, the baud and hot-key tables, the direct-connect mode, the address-by-address map of what
changed, and an honest account of what the VICE testing did and did not prove.