SIDin #10 — Tiny Sid 2, part 1: XL5, Mini Digi Drum, RM, Miniature Rhapsody (Stefano Tognon, 2006)

author Stefano Tognon (ice00), SIDin magazine; sources by Stefano Tognon, Leif Bloomquist and Eric Odland
date 2006-07-16
article SIDin issue 10 (version 1.00, 16 July 2006), pp. 16–35: 'Tiny Sid 2 (part 1)' — sections XL5 (256b), Mini Digi Drum (512b), RM (512b), Miniature Rhapsody (256b), Conclusion; four of the eight entries of the Tiny Sid 2 compo with their sources
created 2026-08-30 · updated 2026-08-31

Four entries of the Tiny Sid 2 compo (2006) — two of 256 bytes, two of 512 — with full source and Tognon's analysis: how a whole tune, player and note table fit into one disk block (tiny-sid-compo, sid-player-routine). "The other 4 will be in next number"; part 2 is s-sidin11-tiny-sid-2-part-2. Results zip: http://digilander.iol.it/ice00/tsid/tinysid2/tinysid2_res.zip.

Key claims#

XL5 (256 bytes, Stefano Tognon) — a cover of Jeff's X-Large 5#

  • The original (about 0:36 long): voice 1 pulse $40, ADSR 00c9, pulse width 2048; voices 2 and 3 triangle $10 with ADSR 0069 and 0029, playing voice 1's line 2 and 4 notes late with low sustain — "a sort of low echo"; all notes equal length, fixed instruments, and a low-pass filter whose cutoff changes every tick (read from a sid2mid dump printed in the article). The filter "is what that give to this tune his style" but is "out of question for a 256 byte tunes", so it was dropped (fake-echo, filter-programming).
  • 17 different notes (F-1A#4), too many for nibble packing; the tune was split into 11 patterns ([1] G#1 G#2 G#3, [2] F#1, [3] G#1 G#2 D#4 G#1 G#2 D-4 G#1 G#2 C-4 G#1 …) played from a track of one-byte relative offsets (orderlist). First working version: 309 bytes.
  • The optimisation log (the source keeps every step): 309 → 301 load at $0326 instead of a BASIC SYS line (the file's first word points at the code — "you save 8 bytes") → 297 raster sync with SEI and Frantic's SBX trick (cycle the voices from the third to the first: .byte $cb,7 = sbx #7, then bpl loopVoice) → 292 pattern 1 cut from 15 to 3 notes, INX for a 0→1 init → 290 LDY abs,X in two places → 277 track/pattern pointers in zero-page locations that are already 0 after loading ($57, $58, and +7, +14 — "Is not granted that if you made other things with the C64 before loading the tune they are still 0") → 274 control-register init moved into the main loop ($11 for voices 2 and 3, $41 for voice 1) → 273 sustain/release $C9/$69/$29 replaced by $30/$60/$90 written with two ASLs, since release is never used → 269 a zero-page byte that is 1 at startup ($2B) as the first-note delay → 265/263/261 patterns run into the next one by removing the $00 end marker where the following pattern is the one played anyway ("pattern 4 → 3") → 259 a redundant instruction → 256 volume init moved to the end of the cycle ("lost first note, but nop not needed").
  • Player shape: cpx $D012 sync; ldx #14; per voice dec duration,x, reload DUR = 8 frames, write the waveform, fetch pat,y (0 = next track entry; a negative track byte restarts the track), look up freqLo-1,y / freqHi-1,y; lda #$ff / sta $D418 sets volume $F (the filter bits are harmless); sbx #7 steps X 14 → 7 → 0 → negative.

Mini Digi Drum (512 bytes, Leif Bloomquist, Schema/AIC) — a sampled tune#

  • "It is a sampled based tune!": two instruments, kick and hi-hat, as 255-byte 4-bit samples packed two per byte (high nibble kick, low nibble hi-hat — "they eat half of the space available"), unpacked at start into two pages ($0900, $0A00); the "513th" byte is filled by code (lda #$14 / sta $09FF).
  • One track byte per hit: bit 7 instrument, bits 6–4 duration, bits 3–0 playback speed (the dex delay-loop count between sample bytes) — the speed nibble turns the same two samples into "bump", "da", "bip", "bup" (kick at speeds 12, 12 short, 1, 5) and "pshhh", "tsk" (hi-hat at 15 and 4); e.g. 76 = kick, quarter note, speed 12; 207 = hi-hat, quarter, speed 15; 164 = hi-hat, eighth, speed 4; 0 = end of track. Playback: lda ($fd),y / sta $d418 / sta $d020 (the border flashes with the sample) over 256 bytes; timing by the jiffy clock at $A2 (duration bits ÷ 4 = jiffies to wait) (chip-samples).
  • "Digibooster" setup: sustain/release $FF and control $49 (pulse + test bit + gate) on all three voices before playing through the volume register; screen blanked ($D011 = 0). Header comment: "Entry into the TinySID2 Compo 2006 (512 byte category)". Bloomquist's own title line reads "Midi Digi Drums".

RM (512 bytes, Stefano Tognon) — a cover of David Whittaker's Red Max#

  • The original (david-whittaker): a four-note arpeggio on voice 3 with a long pulse sweep, a drum on voice 1 from three instruments with an octave arpeggio, a long sound with a falling portamento; 8 instruments defined by ADSR and waveform (the cover's tables hold 9, I1–I9). Pulse fixed at $0800 for all voices in the cover.
  • Runtime frequency table: too many notes to store as 16-bit pairs. "The best runtime generation that I see so far is with about 52 bytes, based onto a 11 bytes of frequency table"; Ivan Del Duca (Modulus) stored 22 top-octave values and shifted down. Tognon inverts it: 12 low bytes of octave 0 (12, 28, 45, 62, 81, 102, 123, 145, 169, 195, 221, 250, "taken from 4mat", high byte always 1) and ASL/ROL up by the octave — about 41 bytes, with the undocumented ASR (.byte $4b,$F0) doing AND and shift in one instruction.
  • Notes are stored as octave/base-note bytes (0ccc.nnnn), and the base-note order in the table is chosen so that a pattern transposition is a plain ADC: C D E D# F G A G# A# C# F# B G A (positions 12–13 repeat 6–7 an octave up). Arpeggio offsets in that numbering: voice 1 0 +1 +8 +16, voice 2 0 +12 +9 +13, voice 3 0 +12. "Using only 2 additional bytes, and this is lot better than the standard method."
  • Pattern bytes: 1000.xxxx instrument, 11nn.nnnn note length (in units of DUR = 12 frames), 0ccc.nnnn note, 0 end. Voice 1 repeats one 10-note pattern and voice 2 one 16-note pattern, each time with a transposition from a transpose track (trans1: 0,0,12,12,0,0,12,12, …, $FF); voice 3 has its own pattern list plus transposes (orderlist, instrument-tables).
  • Instruments: control: $41,$11,$81,$41,$21,$41,$41,$41,$41 and adsr: $4A,$09,$09,$08,$30,$48,$49,$6a,$8C — AD and SR packed in one byte (low nibble → $D405, high nibble → $D406; "-6 use ADSR in same byte"). The voice-3 arpeggio at octave 6 adds a fixed frequency increment per step ($0624) from a 2-entry table — "different from the original", tuned by ear because the real one ran out of space (chord-arpeggio).
  • Other savings: pattern 4 followed by pattern 5 (−3), trPos moved before trans1 to drop two zeros (−2), skip a zero byte because the next is 0 (−1), BPL for JMP (−2), no init of an already-zero value (−5).

Miniature Rhapsody (256 bytes, Eric Odland, Stone Monkey Software) — "almost based onto arpeggio"#

  • Starts at $0326, SEI, "synchronized to raster position using 70 tests passages"; a frequency table built at startup from 12 low bytes ($0C,$1C,$2D,$3F,$52,$66,$7B,$92,$AA,$C3,$DE,$FA, high byte 1) doubled octave by octave with ASL/ROL; fixed SR $9D (voice 1) and $4E (voice 2), AD $4E on voice 3; low-pass filter on all voices ($D416–$D418 = $70, $8F, $1F), written from inside the loop "to save space".
  • Voice 1 plays the arpeggio, voice 2 the same notes one frame late, voice 3 the bass; all sawtooth $21.
  • One byte per bar (xxyyzzzz): xx arpeggio type 0–3 from a 4-byte table (%00111000 "1 4 3 - maj7 3rd inversion", %01100100 "2 3 2 - 4 root", %10111000 "3 4 3 - min7 root", %11010000 "min2 root" — three 2-bit intervals, each +1), yy bass note 0–3 (21, 24, 26, 0), zzzz root 1–F (+31). The player unpacks it into a 17-note array (the chord, then the same four notes +12 three times over) and runs up and down it (arpeggioVector flips at index 16); the pattern is 16 bytes in four parts ($95,$86,$E7,$8B / $D5,$22,$BC,$BB / $29,$89,$29,$8E / $1E,$9C,$73,$73), 0 restarts (chord-arpeggio).
  • Header: "Idea to use undocumented opcode SAX borrowed from Stefano Tognon's xl5" (the $CB opcode XL5 and Frantic call SBX); created 2006-03-22.

Practical takeaways#

  • Sub-kilobyte players trade tables for arithmetic: build the frequency table at startup from 12 bytes (Odland), or compute it per note from an octave/base-note byte (Tognon, 41 bytes); pack duration and pointer, or arpeggio/bass/root, into one byte; let a transposition be an ADC by ordering the note table for it.
  • Sequencing without a sequencer: one-byte relative pattern offsets, $00 end markers dropped where patterns run on, patterns replaced by transpose lists when a voice repeats one phrase (RM) — all forms of an orderlist.
  • Zero-page bytes known to be 0 or 1 after loading are free variables; a file loaded at $0326 starts without a SYS; the undocumented SBX #7 steps a voice index 14 → 7 → 0 and sets the flags for BPL.
  • Cheap echo/chorus: a second voice replaying the first voice's notes a frame or a few notes late with lower sustain (XL5's original, Miniature Rhapsody) — see fake-echo.
  • A 4-bit digi drum kit in 512 bytes: two 255-byte samples nibble-packed, played through $D418 with a per-hit delay count that doubles as the "instrument" (chip-samples).

Notable quotes#

"I have carefully look at address page 0 to find 6 location that are in sequence of 0, +7, +7 and are empty."

"It is not convenient to store only the high/low frequency of the used notes, as for sure generating them at runtime will take less space."

Relevance to the wiki#

Concrete data formats and byte-saving tricks for sid-player-routine and tiny-sid-compo; the smallest sampled drum player in the wiki for chip-samples; packed-arpeggio and transpose-by-ADC ideas for chord-arpeggio and orderlist; authors and sizes for the compo page.

Pages touched#

tiny-sid-compo · sid-player-routine · chip-samples · chord-arpeggio · orderlist · instrument-tables · fake-echo · david-whittaker · sidin · octave-bass · semitone-math · chord-inversions

To verify#

  • Placings are not given in this part ("For seeing the winners of the compo analyzed, you must attend the next number"); part 2 (s-sidin11-tiny-sid-2-part-2) again defers two entries, so which entries won is stated in neither.
  • "Jeff" of the X-Large series is not expanded in the article (Søren Lund is listed as "Jeff" on maniacs-of-noise's roster — unverified that it is the same person).
  • The $0326 start: the file loads over the kernal vector table so its first word becomes the vector the kernal jumps through after LOAD (inference from the two words $032A, $F6ED and Frantic's "four byte init code to make it an executable"; the article does not explain the mechanism).
source file wiki/summaries/s-sidin10-tiny-sid-2-part-1.md · graph