SID player routine, rastertime and player types

aliases player routine, playroutine, rastertime, player type
sources defMON wiki — using a defMON tune in your own program · chipmusic.org — the defMON megathread (2014–2026) · SID-Wizard 1.8 User Manual (summary) · GoatTracker v2.72 readme (summary) · Creating Chiptunes with SID-Wizard, Second Edition (summary) · Newman — Driving the SID chip: Assembly Language, Composition, and Sound Design for the C64 (2017) · Walleij — A SID player routine (MIDI-driven player design, note tables, hard restart) · C64 Programmer's Reference Guide, ch. 4 — Programming Sound and Music on Your Commodore 64 (1982) · MOS 6581 Sound Interface Device (SID) datasheet — the text behind Appendix O · Lemon64: How does one learn SID sound design these days? (2020) · uCApps MIDIbox SID — Wavetable Sounds Tutorial #1 (Thorsten Klose) · chipmusic.org — sid music hints'n'tips (2013) · The SID Homepage — Interviews with Rob Hubbard (Commodore Zone, Happy Computer 7/86) · The SID Homepage — Interviews with Martin Galway (Commodore Zone 6–7, Happy Computer 11/86) · The SID Homepage — Interviews with Jeroen Tel (Commodore Zone 1997, 64'er 3/91) · Hugi #38 — Up Close & Personal: Jeroen Tel of Maniacs of Noise (2014) · 64'er 10/1986 — Musik wie noch nie: the Soundmonitor article and manual · VGMPF wiki: Soundmonitor, TFMX Editor (C64) and The Great Giana Sisters (C64) · VGMPF wiki: Chris Hülsbeck · SIDin #2 — Matt Gray's Driller music routine (Stefano Tognon, 2002) · Matt Gray's Dominator subtune 6 source (PLAYER V4.2, 1987) and the Reformation driver competition · C64.COM interview #36: Matt Gray (2014) · GitHub realdmx/c64_6581_sid_players — Cybernoid II in Deenen's MUSICFILE, the MoN SFX player, the Driller listing · VGMPF wiki: Maniacs of Noise, Jeroen Tel, Charles Deenen (+ the C64-Wiki roster) · Charles Deenen interviews: Domination #11 (1998) and Designing Sound (2010) · Retrogaming Planet / EmuWiki — Interview with Chris Huelsbeck (2011) · The Brief History of SID — Jan Harries (SIDwave), Vandalism News #64 (2015) · Chordian — SID Musicians (JCH's verdicts on the game composers, 2018) · RVG Interviews: Matt Gray (2016) · Remix64 — Last Ninja 2 vs Ninja Remix: what's the deal? (Chris Abbott) · realdmx: Rob Hubbard's driver — McSweeney's Monty on the Run disassembly and the Commando title · realdmx: Martin Galway's Rambo II loader — the macro-language player, reverse-engineered by dmx87 · realdmx: Martin Galway's Arkanoid — Tognon's SIDin #4 reverse-engineering, with the 'sample' synthesis through $D418 · realdmx: Jonathan Dunn's Daley Thompson '88 and Ocean Loader 5 in Paul Hughes's Ocean music driver · realdmx: Johannes Bjerregaard's own player — the James Bond 3 demo (a Maniacs of Noise commission) and Myth · realdmx: Reyn Ouwehand's Armada (MUSICFILE, 'Special version for Reyn Ouwehand', 1989) and Dutch Breeze (the 1990 MoN player) · realdmx: Charles Deenen's two test tunes — an undated MUSICFILE build and the 'Scoop designs' Maniacs of Noise player · realdmx: three smaller drivers — Fred Gray's Mutants (song as code), Jeroen Kimmel's 'Red's music player' (1989) and David Whittaker's Panther driver · realdmx: two scene players with their own sources — Francois Prijt's Audial Arts 'zong player' v1/v2 (1991) and Adam Bulka's FAME 'Sound Routine f 4.1.0' (1988) · SIDin #1 — Ripping a game: Dig Dug (Stefano Tognon, 2002) · SIDin #2 — David Whittaker's Lazy Jones music routine (Stefano Tognon, 2002) · SIDin #1 — Iseq music composition program: the comp.sys.cbm threads and Steve Judd's Commando cover (2002) · SIDin #3 — Music Engine pattern searching, part 1 (Stefano Tognon, 2003) · SIDin #5 — Music Engines Pattern Searching, part 2: the Lostack database (Stefano Tognon, 2004) · SIDin #3 — Ripping BASIC based tune: Sheba and Testcard (Stefano Tognon, 2003) · SIDin #6 — Jan Diabelez Arnt Harries (Rambones) interview (Stefano Tognon, 2004) · SIDin #7 — Inside Modules: Ivan Del Duca's music engine (Stefano Tognon, 2005) · SIDin #7 — xxlarge: Ninja's 256-byte cover of 'X large' analysed (Stefano Tognon, 2005) · SIDin #6 — Inside DigiOrganizer, Polonius's 4-bit sample editor (Stefano Tognon, 2004) · SIDin #8 — Tiny Sid Compo, the 512-byte entries (Stefano Tognon, 2005) · SIDin #9 — Tiny Sid Compo, the 256-byte entries (Stefano Tognon, 2006) · SIDin #8 — Aleksi Eeben interview (Stefano Tognon, 2005) · SIDin #9 — Catweasel Mk4 follow-up: driver, hardware buffer, two 6581s and the filter (Stefano Tognon, 2006) · SIDin #10 — Tiny Sid 2, part 1: XL5, Mini Digi Drum, RM, Miniature Rhapsody (Stefano Tognon, 2006) · SIDin #11 — Tiny Sid 2, part 2: BLOCK ACID DUB and Resolution (Stefano Tognon, 2007) · SIDin #12 — Tiny Sid 2, part 3: Plaster (512 b) and Back To Basics (256 b) (Stefano Tognon, 2008) · SIDin #13 — Inside JITT64: the player routine (Stefano Tognon, 2010) · SIDin #13 — JITT64 Tracker: Tognon's Java Ice Team Tracker from the user's side (2010) · SIDin #12 — Ripping Trivia Arcade (Stefano Tognon, 2008) · SIDin #14 — Inside Hunter's Moon (Martin Walker's sound-effects engine, reverse-engineered, 2015) · DUET research: SID-Wizard 1.97 formats and player (summary) · DUET research: SID-Wizard player.asm deep dive (summary) · HVSC — SID file format description (PSID v1–v4, RSID) · CheeseCutter — the repo, the man pages, and the release paper trail (2011–2026) · The CheeseCutter Guide — the recovered site manual (Wayback) · SIDin #11 — SID Factory vs Ninjatracker (Stefano Tognon, 2007) · SIDin #2 — Lasse Öörni (Cadaver) interview (Stefano Tognon, 2002) · defMONing 102: extended field guide (iLKke et al.) · Cybernoid I / Cybernoid II 'behind the notes' videos (2014) and the Lemon64 thread · Remix64 — An Interview with Chris Huelsbeck (2001) · Happy Computer 3/87 — Der Soundprofessor (Hülsbeck at work) · Old C64/PC Player/Editor Notes — Chordian (2017) · My Computer Chronicles — Jens-Christian Huus / JCH (2017) · SIDin #15 — Inside Matt Gray's Serpent Demo player (the 4-bit sample voice, reverse-engineered, 2015) · SIDin #14 — Inside Matt Gray's Dominator player (Tognon's commented PLAYER V4.2 and the musician's guide to composing in it, 2015) · SIDin #4 — Martin Galway's Arkanoid music routine, the explanation (Stefano Tognon, 2003)
created 2026-08-30 · updated 2026-08-31

On the commodore-64 the music is played by a small machine-code routine called once per frame from an interrupt. It costs two things a game or demo cares about: memory and rastertime (CPU time per frame, measured in raster lines). sid-wizard therefore ships several player types and shows the estimated rastertime while composing and the final measurement in the executable export; the memory footprint of each player type is shown in the startup menu (source: s-sid-wizard-manual).

SID-Wizard player types#

typewhat you get
Normaleverything most tunes need: calculated vibrato, detune, chord, transpose, instrument octave, WF-arp speed, PW/filter reset off, keyboard tracking, 11-bit filter, tempo programs, vibrato types, HR types, frame-1 setting, note-off table index, subtune jump, zeropage restore
Mediumsmaller; drops vibrato type, hard-restart type, frame-1 setting, PW keyboard tracking, note-off index, subtune-jump effect, zeropage save/restore
Lightsmaller and less rastertime; additionally drops calculated vibrato & slide, detune, chord table, transpose effect, instrument octave, WF-arp speed, PW/filter reset off, keyboard tracking, 11-bit filter, tempo programs
ExtraNormal plus: program tables never skipped, filter-switch/resonance effect, ghost registers, fast tempo (0..2), vibrato survives a pitch slide, note/track delay effects — costs more memory and rastertime
Barevery restricted, smaller than Light: no subtunes, multispeed, external volume, filter-shift, orderlist effects, portamento in the note column, WF-arp NOP $80, vibrato-rate effect, filter/detune/waveform small effects
Demowhat demos/intros usually need; drops subtunes, filter shift, note-column portamento, vibrato-frequency effect, small effects for filter/detune/waveform/ring/sync, and the extras (octave, chords, calculated vibrato and types, gate-off program, keyboard track, hard-restart/frame-1 settings)

Custom feature sets: edit /sources/settings.cfg and recompile. The 2SID version uses ghost registers in all player types.

What the player actually does per frame (SW 1.97, m64's DUET research)#

Read out of player.asm (4062 lines of 64tass) while reimplementing it for duet (sources: s-duet-research-sidwizard, s-duet-research-sidwizard-deep):

  • One call per PAL frame, plus in multispeed tunes framespeed−1 extra MULPLY calls spaced evenly across the 312 rasterlines. Each call processes channels 3 → 2 → 1, then COMMONREGS writes the shared registers.
  • The register write order is deliberate: SR, AD, frequency, pulse width, then waveform — "keeping distance" between the ADSR and waveform writes so note starts are reliable — followed by $D417 (filter switch | resonance), $D418 (volume | band), $D416/$D415 (the 11-bit cutoff). This is the same concern that makes CheeseCutter offer a per-instrument write order and GoatTracker offer buffered SID writes.
  • A note's audible start is uniformly two frames after its row's tick 0. That fixed look-ahead is what makes a two-frame hard-restart possible at all, and it is why the NORMAL player needs tempo ≥ 3 (only the EXTRA player's FASTSPEEDBIND folds ticks for tempo 1–2). Tick 0 fetches the row, tick 1 advances the pattern/orderlist, tick 2 starts the note — and no table row executes on the note-start tick; wave-table row 0 lands on the following player call.
  • Sweep and arp counters tick per player call, not per video frame, so every table in a multispeed tune runs at the multispeed rate while the row grid does not.
  • The filter is a shared resource, one track at a time, leftmost-priority — and that priority is emergent, not coded: because tracks are processed 3 → 2 → 1, channel 1 writes the controller variable last and wins. In 2SID the dispatch becomes voice-major, chip-minor (ch3, ch6, ch2, ch5, ch1, ch4), because the race is per chip; each SID gets its own filter program, its own $D415–$D418 block and its own main volume, while the orderlist, patterns and tempo stay global.
  • Quirks worth knowing if you are matching the player's sound: the detune value is added at output with a path-dependent carry-in (a deterministic ±1 LSB jitter) and is never reset by a note start or by init; a gate-off with a waveform pointer leaves the gate on; there is no wave-table self-jump guard (the $40 threshold in SWMconvert.c is a GoatTracker-export heuristic, the player's is $80); plain slides never self-terminate.

The feature lists in the table above are settings.cfg switches, confirmed in the source: EXTRA additionally turns on FILTERALWAYS/PULSEALWAYS/VIBSLIDEALWAYS — which is precisely what "program tables never skipped" means, no table pausing on hard-restart frames.

Calling an exported tune#

LoadAddress = init (subtune in A) · +3 = play, every frame · +6 = multispeed call · +9 = external volume (A) · +12 = sound effect on channel 3 (X note, Y instrument, A length in frames; SID-Maker-SFX) · +15 = tape-slowdown in semitone steps 0–24 (SWP-FX). SWP is the relocatable form: X/Y = load address lo/hi, A = subtune. Sound effects are instruments that override channel-3 notes while they run (sound-effects). See sid-format.

The book adds numbers: the full player needs about $1A–$1C raster lines (light $14–$19), less when fewer than three tracks play; it saves and restores its two zeropage bytes ($FE/$FF by default); the multispeed call at +6 is "done in the same way as XSID's/SDI's (not as Goattracker's)" and uses "much less raster time"; SFX at +12 needs X = note, Y = instrument, A = length in frames (source: s-witchmaster-creating-chiptunes-with-sid-wizard).

The PSID / RSID contract#

Every exported tune has to meet the same interface, whatever tracker made it (source: s-hvsc-sid-file-format; sid-format):

  • initAddress is called once with the song number in the accumulator; playAddress is called repeatedly. A playAddress of 0 means the init routine installs its own interrupt handler — which is always the case for RSID.
  • The speed field is one bit per song: 0 = the vertical-blank interrupt (50 Hz PAL, 60 Hz NTSC), 1 = a CIA 1 timer (60 Hz by default). This is the field a multispeed tune or a CIA-timed one lives or dies by.
  • PSID vs RSID. PSID files run under PlaySID/libsidplay1-style emulation; RSID marks tunes that "strictly require a true Commodore-64 environment" — multispeed, samples, extra interrupt sources or busy loops. An RSID forces loadAddress, playAddress and speed to 0, requires a real load address ≥ $07E8, forbids an init inside the ROM/I/O areas ($A000–$BFFF, $D000–$FFFF), and gets the power-on environment rather than a per-call bank setting. A player "MUST" reject an RSID that violates this — the point being that an old emulator fails safely instead of playing the tune wrongly.
  • The environment the player wakes up in: $02A6 = 1 for PAL, 0 for NTSC; CIA 1 timer A at 60 Hz ($4025 PAL, $4295 NTSC); to run an NTSC tune on a PAL machine the CIA value is $3FFB, a PAL tune on NTSC $5021. The flags word carries the clock (PAL/NTSC/both) and the SID model (6581/8580/both) per file, and startPage/pageLength describe the free page range a host may relocate the driver into.

The NTSC case is not cosmetic: the NTSC refresh (17045 cycles) differs from the CIA latch (17095 cycles), and at that difference "the SID ADSR bugs can actually break the tune" (hard-restart).

Loadable drivers: one editor, many players#

A C64 editor is not usually welded to one player. JCH's editor established the pattern — a file carries its driver, and tunes move between driver versions — and three later tools kept it:

  • cheesecutter: a .ct file contains its player. ct2util import copies data from another song "without overwriting the player" (i.e. moves a tune between player versions) and ct2util init creates "a fresh .ct from player binary", which is the door for custom players; the releases even ship a depacker for JCH NewPlayer 20.G4 tunes (source: s-cheesecutter-repo-and-releases). The trade-off is stated plainly in the guide: versus GoatTracker it is "mostly a matter of taste … The player is more rastertime-heavy but I chose features/comfort over speed" (source: s-cheesecutter-guide).
  • laxity's SID Factory (2007) ships two: v5.0x, "standard music driver with calculated vibrato and no aim at execution speed (multispeed allowed)", and v6.0x, a "'fast' music driver without calculated vibrato [that] aims at a maximum execution spike at around $18 scanlines" — the same trade the SID-Wizard player types make, sold as two files instead of six builds.
  • ninjatracker V2 takes the opposite route: Cadaver released many v2.x versions "differing in some points like hardrestart type, notes duration and other minus effects. This means that you have to choose the editor you need to use" — and Tognon's verdict is that loadable players in one editor, "like in SIDFactory or JCH", would have been better. Ninjatracker exists at all because V1 was built "for having an editor that had low rastertime and memory usage that can be used for the music of his games", with a converter that turned goattracker tunes into Ninjatracker format so only the instruments had to be redone; its relocator can save the song without the player, for games that supply their own (sources: s-sidin11-sid-factory-vs-ninjatracker).

Cadaver's own account of why the list is so long: "I've been steadily improving as a coder, so each time, when starting a new game project, I also wanted to rewrite the playroutine for memory or speed (rastertime) gain. Sometimes, I even wrote editors :)" — and by 2002 he considered the search over, "Goattracker has kind of reached the peak in ease-of-use vs. features … and Ninjatracker in speed & size, so at least now I can't see the need/possibility for another player" (source: s-sidin02-cadaver-interview). The tracker's convenience is what costs the memory: pattern data of the Amiga/PC kind is cheap to write and expensive to ship, which is why a game musician ends up with a second, packed player.

frantic makes the same trade from the other end: defMON's player is "optimized for speed, not size", and he refuses automated hard restart because the look-ahead parsing it needs "adds overhead to the code and consumes CPU that isn't strictly required. Original defMON was intended to be usable in demos/games, with low raster time consumption" (source: s-defmon-wiki-defmoning-102).

GoatTracker's playroutine#

F9 packs and relocates a song (PRG/BIN/SID, start address, two zeropage bytes) and strips unused patterns, instruments, table rows, duplicate table parts and unneeded player code — the four /examples/example*.prg show typical rastertime. Init: LDA #subtune / JSR start; play: JSR start+3. Options: buffered SID writes (write all registers at the end of a channel's processing — "improves sound stability but takes more memory & rastertime"); sound effect support (start+6, A/Y = effect address, X = 0/7/14 for channel 1–3, priority by memory address; implies buffered writes); volume change (start+6, or +9 with effects); author info in start+$20–$3F with timing marks from DXY; zeropage ghost registers (the main program copies them to $D400 — needed when player and data sit under the I/O area; copy ADSR last with the attack-F hard-restart player); disable optimizations if timing-dependent ADSR bugs appear. Playback is a tick schedule: note init on tick 0, audible on tick 1, gate-off and hard restart on the tick set by the instrument's timer; filter every tick, wavetable never skipped, pulse and realtime commands skipped on some ticks to save rastertime (source: s-goattracker-readme).

defMON's player (2008)#

defMON's packed tunes follow the scene convention ("more or less the same as in 90% of all other sid tunes"): the tune sits at $1000 (strip the two-byte load address), lda #$00 : jsr $1000 inits — A = start position, and the init briefly disables interrupts to auto-detect 6581 vs 8580 — and jsr $1003 plays once per frame. Multispeed tunes add the second entry jsr $1006, which only updates sounds: call $1003 once per frame and $1006 the other 1/3/7 times. Global volume is pokeable at $10B1 ($0x). The player is optimized for speed, not size ("When doing a stereo sid tune in 4x speed, having a speed optimized player is almost a requirement"); music lives at $1000–$1FFF by scene custom because the VIC always sees character ROM there, making that RAM useless for graphics. Frantic contrasts it with his own size-optimized 256-byte tiny-sid-compo players (sources: s-defmon-wiki-calling-the-player, s-chipmusic-defmon-thread).

The classic drivers#

Before trackers, C64 composers wrote their own players: rob-hubbard "used to load up a machine code monitor and literally display the bytes in real time … I would tend to work on four bar chunks". McSweeney's 1993 disassembly of an early Hubbard driver: 900–1000 bytes covering SID init, instrument definitions (waveform, envelope, 8-byte structures with flags for drums, skydive and octave arpeggio), note fetch, portamento, vibrato, arpeggio, pulse width, and sequences; a "notework" / "soundwork" split; the main loop runs three times per frame from the raster interrupt at 50/60 Hz, which is why every effect has single-frame resolution. Each driver had a "personality" — Hubbard's percussion, martin-galway's PWM and ring modulation — and was continually extended: "The middle section [of Monty on the Run] was an excuse to use the new pitch bend code". Alternatives of the time (Commodore Music Maker, Huelsbeck's SoundMonitor) cost too much memory or CPU (source: s-newman-driving-the-sid-chip).

What the composers said themselves — Newman's "machine code monitor" and Hubbard's "assembler" are complementary, source edited in an assembler and values tweaked live in a monitor (see rob-hubbard):

  • Hubbard: "I just used an assembler and edit the source code - most people in those days did the same. There really wasn't time to sit and write an editor, as there was so much work to do."; in 1986: "I do my music in pure machine code using an assembler … 'Electrosound' is the only one that I like" among the music programs of the day. Thing on a Spring "was actually something I wrote to test my first driver"; I-Ball "was pushing the driver too much" (source: s-sidmusic-hubbard-interviews).
  • martin-galway: "there never was an editor to make it 'easy'! It was always assembler source files". His tool chain at Ocean: "a really cheesy almost-BASIC Assembler" → Zeus 64 by Crystal → Ocean's own C128D-based system using all 128 K → an Atari ST system; he entered music on one C128D and played it on another. Ocean's earlier music program was "dreadful" but served for the Daley Thompson's Decathlon loader; his own routine debuted on Kong Strikes Back. On the budget: "the music gets the smallest piece of the cake. If I would write a music that uses 60KB and the whole CPU time, that would sound damn good"; Slap Fight's music took "around 5 or 6K" (source: s-sidmusic-galway-interviews). Programmed variation was a driver feature: the Times of Lore title "has a random number generator which selects different guitar solos" over an 11-minute loop (the solos took 20 days), and Insects In Space's "buzzing bees" needed programmer tricks (same source; randomization-and-probability).
  • jeroen-tel: a BASIC player first, then "me and Charles designed our first MoN routine" with charles-deenen (maniacs-of-noise); before that he "hacked into Hubbard's routine to compose music in machine-code" (sources: s-sidmusic-jeroen-tel-interviews, s-hugi38-interview-jeroen-tel). Deenen on the result: Tel would "write out a music piece in assembly with melodies, chords, bass, without sometimes even hitting play once" (source: s-hugi38-interview-jeroen-tel). The division of labour is confirmed from the outside by the CSDb thread on the Cybernoid work disk — "It's just a SID tune recording with the turbo assembler sourcecode of the player and the note/instrument tables", with the coding skills on display being "the coding tech skills from Charles Deenen who did the Maniacs of Noise player routines": "Jeroen is a great musician, but he is no programmer. Deenen did all the maths, Jeroen played the notes." The same thread contains the pointed observation that the two pre-tracker workflows were not equally good — "plugging in the notes using a machine code monitor while the tune is playing off an interrupt (ala Hubbard) is a better situation than typing them silently into Turbo Assembler then assembling…" — and a claim worth chasing, that "future composer is made from a ripped MON player" (forum claim, unverified) (source: s-cybernoid-behind-the-notes; turbo-assembler).
  • chris-huelsbeck, plainly: "My very first music player was pretty basic, but I learned fast and the sound got better" (source: s-remix64-huelsbeck-interview). He also published the era's only how to write one checklist, in Happy Computer (March 1987): decide whether you can build a music program at all ("meistens programmiert man dabei in Assembler"); work out how to drive the sound chip; decide whether the routine runs in the interrupt; lay the data out in memory — "das Anlegen einer Notentabelle in Hexadezimalzahlen. Die Noten kann man dann als Zahlenwerte von $00 bis $FF abrufen. Dies bringt auch Platzersparnisse"; plan the extensions (pulse-width modulation, filter manipulation — "Dadurch entstehen sehr schöne Effekte"); make the sound switchable mid-game; and group notes into bars that can be replayed in any order and any number of times — "diese Gruppen bezeichnet man als Takte … in beliebiger Reihenfolge und beliebig oft abspielen", which is the orderlist, described before anyone called it that (source: s-happy-computer-soundprofessor).
  • JCH's line is the best-dated of all, because he kept the paper. OldPlayer (1987) was fed by hand-written conversion sheets transferring tunes note by note from Aegis Sonix on the Amiga; NewPlayer began July 1988, "from a prototype version with mere bleeps to one with pulsating, arpeggio and drums using a table of waveforms, all within a sensible use of CPU time". The versions run on an NN.GN scheme, and the notes pin the milestones: 10.G0 the packer and relocator, 15.G7 an improvement pass, and 17.G1 the hard-restart implementation — a dated-on-paper confirmation, matching the JCH 02/NEWPLAYER V17.G1 entry in the sidid database. Two pieces of the same toolkit rarely mentioned elsewhere: a separate SFX player that "could do a lot more sound effect tricks than the music player" (sound-effects), and a splitter program that could "spread the player and data out into several smaller memory chunks" for games with awkward free space (sources: s-chordian-player-editor-notes, s-chordian-my-computer-chronicles).

Three documented drivers of 1986–1988#

The wiki now holds three classic drivers in enough detail to compare — one published as a magazine listing, one reverse-engineered and later released by its author, one recovered as source:

Musicmaster / soundmonitormatt-gray-playermusicfile (Maniacs of Noise)
author, yearchris-huelsbeck, driver 1985, editor 1986matt-gray, from late 1986; "PLAYER V4.2 (C)1987"charles-deenen, 1987; "MUSICFILE V01-07-1988"
how music was enteredthe Soundmonitor editor (before it: hex in the SMON monitor)typed into the assembler as tables; a Casio keyboard for ideastyped into the driver's Turbo Assembler source as "hexadecimal numbers and labels"
song modeltrack/step table of bar addresses per voice, with transpose and sound-transpose columnsthree tracks of pattern numbers ($FF restart, $FE stop) → patterns of note bytes and $FA–$FD commandsthree sequences of step-table numbers with ≥ $80 command bytes → 33 step tables
instrument24 bytes: waveform on/off, ADSR, pulse + pulse EG, portamento, vibrato, detune, filter + filter EGtwo 8-byte rows + shared arpeggio lists; V4.2 adds PLEX chord tables and a drum table8-byte row (pulsehi, waveform, attdec, susrel, filcount, fx1–3) + shared pulse, filter, arpeggio, drum and start-waveform tables
effectstranspose, detune, portamento, vibrato, PWM, filter modulation, 8-step arpeggiosarpeggio, vibrato (triangle on the frequency value), lo/hi portamento, pulse bounce $08–$0E, 2-frame drum waveform, noise effecttone/wave/pulse arpeggio, vibrato, glide, sweep, "pulserun", "strangefilter", double voices, "space effect", drum routine
documentationthe 64'er 10/1986 article (s-64er-soundmonitor-article)SIDin #2 (2002) on Driller, SIDin #14 (2015) on V4.2, plus Gray's own source (2014) (s-sidin02-matt-gray-driller, s-sidin14-dominator-player, s-matt-gray-dominator-source)the recovered Cybernoid II source (s-realdmx-sid-players)
rastertime10 lines minimum, up to 34 when fetching a note/pattern

What the authors said about the job:

  • Hülsbeck built the play routine first, "völlig unabhängig", so a tune could run inside anybody's BASIC game (SYS 49152 after loading a "Complete Song"); its 1987 successor The Final Musicplayer took "over 4 KB less" and was "for the first time" able to play a sound effect over three channels of music; in 1988 tfmx replaced the "rigid instrument table" with a per-note script (sources: s-64er-soundmonitor-article, s-vgmpf-soundmonitor-tfmx, s-emuwiki-huelsbeck-interview). He composed Musicmaster's data "in Profi-Ass 64 … in hex" before the editor existed (source: s-vgmpf-chris-huelsbeck).
  • Gray: "I did write my player to adopt a sequencer approach. Rather than program every note played in sequence, it used a series of smaller sequences"; what made it his was the modulation code — a deliberately "incorrect" vibrato and ever-new drum routines; game programmers who re-wrote his player for Mean Streak "left out all the modulation and pitch end routines" (source: s-c64com-matt-gray-interview). His 2014 framing: "Myself, Martin Galway, Rob Hubbard, Fred Gray, Ben Daglish … all typed their tunes directly into their assembler" (source: s-matt-gray-dominator-source).
  • Deenen: "The editor for both of us was easy: Turbo Assembler"; MoN needed "two to six days" per game with that setup (sources: s-charles-deenen-interviews, s-vgmpf-maniacs-of-noise).

Tognon's V4.2 walkthrough is also the wiki's one worked example of composing in an assembler-era driver: the shipped empty_public.asm is one tune, 32 empty instruments, 166 empty patterns, 32 empty drum tables and 16 plex tables, and the six steps are speed → instrument rows → drum tables → plex tables → patterns → tracks. His last step is the one that makes the method bearable — define readability constants (INSTR = $FA, FLUTE = $00, ARPEG = $FD, DUR5 = $75) so a pattern reads INSTR,FLUTE,DUR5,F2,ARPEG,P850,E2 instead of $FA,$00,$75,F2,$FD,$32,E2. The same article corrects two readings of Gray's own source: $FD is the plex (arpeggio) command, not a duration (source: s-sidin14-dominator-player).

Both released sources build with modern cross-assemblers (64tass for Gray's zip, ACME for the GitHub collection) and produce .sid files, so all three can be read next to a siddump of the tune (sources: s-matt-gray-dominator-source, s-realdmx-sid-players).

Nine more drivers from source (1985–1991)#

The rest of the realdmx collection, ingested on 2026-08-30, adds the drivers below — read from recovered or reverse-engineered code, so the sizes and formats are the real ones. Two families stand out: pattern players (a track of pattern numbers per voice, 8-byte instruments, flag-bit effects) and voice programs (a byte-code stream per voice with calls, loops and transposition, instruments edited by the stream). Each has a page with a cheat sheet.

driverauthor, year, provenancesong modelinstrumenteffectssize
hubbard-playerrob-hubbard, 1985; McSweeney's disassembly of Monty on the Run, a Commando disassemblytrack of pattern numbers; 1–3-byte notes (length + flags, instrument or portamento, note)8 bytes: PW, control, AD, SR, vibrato, pulse, flagsvibrato (8-frame 01233210), pulse bounce $08–$0e, portamento, drum / skydive / octave-arpeggio flags≈ 1 KB code
galway-playermartin-galway, 1986; dmx87 (Rambo loader), Tognon / SIDin #4 (Arkanoid)byte-code per voice: CALL/RET, REPEAT/NEXT, JMP, transposed calls, EXEC native code29 bytes: 4 frequency adders + times, 2 pulse adders + times, control, AD, SR, gate time, voice-wipe timeone adder engine for vibrato / slide / PWM / filter; 4-note arpeggios; Arkanoid: filter envelope per note, 2× timer rate, $D418 percussion synthesis, 12 SFX2.3 KB (Rambo) / 4.5 KB (Arkanoid) code
ocean-music-driverPaul Hughes (driver), jonathan-dunn (music), Ocean 1988; original sourcebyte-code per voice: CALL/RET, LOOP/NEXT, CT transposed call, CODE, PORT; notation of note names and note-value constants5-byte patch: gate-on control, AD, SR, hold (or gap), gate-off controlBEND into the note, VIBON, PWM entries, ARPON tables, EFFECT pluck, DRUM tables on voice C; fades as self-modifying data3.8 KB driver in a 5.5 KB budget
bjerregaard-playerjohannes-bjerregaard, 1989; own Turbo Assembler sources (James Bond 3, Myth)track of pattern numbers with transposes; pattern bytes by range ($60 arp, $80 instrument, $a0 glide, $c0 length, $e0 rest)8 bytes pointing at wave / note / pulse / filter programs; JB3 packs nibblessoftware second sustain, two hard-restart schemes, semitone-proportional vibrato, one global filter cutoff, drums as instruments≈ 1.5 KB code
musicfile buildscharles-deenen 1988–89; test tune, Armada ("Special version for Reyn Ouwehand"), Dutch Breeze (1990 rewrite), the "Scoop" playersequences of steps with upward transposes; 1990: intro + loop records, legato, rests8 bytes (pulsehi, waveform, attdec, susrel, filcount, fx1–3) + shared programs13 routines (arpeggios, vibrato, glide, pulse, filter, drums, noisetik …); 1990: segment filters, bounded pulses, looping drums, detune≈ 2.4 KB / 2.1 KB code
whittaker-playerdavid-whittaker, 1986 (Panther); dmx87tracks of pattern addresses; patterns of notes and command prefixesnone — the voice struct holds the last prefixescommands for waveform, ADSR, tempo, arpeggio, vibrato, accelerating slides, pulse bounce, alternate-frame drum1.7 KB engine
Fred Gray, Mutantsfred-gray, Ocean 1987; unsigned disassemblysong as code: 26 section routines + step table; one note byte per row8 bytes without AD (the section writes it)quarter-semitone pitch, one table for chords and vibrato, free-running pulse accumulator, SFX chaining engine≈ 3.8 KB with music
Red's playerjeroen-kimmel, 1989; own sourcesequence words + patterns of (note, instrument·duration) pairs8 bytes per voice table, flag bytewaveform switch, glide, filter sweep, two vibratos, three-frame noise/triangle drums≈ 1.4 KB
FAME "Sound Routine f 4.1.0"Adam Bulka, 27.07.1988; own source, German commentssteps with sticky lengths, transposes, repeats8 bytes: AD, SR, pulse, wave/table, vibrato mode, pulse+filter programs, wave table, releasehard restart (bit 7), auto gate-off in ticks, one table engine for vibrato / arpeggio / drum pitch≈ 2.9 KB
Audial Arts "zong player" v1/v2Francois Prijt, 1991; own sources, Dutch commentstracks of blocks with repeat / transpose / instrument-offset bytes; notes with durations in frames16 bytes: two waveform presets (gated / released), gate length, accelerating pulse sweep, arp + wave pattern, filterstaggered per-voice look-ahead, "tock" noise click, chord tables at one entry per frame≈ 2.6–3.0 KB

(sources: s-realdmx-hubbard-player, s-realdmx-galway-rambo-player, s-realdmx-galway-arkanoid-player, s-realdmx-ocean-dunn-hughes-driver, s-realdmx-bjerregaard-player, s-realdmx-ouwehand-mon-tunes, s-realdmx-deenen-test-tunes, s-realdmx-fredgray-kimmel-whittaker, s-realdmx-audial-arts-fame-players)

What the code adds to the accounts above:

  • SIDwave's size contrast is exact: Hubbard's whole player is about 1 KB, Galway's Rambo loader 2.3 KB of code with the voice interpreter written out three times, Arkanoid 4.5 KB — and Ocean's 1988 driver was written to a stated budget, ALLOCATION = 5*1024+512, with TOGO bytes to spare reported by the assembler (118 for Daley Thompson '88).
  • "Rastertime" was measured in the players themselves: Deenen's test harness toggles the border colour around the play call; Bjerregaard's Myth keeps three raster-line counts per voice at $1009.
  • The gate time became an instrument property in the scene players (Audial Arts, FAME, the Scoop MoN player, Ocean's patches) years before trackers had gate-off tables; Galway's "hrtime" and FAME's bit-7 release are hard restart before the name (hard-restart).
  • Multi-rate players existed in 1986: Arkanoid runs its jingles and effects from a CIA timer at about 104 Hz and the sample tunes at 52 Hz (multispeed).
  • Tognon on why Galway's is the hardest of the classic drivers to read, and what it costs: "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", which is "very powerful if it is compiled by hand, but too complicated by an editor program". What struck him in Arkanoid 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" (source: s-sidin04-galway-arkanoid; orderlist).

Seven more from SIDin (1982–2010)#

The routines that Stefano Tognon's sidin analysed between 2002 and 2015 and that have no page of their own in the tables above, from the smallest thing that plays a tune to a 2010 tracker's player; the sections further down give the detail (sources: s-sidin03-ripping-basic-tune, s-sidin01-dig-dug-rip, s-sidin02-whittaker-lazy-jones, s-sidin14-hunters-moon, s-sidin07-inside-modules, s-sidin07-xxlarge, s-sidin13-inside-jitt64).

driverauthor, year, provenancesong modelinstrumenteffectssize
Colin's Sheba routineAndrew Colin, 1982–83 (Commodore's Introduction to BASIC part 2); Tognon's rip, SIDin #3per voice a list of note numbers and a list of durations, 0 ends the voice; a 16-bit counter × speed byte 8 between notesnone — one control byte per voice, ADSR written once by BASICnone (gate off / on per note)≈ 160 bytes + a 128-byte frequency table (estimate from the listing)
dig-dug routinecomposer unknown; Datasoft's 1984 cartridge; Tognon's rip, SIDin #114 track slots mapped to voices by a table, re-assignable while playing; per-voice byte streams of 22 instructions (byte = 8 × jump-table index), two repeat counters, gotos; a note = control on, frequency, duration, control off, duration; the player sleeps for the shortest pending durationnone — the stream writes ADSR, pulse, filter and volume directlyper-voice filter routing, octave pairs, one-frame frequency runs under a held gatenot stated
Lazy Jones routinedavid-whittaker, Terminal Software 1984; SIDin #2 (whittaker-player)20 pieces × 32 steps × (2 voices × 16-bit frequency), one step per 12 frames; tune 1 = pieces 10 + 11none: AD $66, SR 0, sawtooth, $D418 = $1F at initnone (gate $20$21 per note)21 × $80 bytes of data; code well under 256 bytes (estimate)
hunters-moon SFX enginemartin-walker, Thalamus 1987; Tognon's disassembly from the TAP, SIDin #14no song — 16 effect routines storing one to three table indexes; a 50-frame lock for explosion / star cell13 tables × 32 columns: AD, SR, packed pulse, frequency, start and end control, loop length, frequency steps, two effect selectors, pulse stepsix pitch-ramp modes, byte-swapped pitch, EOR scrambles, $D41B random pitch, ring-modulated voice pairs, pulse bounce $08–$0E≈ 250 listing lines including tables
modules driverIvan Del Duca, 1988; Tognon's disassembly of the PSID "Modulus", SIDin #7three tracks of 4-byte entries (pattern address, byte count, repeats); patterns of octave/note bytes with sticky $A0–$E0 durations and jump-table commands $80–$9B; gosub/return phrases9 tables × 16: AD, SR, pulse lo/hi (fixed), control on, control off, cutoff high, resonance/routing, modeone effect per voice: portamento (step, delay), a triangle LFO on frequency / pulse / cutoff, SID-register copy (osc 3 → pitch, env 3 → cutoff), volume≈ 1.4 KB code ($C000–$C5B5) + ≈ 0.4 KB tables; music $C800–$CFE8
xxlargeNinja/theDream, 2004; source printed in SIDin #7one list of 8 patterns (3-bit index), nibble streams (0 = filter step, 2–F = note); voices 2 and 3 replay voice 1's stream one and two steps latenone — waveform fixed per voice, SR from an interleaved table, no ADpulse from a 20-byte table indexed by the KERNAL cursor-blink counter (also the tempo gate); filter tables stepped up on a pattern's first pass and down on its secondunder 256 bytes including the music
jitt64 playerstefano-tognon, 2010; the author's own commented source, SIDin #13three tracks of pattern numbers 0–222 with REP, TR ±15 and RST; patterns with their own tempo and length; rows of note · instrument · command · parameter5 bytes (HR, AD, SR, CTRL1, CTRL2) + 16 pointers to unshared per-instrument tables — up to 2 KB per instrument16 pattern commands $00–$10 (arpeggio, portamento, vibrato, pulse, fade, filter, gate-SR); per-instrument hard restart with two control bytes and a write-order switch; shadow registers written at IRQ start (one frame of latency)not stated ("very rastertime consuming"); a packer strips unused tables

The tiny-sid-compo entries of 2005–2006 are in their own tables below.

A ripper's taxonomy, and why players converge#

  • SIDwave, after "5600+" players: "Some set up ADSR first, then play a note, then effect, some do it differently. Some write all SID registers into a buffer and write it all at once, and a very few I've seen only update a few registers … Some music data is packed, or efficient in that way, that the raw values is what the SID needs (Hubbard method). Others read data, and 'convert' it to SID values (Galway)." Galway's player is "so large/extensive, with macro-language" because it manipulates MIDI-converted raw data; Hubbard's "needs little RAM and raster consumption"; editors like Soundmonitor and Electrosound ate "around 45-75% CPU". And the convergence claim: "if you try to code the most efficient way and the player will be able to do all effects of the SID, what you end up with is more or less the same as the Hubbard/MON way to do things" (source: s-recollection-brief-history-of-sid).
  • JCH: Hubbard "coded his own music player and delivered a block containing both the player and the sound data, typically with several tunes and jingles as well as an array of sound effects. The game developer would then just call this music player with an argument" — the freelance product that every later driver copied (source: s-chordian-sid-musicians).
  • Gray: "The only tools I had were the routines I designed to handle everything from sequences to modulation … Most of my games tracks were under 4k, often less" (source: s-rvg-matt-gray-interview). And the corollary: a game carries one driver, so when Ninja Remix needed a new composer the score was rewritten in his driver — "there's no room for two music drivers" (source: s-remix64-ninja-remix).

A MIDI-driven player in pseudo-code (Walleij)#

linus-walleij's design notes, written for Michael Kleps, are the smallest complete description of a SID player in the wiki (source: s-walleij-sid-player-routine):

routinewhenwhat it does
instrument_oninstrument changewrites the default cutoff to $D415/$D416, resonance & $F0 | $0F to $D417 (all voices routed) or $00, and the filter mode | $0F (full volume) to $D418
note_onkey pressallocates one of the three channels (optionally stealing the oldest, FIFO); writes frequency, the pulse width if the waveform is pulse ($40), the waveform | $01 (gate) to $D404+offset, then attack/decay and sustain/release; arms the macro speed and the vibrato / pulse-vibrato delays
note_process"a reasonably high rate, say 400 Hz"every macro speed steps (default 8, because "the macro speed on a C64 is usually 50 Hz"; "the highest rate I have seen in a C64 playroutine is 600 Hz") advances the waveform macro, the arpeggio macro (bytes = semitones upwards, "0x00 0x03 0x07 creates a minor chord", skipped when vibrato is on), the pulse macro (high byte only) and the filter macro (cutoff high byte + masked resonance); then, once their delays have elapsed, adds the vibrato LFO to $D400/$D401 and the pulse-width LFO to $D402/$D403, and optionally drives the cutoff from a wheel or LFO
note_offkey releaseclears the gate bit only ("nothing else should be done" — see the hard-restart dilemma on hard-restart)

Tables "can loop or end"; the macro rate is expressed relative to the process rate, which is how sid-wizard's 8× framespeed (400 Hz, multispeed) and this design meet. Pitch comes from a 95-entry note table (index 0 = C-0, 36 = C-3, 57 = A-4; "index 95 = A-7 (last B in octave 8 is not possible to replay with c64)"), with the remark that "the numbers in the C64 hardware reference manual are simply WRONG" and that calculating pitches "is preferable, especially to modulate the pulsewidth and such".

The 1982 view: BASIC and the datasheet#

The c64-programmers-reference-guide's chapter 4 is a player in BASIC: each measure is split into sixteenths, each voice keeps "activity arrays" of frequency high/low bytes and a control byte per sixteenth, notes are packed as ((D×8)+O)×16+N (duration in sixteenths, octave 0–7, note 0–11; a rest is the negative duration), lower octaves are derived from the top octave by halving, and the play loop POKEs all three voices every sixteenth (source: s-c64-prg-chapter-4-sound). The datasheet's Appendix A shows the memory-saving note table every 6502 player uses: keep the 12 sixteen-bit values of the top octave (C7–B7) and right-shift once per octave, addressing them with a note byte whose low nybble is the semitone and high nybble the octave — 24 bytes instead of 192 (source: s-mos-6581-datasheet).

Frames, dumps and comparisons#

  • The frame is 20 ms because trackers "are called from a rasterline interrupt, the calling frequency depends on the screen refresh rate, which is 50Hz in PAL systems" — every wave-table step, and every siddump row, is one such call (source: s-ucapps-wavetable-sounds-tutorial-1).
  • siddump runs the tune's init and play routines in a 6502 emulation and prints the register writes per frame; multispeed players defeat that unless the emulation also covers "the full CPU + CIA timer + IRQ environment", which is why version 1.05's multispeed support is only partial (source: s-chipmusic-sid-music-hints-n-tips).
  • Choosing an editor by its player: chordian's comparison of SID trackers (chordian.net/c64editors.htm) lists "the CPU time in rasterlines that each player requires" (source: s-lemon64-learning-sid-sound-design).

Two game players of 1984 and a timer-driven editor of 2002 (SIDin #1–#2)#

  • Dig Dug (Datasoft, 1984): not a frame-by-frame player but an interpreter of per-voice byte streams — each byte a multiple of 8 indexing a jump table of 22 routines (volume, ADSR, control, frequency, pulse width, filter mode / cutoff / resonance / routing, durations, two repeat counters and gotos); a voice executes instructions until one sets a duration, and the play routine then sleeps for the shortest remaining duration among the three voices (adjustDuration keeps the minimum in $F5). Tracks can be re-assigned to voices at any time (setTrack), which is how the game changes its music "dinamically" with the player's actions; the composer is unknown (source: s-sidin01-dig-dug-rip; the instruction set is on dig-dug).
  • Lazy Jones (Whittaker, 1984): two voices of raw frequency pairs, a step every 12 frames, no instruments and no effects, at most 4 raster lines when a note starts and 1 otherwise — "the most simple I had seen until now" (source: s-sidin02-whittaker-lazy-jones; details on whittaker-player).
  • Iseq (Judd, 2002): play with JSR $1003 "at fixed intervals" — from a CIA timer by default ($4cc7 for a PAL frame, $42c6 for NTSC, $417f for the old 64-cycle NTSC VIC make it "behave just like a VIC interrupt") or from a raster interrupt; "Any music player can only write to SID every time it's called. In iseq, you specify exactly what gets written on each call" — instruments are sequences and modulators compiled into the player, with switches for gate toggling and instrument restarts instead of built-in effects (source: s-sidin01-iseq; iseq). Agemixer's objection is the demo coder's: a tune made with CIA timing cannot be dropped into "a fullscreen FLI demo or raster timing stuff".

Recognising a player from its bytes, and the smallest player of all#

  • A player's code is the same in every tune that uses it, only relocated, so a driver can be identified from a short run of opcodes with the address bytes wildcarded: Tognon's rule (2003) is to keep opcodes, immediates and I/O addresses ($D4xx), wildcard absolute and zero-page operands, and drop branch offsets that point outside the pattern — the galway-player's LDY #$00 / LDA ($xx),Y / CMP #$C0 / BCC (its ≥ $C0 command bytes) is such a fingerprint in seven bytes, the matt-gray-player's double CMP #$FB portamento test a longer one; because tunes are relocated in $100 steps, even the low byte of an absolute address usually survives, which is what the Lostack database's four-byte FUTURE COMPOSER 1.0 entry relies on (sources: s-sidin03-pattern-searching-1, s-sidin05-pattern-searching-2; see player-identification).
  • Every pattern player keeps, per voice, a current position in the track and in the pattern; the Lostack database records their addresses as OrderPos and SecPos so that a player program can display what the driver is doing and detect the true length of a tune from the track end marks — "usually $FF and $FE", one restarting the track, the other silencing the voice; the tune has looped only when all three voices have wrapped (source: s-sidin05-pattern-searching-2).
  • The smallest driver in the wiki is Andrew Colin's routine for The Arrival of the Queen of Sheba in Commodore's Introduction to BASIC (1982–83): seven bytes per voice (control register, a pointer to a list of note numbers, a pointer to a list of durations, a 16-bit counter), a frequency table generated by BASIC, one speed byte (8) subtracted from the counter once per duration unit, and gate off → gate on at each new note; no effects at all, "however the sound is pretty good". Tognon's rip moves BASIC's SID POKEs into a PSID init routine and calls the note routine from the IRQ, which the original never did — "all the players that have not a full C64 environment emulation must use a IRQ calling for generating the sound" (source: s-sidin03-ripping-basic-tune; sid-format).

From SIDin #6–#7: a ripper's method, an Italian game driver, a 256-byte player, a fourth voice#

  • How a ripper finds the player (jan-harries, 2004): music "is a portion of the running program, that writes data to the registers of the SID chip"; in a monitor look for the interrupt setup (writes to $0314/$0315 or $FFFE/$FFFF; an IRQ ends with LDA #$01 / STA $D019 / JMP $EA31 or RTI), follow its subroutines to code with STA $D400,X or STA $D418, find exactly how it is called and what ran before the interrupt was installed (the init, which "often needs to reset the starting point of the music to a zero"), then save the parts and test them in a minimal harness: KERNAL off (LDA #$35 / STA $01), raster IRQ at $D012 = $33 with the CIA timer stopped (STX $DC0E), JSR init with A = 0 "maybe the musicinit needs a number, maybe not", and in the IRQ a border-colour change around JSR play "so you can see how much rastertime the music uses!" (source: s-sidin06-harries-interview).
  • The Modules driver (Ivan Del Duca, 1988; Tognon's disassembly): 4-byte track entries (pattern address, length, repeats), patterns of octave/note bytes with sticky $A0–$E0 durations from a 16-bit table, commands that are the low bytes of a jump table at $C680 ($83 release, $86 instrument, $8C/$8F portamento, $95 copy one SID register into another, $9B LFO / gosub / return / play note), an instrument of nine 16-entry tables holding only ADSR, a fixed pulse width, two control bytes and a filter preset — "al the effects that the player can reproduce are not associated with the instrument, with one exception: the filter"; code $C000–$C5B5, data to $C800, the play address $CFE8 a stub calling $C000 twice. Details on modules (source: s-sidin07-inside-modules).
  • A player in under 256 bytes (Ninja/theDream's xxlarge, a cover of Jeff's "X large" whose original is "about 800 bytes"): no init routine at all; the IRQ installed by the BASIC line POKE 789,8, which turns the default vector $EA31 into $0831 where the player starts; variables in KERNAL zero page, the cursor-blink counter $CD (counting $14$1) as the pulse-table index whose top bit (after asl) also gates the rest of the player — the tempo; one nibble per note, 0 = filter step; the three voices' sustain/release bytes interleaved with the six-entry filter tables at the loop's stride of 7; inc/ror on the filter index chosen by rewriting the opcode; and no attack/decay writes, "according to Ninja there where no more space". It was the model for the tiny-sid-compo (source: s-sidin07-xxlarge).
  • A fourth voice from an NMI, inside one player: Matt Gray's Serpent Demo build of the V4.2 driver keeps the three SID voices on the raster IRQ and adds a sample track driven by a CIA 2 Timer A NMI. SETNMI points $0318/$0319 at the handler, starts the timer ($DD0E = $11) and enables its NMI ($DD0D = $81); each NMI reads one byte through a self-modified pointer and writes its high nibble first, then the low nibble to $D418, stopping or re-starting when the pointer's high byte reaches a stored end page. The pattern byte's NN field is written to $DD04 — the timer's low byte — so the sample's "note" is its NMI rate: "more little is this value and more often the NMI is triggered", and a pattern like $01$40 climbing is an audible pitch sweep of the drums. Sample data is 4-bit, two per byte, seven samples in 4 KB below the code; when no sample is playing MAIN parks $D418 at $09, "volume to middle". This is the concrete answer to Gray's own complaint that those drums were "too loud" (source: s-sidin15-serpent-demo-player; chip-samples, matt-gray-player).
  • A fourth voice from a second player: digiorganizer is a separate $9000/$9003 routine streaming 4-bit samples to $D418 from a CIA #2 NMI, so a four-voice tune is two players — JSR $1000 / JSR $9000 at init, JSR $1003 / JSR $9003 per frame — with the rule that the 3-voice player must not touch the volume register (source: s-sidin06-digiorganizer; chip-samples).

Minimal players (Tiny SID compo)#

The lower bound of a player is documented by the tiny-sid-compo of 2005 — complete tunes with their player in 512 or 256 bytes, sources printed and commented by Tognon in SIDin #8 and #9 (sources: s-sidin08-tiny-sid-512b, s-sidin09-tiny-sid-256b). The winners are recognisable drivers in miniature; the smaller entries replace the driver with the machine.

entry (author, bytes, place)song modelinstrumenttiming
Empty (4mat, 512, 1st)song list of pattern positions / transposes (≤ $7f, $80–$8f, $ff), patterns of notes, lengths $80–$8f, instruments $e0–$ef; 128 bytes of pattern data max5 bytes: SR, pulse add per frame, pulse hi + loop point, three pitch/arpeggio entries ($f0–$ff = offset), three waveformssei, raster wait, tempo $06; 21-byte SID image written in a loop
Twone Five (Laxity, 512, 2nd)one sequence per voice: $00–$7E note, $7F end, $80–$BF duration, $C0–$FF wave pointer; transpose table on voice 2two parallel wave tables (control / relative-or-absolute note, $00 ends); AD $03 fixed, SR per voice; hard restart AD $0f/SR $00 two frames earlysei, IRQ vector → rti, raster $50; 3 frames per step; frequency table computed from 24 bytes
Crue Gurl (A Life in Hell, 512, 4th)order lists of one-byte pattern offsets from pat1; note byte = raw frequency nibbles; durations alternate by eor #8none — SR $e9, $41; voice 2 walks wave + chord (frequency-high) lists one entry per frameloads at $0326, sei, raster wait; compile-time feature flags
New Kid On The Block (Frantic, 256, 1st=)pattern byte = instrument nibble + note nibble, $00 = relative jump3 bytes: AD, control, duration (S*1…S*8, S = $10) — SR is $00loads at $0326; jsr $b5ff (BASIC ROM inc $23 / ldx $23) is the filter sweep; sbx #7 steps the voices
My Block .. one block (Agemixer, 256, 1st=)none — the bits of a free-running 32-value counter select notes, drum on/off (every 4 frames), cutoff and pulse; self-modified masks change the rules every 8 passesfixed per voice from a 23-byte init tablecode copied to the zero page and run there; raster $80
Repeat Me (Laxity, 256, 3rd)sequence bytes: note nibble + octave nibble, $00 rest, negative = duration, $FF repeatone, hard-coded: $21, AD = SR = $55, gate off at note endraster $55; 12 stored pitches, lower octaves by lsr/ror at run time
Random Ninja (Tognon, 256, 7th)one pattern for two voices: bit 7 = drum, low bits = notetick script: $41 then $21 on voice 1, $81 drum with two pitch steps and a $09 restartsei, wait for $D012 = 0 + KERNAL delay $EEB3; $D41B picks a waveform per pattern
128 Byte Blues (freakyDNA, 128, 9th)notes derived from screen RAM bits stepping through two blues scales; tempo from the jiffy clock and cursor blinkone voice, SR $F1, control $15IRQ via POKE 789,8

What the compo adds to the driver comparison above (sources: s-sidin08-tiny-sid-512b, s-sidin09-tiny-sid-256b):

  • The address byte is the cost driver: one-byte pattern pointers as offsets from a base pattern (Crue Gurl, Empty), song data and even code moved into the zero page (Agemixer, Eeben, Judd, A Life in Hell), indices kept in self-modified operands, two notes or note + duration packed per byte (Imperial March, New Kid, Random Ninja).
  • The note table need not be stored: 12 (Empty, 58 bytes of code in all) or 24 (Twone Five, 82 bytes) top-octave bytes shifted right per octave, or 12 pitches shifted at play time (Repeat Me) — the datasheet's Appendix A trick, implemented.
  • No interrupt: sei and a busy-wait on a $D012 raster line is smaller than installing an IRQ; loading the file at $0326 over the KERNAL vectors makes it start on load without a BASIC stub (four bytes; "starting smally", credited to Steve Judd).
  • Instruments collapse to what the ear needs: a waveform, an SR (or AD only), a pulse sweep, and a first frame with the test bit — Frantic's 3-byte rows (AD, control, duration) and Laxity's single $21/$55 instrument won or placed; drums are one noise frame at a fixed pitch. Every entry still gates cleanly, because the envelope bug does not scale down (hard-restart, adsr-envelope).
  • The machine as sequencer: a counter's bits, screen memory, the SID noise register ($D41B), the envelope output as filter modulator ($D41C, Eeben), a BASIC ROM fragment as a sweep, the KERNAL delay as a timer.
  • Tognon: 512-byte engines "could have a more linear structure over a 256 bytes one", which is why he printed them first.

Two side notes from the same issues: aleksi-eeben's John Player was written to be simple and got low rastertime "as a bonus", and his Polly Tracker abandons interrupts entirely — a busy loop polling the CIA timers mixes four sample channels at 9 kHz (chip-samples; source: s-sidin08-eeben-interview); and a player's register timing matters beyond the C64: on the catweasel Mk4 card, tunes with hard restart survived the driver's timing jitter while older ones "without a proper hardrestart" collapsed into what looked like the ADSR bug (source: s-sidin09-catweasel-6581).

Tiny Sid 2: six players under 512 bytes (2006)#

The second tiny-sid-compo shows what is left of a player when the budget is one disk block. Sources: s-sidin10-tiny-sid-2-part-1 and s-sidin11-tiny-sid-2-part-2.

playerauthor, sizesong modelinstrumenteffectssize tricks
XL5Stefano Tognon, 256 btrack of one-byte pattern offsets, patterns of note indices, $00 end (dropped where the next pattern follows anyway)fixed: one waveform per voice, SR $30/$60/$90 made by shiftingnone (the original's filter dropped)load at $0326, zero-page bytes known to be 0/1 after loading, SBX #7 voice loop, a 17-note table
Miniature RhapsodyEric Odland, 256 b16 bytes: xxyyzzzz = arpeggio type, bass note, rootfixed sawtooth; SR/AD constants17-note up-and-down arpeggio, voice 2 one frame behind voice 1, low-pass filterfrequency table built at start from 12 bytes (ASL/ROL per octave)
BLOCK ACID DUBFrantic, 254 b32-byte grid of dddp.pppp (duration, pointer to a 4-entry "probability node"); a CIA timer picks the entryrows = a control byte + the register bytes it flags (SR AD CTRL PULHI PULLO FRQHI), STAY/JUMPdrum hard restart 2 frames early, arpeggio by JUMP, filter type and cutoff from song position and drum counterfrequency high byte only; ANC/ASR/SBX; one data stream for all voices
Mini Digi DrumLeif Bloomquist, 512 btrack bytes: instrument bit, duration, playback speedtwo 255-byte 4-bit samples, nibble-packedspeed nibble = timbre$D418 playback, jiffy-clock timing, test-bit "digibooster"
RMStefano Tognon, 512 bpattern bytes 1000.xxxx instrument / 11nn.nnnn length / 0ccc.nnnn octave+note; transpose tracks per voicecontrol byte + packed AD/SR nibbles (9 instruments)voice-3 arpeggio by fixed frequency adds41-byte runtime frequency routine from 12 octave-0 bytes; note order chosen so a transpose is an ADC
ResolutionEric Odland, 512 b13 patterns by one-byte offsets, a 24-note table, two durationsall voices on one note (voice 3 detuned +8)filter cutoff = ENV3 >> 1 every frame; a "+4 notes" effectall 25 SID registers initialised from a table; the init reused at song end; BASIC SYS
  • What the entries drop: per-note instrument changes (XL5, Rhapsody), the frequency low byte (Frantic), tables of any kind (RM computes, Rhapsody unpacks), the sequencer itself (Frantic's dice, RM's transposition lists). What none of them drops: a raster or jiffy sync and a per-voice countdown — the orderlist and duration logic is the irreducible core (sources: s-sidin10-tiny-sid-2-part-1, s-sidin11-tiny-sid-2-part-2).
  • Rastertime is no concern here (the players run in the foreground with interrupts off, or once per frame from $0314); size is — and the byte-by-byte logs (XL5: 309 → 256 in 13 steps; RM: "-6 use ADSR in same byte", "-5 do not init an already 0 value") are the most detailed size-optimisation notes in the wiki (source: s-sidin10-tiny-sid-2-part-1).

Two sub-kilobyte players, a tracker's player documented by its author, and a game player without the +0/+3 convention (SIDin #12–13)#

  • 512 and 256 bytes: GRG's Plaster and Jaymz Julian's Back To Basics from the Tiny Sid Compo II show what a player keeps when almost everything goes: no interrupt (load over the $0326 vector, sei, spin on $D012), every variable in zero page addressed with ,x at voice offsets 0/7/14 (stepped with the undocumented sbx #$07), one 12-entry octave table halved per octave, one counter driving pulse width and filter cut-off at once, two voices reading one track — and still a $09-then-noise attack and a 0F01 restart write one tick before the note (source: s-sidin12-tiny-sid-2-part-3).
  • jitt64 (2010): Tognon's own player writes all shadow registers to the SID at the start of the IRQ so that the three voices change together (a hard restart could otherwise land late), then decodes the pattern row before the per-tick engines, runs eight per-instrument table cores generated from one macro, the pattern commands, and the hard-restart block last so that it overrides the rest — at the cost of one frame of latency, acceptable "for write only music" but not for demo sync. Instruments are 5 bytes plus 16 table pointers, unshared and up to 2 KB each; the player is "very rastertime consuming" and unquantified (sources: s-sidin13-inside-jitt64, s-sidin13-jitt64-tracker).
  • Three entry points: The Trivia Arcade's music has $5000 init (clears the SID, takes no argument), $5003 called every fourth interrupt and $5006 every interrupt, from a raster chain at lines $01,$41,$6E,$C3, with the subtune chosen by poking $5009; ripping it means writing a small raster driver that reproduces the call pattern (source: s-sidin12-ripping-trivia-arcade; multispeed).

sid-wizard · sid-format · multispeed · sid · linus-walleij · hard-restart · c64-programmers-reference-guide · siddump · rob-hubbard · martin-galway · jeroen-tel

Sources#

s-sid-wizard-manual · s-goattracker-readme · s-witchmaster-creating-chiptunes-with-sid-wizard · s-newman-driving-the-sid-chip · s-walleij-sid-player-routine · s-c64-prg-chapter-4-sound · s-mos-6581-datasheet · s-lemon64-learning-sid-sound-design · s-ucapps-wavetable-sounds-tutorial-1 · s-chipmusic-sid-music-hints-n-tips · s-sidmusic-hubbard-interviews · s-sidmusic-galway-interviews · s-sidmusic-jeroen-tel-interviews · s-hugi38-interview-jeroen-tel · s-64er-soundmonitor-article · s-vgmpf-soundmonitor-tfmx · s-vgmpf-chris-huelsbeck · s-sidin02-matt-gray-driller · s-matt-gray-dominator-source · s-c64com-matt-gray-interview · s-realdmx-sid-players · s-vgmpf-maniacs-of-noise · s-charles-deenen-interviews · s-emuwiki-huelsbeck-interview · s-recollection-brief-history-of-sid · s-chordian-sid-musicians · s-rvg-matt-gray-interview · s-remix64-ninja-remix · s-realdmx-hubbard-player · s-realdmx-galway-rambo-player · s-realdmx-galway-arkanoid-player · s-realdmx-ocean-dunn-hughes-driver · s-realdmx-bjerregaard-player · s-realdmx-ouwehand-mon-tunes · s-realdmx-deenen-test-tunes · s-realdmx-fredgray-kimmel-whittaker · s-realdmx-audial-arts-fame-players · s-sidin01-dig-dug-rip · s-sidin02-whittaker-lazy-jones · s-sidin01-iseq · s-sidin03-pattern-searching-1 · s-sidin05-pattern-searching-2 · s-sidin03-ripping-basic-tune · s-sidin06-harries-interview · s-sidin07-inside-modules · s-sidin07-xxlarge · s-sidin06-digiorganizer · s-sidin08-tiny-sid-512b · s-sidin09-tiny-sid-256b · s-sidin08-eeben-interview · s-sidin09-catweasel-6581 · s-sidin10-tiny-sid-2-part-1 · s-sidin11-tiny-sid-2-part-2 · s-sidin12-tiny-sid-2-part-3 · s-sidin13-inside-jitt64 · s-sidin13-jitt64-tracker · s-sidin12-ripping-trivia-arcade · s-sidin14-hunters-moon · s-defmon-wiki-calling-the-player · s-chipmusic-defmon-thread · s-duet-research-sidwizard · s-duet-research-sidwizard-deep · s-hvsc-sid-file-format · s-cheesecutter-repo-and-releases · s-cheesecutter-guide · s-sidin11-sid-factory-vs-ninjatracker · s-sidin02-cadaver-interview · s-defmon-wiki-defmoning-102 · s-cybernoid-behind-the-notes · s-remix64-huelsbeck-interview · s-happy-computer-soundprofessor · s-chordian-player-editor-notes · s-chordian-my-computer-chronicles · s-sidin15-serpent-demo-player · s-sidin14-dominator-player · s-sidin04-galway-arkanoid

source file wiki/concepts/sid-player-routine.md · graph