SIDin #4 — Ripping a BASIC program into RSID: Telengard (Stefano Tognon, 2003)
How Stefano Tognon turned a BASIC-only rip of Telengard (Avalon Hill, 1983) — a text adventure whose sound is written entirely in BASIC, without interrupts — into machine code a SID player can run: every BASIC statement becomes a few 6502 instructions plus a call to a routine that wastes as many cycles as the interpreter would have spent, and the result is packaged as an RSID, the sid-format variant in which the init routine may simply never return.
Key claims#
- The rip before the rip: Ninja/The Dreams had extracted the sound-handling lines of the game in 2003 (
0 rem ripped by ninja/the dreams in 2003); it runs in a C64 emulator "but this cannot be executed into a Sidplayer emulator as it is not in machine code". Harder than the Testcard rip of sidin #3 "as no interrupts are used in the game and also Basic TI variable is used only in some part". - The BASIC sound engine: setup at line 50000 — SID base
f=54272, all 25 registers zeroed, and a 60-entry note table computed by a constant ratio downwards from a top frequency:i=8098: r=61176/64814: for k=59 to 0 step -1: nt%(k)=int(i): i=i*r(an equal-tempered table in BASIC). Three sound routines: 50100 "chime i times" — a gong whose length "is given by the duration of the execution of each Basic instructions", not by a timer; 50200 title-page music — long noise/triangle sounds timed withTI, then table music, then five chimes; 50600 "throne music" — table music on three triangle voices (poke f+5+fq*7,18: poke f+6+fq*7,244). The sequencer at 50500 reads rows of four bytes: a duration (fs=peek(ff)*3jiffies ofTI) and one byte per voice —0= leave the voice alone,255= gate off (poke f4+i*7,0), otherwise frequency fromnt%and the voice's control valuef(i)— then busy-waitsif ti<fs then. The note tables "are not present in the Basic code" and were added in the port. - Why RSID: "RSID (Real SID) are PSID file that can be executed into some real C64 environment (e.g. real C64 or sidplay emulator), as it use the real features of the machine, not the compatibly one introduced by old PSID format" — sample music plays as on the real machine "as no digi emulation are done by the sidplayer", and, the point here, "we can execute a flow of instructions without using IRQ routine at all".
- The method:
POKE 54272,10becomesJSR xxxDelayCicle / LDA #10 / STA $D400, the delay routine having "to waste the exact number of cycles that the BASIC instruction will need for being executed into the real machine". Two ways to get the counts: (1) measure averages in an emulator, write the code, then correct by ear — "a temporary work … I hope that one day the sidplayers will manage Basic and Kernal roms, so there will be no reason for using this rip anymore"; (2) modify an emulator to log the exact cycles between consecutive SID writes — "a definitive RSID rip … but a cost of lot of work". He took the first ("I should preferred going into rip of Marble Madness instead!"), timing a test program of 290 identicalPOKE 54272,10lines insideFOR i=1 TO 256. - The measurements (source comments): one
poke f+x,y≈ 0.0053 s ≈ 5 200 cycles;for qq=1 to 10: poke fz,rnd(1)*8+200: next≈ 0.1402 s ≈ 138 000;for qq=1 to 600: next≈ 0.682 s ≈ 672 000;for i=1 to 500: next≈ 0.590 s ≈ 580 000; the line-19000 wait (ti$="000000": if ti<90 then …) ≈ 1.572 s ≈ 1 548 000;if ti<1 then goto≈ 18 000; line 50225 ≈ 8 s ≈ 7 882 000; line 50240 ≈ 9 s. - Delay routines
C2600,C5200,C18000,C138000,C580000,C672000,C1548000: save A, X, Y, then nested loops of cycle-annotated instructions —lda ($33,x)(6),lda $3334(4),nop(2),dex/deyandbne(2 + 2) — e.g.C5200= 256 × (6 + 6 + 4 + 2 + 2). Corrections by ear are left in the comments:C18000"18000 are too many: from #17 -> #12",C138000"add some nops as too fast", six extrajsr C5200after the chime's gate-on ("add some more delay"), andsbc #$03before the sequencer's wait loop because "some cycles are already wastle". - The RSID: header
RSID, version 2, data offset$7C, load address0(the data starts$01,$08=$0801), init$0801, play$0000, 3 songs, "Telengard" / "<?>" / "1983 Avalon Hill". Init dispatches on the accumulator:0→ title page,1→ throne music, else one chime (lda #1 / sta $8E / jmp chime; the title plays five). The ported sequencer keeps the 4-byte rows (tuneA, e.g.$20,$24,$00,$1f= 96 jiffies, voice 1 note$24, voice 2 unchanged, voice 3$1F;$ff= gate off; a duration$00ends the tune) and per-voice control bytes inval,val+7,val+14(17,129,17for the title: triangle, noise, triangle, all gated), with theTIwait rendered as(fs − 3)calls ofC18000. Note tableslow/hihold 59 entries (12, 27, 44 … 162/1 … 31);tuneBis three-voice chords ($2f,$2b,$23,$31,$2d,$25…) in rows of duration$04or$0a. - Conclusion: "RSID rip can be a valid solution for BASIC music. I was able even to convert BASIC music that use RND random number, using some random tables of lookup values."
Practical takeaways#
- Ripping BASIC-timed music: translate statement by statement, pad each with a delay routine sized from a 256-iteration measurement of that statement type in an emulator, correct by ear, and ship it as an RSID so the whole thing can be a non-returning init with busy loops — no IRQ, no play address.
- The minimal three-voice song format, as a BASIC programmer of 1983 wrote it: rows of
duration, note1, note2, note3with0= hold and255= release — the same hold/release idea as a tracker's empty row and note-off (orderlist). - An equal-tempered frequency table needs one multiplication per semitone: start at the top and multiply by
61176/64814(≈ 2^(−1/12)) 59 times. - Tognon's wish that players "manage Basic and Kernal roms" is what the RSID BASIC flag (song number in
$030C) later provided; see the header table on sid-format (source: s-hvsc-sid-file-format).
Notable quotes#
"I hope that one day the sidplayers will manage Basic and Kernal roms, so there will be no reason for using this rip anymore."
Relevance to the wiki#
The RSID rationale in a ripper's words for sid-format — foreground code without interrupts, real-machine timing, no digi emulation — and a worked example of cycle-budgeting on the commodore-64; a sibling of the Arkanoid case (s-sidin04-galway-arkanoid), where RSID is needed for the opposite reason (foreground sample synthesis).
To verify#
- (unverified) The header comment says "default song 2" while the field is written
.word $0100, which in the big-endian header is start song 1; the dispatcher's song 0 is the title music. - (unverified) "Ninja/The Dreams" is not identified further in the article.
Pages touched#
sid-format · sidin · commodore-64 · sid · orderlist