realdmx: Jonathan Dunn's Daley Thompson '88 and Ocean Loader 5 in Paul Hughes's Ocean music driver
Two original Ocean Software sources from 1988 — not reverse-engineered — in the realdmx collection (s-realdmx-sid-players): the Daley Thompson '88 soundtrack (six tunes) and the loader tune HVSC calls Ocean Loader 5, which the source itself calls "LOADING MUSIC IV, THE REMIX!". The driver is Paul Hughes's, the music jonathan-dunn's, and the file is the only one in the collection where a composer's notation survives: music written as note names, command names and note-value constants (CR2, QV2) straight into the assembler. The cheat sheet is on ocean-music-driver.
Key claims#
Provenance and budget
- Header (both files): "D.THOMPSON '88 Soundtrack / Orchestration, blood, sweat & tears by Jonathan Dunn / Oh wow (paroxism!) programming by Paul Hughes / (c) Copyright Ocean Software / Imagine Software / Paul Hughes 1988 / Ported to DASM 10/11/2004 by Paulie" (the loader: "LOADING MUSIC IV ,THE REMIX!", "Ported to DASM 15/4/2006 by Paulie"). Who "Paulie" is the file does not say. A 57-byte text sits inside the loader's driver:
SILLYMESS !text "LOADER4 THE REMIX! BY JONATHAN DUNN DRIVER BY PAUL HUGHES". The drum system is "DunnyDrums (tm)" in two comments. - The memory budget is in the source:
ALLOCATION = 5*1024+512,TOGO = ALLOCATION-PROGRAM_LEN— 5632 bytes for driver, data and variables, with the assembler reporting what is left. Assembled, DT88 isDRIVER_LEN3783 +DATA_LEN1540 + 191 bytes of variables = 5514,TOGO= 118 bytes to spare; the loader is 3864 + 1009 + 188 = 5061 bytes (measured by re-assembling the sources with 64tass in this session). The driver is about 70 % of the budget; six tunes and 19 patches fit in 1.5 KB. - API: "To play the music JSR REFRESH every frame / To select a tune LDX #TUNE_NUMBER / JSR TUNE", tune numbers
0 = DISQUALIFIED, 1 = SUCCESS, 2 = FAIL, 3 = NEXT EVENT, 4 = TITLE MUSIC, 5 = QUALIFY. Init writes8then0to every SID register — "Set SID test bit so there's no / grotty clicking". Everything runs once per frame; there is no tempo counter, durations are frame counts.
Each voice is a program (orderlist does not apply — there are no patterns)
- Zero page holds three 16-bit program counters (
PC_A/B/C) and three arpeggio-table pointers; each voice has a 5-deep stack (STACKDEPTH = 5) shared byCALL/RETandLOOP/NEXT. Voices are synchronised only by the composer's durations. - Bytes
$00–$5Dare notes (C0 = 0 … A7 = 93, sharps asCS4,FS5),94=REST;$80–$97are commands:JUMP addr,CALL addr,RET(also clears the transpose),LOOP n … NEXT,BEND dir,step,length,offset16,PATCH n,LENGTH frames(auto-duration),VIBON delay,rate,depth,VIBOFF,ARPON addr,ARPOFF,MANUAL(note,duration pairs),PWM n,FILTER/FILTOFF(empty handlers),END,TRANSPOSE n,CT n,addr(transposed call),MODE mask,DRUM n,transpose[,duration](voice C only),PORT value,sid-offset(write any SID register — used for volume$18),CODE addr(JSRinto arbitrary 6502),EFFECT note. Commands cost no time; the handler runs and the next byte is read in the same frame. - Durations are named:
TEMPO = 5givesSQ 5, QV 10, CR 20, MN 40, SB 80;TEMPO2 = 6givesSQ2 6, QV2 12, DQV2 18, CR2 24, DCR2 36, MN2 48, SB2 96frames — a 4/4 bar at TEMPO2 is 96 frames (BARLENGTH = 96), 125 BPM at 50 Hz. The loader writes raw counts (192,96,12). - A phrase is a subroutine, and harmony is done by calling it transposed:
DITTY2: PATCH,1,PWM,1 / ARPON ARP2 / LENGTH,SQ / CALL MEL1 / CT,-5 MEL1 / CT,-12 MEL1 / CT,-17 MEL1 / D4 / ENDwithMEL1: LOOP,2 / F4,G4,F4,D5 / NEXT / RET; the loader's chord changes areCT,0 BASSSEQ1 / CT,-2 …overBACK1/BACK2(arrangement). - The loader's opening, as written:
LOADER1: MANUAL / PATCH,1,PWM,1 / VIBON,0,9,6 / EFFECT,12 / BEND,UP,$10,48 / !wo $10*48 / C2,192 / BEND,UP,$10,48 / !wo $10*48 / AS1,192 / …— each 192-frame bass note is preceded by a 48-frame bend of$10per frame that lands on the note (the driver pre-offsets the frequency bystep × length, so the composer writes the destination pitch), whileEFFECT,12makes every note start an octave up until its release point (pitch-slide-and-portamento). - Self-modifying music: fades and ramps are
CODEroutines that poke operand bytes in the data —FADEPOS !by PORT,$0F,$18withTFADEAWAY DEC FADEPOS+1in a loop (fade-out),CURRENTVOL !by PORT,1,$18withFADEUP INC CURRENTVOL+1overLOOP,9(fade-in 3 → 11), andDRUMPOS !by LOOP,16,DRUM,2,-9,SQ2/2,CODE UPDRUMwithUPDRUM INC DRUMPOS+4— a 16-hit snare roll whose pitch rises each hit. Endless tunes end inJUMPto their own label.
Patches = 5 bytes (instrument-tables): gate-on control byte, AD, SR, hold length (bit 7 = GAP mode), gate-off control byte. Two waveforms per instrument: one while gated, another for the release; hold is either "gate off N frames after note-on" or, with GAP, "N frames before the next note". DT88's 19 patches: $51,$00,$C8,3,$40 (triangle+pulse, 3 frames, then pulse off), $01,$00,$88,2,$42 (gate with no waveform for 2 frames, then pulse+sync — heard only in release), $55,$00,$99,3,$40 (triangle+pulse+ring), $81,$00,$A8,3,$10 (3 frames of noise, then triangle — a click transient), $11,$AA,$79,QV2+SQ2+GAP,$10 (the only patch with an attack — the title pad), $41,$00,$88,4+GAP,$14 (pulse, released as triangle+ring). The loader's lead is $43,$00,$7B,96+GAP,$22 (pulse+sync gated, gate off 96 frames before the end of a 192-frame note, released as sawtooth+sync), its bend bass $41,$00,$AD,3,$40. Almost all have AD $00 (adsr-envelope).
Modulation, all inline
VIBON delay,rate,depth: after the delay, add/subtractrateto the 16-bit frequency, reversing everydepthframes (first leg half) — a triangle in raw units; valuesVIBON,0,7,6,VIBON,6,$20,6,VIBON,$10,$1A,8, and the loader lead'sVIBON,0,$FF,4(vibrato).VIBONcancels an arpeggio; an active arpeggio suppresses vibrato and bend.PWM n: 5-byte entriesrate, frames up, frames down, initial width (word)—$80,$00,$02 / $0F50,$F0,$04,$04 / $0800, loader$40,48,42 / $0000; one rate both ways (pulse-width-modulation).ARPON addr: table =frames per step, number of steps, offsets…—ARP1 !by 3,4,0,4,7,9,ARP2 !by 1,3,24,12,0(the two-octave "chip" arpeggio),FARP1 !by 2,4,0,3,7,12,TARP1/2/3 !by 2,3,0,3,7 / 0,3,8 / 0,3,10— chord changes under a held note by swapping tables (chord-arpeggio).- Drums (
DRUM, voice C only): 7-byte entrieswave-table ptr, freq-table ptr, length, SR, pulse-hi, AD forced$00; per frame one control byte and one frequency-high byte (+ transpose in coarse units) —BASS DRUM: DRUM1V $41,$41,$10,$80 / DRUM1F $0E,$06,$0F,$10;SNARE: DRUM2V $41,$41,$40,$80,$80,$80,$10,$80,$10,$80,$80,$10,$80 / DRUM2F $11,$0C,$08,$70,$70,$2F,$0C,$2E,$0C,$2D,$BC,$0C,$2B;HI-HAT: DRUM3V $11,$11,$80,$80,$80 / DRUM3F $10,$10,$70,$20,$30(wavetable-programming). When a drum ends the driver writes0to$D417. MODE: a bit mask (bit 7 of the argument = set, else clear) for gate writes, pulse retrigger per note and the "pluck" effect mode — commented "i'll be buggered if i know what this does !";EFFECT,C7,MODE,%00001000in the title makes every note attack on an absolute C-7.
Two builds: DT88 pauses all modulators on the last frame of every note (MOD_OFF, "Shut off modulation for this frame") and has bend only on voice A and effect only on voice C; the loader has no MOD_OFF, bend on A and B, effect on all three — handlers are compiled in or out per tune to fit the budget. Unused leftovers (RATEDOWN, FILT_TOGGLE, BARS = $44 ; Bars to skip, BARLENGTH) point at a filter, a separate pulse-down rate and a bar-skipping tester that are not in these files.
Practical takeaways#
- The "instrument" is two control bytes and a gate length; everything else is a command in the voice's stream. To copy a Dunn patch in a tracker: waveform A while the gate is on, waveform B in release, AD
$00, and a fixed gate time — hard-restart-era thinking about attacks. - "Bend into the note" is the ergonomic form of portamento: write the target note and a (step, length) pair; the driver computes the start pitch.
- Dunn's snare is the SID recipe in the raw: noise frames at a high frequency byte (
$70) alternated with gate-off triangle/pulse frames at a low one ($0C). - A 5.5 KB budget with a 3.8 KB driver: the data has to be subroutines and transposed calls, which is why the music sounds like variations on short cells.
Notable quotes#
- "Orchestration, blood, sweat & tears by Jonathan Dunn / Oh wow (paroxism!) programming by Paul Hughes" (header).
- "Set SID test bit so there's no grotty clicking" (init); "Suprisingly enuf this handles the vibrato"; "Process DunnyDrums (tm)"; "MODE_A - Sets triggering mode, i'll be buggered if i know what this does !" (Hughes's comments).
Relevance to the wiki#
Primary source for ocean-music-driver and jonathan-dunn; the third "macro-language" driver beside Galway's (s-realdmx-galway-rambo-player) for the comparison on sid-player-routine; concrete Ocean-house instrument, drum, arpeggio and bend data for the technique pages; the first source in the wiki that shows a game's music memory budget in numbers.
Pages touched#
ocean-music-driver · jonathan-dunn · martin-galway · galway-player · sid-player-routine · sidid · instrument-tables · instrument-design · wavetable-programming · chord-arpeggio · vibrato · pulse-width-modulation · pitch-slide-and-portamento · hard-restart · adsr-envelope · arrangement · orderlist · octave-bass