SIDin #1 — Ripping a game: Dig Dug (Stefano Tognon, 2002)
Tognon's walkthrough of turning a 1984 cartridge game into a PSID the hard way — "disassemble the game and give a source for the rip" — followed by his commented source of the Dig Dug music routine, an interpreter of per-voice byte streams that the game re-targets as the player digs, pumps and dies. The composer is not named anywhere: the rip's author field is <?>. Everything here is on dig-dug in detail; this page keeps the method and the numbers.
Key claims#
Why and how (the six steps)#
- The request came from LaLa's Sid Hunt list (lala.c64.org/sid-hunt) in early 2001; the game was "not in HVSC" though Tognon rates it "one of the best old game". He prefers the longest method because "I always like to know how the sound is physically made by the music author", and it is "probably the most indicate when ripping old games, where the music routines were together with the games routines" (hvsc).
- Step 1, get the game: download every image you find — crackers "sometimes … introduce some bugs in the music data", intros contain their own music that confuses the search, and an uncompressed image that starts the game directly can be disassembled as a file instead of a memory snapshot. Dig Dug: the cartridge image, "all the game is within the image without extra stuff".
- Step 2, play it: know when and where music plays; keep a list of executed addresses with the vice monitor's breakpoints.
- Step 3, disassemble: the VICE monitor's
dcommand, or dump memory withsaveand use a tool —sid_disfrom the SIDPLAY docs site (it wants a fake.sidaround the image), or Tognon's ownjc64dis(sf.net/projects/jc64), which disassembles.prg,.sidand.musfiles and "add[s] a comment to the known memory locations", so you can search the listing for "IRQ" instead of "0314" and get a commented source for free. He saved$8000–$BFFFfrom the monitor and fed the file to jc64dis. - Step 4, order from chaos: "about 8300 rows of disassembled code"; first tell code from data (runs of
NOOP/JAM), then search for "Voice", "Freq", "Control"; mark the music code by stripping addresses and hex bytes from used lines and keeping the local labels (the worked example is the control-register writer at$BC07–$BC1A,STA $D404,Y). Find the interrupt setup by looking for$0314–$0319: Dig Dug doesLDA #$E1 / STA $0314 / LDA #$9D / STA $0315 / LDA #$20 / STA $D012 / LDA #$01 / STA $D019 / STA $D01A / LDA $DC0D / CLI— IRQ at$9DE1from the raster at line$20. "Some games uses both CIA and VIC IRQ interrupt and even NMI": stop one source in the monitor and listen. Follow the IRQ into the music routines, then find their data — "as the SID register are not readable, there were some locale storage for some variables related to each voices"; here$BA0Esets a track and the track bytes live around$BCxx–$BDxx; rewrite them as commented.bytelines. - Step 5, a source file: drop the unmodified code, comment every routine, replace addresses with names (
setTrack), add an init part — Tognon's builds both "a prg files runnable from a C64 emulator and a psid files runnable from a sid player" from one source with.ifdef sid. - Step 6, listen: "with as many sidplayer/emulators you know or preferably with the real C64".
Inside Dig Dug#
- "In the game there are 8 pieces of music";
setTracktakes a track number and assigns the track to a voice through a table; a track is "a sequence of bytes that are used as meta instructions": set the volume, ADSR, control register, frequency, note-length duration, pulse-width amplitude, filter mode, filter frequency, resonance, plus flow control — a repeat count and a goto. "The instructions are decoded by a table that points directly to the music routines." A typical note:$80(control$41),$08,$18,$0E(frequency$0E18),$98(duration 6),$88(control$40),$90(duration 2) — "the gate bit is released for some time, but there are parts that never clears the gate bit (using short note lengths instead)". "It is however modular and the code is no so nested with the game code as I expect to found from a so old game." - Decoded from the listing (the instruction byte is 8 × its index in
instTable; low three bits must be zero,$B0ends the track):$00control$11·$08 lo hifrequency (the high byte is also kept in$D8+voice) ·$10 ncontrol n ·$18 lo hipulse width ·$20 ad srADSR ·$28 lo hifilter cutoff ·$30 nresonance (n & $F0, written with the voice-routing bits) ·$38 nvolume (n & $0F, written with the filter-mode nibble) ·$40 nduration n ·$48restart the track from its start ·$50route this voice through the filter ·$58 nfilter mode (n & $F0) ·$60take this voice out of the filter ·$68control$10·$70 lo higoto (repeat A) ·$78 nrepeat A count n+1 ·$80control$41·$88control$40·$90duration 2 ·$98duration 6 ·$A0 nrepeat B count n+1 ·$A8 lo higoto (repeat B). A voice runs instructions until one sets a duration. - Scheduler:
playcounts$F5down; when it reaches zero every active voice whose own counter is zero fetches its next instructions, thenadjustDurationtakes the smallest of the three voice counters as the next wait and subtracts it from the others — the player sleeps between events instead of ticking every frame.setTrackre-bases the counters, zeroes the new voice's and sets$F5 = 1, so a re-assigned track starts on the next call without disturbing the other voices — the mechanism behind "the music in the game are changed dinamically during the games". Voices are addressed throughvoiceIndex: $00, $07, $0E;$FAholds the filter routing bits,$FCthe resonance nibble,$FBthe filter mode and$FDthe volume, so$D417 = $FC + $FAand$D418 = $FB + $FD(filter-programming). - Subtunes (
initmusic, A = 0–7): 0 = tracks 1–3 (a three-voice one-shot: triangle, triangle, pulse$0900); 1–5 = a single voice-3 track (a 21-step frequency run at one frame per step with the gate held, a repeated two-note figure, six low pulse notes with ADSR$4F/$F4, three high triangle chirps repeated three times, six very high two-frame triangle notes); 6 = the default PSID song: track 9 on voice 1 (triangle, ADSR$07/$00, low-pass filter mode$10, cutoff$00/$87, resonance$20) with track 10 on voice 2 (pulse$0800, ADSR$37/$32, routed through the filter, notes in octave pairs —$10C3/$2187,$0FD2/$1FA5,$0E18/$1C31… — six frames on, two off), both looping with$48; 7 (and any other value) = track 11 alone (a looping triangle figure). Track 12 is the silent track (control$00, frequency 0). - The PSID:
.byte "PSID",.word $0200(version 2),.word $7C00(data offset),.word $0000("load address in cbm format" — the data starts with$01,$08), init =initmusic, play =play,.word $0800= 8 songs,.word $0700= start song 7, four zero speed bytes, the strings"Dig Dug","<?>","1984 Datasoft"; the source header reads "copyright 1982 Namco, 1983 Atari, 1984 Datasoft". The.prgbuild adds a BASIC stub (SYS 2061), installs a raster IRQ at line$82through$0314/$0315, forces subtune 6 (lda #$06) and bracketsjsr playwithsta $D020/dec $D020— the border colour as a rastertime meter (sid-format, sid-player-routine). - The rights note: "this code is a my reverse engineering of an existent work that is copyrighted by the respective author/software house … you are not allowed to do any work to this listing that can damage the copyright owner!" Thanks go to Petri Keränen.
Practical takeaways#
- A ripping checklist that still holds: several images, the uncracked one; find the interrupt vector writes; kill one interrupt source to prove which drives the music; keep per-voice shadow variables in mind because the SID cannot be read back; build PSID and PRG from one source; compare in several players and on hardware (sid-format, hvsc, vice).
- The Dig Dug routine is a third player model next to the pattern player and the voice program on sid-player-routine: an instruction stream that writes registers directly, with durations as explicit waits and a next-event scheduler — a game-effects engine that also plays the tunes.
- Two of its idioms are tracker techniques: the voice-2 octave pairs of the in-game tune (octave-bass) and the one-frame-per-step frequency run held under a single gate (pitch-slide-and-portamento).
Notable quotes#
"I always like to know how the sound is physically made by the music author and looking in so deep level is a good way to understand that."
"Even if this kind of player can seems so different from today music player, it is however modular and the code is no so nested with the game code as I expect to found from a so old game."
Relevance to the wiki#
Creates dig-dug (the routine and its instruction set); the ripping method for sid-format and hvsc; a new player model and a table row for sid-player-routine; a 1984 octave bass for octave-bass; vice's monitor as a ripping tool.
Pages touched#
dig-dug · sid-format · hvsc · sid-player-routine · octave-bass · vice · filter-programming · sidin
To verify#
- (unverified) Which subtune plays during which game situation is not stated; that song 7 (the looping two-voice piece) is the in-game walking music is an inference from its being the default and the only looping two-voice one.
- (unverified) No rastertime or code size is given; the border-colour meter in the
.prgbuild is for the reader to measure. - (unverified) The OCR of the listing pages is poor; every byte above was taken from the PDFKit text (
sidin01.txt), whose columns are reliable for the.bytedata but interleave the comments.