realdmx: Rob Hubbard's driver — McSweeney's Monty on the Run disassembly and the Commando title

author Rob Hubbard (the driver and both tunes); Anthony McSweeney (original disassembly and commenting of the Monty file); dmx87 (ACME port; the Commando file carries no attribution beyond the repository)
date 2023-06-24
article Hubbard_Rob_Monty_on_the_Run.asm (1431 lines, header: 'This player was used (with small modifications) for his first approx. 30 tunes') and Hubbard_Rob_Commando.asm (850 lines, 'The classic Commando Title music by Rob Hubbard'); both assemble to .sid files. Dates are the repository's (last push 2023-06-24); the tunes are 1985 Gremlin Graphics / 1985 Elite
created 2026-08-30 · updated 2026-08-30

Two files from the realdmx collection (s-realdmx-sid-players) hold the 6502 driver rob-hubbard used, per the header, "(with small modifications) for his first approx. 30 tunes": Anthony McSweeney's commented disassembly of monty-on-the-run (1985, Gremlin Graphics), and a separately labelled disassembly of the commando title (1985, Elite). Together they show what a "Hubbard routine" is: about 1 KB of code, a 3-byte note format, 8-byte instruments, and three flag-bit effects — drum, "skydive" and octave arpeggio — that Newman's article only names (s-newman-driving-the-sid-chip). The page hubbard-player carries the cheat sheet.

Key claims#

Provenance and shape

  • Monty header: "Rob Hubbard Music Driver / Original disassembly and commenting by Anthony McSweeney / Port to ACME by dmx87 / This player was used (with small modifications) for his first approx. 30 tunes". PSID: one song, load $8000, init and play are jmp initmusic / jmp playmusic. Commando: load $1000, init/play likewise, one song; the file names note constants (C0 = $00 … H7 = $5f, German H for B), an INSTR macro and flag names — the reverse-engineer's conveniences, not Hubbard's.
  • Commando's variable labels carry their original addresses (posoff … ; 14a2), so the player code plus its 192-byte note table occupies $1000–$14a1 = 1186 bytes — about 1 KB of code, matching Newman's "900–1000 bytes" for McSweeney's disassembly (s-newman-driving-the-sid-chip).
  • The play routine runs once per frame and loops over the three voices (ldx #2 … dex, register offsets $00,$07,$0e). A tempo counter (resetspd = $01 in Monty; Commando reads it from a per-song table, songtempos !by 2) decides on which frames new note data is fetched; the effects (vibrato, pulse, portamento, drum, skydive, arpeggio) run every frame. So a "tick" in Monty is 2 frames and in Commando 3 frames, and note lengths are counted in ticks.

Song → tracks → patterns (orderlist)

  • init takes the song number, multiplies by 6 and copies three track pointers (lo/hi) from songs; a track is a list of pattern numbers; $ff restarts the track from its first entry, $fe stops the music. Pattern pointers sit in two tables (patptl/patpth). Monty: 60 patterns (ptn00–ptn3b, $2e empty), tracks of 65, 77 and 129 entries; Commando: 32 patterns.
  • A pattern is a byte stream of notes, each 1–3 bytes (McSweeney's header comment): Byte 1 — bits 0–4 the length in ticks (0–31), bit 5 "no release", bit 6 "retrig off", bit 7 "new instr/porta coming"; Byte 2 (only when bit 7 is set) — the instrument number, or a portamento speed if negative; Byte 3 — the note number ($00 = C-0 … $5f = B-7, a 96-entry frequency table $0116 … $fd2e). $ff ends the pattern.
  • Read from the code: bit 6 makes a one-byte entry that extends the current note by its length without fetching a new note; the instrument registers are re-written with the gate bit masked off (and appendfl — "clever eh?"), so the tail plays in the release phase, and with bit 5 also set the ADSR bytes are left alone. This is how notes longer than 32 ticks are written: ptn12: $bf,$0c,$56, $7f,$7f,$7f,$7f,$7f,$7f,$7f = instrument $0c, note $56, 32 ticks, then seven 32-tick extensions. Without bit 5 the end of a note clears the gate and writes $00 to AD and SR — the note is cut with the fastest release (hard-restart).
  • Portamento is a pattern-level, per-note command: a second byte with bit 7 set, bits 1–6 the amount added to the 16-bit frequency every frame, bit 0 the direction (clear = up, set = down — the Commando file's constants PBU = $81/PBD = $80 name them the other way round; the code adds when bit 0 is clear). Monty's middle section (ptn30–ptn33) is full of them: $af,$90,$35 (32 ticks, bend up 16/frame, note $35), $a7,$99,$37 (down 24/frame), $a3,$e8,$35 (up 104/frame) — Hubbard's "new pitch bend code" for this game (pitch-slide-and-portamento; the quote is in s-newman-driving-the-sid-chip).

Instruments — 8 bytes (instrument-tables): 0 pulse width lo, 1 pulse width hi, 2 control register (waveform + gate), 3 attack/decay, 4 sustain/release, 5 vibrato depth, 6 pulse-modulation byte, 7 effect flags. Monty has 20, Commando 13. Rows (Monty): $80,$09,$41,$48,$60,$03,$81,$00 (pulse lead with vibrato and PWM); $00,$08,$81,$02,$08,$00,$00,$01 (noise drum); $00,$09,$41,$3f,$ff,$01,$e7,$02 ; first skydive; $00,$08,$41,$08,$50,$02,$00,$04 (pulse with octave arpeggio); $00,$02,$81,$09,$09,$00,$00,$05 (drum + arpeggio); $00,$00,$11,$0a,$fa,$00,$00,$05 (triangle drum + arpeggio). Commando names two: $52,$01,$41,$09,$9f,$00,$16,$08 ; bass and $00,$02,$81,$0a,$09,$00,$00,$05 ; snare IARP | IDRUM.

The effects, as coded

  • Vibrato (byte 5 ≠ 0; only for notes of 8+ ticks in Monty, 6+ in Commando): the frame counter and #7, mirrored above 3, gives the sequence 0,1,2,3,3,2,1,0 ("this is clever!!"); the step is the frequency difference to the next semitone shifted right depth+1 times; the frequency written is the note plus step × sequence value — an 8-frame triangle that only bends upwards from the note. Depth $02 = steps of ⅛ semitone, peak ⅜ (vibrato).
  • Pulse (byte 6): bits 0–4 a delay in frames between steps, bits 5–7 the step ($20, $40, $60 …) added to the pulse width; the direction flips when the high byte reaches $0e going up or $08 going down — every Hubbard pulse sweep lives between $0800 and $0eff, and the routine modifies the instrument's own bytes 0–1 in place (pulse-width-modulation). Commando adds flag $08 (IPWL, "pulse width lo modulation instead of pulse"): byte 6 is added to the low byte every frame, wrapping — used by its bass ($16 per frame).
  • Drum (flag $01): McSweeney's comment — "Drum timbre depends on ctrl register value for instrument. If ctrl is 0, it's all noise, otherwise ctrl waveform and noise after 1st vblank. Fast attack/decay and a rapid downwards frequency slide creates this sound". The code writes noise ($80) on the first tick and then, every frame, the instrument's waveform with the gate cleared while decrementing the frequency high byte. This is the "tell-tale" Hubbard drum (wavetable-programming).
  • Skydive (flag $02): "The famous Hubbard signature effect — a long slide down from note to zero": every second frame the frequency high byte is decremented until it is 0. In the Commando file the same flag increments the high byte by 2 every second frame (for notes of 3+ ticks) — an upward sweep until the byte wraps; the file still calls it skydive.
  • Octave arpeggio (flag $04): "Simple octave arpeggio, also a signature sound" — odd frames play note + 12, even frames the note, i.e. a 25 Hz octave trill (chord-arpeggio, octave-bass). Flags combine: $05 = drum + arpeggio (Commando's snare), and the Commando file's comment on IARP: "octave arp, combine with ISKY for classic effect".
  • Order of processing each frame: note fetch (or gate-off check) → vibrato → pulse → portamento → drum → skydive → arpeggio; later effects overwrite the frequency written by earlier ones.

Practical takeaways#

  • To make a "Hubbard drum" in a tracker: one frame of noise, then the instrument's waveform with the gate off, pitch falling by one frequency-high-byte unit per frame — an absolute-pitch wave table, as on wavetable-programming and monty-on-the-run.
  • To make a Hubbard lead: pulse $41, pulse width between $0800 and $0e00, vibrato of an eighth-semitone step over an 8-frame triangle, delayed until the note is long enough.
  • Long skydive notes are "note + release": gate on for 32 ticks, then extensions with the gate off and a long release (SR = $ff), while the skydive keeps decrementing.
  • The whole feature set fits in 1 KB because the effects are per-instrument bits, not per-note tables — the reason later trackers' "player types" exist (sid-player-routine).

Notable quotes#

  • "Rob Hubbard Music Driver … This player was used (with small modifications) for his first approx. 30 tunes" (Monty header).
  • "A clever little trick to create an oscillating value" (vibrato); "The famous Hubbard signature effect — A long slide down from note to zero" (skydive); "Simple octave arpeggio, also a signature sound" (McSweeney's comments).

Relevance to the wiki#

Primary source for hubbard-player; verifies from code the claims Newman and SIDwave make about the driver on rob-hubbard (McSweeney's disassembly, the drum, the "Monty player used in almost 30 games"); gives monty-on-the-run and commando their actual data; adds a fourth column to the driver comparison on sid-player-routine.

Pages touched#

hubbard-player · rob-hubbard · monty-on-the-run · commando · sid-player-routine · sidid · instrument-tables · wavetable-programming · vibrato · pulse-width-modulation · pitch-slide-and-portamento · chord-arpeggio · octave-bass · hard-restart · orderlist

source file wiki/summaries/s-realdmx-hubbard-player.md · graph