Homeentitytool

Martin Galway's player (the Ocean "macro-language" driver)

The 6502 music system martin-galway wrote at Ocean from 1984 ("there never was an editor … It was always assembler source files"; source: s-sidmusic-galway-interviews), known from two reverse-engineered tunes: the Rambo: First Blood Part II loader (1986; dmx87) and the whole Arkanoid program (1986 Imagine; Stefano Tognon, sidin #4) (sources: s-realdmx-galway-rambo-player, s-realdmx-galway-arkanoid-player). SIDwave's description holds up in the code: a player "so large/extensive, with macro-language" because it manipulates MIDI-converted data (source: s-recollection-brief-history-of-sid) — each voice runs a small interpreter over a stream of notes and opcodes, with subroutine calls, loops, transposition and even native-code hooks, and every modulation (vibrato, slide, pulse sweep, filter envelope, arpeggio) is one generic engine of timed 16-bit adders.

Facts#

  • Size: the Rambo loader is about 2.3 KB of code (the interpreter and effect engine exist three times, once per voice), 0.6 KB of tables and 1.7 KB of music; Arkanoid about 4.5 KB of code, 2.5 KB of music, 1.1 KB of sound effects and 0.5 KB of tables — against about 1 KB of code in hubbard-player, which is why "his player was so cleverly coded, that it needs little RAM and raster consumption (compared to Galway)" (sources: s-realdmx-galway-rambo-player, s-realdmx-galway-arkanoid-player, s-recollection-brief-history-of-sid).
  • Two builds: the Rambo loader (opcodes $c0–$dc, no filter) and the Arkanoid program (opcodes $c0–$f0, a filter envelope retriggered per note of one voice, a sound-effect bank of twelve 3-voice effects, a CIA-timer IRQ at two rates — about 104 Hz for jingles and effects, 52 Hz for the two tunes with the "sample" track — and the volume-register percussion). Arkanoid is an RSID: the init never returns for those two songs because the foreground synthesizes the drums (source: s-realdmx-galway-arkanoid-player; multispeed, chip-samples).
  • Samples that are not samples: the "digitized" percussion of Arkanoid is six routines writing a stepped 4-bit value into $D418 with busy-wait delay tables — Galway's "wiggling the volume register … the flatulence stuff" and Newman's "a series of farts and burps" (sources: s-realdmx-galway-arkanoid-player, s-sidmusic-galway-interviews, s-newman-driving-the-sid-chip).
  • What it lacks: a drum routine — drums are a frequency adder of −80 per frame and a short gate on a normal voice, which is why he wanted Hubbard's percussion ("Perhaps we should have swapped drivers for a game"; source: s-remix64-galway-interview). chordian's "clean pulsating with the instantaneously triggered notes" is the two-stage pulse program plus the per-note register reset (sources: s-chordian-sid-musicians, s-realdmx-galway-rambo-player). matt-gray: "His Rambo loader is my favourite C64 tune of all time" (source: s-c64com-matt-gray-interview).
  • SIDId: Martin_Galway, Martin_Galway_Digi (source: s-sidid-player-list).

Cheat sheet#

All from the two reverse-engineered files (sources: s-realdmx-galway-rambo-player, s-realdmx-galway-arkanoid-player).

The stream — one byte-code program per voice, no patterns, no order list:

bytesRambo loaderArkanoid
$00–$58 / $00–$5e + nnnote (C-0 … E-7) with nn = index into a 17-entry duration table (multiples of 4 frames)note with nn = 1-based index into a 32-entry table rebuilt per tune as multiples of its minimum duration
note + $60 + nnnote with an immediate duration in framesnote with a raw tick count
$5f / $bfrest (table / immediate time)$5f nn rest
≥ $c0opcode + 2 parameter bytes (3 bytes; CE has 4)opcode + parameters (1–4 bytes)

Opcodes

Rambo loaderArkanoidmeaning
C0 RETC0 RTSreturn from a call; on an empty stack, stop the voice
CA addr PLAYC2 addr JSRcall a sub-stream (8-level stack per voice)
CE addr,n PLAYTC6 n,addr JSRTcall with a note transpose ($f4 = −12)
C4 addr JMPjump (endless tunes; voices can jump into each other's streams — a delayed echo)
DA nDC REPEAT/NEXTCC nCE FOR/NEXTloops, nestable
CC n TRANS(in JSRT)set the transpose
C2 addr INSTR5D4 addr INSTRcopy 5 bytes: control, AD, SR, gate time, "hrtime"
C4 addr INSTRD2 n,addr SETNIcopy a whole instrument (29 bytes)
C6 addr SETFQF0 addr SETFIcopy the 14-byte frequency program
C8 addr SETPMcopy the 10-byte pulse program
D8 addr ARP(DC $08 addr)load a 10-byte / 4-byte arpeggio table
D0 off,val ISETCA id,val SET, DC id,v1,v2 SET2Ipoke the instrument definition
D6 off,val CISETD6 id,val SETCI, DE id,v1,v2 SET2CIpoke the running instrument — change the sounding note
D4 addr EXECD8 addr EXCTexecute 6502 code from the song (Rambo's morse-code intro; Arkanoid's filter switches)
E0 LF3, E2 addr FILTAfilter on voice 3 (resonance $f, low-pass); load a 16-byte filter envelope

Reading a phrase: p2c9d: REPEAT 8 / $00,$01 / NEXT / REPEAT 4 / $00,$02 / NEXT / RET then PLAYT G4,p2c9d and PLAYT Fx4,p2cb4 (Rambo); sub07: JSRT $1f sub06 / JSRT $1d sub06 / JSRT $1b sub06 / JSRT $1a sub06 / RTS (Arkanoid).

Instrument = 29 bytes ($00–$1c):

offsetcontent
$00–$07four signed 16-bit frequency adders
$08–$0btheir durations in frames/ticks (0 = skip)
$0cinitial delay
$0dflags: $01 reload the stages (loop), $80 reload from the note's own frequency after stage 4, $02 apply adder 4 during the delay (a slide into the note), $08 arpeggio mode (Arkanoid: voice 2 only)
$0e/$0ftwo pulse-width stage lengths
$10pulse delay
$11pulse flags ($80 reset the width and restart, $01 restart the counters)
$12–$15two signed 16-bit pulse adders
$16/$17initial pulse width
$18control byte (the test bit here is a mode flag: release the gate $1b ticks before the next note instead of after the note-on)
$19/$1aAD, SR
$1bgate time ($ff = never release)
$1c"hrtime": frames after gate-off at which the voice's 7 registers are zeroed ($ff = never) — Tognon's "; hard restart"

A vibrato is three adders: !wo 30, !wo -30, !wo 30, !wo 0 / $03,$06,$03,$00 / delay $16, flags $05 (Rambo fq2b91); $14,$00 / $ec,$ff / $14,$00 / $00,$00 / $03,$06 / $03,$00 / $1e,$05 (Arkanoid ins01). A drum: ISET $00,$b0 ; fq1add / ISET $08,$ff ; fq1time / ISET $1a,$a9 ; sr / ISET $1b,$0a ; release. A pulse: $14,$14,$00,$05, $14,$00,$ec,$ff, $00,$08 = 20 frames +20, 20 frames −20 around $0800. An arpeggio table is a 4-note stack including the octave — $00,$04,$07,$0c (Rambo, doubled to 8 entries), arp37: $0c,$00,$03,$07 (Arkanoid, read backwards). Every note starts by writing $00 and then the five register bytes (Rambo) or $08 (test) and then the registers (Arkanoid).

Arkanoid's sample track (own language, opcodes = 6502 opcodes): 81–86 xx play synthesized drum n, wait xx ticks; 87 xx nothing; 20 lo hi JSR; 40 NEXT; 49 xx FOR; 4C lo hi JMP; 60 RTS.

Its fingerprint#

  • Because the byte-code values ≥ $C0 are commands, the player's fetch loop is a seven-byte signature — LDY #$00 / LDA ($xx),Y / CMP #$C0 / BCC, in Perl '\xA0\x00'.'\xB1.'.'\xC9\xC0'.'\x90' — which Tognon offered in 2003 with the caveat that "7 bytes are very little" and another engine might share them "with other meanings" (source: s-sidin03-pattern-searching-1; see player-identification).

Tognon's explanation of the Arkanoid engine (SIDin #4, 2003)#

The prose around the listing (s-sidin04-galway-arkanoid) adds what the code alone does not say:

  • Why three copies of everything: "each voice has its code that are perfectly equal each other" and could have been one indexed routine, but "as all effects are coded into tables, this made the Galway engine very powerful"; the per-voice opcode sets — JSRT only on voices 1 and 3, EXCT, LF3 and FILTA only on voice 3, SET2I on 2 and 3 — look to Tognon like instructions "added … when needed", or unused handlers removed to save memory (source: s-sidin04-galway-arkanoid).
  • Song header and note forms: three stream pointers plus a minimum note duration$03$0D across the eight tunes of the listing; raising it slows only the notes timed through the table, not those written + $60 with an immediate count ($12 nn from the table ≡ $72 nn immediate). Notes run $00–$5E; $5F/$BF are rests (source: s-sidin04-galway-arkanoid).
  • Two tables per voice: the 29-byte definition and a 34-byte running copy ($00–$22) that SETCI/SET2CI edit — the same bytes to $17, then the current frequency at $18–$19, control $1A, gate-off and wipe times $1B–$1C, and the live phase counters ($1D–$20 for the four frequency phases, $21–$22 for the two pulse phases); "reloading the cycles" copies $08–$0B/$0E–$0F into them. SETFI copies $00–$0D, INSTR five bytes ("control/ADSR"), SETNI the first nn bytes (source: s-sidin04-galway-arkanoid).
  • The flag byte in his words (frequency $0D, pulse $11, filter $0D): zero = effect off; bit 7 = at the end of the last phase reload the cycles and the stored start value; bit 0 = reload the cycles but continue from the current value; any other bit = hold the current value, no more phases. So the vibrato flag $05 loops from wherever the pitch is, and a portamento $01,$00 × $35 cycles with flag $08 climbs 53 units and stays. During the initial delay the engine also tests bit 1 — adder 4 applied every tick, a slide into the note — which Tognon's text does not separate out (sources: s-sidin04-galway-arkanoid, s-realdmx-galway-arkanoid-player).
  • Sound effects need no stream: an effect is an instrument table "with two extra bytes at the end: the low/high value of the frequency", pushed straight into makeTimbreVx; it lasts as long as its phases; "all the 3 voices can be used for generating the sound effect (and else, this is the way used in the game)" (source: s-sidin04-galway-arkanoid).
  • The hidden sample synth: a PSample routine's timbre "is governed by two parameters: a repeat value and a table of delays" (ldx #$0C repeats per delay, six delays read from the end); the sta $D418 operand is stored masked ($9418, $DD18 in PSample1) and unmasked by $94 + $40 on entry; the sample stream's opcodes are real 6510 opcodes; routines and pointer tables sit in "very sparse memory" — "maybe some suspicious of a way to hidden the sample generation". Zero page: 8-deep call stacks at $31/$39/$40 (voice 1), $A9/$B1/$B9 (2), $61/$69/$71 (3), $78/$80/$89 (sample stream); $E0–$E5 stream pointers, $E6–$E8 durations, $EC–$EE transposes, $F1–$F6 pulse width, $F7–$FC frequency, $FF the current note (source: s-sidin04-galway-arkanoid).
  • Why it sounds wrong in old players: the PSID sample extension "try to emulated this kind of sound generation", so the tune "will play so different into a sidplayer (with extended sid register) instead of the real machine (or today RSID rip)" (sid-format, arkanoid; source: s-sidin04-galway-arkanoid).

martin-galway · hubbard-player · ocean-music-driver · musicfile · matt-gray-player · sid-player-routine · chip-samples · multispeed · instrument-tables · vibrato · pulse-width-modulation · chord-arpeggio · filter-programming · sidin · sidid

Sources#

s-realdmx-galway-rambo-player · s-realdmx-galway-arkanoid-player · s-recollection-brief-history-of-sid · s-sidmusic-galway-interviews · s-newman-driving-the-sid-chip · s-chordian-sid-musicians · s-c64com-matt-gray-interview · s-sidid-player-list · s-remix64-galway-interview · s-sidin03-pattern-searching-1 · s-sidin04-galway-arkanoid

source file wiki/entities/galway-player.md · graph