SIDin #12 — Tiny Sid 2, part 3: Plaster (512 b) and Back To Basics (256 b) (Stefano Tognon, 2008)

author Stefano Tognon (ice00), SIDin magazine
date 2008-05-24
article SIDin issue 12 (version 1.00, 24 May 2008), pp. 18–25: 'Tiny Sid 2 (part 3)' — sections Plaster 512b, Back To Basics, Conclusion; the last two entries of the Tiny Sid Compo II, each with a commented disassembly. Parts 1 and 2 are in issues 10 and 11. The PDF's text extraction shifts some comment lines
created 2026-08-30 · updated 2026-08-30

The end of Tognon's series on the Tiny Sid Compo II: the two remaining entries, GRG's 512-byte Plaster — "the tune that take more points over all the others" — and Jaymz Julian's 256-byte Back To Basics, both "done by reverse engineering the code". Two complete SID players that fit in half a kilobyte and a quarter of one, with the tricks that make that possible.

Key claims#

Plaster (GRG, 512 bytes)#

  • "Autostarting at $0326 by setting the IRQ": the file loads over the vector at $0326 (bytes $2A,$03 → the code at $032A, then $ED,$F6), so the tune starts on its own. The player then does sei, synchronises to the raster (cmp $D012 loop) and never returns — no interrupt at all.
  • Init clears the zero page with a sta $0002,y loop and copies per-voice start values there; all voice state lives in zero page ($84–$D8, a commented map in the article: note duration, pattern offset, pattern index, track index, note, pattern address, control index, instrument, transpose, "note duration for control", control value, pulse low byte per voice, plus the filter direction and cutoff high byte).
  • Filter sweep: the cutoff high byte ($D416) runs from $3F to $FF and back, one step per frame, driven by a direction flag; $D417 = $F1 (resonance $F, voice 1 filtered), $D418 = $3F (filter-programming).
  • Song = two tracks: one for voice 1 ($83,$01,$01,$01,$01,$8A,$01 … $06,$00) and one shared by voices 2 and 3 ($02,$02,$03,$03,$04,$04,$05,$05,$00). A track byte is an index into a pattern table; a negative byte's low five bits are the transpose for the next pattern (orderlist).
  • Pattern bytes: a value below 8 selects an instrument, otherwise the low 7 bits are the note; a negative byte is the last command of the pattern. Example patAC: $03, $3A,$33,$3A, $03, $36,$3A,$35, $36, $B3.
  • Instruments are three parallel 5-entry tables — controlIndex $00,$04,$02,$02,$0C (an index into a shared list of control-register values), AD_inst $00,$0F,$02,$E0,$08, SR_inst $A4,$FA,$8A,$8A,$E8. The control list is three sequences: $09,$81,$41,$00, $09,$81,$11,$11,$11,$11,$11,$00, $09,$81,$41,$40,$40,$80 — every sound starts with $09 (test + gate) and a frame of noise before its waveform (wavetable-programming, hard-restart). A parallel useFreq list supplies a fixed high frequency byte for some steps ($8B,$89,$86,$84, $8E,$8C,$8A — drum pitches, negative = "7bits=high freq. to use for effect").
  • Pulse width is "varied by the player for making better effect": $18 is added to each voice's pulse low byte every frame; the high bytes start at $08,$08,$04 (pulse-width-modulation).
  • "Hardrestart of note is performed by the player": when a note's remaining duration reaches 1 the code writes SR $01 and AD $0F — the 0F01 value trackers later made a default — one tick before the next note (read from the listing; see To verify).
  • Notes come from a 12-entry octave table (loFreq/hiFreq), halved per octave with lsr $C2 / ror $C1; the note byte's low nibble is the semitone, the high nibble the number of halvings.
  • Voices 2 and 3 play the same track but are kept apart: voice 2's transpose is XORed with $F0 at each track step, voice 3's instrument is XORed with $07 at each pattern, voice 3 adds a fixed $1E to its frequency low byte (detune), and voice 1 forces instrument 5 through a self-modified pattern byte. The voice offset (0, 7, 14) is stepped with the undocumented sbx #$07.

Back To Basics (Jaymz Julian, 256 bytes)#

  • Same $0326 autostart and raster sync; $46 bytes of data are copied to zero page $01–$47 "for access them with short instructions" — patterns, tables, counters and the three voices' state all sit in one block.
  • One 8-bit counter ($47), incremented every four main loops, is written both as the pulse-width high byte and as the filter cutoff high byte (txa; adc $47; sta $D403,x; sta $D416), so each voice gets a different pulse width from the same counter and the filter sweeps with it; pulse low byte fixed at $80; $D418 = $1F, $D417 = $F4 (voice 3 filtered).
  • Voice 2 reads a table of frequency high bytes ($77,$FF,$28,$2F,$3C,$00,$02,$FF,$0B,$0B,$B5,$FF) and a table of control registers ($0C,$81,$41,$41,$41,$00); voices 1 and 3 read patterns of raw frequency bytes (voice 3 writes the byte to the low register and its low nibble to the high one).
  • "Note duration are triggered between 2 values during the play": the duration is XORed with a constant ($39 = $08) after every note, so successive notes alternate between two lengths (shuffle-funktempo).
  • At every pattern end the voice gets SR $E9 and control $41; every 16 pattern ends the SR and control are cleared and voice 2's frequency high byte is XORed with $0C to vary the bass.

Conclusion#

"Remember that writing tiny music is a sort of programming art: you have to customize your player according to the instruments to use, with the music effect you want to achieve and the melody of your tune, arranging all the code in a manner that must be the smallest one."

Practical takeaways#

  • Sub-kilobyte players are built from: a load address over a system vector for autostart; zero-page state with ,x voice offsets 0/7/14; one octave table shifted per octave; one shared counter driving pulse width and filter at once; two voices on one track differentiated by XORed transpose, instrument or a fixed detune; alternating note lengths for swing; and the same $09$81 → waveform start and 0F01 restart write that full trackers use.
  • GRG — credited elsewhere in the wiki with turning JCH's hard restart into a "soft restart" (hard-restart) — wins a 512-byte compo with a player that still hard-restarts.

Notable quotes#

"All the engines present here are done by reverse engineering the code."

Relevance to the wiki#

Results and player descriptions for tiny-sid-compo; the smallest complete players on sid-player-routine; dated examples of the $09/noise attack, the 0F01 restart, a shared PWM/filter counter and alternating durations for hard-restart, pulse-width-modulation, filter-programming and shuffle-funktempo.

To verify#

  • (unverified) $0326 is the KERNAL's CHROUT (character output) vector, so a file loaded over it starts on the next character printed — the article only says "by setting the IRQ".
  • The 0F01 write is read from a listing whose comment lines were shifted by the PDF text extraction; the order "lda $84,x (note duration) — cmp #$01sta $D406,xlda #$0Fsta $D405,x" is the reading that matches the comments.

Pages touched#

tiny-sid-compo · sid-player-routine · hard-restart · pulse-width-modulation · filter-programming · wavetable-programming · shuffle-funktempo · detune · orderlist · sidin

source file wiki/summaries/s-sidin12-tiny-sid-2-part-3.md · 1 unverified · graph