SID player routine, rastertime and player types
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#
| type | what you get |
|---|---|
| Normal | everything 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 |
| Medium | smaller; drops vibrato type, hard-restart type, frame-1 setting, PW keyboard tracking, note-off index, subtune-jump effect, zeropage save/restore |
| Light | smaller 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 |
| Extra | Normal 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 |
| Bare | very 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 |
| Demo | what 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
MULPLYcalls spaced evenly across the 312 rasterlines. Each call processes channels 3 → 2 → 1, thenCOMMONREGSwrites 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
FASTSPEEDBINDfolds 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–$D418block 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
$40threshold inSWMconvert.cis 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):
initAddressis called once with the song number in the accumulator;playAddressis called repeatedly. AplayAddressof 0 means the init routine installs its own interrupt handler — which is always the case for RSID.- The
speedfield 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,playAddressandspeedto 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 ($4025PAL,$4295NTSC); to run an NTSC tune on a PAL machine the CIA value is$3FFB, a PAL tune on NTSC$5021. Theflagsword carries the clock (PAL/NTSC/both) and the SID model (6581/8580/both) per file, andstartPage/pageLengthdescribe 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
.ctfile contains its player.ct2util importcopies data from another song "without overwriting the player" (i.e. moves a tune between player versions) andct2util initcreates "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
$18scanlines" — 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
$00bis$FFabrufen. 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.GNscheme, 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 theJCH 02/NEWPLAYER V17.G1entry 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 / soundmonitor | matt-gray-player | musicfile (Maniacs of Noise) | |
|---|---|---|---|
| author, year | chris-huelsbeck, driver 1985, editor 1986 | matt-gray, from late 1986; "PLAYER V4.2 (C)1987" | charles-deenen, 1987; "MUSICFILE V01-07-1988" |
| how music was entered | the Soundmonitor editor (before it: hex in the SMON monitor) | typed into the assembler as tables; a Casio keyboard for ideas | typed into the driver's Turbo Assembler source as "hexadecimal numbers and labels" |
| song model | track/step table of bar addresses per voice, with transpose and sound-transpose columns | three tracks of pattern numbers ($FF restart, $FE stop) → patterns of note bytes and $FA–$FD commands | three sequences of step-table numbers with ≥ $80 command bytes → 33 step tables |
| instrument | 24 bytes: waveform on/off, ADSR, pulse + pulse EG, portamento, vibrato, detune, filter + filter EG | two 8-byte rows + shared arpeggio lists; V4.2 adds PLEX chord tables and a drum table | 8-byte row (pulsehi, waveform, attdec, susrel, filcount, fx1–3) + shared pulse, filter, arpeggio, drum and start-waveform tables |
| effects | transpose, detune, portamento, vibrato, PWM, filter modulation, 8-step arpeggios | arpeggio, vibrato (triangle on the frequency value), lo/hi portamento, pulse bounce $08–$0E, 2-frame drum waveform, noise effect | tone/wave/pulse arpeggio, vibrato, glide, sweep, "pulserun", "strangefilter", double voices, "space effect", drum routine |
| documentation | the 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) |
| rastertime | — | 10 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 49152after 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.
| driver | author, year, provenance | song model | instrument | effects | size |
|---|---|---|---|---|---|
| hubbard-player | rob-hubbard, 1985; McSweeney's disassembly of Monty on the Run, a Commando disassembly | track of pattern numbers; 1–3-byte notes (length + flags, instrument or portamento, note) | 8 bytes: PW, control, AD, SR, vibrato, pulse, flags | vibrato (8-frame 01233210), pulse bounce $08–$0e, portamento, drum / skydive / octave-arpeggio flags | ≈ 1 KB code |
| galway-player | martin-galway, 1986; dmx87 (Rambo loader), Tognon / SIDin #4 (Arkanoid) | byte-code per voice: CALL/RET, REPEAT/NEXT, JMP, transposed calls, EXEC native code | 29 bytes: 4 frequency adders + times, 2 pulse adders + times, control, AD, SR, gate time, voice-wipe time | one adder engine for vibrato / slide / PWM / filter; 4-note arpeggios; Arkanoid: filter envelope per note, 2× timer rate, $D418 percussion synthesis, 12 SFX | 2.3 KB (Rambo) / 4.5 KB (Arkanoid) code |
| ocean-music-driver | Paul Hughes (driver), jonathan-dunn (music), Ocean 1988; original source | byte-code per voice: CALL/RET, LOOP/NEXT, CT transposed call, CODE, PORT; notation of note names and note-value constants | 5-byte patch: gate-on control, AD, SR, hold (or gap), gate-off control | BEND into the note, VIBON, PWM entries, ARPON tables, EFFECT pluck, DRUM tables on voice C; fades as self-modifying data | 3.8 KB driver in a 5.5 KB budget |
| bjerregaard-player | johannes-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 nibbles | software second sustain, two hard-restart schemes, semitone-proportional vibrato, one global filter cutoff, drums as instruments | ≈ 1.5 KB code |
| musicfile builds | charles-deenen 1988–89; test tune, Armada ("Special version for Reyn Ouwehand"), Dutch Breeze (1990 rewrite), the "Scoop" player | sequences of steps with upward transposes; 1990: intro + loop records, legato, rests | 8 bytes (pulsehi, waveform, attdec, susrel, filcount, fx1–3) + shared programs | 13 routines (arpeggios, vibrato, glide, pulse, filter, drums, noisetik …); 1990: segment filters, bounded pulses, looping drums, detune | ≈ 2.4 KB / 2.1 KB code |
| whittaker-player | david-whittaker, 1986 (Panther); dmx87 | tracks of pattern addresses; patterns of notes and command prefixes | none — the voice struct holds the last prefixes | commands for waveform, ADSR, tempo, arpeggio, vibrato, accelerating slides, pulse bounce, alternate-frame drum | 1.7 KB engine |
| Fred Gray, Mutants | fred-gray, Ocean 1987; unsigned disassembly | song as code: 26 section routines + step table; one note byte per row | 8 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 player | jeroen-kimmel, 1989; own source | sequence words + patterns of (note, instrument·duration) pairs | 8 bytes per voice table, flag byte | waveform 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 comments | steps with sticky lengths, transposes, repeats | 8 bytes: AD, SR, pulse, wave/table, vibrato mode, pulse+filter programs, wave table, release | hard restart (bit 7), auto gate-off in ticks, one table engine for vibrato / arpeggio / drum pitch | ≈ 2.9 KB |
| Audial Arts "zong player" v1/v2 | Francois Prijt, 1991; own sources, Dutch comments | tracks of blocks with repeat / transpose / instrument-offset bytes; notes with durations in frames | 16 bytes: two waveform presets (gated / released), gate length, accelerating pulse sweep, arp + wave pattern, filter | staggered 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, withTOGObytes 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).
| driver | author, year, provenance | song model | instrument | effects | size |
|---|---|---|---|---|---|
| Colin's Sheba routine | Andrew Colin, 1982–83 (Commodore's Introduction to BASIC part 2); Tognon's rip, SIDin #3 | per voice a list of note numbers and a list of durations, 0 ends the voice; a 16-bit counter × speed byte 8 between notes | none — one control byte per voice, ADSR written once by BASIC | none (gate off / on per note) | ≈ 160 bytes + a 128-byte frequency table (estimate from the listing) |
| dig-dug routine | composer unknown; Datasoft's 1984 cartridge; Tognon's rip, SIDin #1 | 14 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 duration | none — the stream writes ADSR, pulse, filter and volume directly | per-voice filter routing, octave pairs, one-frame frequency runs under a held gate | not stated |
| Lazy Jones routine | david-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 + 11 | none: AD $66, SR 0, sawtooth, $D418 = $1F at init | none (gate $20 → $21 per note) | 21 × $80 bytes of data; code well under 256 bytes (estimate) |
| hunters-moon SFX engine | martin-walker, Thalamus 1987; Tognon's disassembly from the TAP, SIDin #14 | no song — 16 effect routines storing one to three table indexes; a 50-frame lock for explosion / star cell | 13 tables × 32 columns: AD, SR, packed pulse, frequency, start and end control, loop length, frequency steps, two effect selectors, pulse step | six 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 driver | Ivan Del Duca, 1988; Tognon's disassembly of the PSID "Modulus", SIDin #7 | three 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 phrases | 9 tables × 16: AD, SR, pulse lo/hi (fixed), control on, control off, cutoff high, resonance/routing, mode | one 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 |
| xxlarge | Ninja/theDream, 2004; source printed in SIDin #7 | one 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 late | none — waveform fixed per voice, SR from an interleaved table, no AD | pulse 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 second | under 256 bytes including the music |
| jitt64 player | stefano-tognon, 2010; the author's own commented source, SIDin #13 | three tracks of pattern numbers 0–222 with REP, TR ±15 and RST; patterns with their own tempo and length; rows of note · instrument · command · parameter | 5 bytes (HR, AD, SR, CTRL1, CTRL2) + 16 pointers to unshared per-instrument tables — up to 2 KB per instrument | 16 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):
| routine | when | what it does |
|---|---|---|
instrument_on | instrument change | writes 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_on | key press | allocates 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_off | key release | clears 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 (
adjustDurationkeeps 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 ($4cc7for a PAL frame,$42c6for NTSC,$417ffor 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'sLDY #$00 / LDA ($xx),Y / CMP #$C0 / BCC(its≥ $C0command bytes) is such a fingerprint in seven bytes, the matt-gray-player's doubleCMP #$FBportamento test a longer one; because tunes are relocated in$100steps, even the low byte of an absolute address usually survives, which is what the Lostack database's four-byteFUTURE COMPOSER 1.0entry 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
OrderPosandSecPosso 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/$0315or$FFFE/$FFFF; an IRQ ends withLDA #$01 / STA $D019 / JMP $EA31orRTI), follow its subroutines to code withSTA $D400,XorSTA $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 = $33with the CIA timer stopped (STX $DC0E),JSR initwithA = 0"maybe the musicinit needs a number, maybe not", and in the IRQ a border-colour change aroundJSR 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–$E0durations from a 16-bit table, commands that are the low bytes of a jump table at$C680($83release,$86instrument,$8C/$8Fportamento,$95copy one SID register into another,$9BLFO / 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$CFE8a stub calling$C000twice. 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$EA31into$0831where the player starts; variables in KERNAL zero page, the cursor-blink counter$CD(counting$14→$1) as the pulse-table index whose top bit (afterasl) 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/roron 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.
SETNMIpoints$0318/$0319at 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'sNNfield 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–$40climbing 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 playingMAINparks$D418at$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/$9003routine streaming 4-bit samples to$D418from a CIA #2 NMI, so a four-voice tune is two players —JSR $1000 / JSR $9000at init,JSR $1003 / JSR $9003per 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 model | instrument | timing |
|---|---|---|---|
| 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 max | 5 bytes: SR, pulse add per frame, pulse hi + loop point, three pitch/arpeggio entries ($f0–$ff = offset), three waveforms | sei, 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 2 | two parallel wave tables (control / relative-or-absolute note, $00 ends); AD $03 fixed, SR per voice; hard restart AD $0f/SR $00 two frames early | sei, 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 #8 | none — SR $e9, $41; voice 2 walks wave + chord (frequency-high) lists one entry per frame | loads 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 jump | 3 bytes: AD, control, duration (S*1…S*8, S = $10) — SR is $00 | loads 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 passes | fixed per voice from a 23-byte init table | code 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 repeat | one, hard-coded: $21, AD = SR = $55, gate off at note end | raster $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 = note | tick script: $41 then $21 on voice 1, $81 drum with two pitch steps and a $09 restart | sei, 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 blink | one voice, SR $F1, control $15 | IRQ 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:
seiand a busy-wait on a$D012raster line is smaller than installing an IRQ; loading the file at$0326over 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/$55instrument 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.
| player | author, size | song model | instrument | effects | size tricks |
|---|---|---|---|---|---|
| XL5 | Stefano Tognon, 256 b | track 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 shifting | none (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 Rhapsody | Eric Odland, 256 b | 16 bytes: xxyyzzzz = arpeggio type, bass note, root | fixed sawtooth; SR/AD constants | 17-note up-and-down arpeggio, voice 2 one frame behind voice 1, low-pass filter | frequency table built at start from 12 bytes (ASL/ROL per octave) |
| BLOCK ACID DUB | Frantic, 254 b | 32-byte grid of dddp.pppp (duration, pointer to a 4-entry "probability node"); a CIA timer picks the entry | rows = a control byte + the register bytes it flags (SR AD CTRL PULHI PULLO FRQHI), STAY/JUMP | drum hard restart 2 frames early, arpeggio by JUMP, filter type and cutoff from song position and drum counter | frequency high byte only; ANC/ASR/SBX; one data stream for all voices |
| Mini Digi Drum | Leif Bloomquist, 512 b | track bytes: instrument bit, duration, playback speed | two 255-byte 4-bit samples, nibble-packed | speed nibble = timbre | $D418 playback, jiffy-clock timing, test-bit "digibooster" |
| RM | Stefano Tognon, 512 b | pattern bytes 1000.xxxx instrument / 11nn.nnnn length / 0ccc.nnnn octave+note; transpose tracks per voice | control byte + packed AD/SR nibbles (9 instruments) | voice-3 arpeggio by fixed frequency adds | 41-byte runtime frequency routine from 12 octave-0 bytes; note order chosen so a transpose is an ADC |
| Resolution | Eric Odland, 512 b | 13 patterns by one-byte offsets, a 24-note table, two durations | all voices on one note (voice 3 detuned +8) | filter cutoff = ENV3 >> 1 every frame; a "+4 notes" effect | all 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
$0326vector,sei, spin on$D012), every variable in zero page addressed with,xat voice offsets 0/7/14 (stepped with the undocumentedsbx #$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 a0F01restart 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
$5000init (clears the SID, takes no argument),$5003called every fourth interrupt and$5006every 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).
Related#
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