realdmx: Martin Galway's Arkanoid — Tognon's SIDin #4 reverse-engineering, with the 'sample' synthesis through $D418

author Martin Galway (player and music, 1986 Imagine — 'all copyright for this code remains to Martin Galway'); reverse-engineered by Stefano Tognon for SIDin #4; ACME conversion by dmx87
date 2023-06-24
article Galway_Martin_Arkanoid.asm, 5483 lines / 162 KB: an RSID with 20 songs (two tunes with 'samples', six jingles, twelve sound effects). The date is the repository's last push; SIDin #4 is dated 2003-06-21 (its own date line; Tognon's prose is summarised in s-sidin04-galway-arkanoid)
created 2026-08-30 · updated 2026-08-31

The full Arkanoid (Imagine, 1986) music and sound-effect program of martin-galway, reverse-engineered by Stefano Tognon for sidin #4 and converted to ACME by dmx87 (s-realdmx-sid-players). It is the later, larger sibling of the Rambo loader player (s-realdmx-galway-rambo-player): the same per-voice byte-code with subroutines and loops and the same piecewise-linear modulation engine, now with a filter envelope, two engine rates, a sound-effect bank — and the famous "digitized samples", which the code shows to be synthesized, not sampled: six routines that wiggle the $D418 volume nibble with busy-wait delay tables. Cheat sheet on galway-player.

Key claims#

Provenance and shape

  • Header: "Arkanoid reverse enginnering source / all copyright for this code remains to Martin Galway / Reverse Engineered by Stefano Tognon (SIDIn Magazine #4) / Converted to ACME by dmx87 / The classic loader tune was a conversion from Martin's previous work on Cobra for the ZX Spectrum / One of the first, if not the first, game music featuring digitized samples."
  • RSID, not PSID: init at $0801, play $0000, 20 songs. Init installs its own IRQ on CIA timer A$49F0 for songs 0–1, $24F8 for the rest — and for the two sample tunes never returns: the foreground loops in SLoop running the sample generator while the IRQ plays the music. On a PAL clock the two timer values are about 52 Hz and 104 Hz (inference from the values), so the jingles and effects run at roughly twice the frame rate — a 2× player in 1986 (multispeed).
  • Song map (read from the code): 0–1 the title/loader and in-game tunes with the sample track, 2–7 six jingles, 8–19 twelve sound effects stored as 31-byte blocks (three per effect, one per voice) at $2000.
  • Sizes (counted from the listing): player code ≈ 4.5 KB (core ≈ 3.6 KB — everything exists three times, once per voice — plus init, dispatch and the ≈ 0.5 KB sample engine), music ≈ 2.5 KB (1889 bytes of voice programs + 582 bytes of sample patterns), sound effects 1116 bytes, tables ≈ 0.5 KB. The whole thing spans $0801 to about $4454 with gaps.

A program per voice (the file's own format block, verbatim)

xx=00..5F play note xx for time from table(nn)
xx=60..BF play note xx-60h for time nn
C0 RTS · C2 lo hi JSR · C4 lo hi JMP · C6 ht lo hi JSRT (1,3) · CA id va SET · CC nn FOR · CE NEXT
D2 nn lo hi SETNI · D4 lo hi INSTR · D6 nn va SETCI · D8 lo hi EXCT (3) · DC id v1 v2 SET2I (2,3)
DE id v1 v2 SET2CI · E0 LF3 (3) · E2 lo hi FILTA (3) · F0 lo hi SETFI
  • A track-table entry is three voice-program pointers plus a minimum note duration; setTracks rebuilds a 32-entry duration table as multiples of it, and a note's second byte is a 1-based index into that table ($1F,$20 = note $1F for 32 × min); notes written + $60 carry a raw tick count instead; $5F is a rest. Each voice has an 8-level stack for JSR/FOR; JSRT calls with a transpose ($F4 = −12); JMP makes endless tunes (tune2_voice1 ends in JMP t2v1_) and lets voices 2 and 3 of tune 6 jump into voice 1's stream after a staggered rest — one pattern played by three voices as a delayed echo (fake-echo). EXCT runs native code from the song (two tiny routines that flip the filter-mode nibble). The comments say "instruction", never "macro".
  • Transposed reuse, verbatim: sub06: FOR 2 / $00,$02 / $00,$02 / $0C,$02 / $0C,$02 / NEXT / RTS then sub07: JSRT $1F sub06 / JSRT $1D sub06 / JSRT $1B sub06 / JSRT $1A sub06 / RTS (arrangement).
  • Seven instructions edit the instrument — including the one that is sounding: tune1_voice1 starts SETNI $1C ins01 (load a whole instrument), plays, then SET2CI $0C,$80,$07 and SET2CI $06,$3C,$00 turn the held note $1F into a 128-tick upward slide of $003C per tick, then JSRT $F4 sub01 (an octave down), SET $1A,$8D (change the SR byte), SETCI $1B,$01 — annotated by Tognon "(??????????????????)".

Instrument = 29 bytes, one generic engine (instrument-tables): $00–$07 four signed 16-bit frequency adders, $08–$0B their durations in ticks, $0C initial delay, $0D flags ($80 reload from the note's frequency after stage 4, $01 reload the stages but keep the current frequency, $02 apply adder 4 every tick during the delay, $08 arpeggio mode — voice 2 only); $0E/$0F two pulse-width stage lengths, $10 delay, $11 flags, $12–$15 two 16-bit pulse adders, $16/$17 initial width; $18 control byte, $19 AD, $1A SR; $1B gate-off time (with the test bit set in the control byte: release when the remaining duration drops below it, else that many ticks after note-on; $FF never); $1C a countdown after gate-off at whose end the voice's seven registers are zeroed — Tognon's comment: "; hard restart" (hard-restart). The test bit in $18 is a mode flag, never written to the SID; a separate $08 write precedes every note.

  • ins01 (the lead of the title tune): $14,$00 / $EC,$FF / $14,$00 / $00,$00 adders, $03,$06 / $03,$00 cycles, $1E,$05 delay/flags, $32,$32 / $0A,$05 pulse cycles and delay/flags, $0A,$00 / $F6,$FF pulse adders, $00,$08 width, $41 "control to rectangular", $14,$C8 AD/SR, $FF,$FA — after 30 ticks a vibrato of +20 × 3, −20 × 6, +20 × 3 ticks, repeating, over a pulse sweep of ±10 × 50 ticks around $0800 (vibrato, pulse-width-modulation). ins02: $19 "control to triangular + test bit", AD/SR $A4,$F9, gate-off $14, wipe $FE. ins0B: $49 "rectangular and test bit on", AD/SR $06,$98, gate-off $05, wipe $1E. Other control bytes seen: $29 (sawtooth + test), $11, $15 (an effect: triangle + ring + sync).
  • Arpeggio (voice 2 only): a 4-byte table read backwards, the instrument bytes $08–$0B repurposed as pointer, base note and index: arp39: $0C,$00,$03,$09, arp37: $0C,$00,$03,$07, arp5b: $0C,$00,$05,$0B, arp59: $0C,$00,$05,$09 — octave, root, third, sixth/seventh (chord-arpeggio).
  • Filter: bound to one voice (voiceNumber) and retriggered with a 16-byte cutoff envelope on every note of that voice — fil02: $4D,$01 / $D3,$FF / $FB,$FF / $FF,$FF / $03,$14 / $0A,$32 / $00 / $04 / $01 / $00 = cutoff up by $014D for 3 ticks, then down by $2D × 20, $05 × 10, $01 × 50 (a filter "pluck"); LF3 = "max resonance/filter on voice 3" ($D417 = $F4) and "low pass filter" ($D418 = $1F) in one byte; the mode nibble is kept in TEMP so it survives the sample writes (filter-programming).
  • Sound effects: 31-byte blocks = a 29-byte instrument plus a raw SID frequency, pushed straight into the modulation engine with no note — an effect is an instrument with its own pitch program.

The "samples" (chip-samples, sid)

  • There is no PCM data in the file. Six routines PSample1–6 each run a loop: busy-wait delayTab[Y] iterations, add a constant to a 4-bit accumulator in $DE ($98, $65, $DD, $DD, $0E, $01) and write its low nibble to the master volume register $D418, for X steps ($14, $19, $0C …) per delay value, Y counting the delay table down from the end: delayTabP3: $20,$10,$08,$04,$02,$01 — each stage twice as slow, a falling drum; delayTabP1: $40,$1E,$3E,$19,$3C,$14. Steps of +8 make a square, +5 / −3 scramble all 16 levels (noise-like), −2 an 8-step falling saw, +1 a 16-step rising saw (inference from the constants). Galway's own description — "wiggling the volume register … the flatulence stuff that shipped in 'Arkanoid'" (s-sidmusic-galway-interviews) and Newman's "synthesized the drums as a series of farts and burps" (s-newman-driving-the-sid-chip) — is exactly this code.
  • The sample track has its own language, whose opcodes are 6502 opcodes: 81–86 xx play sample n then wait xx ticks, 87 xx nothing, 20 lo hi JSR, 40 NEXT, 60 RTS, 49 xx FOR, 4C lo hi JMP — with its own 8-level stack. Sample_Tune2: $87,$01 / FOR 7 / $85,$18 / NEXT / $85,$0C / $85,$06 / $85,$06 / FOR 6 / $85,$02 / $85,$16 / NEXT / $82,$02 / $82,$16 …, looping with JMP. The IRQ only schedules; the foreground plays, so the CPU is fully used and the music IRQ jitters the percussion. After each hit: lda #$0F ; turn the volume to max / ora TEMP ; turn filter to the store value / sta $D418. The $D418 operand is masked to $9418 ($DD18 in PSample1, the routine SIDin prints — source: s-sidin04-galway-arkanoid) while dormant and "unmask"-ed on entry — purpose not stated.

Practical takeaways#

  • Galway's 1986 percussion is a volume-register waveform synth: a handful of bytes of code and a 6-entry delay table per drum, no memory for samples. The equivalent today is a 4-bit sample played through $D418 — but the original had no sample at all.
  • The modulation engine is the same as the Rambo loader's: vibrato = three adders (+, −, +) with durations, PWM = two adders, filter = four adders; the arpeggio is a 4-note stack including the octave. Instruments are edited by the song while they sound, so a "patch change" can be one byte.
  • Two timer rates in one player: run the effects at 2× when the CPU is free, at 1× when the sample synthesis needs the foreground.

Notable quotes#

  • "One of the first, if not the first, game music featuring digitized samples." (header — the code shows synthesis, not digitizing.)
  • "; hard restart" (Tognon, on the timed voice wipe); "Excecute a code gave by paramethers" (on EXCT); "SETCI instr (??????????????????)".

Relevance to the wiki#

Primary source, with s-realdmx-galway-rambo-player, for galway-player; settles what "samples" in Arkanoid means on martin-galway and chip-samples; the earliest 2×-rate player in the wiki (multispeed); a filter-envelope-per-note ancestor for filter-programming; the sample-track language and the sound-effect bank for sid-player-routine; the SIDin #4 listing for sidin.

Pages touched#

galway-player · martin-galway · sidin · sid-player-routine · chip-samples · multispeed · filter-programming · instrument-tables · vibrato · pulse-width-modulation · chord-arpeggio · hard-restart · fake-echo · arrangement · sid · sound-effects

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