Dig Dug (C64, Datasoft 1984) — the music routine
The C64 cartridge conversion of Namco's arcade game ("copyright 1982 Namco, 1983 Atari, 1984 Datasoft"), whose music Stefano Tognon ripped into a PSID for hvsc in 2001–2002 and documented in sidin #1 with a commented source. The composer is not named: the game credits nobody and the rip's author field is <?> (source: s-sidin01-dig-dug-rip). What makes it worth a page is the routine — an interpreter of per-voice byte streams that writes SID registers directly, waits explicit durations and can be re-targeted voice by voice while the game runs, so "the music in the game are changed dinamically during the games".
Facts#
- Eight pieces of music, selected by the value in the accumulator at
initmusic(0–7, any other value = 7); the PSID has 8 songs and starts on song 7. The game's IRQ is a raster interrupt at line$20(vector$9DE1); the track-setting routine sat at$BA0Eand the track data around$BCxx–$BDxxin the cartridge image ($8000–$BFFF) (source: s-sidin01-dig-dug-rip). - Fourteen track slots (
tracks) mapped to voices by a table (voices: $00,$01,$02,$02,$02,$02,$02,$02,$00,$01,$00,$00,$01,$02); track 12 is the silent track (control$00, frequency 0) (source: s-sidin01-dig-dug-rip). - Subtune contents (read from the data): song 0 = tracks 1–3, a three-voice one-shot — voice 1 triangle (ADSR
$0F/$00, notes$430F $5983 $70C7 $430F $4B45, four frames each), voice 2 triangle ($2CC1 $12D1 $1FA5 $191E $25A2), voice 3 pulse$0900(ADSR$0C/$00, six frames on, two off); songs 1–5 = one voice-3 track each: a 21-step rising frequency run at one frame per step under a held gate (pulse$0800, ADSR$00/$F0), a repeated two-note figure, six low pulse notes with ADSR$4F/$F4($0368 … $0393), three high triangle chirps played three times (ADSR$2C/$C2), six very high two-frame triangle notes ($861E … $B306); song 6 = tracks 9 + 10, the looping two-voice piece: voice 1 triangle (ADSR$07/$00) with the filter set to low-pass ($10), cutoff$00/$87and resonance$20, voice 2 pulse$0800(ADSR$37/$32) routed through the filter and playing every note as an octave pair —$10C3then$2187,$0FD2/$1FA5,$0EEF/$1DDF,$0E18/$1C31,$0D4E/$1A9C(four times),$0C8F/$191E— six frames on, two off (octave-bass, filter-programming); song 7 = track 11 alone, a looping triangle figure (ADSR$07/$60) (source: s-sidin01-dig-dug-rip). - PSID: version 2, load address 0 (the data starts with its own
$01,$08), initinitmusic, playplay, 8 songs, start song 7, speed 0 (vertical blank), "Dig Dug" / "<?>" / "1984 Datasoft"; the same source assembles withoutsiddefined to a.prgwith aSYS 2061stub, a raster IRQ at line$82and the border colour toggled aroundjsr playas a rastertime meter (source: s-sidin01-dig-dug-rip; sid-format).
The music routine#
The model, from Tognon's listing (source: s-sidin01-dig-dug-rip):
- A track is an instruction stream.
playNextreads a byte, rejects anything with its low three bits set or above$B0, treats$B0as the end (the voice goes inactive with a duration of$7F), and otherwise jumps throughinstTable— the byte is 8 × the routine's index. Instructions execute one after another in the same call until one sets a duration; the duration is the voice's wait in play calls. - The player sleeps.
playdecrements one global counter ($F5); at zero it lets every active voice with a zero counter fetch instructions, thenadjustDurationtakes the smallest of the three voice counters as the next wait and subtracts it from the others — a next-event scheduler rather than a per-frame tick. Voices are addressed byvoiceIndex: $00, $07, $0E; a busy flag ($D0) guards re-entry. - Re-targeting a voice.
setTrack(A = track slot) looks up the voice, stores the track address as the voice's pointer and as its restart address ($EC/$ED), adds the pending wait back to all counters, zeroes the new voice's and sets$F5 = 1, so the new track starts on the next call while the other voices keep time — the game's dynamic music. Because the SID cannot be read back, the frequency high byte is also kept per voice in$D8+voice. - Filter and volume are shared state.
$FA= the voice-routing bits,$FC= the resonance nibble,$FB= the filter-mode nibble,$FD= the volume nibble;$D417is always written as$FC + $FAand$D418as$FB + $FD, so any voice can change one nibble without disturbing the rest (filter-programming). - Repeats. Two independent counters per voice (A in
$D2+voice, B in$D5+voice): "set repeat n" stores n + 1, "goto" decrements and jumps back while the count is not zero, so a block bracketed by$78,$02 … $70,lo,hiplays three times;$48restarts the whole track (the looping tunes end with$90, $48, $B0).
Instruction set#
Byte values decoded from instTable; "voice" is the voice the track is assigned to (source: s-sidin01-dig-dug-rip).
| byte | operands | effect |
|---|---|---|
$00 | — | control register = $11 (triangle, gate on) |
$08 | lo, hi | frequency (also saved in $D8+voice) |
$10 | n | control register = n |
$18 | lo, hi | pulse width |
$20 | ad, sr | attack/decay, sustain/release |
$28 | lo, hi | filter cutoff $D415/$D416 |
$30 | n | resonance nibble n & $F0 → $D417 with the routing bits |
$38 | n | volume nibble n & $0F → $D418 with the filter-mode nibble |
$40 | n | duration n calls — ends the step |
$48 | — | restart the track from its first byte |
$50 | — | route this voice through the filter (set its bit in $FA) |
$58 | n | filter mode nibble n & $F0 → $D418 |
$60 | — | take this voice out of the filter |
$68 | — | control register = $10 (triangle, gate off) |
$70 | lo, hi | goto lo/hi while repeat counter A > 0 |
$78 | n | repeat counter A = n + 1 |
$80 | — | control register = $41 (pulse, gate on) |
$88 | — | control register = $40 (pulse, gate off) |
$90 | — | duration 2 — ends the step |
$98 | — | duration 6 — ends the step |
$A0 | n | repeat counter B = n + 1 |
$A8 | lo, hi | goto lo/hi while repeat counter B > 0 |
$B0 | — | end of track (voice inactive) |
A typical note in the data: $80 / $08,$18,$0E / $98 / $88 / $90 — pulse on, frequency $0E18, six calls, gate off, two calls. The triangle tracks write gate-off and gate-on in the same call before the next frequency ($68 / $00 / $08,lo,hi / $40,$04), so the envelope restarts on every note; the sweep track never releases the gate and uses one-call durations instead (adsr-envelope, pitch-slide-and-portamento).
Related#
sid-player-routine · sid-format · hvsc · octave-bass · filter-programming · whittaker-player · vice · sidin
Sources#
To verify#
- (unverified) Which song accompanies which game situation (digging, pumping, dying, the start jingle) is not stated; song 6 is presumably the walking music, being the default and the only looping two-voice piece.
- (unverified) Code size and rastertime are not given; the
.prgbuild's border-colour toggle is there to measure them.