realdmx: two scene players with their own sources — Francois Prijt's Audial Arts 'zong player' v1/v2 (1991) and Adam Bulka's FAME 'Sound Routine f 4.1.0' (1988)

author Francois Prijt (Audial Arts / Flash Inc., 1991: 'zong player v1.0' and 'v2.0'); Adam Bulka (FAME, 'SOUND ROUTINE f 4.1.0, WORKING VERSION I, DATE: 27.07.1988 2.29 pm'); original Turbo Assembler sources, ACME conversion by dmx87
date 2023-06-24
article aaplayer1.asm (1255 lines, PSID 'Audial Arts v1', 1991 Audial Arts), aaplayer2.asm (1143 lines, 'Audial Arts v2', 2 songs), Bulka_Adam_SoundRoutine_f_4.1.0.asm (1739 lines, PSID 'Sound Routine f 4.1.0', 1988 FAME). The date is the repository's last push
created 2026-08-30 · updated 2026-08-30

Three original Turbo Assembler sources in the realdmx collection (s-realdmx-sid-players) from the demoscene rather than the games industry: Francois Prijt's "zong player" for the Dutch group Audial Arts (two versions, 1991, Dutch comments) and Adam Bulka's "Sound Routine f 4.1.0" for FAME (dated to the hour: 27.07.1988, 2.29 pm; German comments). Both are ~3 KB players with 16-byte and 8-byte instruments, both put the gate length in the instrument, and the FAME routine does a hard restart in 1988.

Key claims#

Audial Arts "zong player" v1.0 / v2.0 (Francois Prijt, 1991)

  • v1 header: "(c) copyright FRANCOIS PRIJT / -AA- / Audial Arts / Flash Inc. 1991 / >> zong player v1.0 <<", text "player by fp/-aa-(c) "; entries reset (A = song), play, stop, and a demo raster-IRQ entry; its variables live outside the player at $4a08 (varia = $4a08 ;geheugen adres variab.) and leftover keyboard-controller variables (;keyboard on(1)off(0), ;sound nr.#$, ;wich voice (0-2)) show it was cut out of an editor build. v2 ("zong player v2.0 (c) Francois Prijt!") is self-contained, drops the demo entry, and its instrument comment says ";sound instelling (pak handleiding!)" — "sound settings (grab the manual!)" — so a manual existed.
  • Data model: song = three track pointers; track bytes $00–$3f block, $40+n play the next block n times, $80–$bf transpose up & 31, $c0+n / $e0+n instrument offset ±n, $ff,pos loop to a track position (t11 !by $e0,$98,$43,$05 / $98,$43,$06 / $98,$43,$05 / $80,$42,$07 / $ff,$00); block bytes = note + duration in frames (2 bytes per note, on a 6-frame grid in v1), $c0+n instrument, $e0/$e1 target,dur,speed glide up/down, $ff end. No tempo variable at all (orderlist).
  • Instrument = 16 bytes (instrument-tables): 0 = two preset indexes into wavepr !by 0,$10,$20,$40,$80,$50,$12,$14,$44,$14,$20,$14,$10,$14,$20,$24 — the waveform while gated and the waveform after gate-off ($23 = sawtooth gated, pulse released; $44 = noise/noise; $66 = triangle + sync both); 1–2 AD/SR; 3 = gate length − 3 frames (the gate is an instrument property, the note duration only says when the next note starts); 4 = frame at which the pulse sweep starts; 5 = pulse width ($30$0300); 6 = sweep turnaround in frames; 7 = sweep add; 8 = acceleration added to the sweep add every frame; 9 = arpeggio table (high nibble) + waveform pattern (low nibble), sharing one counter — a drum is one arp table (pitch per frame) plus one wave pattern; 10–14 filter (on/off + direction, mode + resonance, cutoff, min/max nibbles, add per frame); 15 flags: bit 0 "tock" (two frames of noise at note $5f — a click), bit 1 "haper" (two frames of $01, gate only), bit 2 fixed pitch (drums). Rows: bass drum s2 !by $00,$0f,$e7,$00 / $00,$08,$04,$40 / $00,$22,$00,$00 / $00,$00,$00,$04; a filtered pulse lead s0f !by $33,$00,$ec,$01 / $00,$03,$04,$70 / $00,$60,$01,$3f / $30,$2c,$0c,$02; v2's bass !by $33,$01,$ec,$00 / $00,$03,$04,$60 / $00,$00,$03,$1f / $c0,$4c,$18,$00 (LP, resonance $f, cutoff $c0 falling $18 per frame between $40 and $c0) (pulse-width-modulation, filter-programming).
  • Tables: arpeggios with $ff restart (ap5 !by 0,12,7,3,$ff; v2 ap3 !by 14,14,19,19,22,22,$ff, ap4 !by 12,15,19,$ff — three-note chords cycling one entry per frame for the lead instruments 4–7) and drum pitch lists (ap2 !by $5f,$1e,$1c,$18,$14,$10,$0e,$0c, a kick falling from note 95); waveform patterns (;golfvorm pattern tabel(for drums ect)) wv1 !by $41,$41,$80,$80,$80,$10,$80,$c4 ($c0|n = jump to index n, $ff = freeze), wv2 !by $81,$41,$40,$40,$40,$40,$40,$40,$ff (chord-arpeggio, wavetable-programming).
  • Engineering notes (read from the code): a staggered look-ahead — voice n pre-reads its next event on frame n + 1 of the current note into shadow variables, spreading the sequencer over frames; v1's glide path pops the stack without a push (a latent crash; no track uses the glide block), fixed in v2 by staging AD/SR in self-modified operands; v2 zeroes $d404/$d405/$d406 at note-on and rewrites them in the same frame — a retrigger, not a hard restart; v2 has a $fe "stop voice" track command that can never fire. Sizes (estimated): v1 ≈ 2.6 KB, v2 ≈ 3.0 KB.

FAME "Sound Routine f 4.1.0" (Adam Bulka, 1988)

  • Header box: "SOUND ROUTINE f 4.1.0 / WRITTEN BY ADAM BULKA / WORKING VERSION I / DATE: 27.07.1988 2.29 pm". Entries musicset0 (A = song) and music. Comments in German: NOTEN DEFINITION, werte direkt in sid, Betriebsart, GEFILTERT, floete, lange halten. Drum "notes" are constants: bd = $21, sd = $29, cd = $1a.
  • Timing: a tick every speedtab + 1 = 3 frames; voices processed 2 → 0; a note length $80+n lasts n + 1 ticks. Track bytes: step number, $80–$9f transpose up, $a0–$bf instrument offset, $c0+n repeat the next step n + 1 times, $ff lo hi absolute jump (track1 !by $bf+2,2 / 3,3,3,3,3,3,3,11 / 17,17 / 0 / $ff / !wo track1). Step bytes: < $60 note, $60–$7f extended arpeggio 1–4 (switchable mid-step), $80–$9f length (sticky), $a0–$bf instrument, $c0–$df wait without retrigger, $e0–$fe portamento len,time,from,to, $ff end. Drums and melody share a voice by switching instruments per note: s3 !by $81,$a1,bd / $af,e5,d5,h4,e4,g4 / $a1,bd,$af,d5 (channel-interleaving).
  • Instrument = 8 bytes: AD, SR, puls (one 8-bit value written to both $d402 and $d403), wave (a raw $d404 value, or the wave-table length), wibbyte — ";Vibrato Pointer / ;Betriebsart / ;$80=tab/$c0=arp" (0 none, 1–31 vibrato table, $80|n direct-frequency table, $c0|n note-offset table), fpbyte — ";Lownibble=Puls Po. / ;Highnibble=Fil Po.", wavetab (table number, bit 6 = gate-off time counted from the note end, bit 7 = never), release (bit 7 = hard restart, bits 0–6 = gate-off time in ticks). Rows: ;1 bd $00,$d8,$08,$0c, $c1,$00,$81,$80; ;2 sd ;GEFILTERT $00,$f8,$08,$0f, $c2,$00,$82,$80; ;4 main I $01,$98,$e7,$41, $04,$01,$40,$03; ;5 bass I $01,$ef,$05,$02, $05,$12,$43,$81; ;7 floete I $00,$f8,$00,$11, $04,$00,$40,$03; ;$10 main II lange halten $01,$9d,$a0,$04, $04,$01,$44,$01.
  • Hard restart, 1988: on the trigger tick an instrument with release bit 7 gets $d405/$d406 = $00 and no other SID write that frame; frequency, waveform and ADSR follow on the next frame — every drum and both basses use it (hard-restart). Gate-off is automatic per instrument, in ticks from the start or from the end.
  • One table engine for vibrato, arpeggio and drum pitch: tab1 !by $26,$0b,$08,$05,$03,$01,$00,$ff,$fe,$fd,$fc (the kick: +38 semitones on the noise frame, then falling to −4), tab6 !by $00,$07,$04, tab7 !by $00,$18,$0c (chords/octaves), vibrato tables as 2-byte segments; extended arpeggios arp1 !by $00,$03,$07arp4 !by $00,$04,$08 on a 3-frame cycle; wave tables wtab1 !by $81,$41,$40 ×9,$08 (the kick ends on the test bit $08 as a mute), wtab3 !by $81,$41 (one noise frame then pulse — the bass); pulse programs as (frames, add) pairs ptab1 !by $3a,$20 / $0f,$df / $07,$20,$07,$df; filter programs ftab1 !by $01,$00,$00,$a0 / $01,$f0,$05,$f8 / $00,$f0,$7f,$00 with filtermode !by $1f,$1f; the filter belongs to the voice that last selected a filtered instrument (wavetable-programming, chord-arpeggio, vibrato, pulse-width-modulation, filter-programming). Size ≈ 2.9 KB (estimated).
  • Data quirks: high[5] = 254 in the frequency table (a data error), two byte-identical snares labelled "GEFILTERT" and "Nicht Gefiltert", a tab2 one byte shorter than its pointer says.

Practical takeaways#

  • Both scene players make the gate time an instrument parameter with a second "release" waveform or an automatic gate-off — the same idea as Ocean's patches (s-realdmx-ocean-dunn-hughes-driver) and a precursor of tracker gate-off tables.
  • Audial Arts' drum recipe: an arpeggio table as an absolute-pitch list plus a waveform pattern, sharing a counter — exactly a two-column wave table.
  • FAME's hard restart — zero ADSR on the trigger tick, write the note a frame later — is the minimal form of what goattracker and sid-wizard do with their HR timers.
  • An accelerating pulse sweep (Audial Arts byte 8) is a cheap way to get the "opening" PWM that a linear sweep lacks.

Notable quotes#

  • ";sound instelling (pak handleiding!)" (Audial Arts v2); "varia = $4a08 ;geheugen adres variab." (v1).
  • "SOUND ROUTINE f 4.1.0 / WRITTEN BY ADAM BULKA / WORKING VERSION I / DATE: 27.07.1988 2.29 pm" (FAME); ";$10 main II lange halten".

Relevance to the wiki#

Two more driver designs for the comparison on sid-player-routine and rows for sidid (Audial_Arts, FAME); a dated 1988 hard restart for hard-restart; instrument-as-gate-length examples for instrument-tables and adsr-envelope; drum and chord tables for the technique pages; the first Audial Arts / FAME material in the wiki (tracker-scene-history).

To verify#

  • (unverified) Who Francois Prijt and Adam Bulka are beyond these files (groups Audial Arts / Flash Inc. and FAME); the files give no more than the credits quoted.

Pages touched#

sid-player-routine · sidid · hard-restart · instrument-tables · adsr-envelope · wavetable-programming · chord-arpeggio · pulse-width-modulation · filter-programming · vibrato · orderlist · channel-interleaving · tracker-scene-history

source file wiki/summaries/s-realdmx-audial-arts-fame-players.md · 1 unverified · graph