SIDin #4 — Martin Galway's Arkanoid music routine, the explanation (Stefano Tognon, 2003)

author Stefano Tognon (ice00), SIDin magazine
date 2003-06-21
article SIDin issue 4 (version 1.00, 2003-06-21), pp. 23–98: 'Martin Galway's Arkanoid music routine' — prose sections Songs, Tracks/Patterns, Instructions/Notes, Note and Duration, Instrument Table, Frequencies Timbre, Rectangular Wave Timbre, Filter Table, Other Instrument Effects, Game Sound Effects, Sample (pp. 23–32), the commented source (pp. 32–98) and Conclusion (p. 98). The source is the same code as raw/github-realdmx/Galway_Martin_Arkanoid.asm, already summarised in s-realdmx-galway-arkanoid-player; this page covers Tognon's explanations and consults the listing only to check values
created 2026-08-30 · updated 2026-08-31

Tognon's prose around his reverse-engineering of arkanoid — the explanation of the galway-player that the code alone does not give: why he picked this tune, what he thinks of the code, which instructions exist on which voice, the instrument table and its running copy, the four-phase adder engine that makes vibrato, portamento, pulse sweeps and filter envelopes from the same idea, how sound effects bypass the streams, and why the "samples" were, in his reading, deliberately hidden. The listing itself is covered by s-realdmx-galway-arkanoid-player.

Key claims#

Why Arkanoid, and the verdict on the code

  • Chosen "as the tune contains the new sample music system in it"; what struck him was "a self modified code to mask the use of volume register for sample, and the use of 6502 JMP/JSR opcode as music pattern … I think that understand the sample routine at that time using the available tools was very difficult due to the use of all this kind of masked code."
  • "each voice has its code that are perfectly equal each other, so it may be coded using some indexed addressing mode for reducing size … However, as all effects are coded into tables, this made the Galway engine very powerful: maybe having left each voice separate could be a Martin choice for not complicating more the engine."
  • Some instructions exist for one voice only: "this seems me that Martin had added some instruction when needed: maybe other version of the engine has other implemented instructions. Otherwise, he removed the code not used by one voice for reducing memory size."

Songs, streams, notes

  • A song is three voice-stream pointers and one byte, "the minim duration used for a note" ($09 in his example; the listing's eight tunes use $03, $04, $07, $09, $0B and $0D); raising it lengthens only the notes timed through the table.
  • "there are not the division in tracks and patterns … there are other methods to take advantage of the use of patterns: subroutine call" — "programming a track is like creating a music program using a high level language"; an approach "very powerful if it is compiled by hand, but too complicated by an editor program". The mnemonics are his: "maybe Martin should had used directly the hex value instead" (sid-player-routine, orderlist).
  • His instruction table, with the voices each exists on: 00–5F nn note with table duration (5F rest) and 60–BF nn note − $60 with the duration in the byte (BF rest) — all voices; C0 RTS ("Also used for terminating a track"), C2 lo hi JSR, C4 lo hi JMP, CA id va SET ("all offset of the table can be used"), CC nn FOR, CE NEXT, D2 nn lo hi SETNI ("the first nn item of the instrument table"), D4 lo hi INSTR ("5 instrument parameters … control/ADSR"), D6 nn va SETCI, DE id v1 v2 SET2CI, F0 lo hi SETFI (copies the frequency program to offsets $00–$0D; he counts "13 values") — all voices; C6 ht lo hi JSRT (call with ht halftones added) — voices 1 and 3; DC id v1 v2 SET2I — voices 2 and 3; D8 lo hi EXCT, E0 LF3 ("low filter on voice 3 with max resonance"), E2 lo hi FILTA — voice 3 only.
  • "the note 12h is equivalent to note 72h" (with an immediate duration); "something like 6 octaves … so the max notes to use is 5Eh (BEh)".

The instrument (29 bytes) and its running copy (34 bytes) (instrument-tables)

  • Definition $00–$1C: $00–$07 four signed frequency adders ("low/high to add in each cycle of phase 1–4"), $08–$0B the cycle counts, $0C initial delay, $0D flag; $0E–$0F two "wave (pulsation amplitude)" — pulse-width — cycle counts, $10 delay, $11 flag, $12–$15 two pulse adders, $16–$17 the pulse width; $18 control register, $19 AD, $1A SR; $1B "Duration before apply release ($FF means not apply release) [?]"; $1C "Duration before an hard restart (after the end of event of previous 1Bh register)" — the table says "0 means no effect to apply [?]", the prose "A $FF value means no hard restart". "not all the meaning of this flags I was able to understand … try to found by yourself the answers :(" (hard-restart).
  • The running copy ($00–$22) is what SETCI/SET2CI edit: the same bytes to $17, then the current frequency at $18–$19, control $1A, the two durations $1B–$1C, and the live counters — $1D–$20 "number of cycles of phase 1–4" for frequency, $21–$22 for the two pulse phases; the definition's $08–$0B/$0E–$0F are the values "to reload" into them.
  • "The main part of the instruments table is the AD/SR (19h-1Ah offsets), control (18h offset), and wave pitch for rectangular waveform (16h-17h offsets) values (as usual). All the other values are for pitch control".
  • Test bit in the control byte: "it is tested if 1Bh table value is below current duration before apply a release (even if test bit is selected, the effective control putted into sid register is the one without the test bit)".
  • Voice 2 only: "if bit 3 is 1, other task are performed: very important task for the timbre as it heavy used into tune 1" — the arpeggio mode identified from the code in s-realdmx-galway-arkanoid-player (chord-arpeggio).

Frequency timbre: four phases (vibrato, pitch-slide-and-portamento)

  • In each phase the value the SID is producing gets the phase's 16-bit adder added every cycle for the phase's count; "the added value are in two complement logic, so you can produce even a subtraction"; $0C delays the first phase; the flag "must be different from 0 for having the frequency timbre engaged".
  • The flag, from his table (xyyy yyyyz): x (bit 7) = 1 → "reload the freq. cycles with freq. value of instrument table again" (restart from the note's stored frequency); z (bit 0) = 1 → "reload the freq. cycles but use the actual frequency value"; y = 1 → "continue with actual freq., no more cycle". His prose says "bit 1" for the last case; the listing tests and #$81 at the end of phase 4 and and #$02 only during the initial delay, where bit 1 applies adder 4 every tick — the slide-into-the-note reading of the realdmx summary.
  • Vibrato: $14,$00 / $EC,$FF / $14,$00 / $00,$00 / $03,$06 / $03,$00 / $1E,$05 — "after an initial delay of 1Eh cycles, there are 3 cycles where 14h is added to currently frequencies, then for 6 cycles -14h is added (and so subtracted), finally for 3 cycles 14h is added. Now, the cycles can restart (05h) continuing with the actual frequencies." His figure shows "why cycle 2° is double longer (this is necessary for having the correct up/down sequence)" — up from the centre, across to the bottom, back to the centre.
  • Portamento: $01,$00 / $00,$00 / $00,$00 / $00,$00 / $35,$00 / $00,$00 / $00,$08 — "a long up portamento with little frequency increment": +1 per cycle for $35 cycles, then (flag $08, no bit 7 or 0) hold. "Just changing the phases, you can reach very complex frequencies tasks: look at the source for more examples of real Martin use of the phases."

Rectangular wave timbre: two phases (pulse-width-modulation)

  • Same logic with two phases and the same three flag cases (bit 7 restart from the stored width, bit 0 restart from the current one, other bits hold). "a classical up/down pulse amplitude effect": $32,$32 / $14,$05 / $0A,$00 / $F6,$FF — 50 cycles of +10, 50 of −10, after a 20-cycle delay, looping from the current width.

Filter table: 16 bytes (filter-programming)

  • "The Martin engine have a very complex filter manipulation": $00–$07 four cutoff adders, $08–$0B cycles, $0C delay, $0D flag (same three cases), $0E "Filter low value (8 bit)", $0F "Filter high value (3 bit)" — "analogue to what we can achieve with frequencies table". The Arkanoid table: $4D,$01 / $D3,$FF / $FB,$FF / $FF,$FF / $03,$14,$0A,$32 / $00 / $04 / $01,$00. Loaded by FILTA; "even the FL3 (that set the resonance of filter to max) and some calling to EXCT of custom code are used for better controlling the filter generation".

Game sound effects

  • "a sound effect is activated by compiling an effect instrument table, and then activating the same methods (makeTimbreVx) used by the engine. The sound instruments table is a perfect copy of the instrument table, but with two extra bytes at the end: the low/high value of the frequency to use." No note, no duration byte — "the duration is taken according by the timbre effect the instrument table is executing"; "all the 3 voices can be used for generating the sound effect (and else, this is the way used in the game)".

Sample (chip-samples)

  • "If you are thinking that sample generation used by Martin is the 'reproduction of sample' … you are not in the right way. If you were a ripper, maybe you know … the PSID specific extension that were introduced for managing this kind of 'sample'." Six procedures each "reproduce a particular timbre" by volume variation, called by "a flow instruction control": 81–86 xx play sample 1–6 for xx, 87 xx nothing, 20 lo hi JSR, 40 NEXT, 60 RTS, 49 xx FOR, 4C lo hi JMP — "exactly the same opcodes as 6510 instructions … This can be a coincidence, or maybe a way to make the part related to sample generation a bit harder to understand". The pointer tables "are located in two area very far", the six routines "into very sparse memory area" with self-modifying code — "maybe some suspicious of a way to hidden the sample generation seems to be present into Martin engine" (or "a memory restriction of the game").
  • PSample1, printed in full: ldy #$05 (six delays, read from the end), ldx #$0C repeats per delay, a busy loop counting delayTabP1[y] down, $DE incremented by 1, and #$0F, sta $DD18 — whose operand is unmasked to $D418 on entry (lda #$94 / clc / adc #$40 / sta Vol1+2) and masked again on exit (lda #$DD; the other five routines store $94). "Each call to the Psample1 routine will generate a sample sound witch timbre is governed by two parameters: a repeat value and a table of delays" — the volume climbs and "restart from low level when the max level is reached"; the tables contain "5 or more delay values" (delayTabP1: $40,$1E,$3E,$19,$3C,$14; delayTabP3: $20,$10,$08,$04,$02,$01).
  • Why it sounds different in a player: "Now you probably should be able to understand why the Arkanoid tune will play so different into a sidplayer (with extended sid register) instead of the real machine (or today RSID rip). PSID extended register try to emulated this kind of sound generation that is quite an unusual way for common samples sound." (sid-format)
  • The engine (p. 98): playSample counts $DC down, reads the stream at ($DA); a byte ≥ $80 is a routine index ($DF) followed by a duration, anything else is looked up in operTable ($20,$40,$60,$49,$4C) and dispatched; after a hit lda #$0F / ora TEMP / sta $D418 — "turn the volume to max / turn filter to the store value".
  • Zero page, from the listing's comments (p. 33): 8-deep call stacks (lo, hi, repeat count) at $31/$39/$40 for voice 1, $A9/$B1/$B9 voice 2, $61/$69/$71 voice 3, $78/$80/$89 the sample stream; $DA–$DB sample stream pointer, $DC its duration, $DD its stack index, $DF routine index; $E0–$E5 voice stream pointers, $E6–$E8 note durations, $E9–$EB stack indexes, $EC–$EE "halftone to add to current note"; $F1–$F6 pulse width, $F7–$FC frequency, $FD–$FE table pointer, $FF current note.

Conclusion (p. 98): "It is a very clean rip, where the ripper had manage a complete code initialization that take order especially into sound effects generation. However in the rip there other part of Martin Engine (a voice 1 and 2 part) … probably they are part of the relocation of the code into upper memory." The header: RSID version 2, 20 songs, init $0801, "Arkanoid / Martin Galway / 1986 Imagine".

Practical takeaways#

  • One mechanism, three sizes: a phase is a signed 16-bit adder and a cycle count; pitch gets four phases, pulse width two, cutoff four; each program has a delay and a flag that says what happens at the end — restart from the stored value (bit 7), restart from where you are (bit 0), or hold (anything else). Vibrato = 3/6/3 cycles of +/−/+; portamento = one phase and hold; a pulse sweep = two phases looping; a filter pluck = a fast rise and three slowing falls.
  • The song header's minimum-duration byte is a tempo control that leaves immediate-duration notes untouched — write ornaments with + $60 notes and they keep their length when the tune is slowed.
  • A sound effect is an instrument plus a pitch and nothing else: the modulation program is the effect, and it works on any voice.
  • Galway's drums are a repeat count and six delays per timbre; a PSID-era player's sample emulation gets them wrong — listen as RSID or on hardware.

Notable quotes#

"as all effects are coded into tables, this made the Galway engine very powerful"

"programming a track is like creating a music program using a high level language"

Relevance to the wiki#

The explanation layer for galway-player and arkanoid: per-voice instruction sets, the running-instrument copy, the flag semantics, SFX as instrument + pitch, the "hidden" sample synth and the PSID/RSID consequence for sid-format; concrete recipes for vibrato, pitch-slide-and-portamento, pulse-width-modulation and filter-programming; Tognon's verdict on Galway's code for martin-galway.

To verify#

  • (unverified) Tognon's table lists SETCI (D6) for all three voices; the listing's dispatch (checked in the realdmx copy) has handlers inst_D6_v1 and inst_D6_v2 only. SET2I (DC) on voices 2 and 3 and JSRT (C6) on 1 and 3 agree with the listing.
  • (unverified) "something like 6 octaves": $5E is 94 semitones above note $00, nearly eight octaves; the wiki keeps the range $00–$5E from the code summary.
  • The excerpt of PSample1 on p. 32 reads lda delayTabP1-1,y, the listing (p. 97 and the realdmx copy) lda delayTabP1,y — a typo in the excerpt.
  • (unverified) His example song header $09 is labelled tune1; in the realdmx copy tune1 carries $04 and tune3 $09 — one of the two relabelled the tunes.

Pages touched#

arkanoid · galway-player · martin-galway · sid-format · filter-programming · pulse-width-modulation · vibrato · pitch-slide-and-portamento · chip-samples · instrument-tables · sid-player-routine · hard-restart · chord-arpeggio · orderlist · sidin · sound-effects

source file wiki/summaries/s-sidin04-galway-arkanoid.md · 3 unverified · graph