SIDin #10 — Tiny Sid 2, part 1: XL5, Mini Digi Drum, RM, Miniature Rhapsody (Stefano Tognon, 2006)
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, ADSR00c9, pulse width 2048; voices 2 and 3 triangle$10with ADSR0069and0029, 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 asid2middump 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-1…A#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
$0326instead of a BASICSYSline (the file's first word points at the code — "you save 8 bytes") → 297 raster sync withSEIand Frantic'sSBXtrick (cycle the voices from the third to the first:.byte $cb,7=sbx #7, thenbpl loopVoice) → 292 pattern 1 cut from 15 to 3 notes,INXfor a 0→1 init → 290LDY abs,Xin 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 ($11for voices 2 and 3,$41for voice 1) → 273 sustain/release$C9/$69/$29replaced by$30/$60/$90written with twoASLs, 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$00end 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 $D012sync;ldx #14; per voicedec duration,x, reloadDUR = 8frames, write the waveform, fetchpat,y(0 = next track entry; a negative track byte restarts the track), look upfreqLo-1,y/freqHi-1,y;lda #$ff / sta $D418sets volume$F(the filter bits are harmless);sbx #7steps 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
dexdelay-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
$FFand 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$0800for 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) andASL/ROLup by the octave — about 41 bytes, with the undocumentedASR(.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 plainADC: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 10 +1 +8 +16, voice 20 +12 +9 +13, voice 30 +12. "Using only 2 additional bytes, and this is lot better than the standard method." - Pattern bytes:
1000.xxxxinstrument,11nn.nnnnnote length (in units ofDUR = 12frames),0ccc.nnnnnote,0end. 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,$41andadsr: $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),
trPosmoved beforetrans1to drop two zeros (−2), skip a zero byte because the next is 0 (−1),BPLforJMP(−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 withASL/ROL; fixed SR$9D(voice 1) and$4E(voice 2), AD$4Eon 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):xxarpeggio 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),yybass note 0–3 (21, 24, 26, 0),zzzzroot 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 (arpeggioVectorflips 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),0restarts (chord-arpeggio). - Header: "Idea to use undocumented opcode SAX borrowed from Stefano Tognon's xl5" (the
$CBopcode XL5 and Frantic callSBX); 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
ADCby ordering the note table for it. - Sequencing without a sequencer: one-byte relative pattern offsets,
$00end 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
$0326starts without aSYS; the undocumentedSBX #7steps a voice index 14 → 7 → 0 and sets the flags forBPL. - 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
$D418with 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
$0326start: the file loads over the kernal vector table so its first word becomes the vector the kernal jumps through afterLOAD(inference from the two words$032A,$F6EDand Frantic's "four byte init code to make it an executable"; the article does not explain the mechanism).