David Whittaker's player (the Panther driver)
The 1986 driver of david-whittaker as reverse-engineered by dmx87 from Panther (Mastertronic): a three-voice player without an instrument table — the timbre of a voice is whatever the last command prefixes in its pattern set (waveform, ADSR, pulse bounce, vibrato, slide, drum), kept in a 36-byte voice struct (source: s-realdmx-fredgray-kimmel-whittaker). It is not the Lazy Jones routine that sidin #2 analysed — that one is raw two-voice frequency lists with equal note lengths and no effects (sources: s-realdmx-fredgray-kimmel-whittaker, s-sidin02-matt-gray-driller).
Facts#
$9000–$9cff, 3328 bytes: engine and structs ≈ 1.7 KB, note table and arpeggios 267 bytes, tracks 354, patterns 1022. Unlabelled bytes afterinitare a sub-tune selector (6 bytes per tune), so the original driver served several tunes per game (source: s-realdmx-fredgray-kimmel-whittaker).- Once per frame: the sequencer every
SongTempoframes (7 by default, changeable in the pattern), the effects every frame; every frame writes frequency, pulse, AD, SR and control, gate off then on at note starts — silence is made withSR = 0, not with the gate (hard-restart). SIDId'sDavid_Whittakersignature is that gate write (sources: s-realdmx-fredgray-kimmel-whittaker, s-sidid-player-list).
Cheat sheet#
All from the Panther disassembly (source: s-realdmx-fredgray-kimmel-whittaker).
Track = list of pattern addresses, !wo 0 loops it; the three tracks have different lengths (53 / 65 / 53 entries). Pattern = byte stream; FLAGS is cleared at every note, so arpeggio, slide and drum must be re-issued per note.
| byte | meaning |
|---|---|
$00–$7f | note (index into a 95-entry table, $0116 = C-0); ends the step |
$80–$93 | command (below) |
$b8–$bf | tempo = value − $b7 (1–8 frames per tick) |
$c0–$cc | arpeggio list 0–12 |
$d0 aa ss | ADSR follows (two bytes) |
$e0–$ff | duration = value − $df (1–32 ticks) |
| command | effect |
|---|---|
$80 | hold the current note for another duration (no retrigger) |
$81 / $82 | vibrato off / on |
$83 v | volume / mode byte |
$84 lo,hi,delay / $85 | slide down / up: after delay frames the step accumulates every frame (a quadratic sweep) |
$86 / $87 | bend down / up one semitone per tick |
$88 | end of pattern |
$89 speed,depth | vibrato parameters |
$8a / $8b / $8c / $8d | waveform noise / pulse / sawtooth / triangle |
$8e | drum: on odd frames control $80 (noise) with the frequency high byte + $31 |
$8f n | pulse width high byte |
$90 min,max,speed | pulse bounce between two high bytes |
$91 | stop music |
$92 / $93 | triangle + ring modulation / pulse + sync |
Arpeggio lists (one step per frame; any byte ≥ $54 restarts): $00,$03,$07,$88 · $00,$04,$07,$88 · $00,$03,$07,$0c,$88 · $00,$04,$07,$0c,$88 · $07,$0c,$0f,$88 · $07,$0c,$10,$88 · $03,$07,$0c,$88 · $04,$07,$0c,$88 · $00,$0c,$88 · $00,$04,$88 · $00,$03,$88 · $00,$05,$88 · $00,$07,$88 (chord-arpeggio).
"Instruments" as they appear in the data: PC_PULSE,$90,$02,$08,$28,$d0,$0a,$40 (pulse bouncing between $02xx and $08xx at speed $28, AD $0a, SR $40); $92,$90,$02,$08,$14,$89,$7f,$fe,$82,$d0,$df,$00,$ff,$84,$80,$00,$01 (ring-triangle, pulse bounce, deep vibrato, slide down — a siren); $8d,$89,$28,$50,$82,$d0,$0d,$70 / $f7,$c0,$3c,$e7,$c1,$3a (triangle, vibrato, then 24 ticks of a minor arpeggio on $3c and 8 of a major one on $3a); $be,$d0,$00,$00,$ff,$80,$88 (tempo 7, ADSR $00/$00, 32 ticks, hold — a silent pattern).
The Lazy Jones routine (1984)#
sidin #2's reverse-engineering of the hvsc rip of Lazy Jones (Terminal Software, 1984) — a different and far smaller routine than the Panther driver above, "the most simple I had seen until now" in Tognon's words (source: s-sidin02-whittaker-lazy-jones):
- Data: 20 pieces of
$80bytes; a step is 4 bytes — frequency low/high for voice 1, then for voice 2 (voice 3 is never used) — so a piece is 32 steps, looped when the byte index reaches$80. A zero low byte skips that voice for the step (its registers are left alone); the rests inTune17are$00,$00,$00,$00rows. Tune 1 is the exception: for song 0initpatches the loop compare to$00, so the index wraps after 256 bytes and tunes 10 and 11, consecutive in memory, play back to back. - Timing: one step every
$0Ccalls (DELAY); every note has the same length, there is no duration byte. - One sound, no effects:
setADwrites$D418 = $1Fand attack/decay$66to$D405and$D40C;initEnginewrites$D416 = $FFand$D417 = $03; sustain/release is never written (0). A new note is frequency low, frequency high, then control$20and immediately$21— sawtooth with the gate dropped and raised in the same call, restarting the attack/decay envelope (adsr-envelope). - Per call: on eleven of twelve frames only
dec DELAY; on the twelfth the writes above for two voices and the pointer/index update, thenjmp $EA31. Rastertime: at most 4 raster lines when picking a new note, 1 otherwise (sid-player-routine). - The PSID: 21 songs (tune 1 = 10 + 11, plus the 20 pieces), a header typed as
.byte "PSID",.word $0200(v2), data offset$7C, load and init at the listing'sinit, play atIRQ, "lazy Jones / David Whittaker / 1984 Terminal Software"; variablesMUSIC,INDEX,DELAY,PATTL/Hat$03E0–$03E5, the running pointer in$B4/$B5. The rip lacks the sound effects and came from a cracked version: a faulty, unused track (labelwrong) follows tune 21 (sid-format). - In the notes: Tognon's annotation of tune 10 has voice 1 alternating
H1/H2(B in two octaves, German naming) on every 12-frame step under the melody of voice 2 — an octave-bass in 1984. His verdict on where the magic comes from: "I think from the music notes themselves!"
Related#
david-whittaker · hubbard-player · galway-player · sid-player-routine · sidin · sidid · pulse-width-modulation · pitch-slide-and-portamento · chord-arpeggio
Sources#
s-realdmx-fredgray-kimmel-whittaker · s-sidin02-matt-gray-driller · s-sidid-player-list · s-sidin02-whittaker-lazy-jones