SIDin #14 — Inside Matt Gray's Dominator player (Tognon's commented PLAYER V4.2 and the musician's guide to composing in it, 2015)
Written for the Reformation driver competition (s-matt-gray-dominator-source): Tognon takes matt-gray's released dom6-public.asm, comments every line, explains 64tass syntax to non-programmers, walks through the matt-gray-player as "PLAYER V4.2" and ships an empty template so that "a musician with no programming knowledge" can type a tune into it. It is the authoritative reading of the V4.2 tables — it settles several bytes the wiki had marked unverified — and it lists what changed since Driller (s-sidin02-matt-gray-driller).
Key claims#
Starting — build: download the zip from Remix64, 64tass.exe dom6-public.asm (Windows, 64tass is in the zip) or c64tass dom6-public.asm (Linux, yum/apt-get install 64tass), load the resulting a.out in vice. The addendum to the issue holds the original source, Tognon's re-commented version (only comments and blank lines added) and an empty empty_public.asm. Entries must stay in 64tass format to be accepted by Gray; help thread: CSDb forum topic 107150.
Background — a primer: ; comments, mnemonics, .BYTE / .WORD / .TEXT, *= $0801, labels vs constants (BB = 55), and the two operators the tables rely on: CC&255 = low byte, CC/256 = high byte of an address (other assemblers write < and >). "Use hexadecimal numbers … all is more simple."
Player — starts at $0801 with a BASIC stub, installs a raster IRQ (SETIRQ, PAL 50 Hz, "all tunes are played at speed 1X" — multispeed or CIA timing would need code changes; multispeed); the player itself sits at STARTADD = $C000, movable down to "the (safe) minimum of $1000". Note frequencies are hard-coded in NTL/NTH from C0: NTL .BYTE 12,28,45,62,81,102,…, NTH .BYTE 1,1,1,… — C0 = 1*256+12 = 268 → 268 × 0.0587235 = 15.73 Hz; A4 comes out at 424 Hz on PAL (441 Hz on NTSC). Constants C0 = 1 … B7 = 96 let notes be typed by name. TN holds the tune number (initial value 3; the player sets it to $AB while playing and to 0 at the end, "or you can use it for stopping the tune playback at any time"). TDATA .BYTE 0,5,3,4 = speed per tune (tune 0 is silence; the shortest note of tune 3 "will take 4 frames"; "the higher is that value and more slowly is the tune"); VOICE1L/H … VOICE3L/H hold the three track addresses per tune (TUNE1–3, OVER1–3, FIN1–3), so the released file plays three songs, not one.
Tracks — XX = pattern number $00–$FD, $FF repeat the track, $FE end of music — and "in this version of the player (but was not present into Driller) there is an automatically fade out volume effects at the end of the tune". (In the listing: $FE stores $5F in FADE, then every $5F frames VOLUME is decremented and written to $D418 until it runs out and TN is cleared.) BARLO/BARHI map pattern numbers to labels T0 … T56; Tognon shows how to rename them (PIPPO, PLUTO) and define index constants (orderlist).
Patterns — the V4.2 command set as Tognon tabulates it:
| byte | meaning |
|---|---|
$00 | rest — implemented as gate off, "so starting the release phase" (hard-restart) |
$01–$6F | note (1 = C0) |
$70–$F9 | duration kk − $70 — "more efficient" than Driller's two-byte $FD kk |
$FA NN | instrument NN (0 = first) |
$FB MM / $FC KK | slide down / up by that amount per cycle — "you can ever see this as a portamento" (pitch-slide-and-portamento) |
$FD CI | plex (arpeggio): C = number of notes, I = table index — $FD,$30 = table 0 × 3, $FD,$4A = table 10 × 4 (chord-arpeggio) |
$FF | end of pattern |
Persistence rules: an instrument holds until the next $FA; a duration until the next duration byte; a slide "lies only for the next note to play" and cancels plex and vibrato; a plex cancels slide and vibrato and "stays up until a new instrument is activated (or there is a slide)". The plex moved from the Driller instrument byte to a pattern command; tables PLEXLH .WORD P0,P1,P2 … with P0 .BYTE $07,$03,$00 (note+7, +3, +0, repeated); a longer table can be used with different counts ($FD,$30 then $FD,$40 on P0 .BYTE $07,$03,$00,$09). Worked examples: T56 .BYTE $FA,$06,$7F,$FD,$36,G4,$FD,$31,F4,$FD,$32,G4,$FD,$35,F4,$FF (instrument 6, duration 15, four chords); T6 .BYTE $FA,$02,$EF,$FC,$0A,AS3,$FB,$0A,AS4,$FF (duration $EF−$70 = 127, slide up +10 into AS3, down −10 into AS4). The listing adds one command Tognon's table folds into the duration range: $F9 nn = vibrato delay (VIBDELAY; durations therefore end at $F8), and a transpose variable TP that "seems to not be used".
Instruments — $FA NN × 8 indexes two 8-byte tables directly (VDATA, VDATA2; "maximum of 256/8 = 32 instruments"):
| index | meaning |
|---|---|
| 0 | pulse width as nibbles: $12 → high $02, low $10 ($0210 = 528) |
| 1 | control register |
| 2 / 3 | AD / SR (adsr-envelope) |
| 4 | pulse step, added/subtracted per frame |
| 5 | not used ("were used in old player for plex/arpeggio") |
| 6 | control register 2: written with the gate bit forced off when the instrument is selected, and just before the normal control register at each new note — "a 'basic' restart of note" |
| 7 | effect bits: 1 drum table, 2 pulse modulation, 4 implex (waveform switch on the first frames of a note, using byte 10), 16 hat |
| 8 / 9 | vibrato step / vibrato length (vibrato) |
| 10 | control register for implex |
| 11 / 12 | instrument slide value / flag: 0 none, 1 down, 2 up, 3 down on the high byte, 4 up on the high byte — "instrument slide is more capable in comparison with pattern slide" |
| 13 | drum table index |
| 14 | pulse-width limits as nibbles $LH → bounce between $0Lxx and $0Hxx ("in Driller it was fixed into the source") |
| 15 | not used |
Pulse example: start $23 (= $0320 = 800), step $A0 (160), limits $18 → rises 800, 960, 1120 … 2080 ($0820), then falls to $01xx and back (pulse-width-modulation). Vibrato direction sequence 0 (down, first time), 1, 2 (up), 3, 4 (down), then 1, 2, 3, 4 … — the Driller triangle. Hat = "a brief noise waveform at frequency $50xx played after every note duration decrements" — "not present into Driller player". Drum tables: DTL/DTH point to DT, BT; pairs of (control, value) ended by $FF, up to 127 pairs; a control byte with bit 7 set ($8x noise) sets the frequency high byte to the value, one without ($1x, $4x) subtracts the value from the current high byte — "a 'negative value' of control register is when you choose a noise waveform … while a 'positive' value is for the other waveform (like $4x) that you can use for a bass". DT decoded: $81,$30 noise on at $30xx (≥ 721 Hz, "AS4/B4"), $11,$02 triangle at note − $02xx, $41,$04 pulse at note − $04xx, then gate-off noise frames at $30, $15, $20, $10, $20, $20, $10, $20; BT: $81,$30, $41,$03, $40,$03, then noise $20/$10 alternating (wavetable-programming, instrument-design). Decoded instruments: 0 = drum table BT then plain triangle ($87,$11,$00,$E6,$00,$00,$10,$01 / $00,$00,$81,$00,$00,$01,$8E,$00); 1 = pulse from $0130 up to $0E00 and down to $08xx in $15 steps; 4 = pulse $01F0, step $20, limits $33 ("stay around" $03xx) plus hat; 9 = pulse $0440 between $0300 and $0500 in $C0 steps plus vibrato step $90, length 2; 13 = triangle with an instrument slide $B3, flag 3 ("a 'big' negative slide effect").
Use it — empty_public.asm: one tune, 32 empty instruments, 166 empty patterns (T0 … T165), 32 empty drum tables (DBT0 … DBT31), 16 plex tables (P0–P8 three notes, P9–P12 four, P13–P15 two — "95% of arpeggio are based into 3 values"). Steps: 1 TDATA .BYTE 0,5; 2 fill VDATA/VDATA2 rows; 3 drum tables, e.g. DBT2 .BYTE $81,$30,$11,$02,$FF; 4 plex rows, e.g. P1 .BYTE $08,$03,$00, a seven-note P2 .BYTE $10,$0E,$08,$05,$03,$01,$00 used with $FD $72; 5 patterns, e.g. T0 .BYTE $FA,$01,$75,G1,$FF; 6 tracks TUNE1 .BYTE T0,T1,T1,T2,T12,$FF ($FE for no repeat); 7 readability constants — INSTR = $FA, FLUTE = $00, ARPEG = $FD, P850 = $32, DUR5 = $75 — so $FA,$00,$75,F2,$FD,$32,E2,$FA,$01,F1,$76,G1,$FF becomes INSTR,FLUTE,DUR5,F2,ARPEG,P850,E2,INSTR,GUITAR,F1,DUR6,G1,$FF.
Practical takeaways#
- The whole V4.2 workflow in six tables: speed, instrument rows, drum tables, plex tables, patterns, tracks — a checklist for composing in any assembler-era driver (sid-player-routine, tracking-workflow).
- Byte 6 + the implex control byte are a pre-tracker hard restart: gate off on instrument change, a different waveform for the first frames of the note.
- Drum tables are (waveform, pitch) frame lists where noise frames set an absolute pitch and tonal frames step down from the note — the same shape as a GoatTracker/SID-Wizard wave table with pitch column.
- Gray's note table is tuned to A4 ≈ 424 Hz on PAL; retune
NTL/NTHif 440 Hz matters (Gray's own comment in s-sidin15-matt-gray-interview).
Notable quotes#
"I will try to describe the player not in the standard SIDin way (that means you are a coder …), but I will go in trivial description of some tasks that for a musician with no programming knowledge will let him to use the source."
"The previous work is substantially correct if compared with the actual knowledge we can get from this player. However the player is changed from the previous one in a non compatible way and it has lot of more features."
Relevance to the wiki#
Completes matt-gray-player's V4.2 cheat sheet (pattern bytes, 16-byte instrument layout, drum-table semantics, fade-out, per-instrument pulse limits); corrects two readings on s-matt-gray-dominator-source ($FD is plex, not duration; $AF/$DF/$EF/$EE/$97/$E7 are durations); adds the A4 = 424 Hz tuning; gives dominator the build-and-compose story; examples for chord-arpeggio, pulse-width-modulation, vibrato, wavetable-programming, instrument-tables.
To verify#
- The fade-out length:
$5Fframes per volume step × 16 steps ≈ 30 s at 50 Hz — computed from the listing, not stated by Tognon (unverified). - Tognon writes "65tass syntax" once and "64tass" elsewhere; the assembler is 64tass.
Pages touched#
matt-gray-player · dominator · matt-gray · driller · instrument-tables · chord-arpeggio · pulse-width-modulation · vibrato · wavetable-programming · pitch-slide-and-portamento · orderlist · hard-restart · sid-player-routine · tracking-workflow · vice · sidin · song-init-and-looping