; easyplayer.a - A simple-to-use 4-channel MED3.10/OctaMED1.00 ; player routine ; Written by Teijo Kinnunen. ; Works on Devpac 2 assembler ; Type in the name of your module to the INCBIN statement. ; Call _startmusic at the beginning, _endmusic at the end. ac_ptr EQU $00 ac_len EQU $04 ac_per EQU $06 ac_vol EQU $08 T03SZ EQU 74 T415SZ EQU 18 MIDI EQU 0 ;1 = include MIDI code AUDDEV EQU 1 ;1 = allocate channels using audio.device SYNTH EQU 1 ;1 = include synth-sound handler CHECK EQU 1 ;1 = do range checkings (track, sample in mem etc.) RELVOL EQU 1 ;1 = include relative volume handling code IFF53 EQU 1 ;1 = play IFF 3- and 5-octave samples correctly ;****** Timing control ****** VBLANK EQU 0 ;1 = use VBlank interrupt (when absolutely necessary) CIAB EQU 1 ;1 = use CIAB timers (default) ; Please use CIAB whenever possible to avoid problems with variable ; VBlank speeds and to allow the use of command F01 - FF0 (set tempo) ; If both are set to 0, the timing is left for you (never set both to 1!!), ; then you just call _IntHandler for each timing pulse. section "text",code _ChannelOff: ;d0 = channel # lea trackdataptrs(pc),a1 lsl.b #2,d0 adda.w d0,a1 lsr.b #2,d0 movea.l (a1),a1 IFNE MIDI move.b trk_prevmidin(a1),d1 ;first: is it MIDI?? beq.s notcomidi ;not a midi note choff_midi: clr.b trk_prevmidin(a1) lea noteondata(pc),a0 move.b d1,1(a0) move.b trk_prevmidich(a1),(a0) ;prev midi channel clr.b 2(a0) or.b #$90,(a0) ;note off moveq #3,d0 bra.w _AddMIDIData notcomidi: ENDC cmp.b #4,d0 bge.s notamigatrk IFNE SYNTH clr.l trk_synthptr(a1) clr.b trk_synthtype(a1) ENDC moveq #1,d1 lsl.w d0,d1 move.w d1,$dff096 notamigatrk: rts SoundOff: move.l d2,-(sp) moveq #15,d2 SO_loop0 move.l d2,d0 bsr.s _ChannelOff dbf d2,SO_loop0 lea _module(pc),a0 clr.l (a0) ;play nothing!! move.l (sp)+,d2 rts _PlayNote: ;d0(w) = trk #, d1 = note #, d2 = vol, d3(w) = instr # a3 = add r of instr movem.l d3-d7,-(sp) ;All right, let's start!! move.w d2,-(sp) moveq #0,d4 bset d0,d4 ;d4 is mask for this channel movea.l 24(a6),a0 ;ptr to sample array move.w d3,d7 add.w d3,d3 ;d3 = instr.num << 2 add.w d3,d3 move.l 0(a0,d3.w),d5 ;get address of instrument IFNE MIDI bne.s inmem tst.b 4(a3) ;is MIDI channel set ENDC IFNE CHECK beq.w retsn2 ; NO!!! ENDC inmem: add.b 766(a4),d1 ;add play transpose add.b 7(a3),d1 ;and instr. transpose cmp.b #4,d0 bge.s nodmaoff ;track # >= 4: not an Amiga channel move.l d5,a1 IFNE SYNTH tst.l d5 ;is there sample in memory? beq.s stpdma tst.b trk_synthtype(a5) ble.s stpdma cmp.w #-1,4(a1) ;type == SYNTHETIC?? beq.s nostpdma ENDC stpdma: move.w d4,$dff096 ;stop this channel (dmacon) nostpdma: IFNE SYNTH clr.l trk_synthptr(a5) ENDC nodmaoff: IFNE MIDI move.b trk_prevmidin(a5),d6 ;get prev. midi note beq.s noprevmidi clr.b trk_prevmidin(a5) lea noteondata(pc),a0 move.b d6,1(a0) move.b trk_prevmidich(a5),(a0) ;prev midi channel or.b #$90,(a0) ;note off clr.b 2(a0) ;clear volume movem.w d0-d1,-(sp) moveq #3,d0 bsr.w _AddMIDIData movem.w (sp)+,d0-d1 noprevmidi: tst.b 4(a3) bne.w handleMIDInote ENDC IFNE CHECK cmp.w #4,d0 ;track > 3??? bge.w retsn2 ;no Amiga instruments here!!! ENDC ; handle decay (for tracks 0 - 3 only!!) clr.b trk_fadespd(a5) ;no fade yet.. move.b trk_initdecay(a5),trk_decay(a5) ;set decay clr.b trk_vibroffs(a5) ;clr vibrato offset or.w d4,dmaonmsk move.l d5,a0 subq.b #1,d1 IFNE SYNTH tst.w 4(a0) bmi.w handleSynthnote clr.b trk_synthtype(a5) ENDC tlwtst0: tst.b d1 bpl.s notenot2low add.b #12,d1 ;note was too low, octave up bra.s tlwtst0 notenot2low: cmp.b #62,d1 ble.s endpttest sub.b #12,d1 ;note was too high, octave down endpttest: move.w d0,d4 ;d4 = track number bsr.w getinsdata movea.l trk_audioaddr(a5),a1 ;base of this channel's regs move.w (sp),ac_vol(a1) add.w d4,d4 ;d4 = trk << 1 move.w d4,d6 add.w d6,d6 ;d6 = trk << 2 move.l d0,ac_ptr(a1) ;put it in ac_ptr cmp.w #1,d3 bhi.s repeat move.l #_chipzero,trk_sampleptr(a5) ;pointer of zero word move.w #1,trk_samplelen(a5) ;length: 1 word lsr.l #1,d1 ;shift length right move.w d1,ac_len(a1) ;and put to custom chip bra.s retsn1 repeat: tst.w d2 beq.s begin0 ;rep. start < 2 move.w d2,ac_len(a1) ;move repeat to hardware bra.s beginn0 begin0: move.w d3,ac_len(a1) beginn0: lsl.l #1,d2 ;shift add.l d2,d0 ;d0 = starting address of repeat move.l d0,trk_sampleptr(a5) ;remember rep. start move.w d3,trk_samplelen(a5) ;remember rep. length retsn1: move.w d5,ac_per(a1) ;getinsdata puts period to d5 move.w d5,trk_prevper(a5) IFNE SYNTH tst.b trk_synthtype(a5) bne.w hSn2 ENDC retsn2: addq.l #2,sp ;forget volume movem.l (sp)+,d3-d7 rts IFNE MIDI handleMIDInote: lea noteondata(pc),a0 add.b #23,d1 ;2 octaves higher and -1 bpl.s mnot2low ;note number not too low add.b #12,d1 ;it was too low, 1 octave up bra.s endmtst mnot2low: tst.b d1 ;is it too high then?? bpl.s endmtst ;no, not greater than 127 sub.b #12,d1 ;1 octave down, if yes endmtst: move.b d1,1(a0) ;MIDI msg note # move.b d1,trk_prevmidin(a5) ;save this note number move.b d2,d4 ;temporary save the volume subq.b #1,d2 ;if 64 => 63 bpl.s nooops moveq #0,d2 ;oops, too low!! nooops: lsl.b #1,d2 ;volume 0 - 63 => 0 - 127 bclr #7,d2 ;be sure that bit 7 is clear move.b d2,2(a0) ;MIDI msg volume moveq #0,d1 move.b 4(a3),d1 ;get midi chan of this instrument subq.b #1,d1 ;from 1-16 to 0-15 move.b d1,trk_prevmidich(a5) ;save to prev midi channel move.b #$90,(a0) ;MIDI: Note on or.b d1,(a0) ;MIDI msg Note on & channel move.b 5(a3),d2 ;get preset # beq.s nochgpres ;zero = no preset lea prevmidicpres(pc),a1 cmp.b 0(a1,d1.w),d2 ;is this previous preset ?? beq.s nochgpres ;yes...no need to change move.b d2,0(a1,d1.w) ;save preset to prevmidicpres subq.b #1,d2 ;sub 1 to get 0 - 127 lea preschgdata(pc),a0 move.b #$c0,(a0) ;command: $C or.b d1,(a0) ;"or" midi channel move.b d2,1(a0) ;push the number to second byte moveq #5,d0 ;Noteondata follows preschgdata bra.s preschanged ;struct, so this is a bit faster nochgpres: moveq #3,d0 preschanged: bsr.w _AddMIDIData bra.s retsn2 ENDC IFNE SYNTH handleSynthnote: move.b d1,trk_prevnote2(a5) move.l a0,trk_synthptr(a5) cmp.w #-2,4(a0) ;HYBRID?? bne.s hSn_nossn st trk_synthtype(a5) movea.l 278(a0),a0 ;yep, get the waveform pointer bra.w tlwtst0 ;go and play it hSn_nossn: move.b #1,trk_synthtype(a5) lea _synthper(pc),a1 move.l a1,trk_periodtbl(a5) ;save table ptr for synth periods add.w d1,d1 move.w 0(a1,d1.w),d1 movea.l trk_audioaddr(a5),a1 move.b trk_prevvol(a5),trk_synvol(a5) move.w d1,trk_prevper(a5) move.w d1,ac_per(a1) clr.l trk_sampleptr(a5) hSn2: lea trk_arpgoffs(a5),a1 ;clear synth-variables for new note clr.l (a1)+ clr.l (a1)+ clr.l (a1)+ clr.l (a1)+ clr.b (a1)+ addq.l #1,a1 ;skip trk_prevnote2 movea.l trk_synthptr(a5),a0 move.w 18(a0),(a1)+ ;trk_initvolxspd/trk_initwfxspd clr.l (a1)+ clr.w (a1) bsr.s synth_start bra.w retsn2 synth_start: move.l a3,-(sp) movea.l trk_audioaddr(a5),a3 ;audio channel base address subq.b #1,trk_volxcnt(a5) ;decrease execute counter.. bgt.w synth_wftbl ;not 0...go to waveform move.b trk_initvolxspd(a5),trk_volxcnt(a5) ;reset counter move.b trk_volchgspd(a5),d0 ;volume change?? beq.s synth_nochgvol ;no. add.b trk_synvol(a5),d0 ;add previous volume bpl.s synth_voln2l ;not negative moveq #0,d0 ;was negative => 0 synth_voln2l: cmp.b #$40,d0 ;too high?? ble.s synth_voln2h ;not 2 high. moveq #$40,d0 ;was 2 high => 64 synth_voln2h: move.b d0,trk_synvol(a5) ;remember new... move.b d0,ac_vol+1(a3) ;and change it synth_nochgvol: move.w trk_volcmd(a5),d0 ;get table position ptr tst.b trk_volwait(a5) ;WAI(t) active beq.s synth_getvolcmd ;no subq.b #1,trk_volwait(a5) ;yep, decr wait ctr ble.s synth_getvolcmd ;0 => continue bra.w synth_wftbl ;> 0 => still wait synth_inccnt: addq.b #1,d0 synth_getvolcmd: addq.b #1,d0 ;advance pointer move.b 21(a0,d0.w),d1 ;get command bmi.s synth_cmd ;negative = command move.b d1,trk_synvol(a5) ;set synthvol move.b d1,ac_vol+1(a3) ;change it!! bra.s synth_endvol ;end of volume executing synth_cmd: and.w #$000f,d1 add.b d1,d1 move.w synth_vtbl(pc,d1.w),d1 jmp syv(pc,d1.w) synth_vtbl: dc.w syv_f0-syv,syv_f1-syv,syv_f2-syv,syv_f3-syv dc.w synth_endvol-syv,synth_endvol-syv,synth_endvol-syv dc.w synth_endvol-syv,synth_endvol-syv,synth_endvol-syv dc.w syv_fa-syv,syv_ff-syv,synth_endvol-syv dc.w synth_endvol-syv,syv_fe-syv,syv_ff-syv syv: syv_fe: move.b 22(a0,d0.w),d0 ;JMP bra.s synth_getvolcmd syv_f0: move.b 22(a0,d0.w),trk_initvolxspd(a5) ;change volume ex. spee d bra.s synth_inccnt syv_f1: move.b 22(a0,d0.w),trk_volwait(a5) ;WAI(t) addq.b #1,d0 bra.s synth_endvol syv_f3: move.b 22(a0,d0.w),trk_volchgspd(a5) ;set volume slide up bra.s synth_inccnt syv_f2: move.b 22(a0,d0.w),d1 neg.b d1 move.b d1,trk_volchgspd(a5) ;set volume slide down bra.s synth_inccnt syv_fa: move.b 22(a0,d0.w),trk_wfcmd+1(a5) ;JWS (jump wform sequence) clr.b trk_wfwait(a5) bra.s synth_inccnt syv_ff: subq.b #1,d0 synth_endvol: move.w d0,trk_volcmd(a5) synth_wftbl: move.b trk_synvol(a5),trk_prevvol(a5) adda.w #158,a0 subq.b #1,trk_wfxcnt(a5) ;decr. wf speed counter bgt.w synth_arpeggio ;not yet... move.b trk_initwfxspd(a5),trk_wfxcnt(a5) ;restore speed counte r move.w trk_wfcmd(a5),d0 ;get table pos offset move.w trk_wfchgspd(a5),d1 ;CHU/CHD ?? beq.s synth_tstwfwai ;0 = no change wytanwet: add.w trk_perchg(a5),d1 ;add value to current change move.w d1,trk_perchg(a5) ;remember amount of change add.w trk_prevper(a5),d1 ;add initial period to it cmp.w #113,d1 ;overflow?? bge.s synth_pern2h moveq #113,d1 synth_pern2h: move.w d1,ac_per(a3) ;push the changed period synth_tstwfwai: tst.b trk_wfwait(a5) ;WAI ?? beq.s synth_getwfcmd ;not waiting... subq.b #1,trk_wfwait(a5) ;decr wait counter beq.s synth_getwfcmd ;waiting finished bra.w synth_arpeggio ;still sleep... synth_incwfc: addq.b #1,d0 synth_getwfcmd: addq.b #1,d0 ;advance position counter move.b -9(a0,d0.w),d1 ;get command bmi.s synth_wfcmd ;negative = command ext.w d1 ;was positive->change wform lsl.w #2,d1 ;create index... movea.l 120(a0,d1.w),a1 ;get wf address addq.l #2,a1 ;forget length-word move.l a1,ac_ptr(a3) ;push new pointer move.w -(a1),ac_len(a3) ;and the waveform length bra.w synth_wfend ;no new commands now... synth_wfcmd: and.w #$000f,d1 ;get the right nibble add.b d1,d1 ;* 2 move.w synth_wfctbl(pc,d1.w),d1 jmp syw(pc,d1.w) ;jump to command synth_wfctbl: dc.w syw_f0-syw,syw_f1-syw,syw_f2-syw,syw_f3-syw,syw_f4-syw dc.w syw_f5-syw,syw_f6-syw,synth_wfend-syw,synth_wfend-syw dc.w synth_wfend-syw,syw_fa-syw,syw_ff-syw dc.w syw_fc-syw,synth_getwfcmd-syw,syw_fe-syw,syw_ff-syw syw: syw_fe: move.b -8(a0,d0.w),d0 ;jump (JMP) bra.s synth_getwfcmd syw_fc: move.w d0,trk_arpsoffs(a5) ;new arpeggio begin move.w d0,trk_arpgoffs(a5) synth_findare: addq.b #1,d0 tst.b -9(a0,d0.w) bpl.s synth_findare bra.s synth_getwfcmd syw_f0: move.b -8(a0,d0.w),trk_initwfxspd(a5) ;new waveform speed bra synth_incwfc syw_f1: move.b -8(a0,d0.w),trk_wfwait(a5) ;wait waveform addq.b #1,d0 bra.s synth_wfend syw_f4: move.b -8(a0,d0.w),trk_synvibdep+1(a5) ;set vibrato depth bra.w synth_incwfc syw_f5: move.b -8(a0,d0.w),trk_synthvibspd+1(a5) ;set vibrato speed addq.b #1,trk_synthvibspd+1(a5) bra.w synth_incwfc syw_f2: moveq #0,d1 ;set slide down move.b -8(a0,d0.w),d1 synth_setsld: move.w d1,trk_wfchgspd(a5) bra.w synth_incwfc syw_f3: move.b -8(a0,d0.w),d1 ;set slide up neg.b d1 ext.w d1 bra.s synth_setsld syw_f6: clr.w trk_perchg(a5) ;reset period move.w trk_prevper(a5),ac_per(a3) bra.w synth_getwfcmd syw_fa: move.b -8(a0,d0.w),trk_volcmd+1(a5) ;JVS (jump volume sequence ) clr.b trk_volwait(a5) bra.w synth_incwfc syw_ff: subq.b #1,d0 ;pointer = END - 1 synth_wfend: move.w d0,trk_wfcmd(a5) synth_arpeggio: move.w trk_arpgoffs(a5),d0 beq.s synth_vibrato moveq #0,d1 move.b -8(a0,d0.w),d1 add.b trk_prevnote2(a5),d1 movea.l trk_periodtbl(a5),a1 ;get period table add.w d1,d1 move.w 0(a1,d1.w),d1 add.w trk_perchg(a5),d1 move.w d1,trk_prevper(a5) move.w d1,ac_per(a3) addq.b #1,d0 tst.b -8(a0,d0.w) bpl.s synth_noarpres move.w trk_arpsoffs(a5),d0 synth_noarpres: move.w d0,trk_arpgoffs(a5) synth_vibrato: move.w trk_synvibdep(a5),d1 ;get vibrato depth beq.s synth_rts ;0 => no vibrato move.w trk_synviboffs(a5),d0 ;get offset lsr.w #4,d0 ;/ 16 and.w #$1f,d0 ;sinetable offset (0-31) move.b sinetable(pc,d0.w),d0 ;get byte ext.w d0 ;to word muls d1,d0 ;amplify (* depth) asr.w #8,d0 ;and divide by 64 move.w trk_prevper(a5),d1 ;get the old period add.w d0,d1 ;add vibrato... add.w trk_perchg(a5),d1 ;and pitch change... move.w d1,ac_per(a3) ;change. move.w trk_synthvibspd(a5),d0 ;vibrato speed add.w d0,trk_synviboffs(a5) ;add to offset synth_rts: move.l (sp)+,a3 rts ENDC sinetable: dc.b 0,25,49,71,90,106,117,125,127,125,117,106,90,71,49 dc.b 25,0,-25,-49,-71,-90,-106,-117,-125,-127,-125,-117 dc.b -106,-90,-71,-49,-25,0 _Wait1line: move.l d0,-(sp) moveq #$79,d0 wl0: move.b $dff007,d1 wl1: cmp.b $dff007,d1 beq.s wl1 dbf d0,wl0 move.l (sp)+,d0 rts pushnewvals: movea.l (a1)+,a5 lsr.b #1,d0 bcc.s rpnewv move.l trk_sampleptr(a5),d1 beq.s rpnewv movea.l trk_audioaddr(a5),a0 move.l d1,ac_ptr(a0) move.w trk_samplelen(a5),ac_len(a0) rpnewv: rts _StartDMA: ;This small routine turns on audio DMA move.w dmaonmsk(pc),d0 ;dmaonmsk contains the mask of beq.s rpnewv ;the channels that must be turned on bset #15,d0 ;DMAF_SETCLR: set these bits in dmacon bsr.s _Wait1line move.w d0,$dff096 ;do that!!! bsr.s _Wait1line lea trackdataptrs(pc),a1 bsr.s pushnewvals bsr.s pushnewvals bsr.s pushnewvals bra.s pushnewvals dmaonmsk: dc.w 0 IFNE MIDI prevmidicpres: dc.l 0,0,0,0 ; 16 bytes prevmidipbend: dc.w $2000,$2000,$2000,$2000,$2000,$2000,$2000,$2000 dc.w $2000,$2000,$2000,$2000,$2000,$2000,$2000,$2000 ENDC ; TRACK-data structures (see definitions at the end of this file) t03d: ds.b 20 dc.l $dff0a0 ds.b 50+20 dc.l $dff0b0 ds.b 50+20 dc.l $dff0c0 ds.b 50+20 dc.l $dff0d0 ds.b 50 t415d: ds.b 4*T415SZ t815d: ds.b 8*T415SZ ;8 bytes * 12 tracks = 96 bytes trackdataptrs: dc.l t03d,t03d+T03SZ,t03d+2*T03SZ,t03d+3*T03SZ dc.l t415d,t415d+T415SZ,t415d+2*T415SZ,t415d+3*T415SZ dc.l t815d,t815d+T415SZ,t815d+2*T415SZ,t815d+3*T415SZ dc.l t815d+4*T415SZ,t815d+5*T415SZ,t815d+6*T415SZ dc.l t815d+7*T415SZ numtracks: dc.w 0 numlines: dc.w 0 _counter: dc.b 0 nextblock: dc.b 0 _IntHandler: movem.l d2-d7/a2-a5,-(sp) movea.l _module(pc),a6 ;a6 = pointer of MMD0 move.l a6,d0 beq.w plr_exit clr.w dmaonmsk movea.l 8(a6),a4 ;a4 = pointer of MMD0song moveq #0,d3 lea _counter(pc),a0 move.b (a0),d3 addq.b #1,d3 cmp.b 769(a4),d3 bge.s plr_pnewnote ;play new note move.b d3,(a0) bra.w nonewnote ;do just fx ; --- new note!! first get address of current block plr_pnewnote: clr.b (a0) ;clear counter ; --- now start to play it move.w pblock(pc),d0 movea.l 16(a6),a0 lsl.w #2,d0 movea.l 0(a0,d0.w),a2 ;block... move.b (a2)+,numtracks+1 move.b (a2)+,numlines+1 move.w pline(pc),d0 move.w d0,d1 add.w d0,d0 ;d0 * 2 add.w d1,d0 ;+ d0 = d0 * 3 mulu numtracks(pc),d0 adda.w d0,a2 ;a2 => pointer of curr. note moveq #0,d7 ;number of track pea trackdataptrs(pc) plr_loop0: moveq #0,d5 move.l (sp),a1 movea.l (a1)+,a5 ;get address of this track's struct move.l a1,(sp) ; ---------------- get the note numbers move.b (a2)+,d5 ;get the number of this note move.b (a2)+,d6 ;and the 4 numbers containing fx move.b (a2)+,trk_cmdqual(a5) ;get & save the fx numbers ; ---------------- clear some instrument # flags moveq #0,d4 ;d4 is a flag: if set, instr. is moveq #0,d3 ;in range G-V. If clr, it's 1-F. ; ---------------- and set them, if needed bclr #7,d5 ;d3 is also a flag. If it's set, sne d4 ;the instr. is in range 10 - 1V bclr #6,d5 sne d3 ; ---------------- check if there's an instrument number move.b d6,d0 and.w #$f0,d0 ;d0 now contains only the # of instr bne.s instnum ;instrument number is not 0 tst.b d4 ;maybe it's G (instr. #0, d4 set) bne.s instnum ;yes, it really was G!! tst.b d3 beq.s noinstnum ;it wasn't 10 - 1V either.. ; ---------------- if there was, GET IT!! instnum: lsr.b #4,d0 ;shift it right to get number 0-F tst.b d4 beq.s nogtov2 add.w #16,d0 ;if G-V, add 16 to the number nogtov2: tst.b d3 beq.s no10to1v add.w #32,d0 ; ---------------- finally, save the number no10to1v: subq.b #1,d0 move.b d0,trk_previnstr(a5) ;remember instr. number! ; ---------------- remember hold/decay values lea holdvals(pc),a0 move.b 0(a0,d0.w),trk_inithold(a5) move.b 63(a0,d0.w),trk_initdecay(a5) ; ---------------- get the pointer of data's of this sample in Song-struct lsl.w #3,d0 ;* 8 lea 0(a4,d0.w),a3 ;a3 contains now address of it move.l a3,trk_previnstra(a5) moveq #0,d0 ; ---------------- get volume and make it relative (0 - 100 %) IFNE RELVOL move.b 6(a3),d0 mulu trk_trackvol(a5),d0 lsr.w #8,d0 move.b d0,trk_prevvol(a5) ;vol of this instr ELSEIF move.b 6(a3),trk_prevvol(a5) ENDC ; ---------------- remember transpose move.b 7(a3),trk_stransp(a5) ; ---------------- check the commands noinstnum: move.b d6,d0 ;effect again... and.b #$0f,d0 ;now check only the effect part move.b d0,trk_cmd(a5) ;save the effect number beq.w plr_nocmd ;no effect move.b trk_cmdqual(a5),d6 ;get qualifier... ; ---------------- there was a command (effect), but which one?? cmp.b #$0f,d0 ;yes effect...is it Tempo??? bne.w not0f ;not Tempo ; ---------------- it was tempo (F) tst.b d6 ;test effect qual.. beq.s fx0fchgblck ;if effect qualifier (last 2 #'s).. cmp.b #$f0,d6 ;..is zero, go to next block bhi.s fx0fspecial ;if it's F1-FF something special ; ---------------- just an ordinary "change tempo"-request IFNE CIAB moveq #0,d0 ;will happen!!! move.b d6,d0 bsr _SetTempo ;change The Tempo ENDC bra.w plr_nocmd ; ---------------- no, it was FFx, something special will happen!! fx0fspecial: cmp.b #$f2,d6 ; | rest - play | SpecialFX#2: no note..yet bne.s isfxfe ;not SpecFX2 ; ---------------- it was FF2, nothing to do now move.b d5,(a5) ;Yes!!! Save the note number moveq #0,d5 ; clear the number for awhile bra.w plr_nocmd isfxfe: cmp.b #$fe,d6 bne.s notcmdfe ; ---------------- it was FFE, stop playing clr.w pstate IFNE CIAB movea.l craddr(pc),a0 bclr #0,(a0) ;stop timer ENDC bsr.w SoundOff addq.l #4,sp ;trackdataptrs... bra.w plr_exit notcmdfe: cmp.b #$fd,d6 ;change period bne.s isfxff ; ---------------- FFD, change the period, don't replay the note IFNE CHECK cmp.w #4,d7 ;no tracks 4 - 15, thank you!! bge.w plr_nocmd ENDC movea.l trk_periodtbl(a5),a0 ;period table subq.b #1,d5 ;sub 1 to make "real" note number bmi.w plr_endloop0 ;under zero, do nothing add.b d5,d5 move.w 0(a0,d5.w),d0 ;get the period movea.l trk_audioaddr(a5),a0 move.w d0,ac_per(a0) ;push the period moveq #0,d5 ;and clear it so that it won't be replayed bra.w plr_nocmd ;done!! isfxff: cmp.b #$ff,d6 ;note off?? bne.w plr_nocmd move.w d7,d0 bsr.w _ChannelOff bra.w plr_nocmd ; ---------------- F00, called Pattern Break in ST fx0fchgblck: addq.b #1,nextblock ;next block????...YES!!!! (F00) bra.w plr_nocmd ; ---------------- was not Fxx, then it's something else!! not0f: cmp.b #$0e,d0 bne.s not0e move.b d6,trk_wfcmd+1(a5) ;set waveform command position ptr bra.w plr_nocmd not0e: cmp.b #$0c,d0 ;new volume??? bne.s not0c ;NO!!!!!!!!!!!!!!!!!!!!!! ; ---------------- change volume move.b d6,d0 btst #4,767(a4) ;look at flags bne.s volhex lsr.b #4,d0 ;get number from left mulu #10,d0 ;number of tens move.b d6,d1 ;get again and.b #$0f,d1 ;this time don't get tens add.b d1,d0 ;add them volhex: IFNE CHECK cmp.b #64,d0 bls.s novolov64 moveq #64,d0 ENDC novolov64: IFNE RELVOL mulu trk_trackvol(a5),d0 lsr.w #8,d0 ENDC move.b d0,trk_prevvol(a5) bra.s plr_nocmd ; ---------------- tempo2 change?? not0c: cmp.b #$09,d0 bne.s not09 IFNE CHECK and.b #$1F,d6 bne.s fx9chk moveq #$20,d6 ENDC fx9chk: move.b d6,769(a4) bra.s plr_nocmd ; ---------------- note off time set?? not09: cmp.b #$08,d0 bne.s not08 move.b d6,d0 lsr.b #4,d6 ;extract left nibble and.b #$0f,d0 ; " " right " " move.b d6,trk_initdecay(a5) ;left = decay move.b d0,trk_inithold(a5) ;right = hold bra.s plr_nocmd ; ---------------- cmd Bxx, "position jump", like Goto, yäk!! not08: cmp.b #$0b,d0 bne.s not0b move.w d6,d0 and.w #$00ff,d0 IFNE CHECK cmp.w 506(a4),d0 ;test the song length bhi.s plr_nocmd ENDC move.w d0,pseq st nextblock ; = 1 bra.s plr_nocmd ; ---------------- try portamento (3) not0b: cmp.b #$03,d0 bne.s plr_nocmd subq.b #1,d5 ;subtract note number bpl.s plr_fx3note ;there's a note... tst.b d6 ;qual?? beq.s plr_endloop0 ;0 -> do nothing bra.s plr_setfx3spd ;not 0 -> set new speed plr_fx3note: IFNE CHECK cmp.w #4,d7 bge.s plr_endloop0 ;hey, what are you trying to do?? ENDC movea.l trk_periodtbl(a5),a0 add.b 766(a4),d5 ;play transpose add.b trk_stransp(a5),d5 ;and instrument transpose bmi.s plr_endloop0 ;again.. too low add.w d5,d5 move.w 0(a0,d5.w),trk_porttrgper(a5) ;period of this note is t he target plr_setfx3spd: move.b d6,trk_prevportspd(a5) ;remember size moveq #0,d5 ;don't play this one ; ---------------- everything is checked now: play or not to play?? plr_nocmd: tst.b d5 ;Now we'll check if we have to play a note beq.s plr_endloop0 ;no. ; ---------------- we decided to play move.b d5,(a5) move.w d7,d0 move.w d5,d1 moveq #0,d2 move.b trk_prevvol(a5),d2 ;get volume moveq #0,d3 move.b trk_previnstr(a5),d3 ;instr # movea.l trk_previnstra(a5),a3 ;instr data address ; ---------------- does this instrument have holding?? move.b trk_inithold(a5),trk_noteoffcnt(a5) ;initialize hold bne.s plr_holdok ;not 0 -> OK st trk_noteoffcnt(a5) ;0 -> hold = 0xff (-1) ; ---------------- and finally: plr_holdok: bsr _PlayNote ;play it!!!!!!!!!!! ; ---------------- end of loop: handle next track, or quit plr_endloop0: addq.b #1,d7 cmp.w numtracks(pc),d7 blt.w plr_loop0 addq.l #4,sp ;trackdataptrs ; and advance song pointers lea pline(pc),a3 move.w (a3),d1 ;pline addq.w #1,d1 ;very important!!! advance line!! cmp.w numlines(pc),d1 ;important too!!! advance block?? bgt.s plr_chgblock ;yes!!! tst.b nextblock ;command F00 ?? beq.s plr_nochgblock ;no, don't change block plr_chgblock: moveq #0,d1 ;clear line number cmp.w #2,6(a3) ;play block/play song? bne.s plr_nonewseq ;play block only... move.w 4(a3),d0 ;get play sequence number tst.b nextblock bmi.s plr_noadvseq ;Bxx sets nextblock to 0xff (= neg) addq.w #1,d0 ;advance sequence number plr_noadvseq: cmp.w 506(a4),d0 ;is this the highest seq number?? blt.s plr_notagain ;no. moveq #0,d0 ;yes: play song again moveq #0,d1 ;...forever!!! plr_notagain: move.b d0,5(a3) ;remember new playseq-# lea 508(a4),a0 ;offset in sequence table move.b 0(a0,d0.w),d0 ;get number of the block IFNE CHECK cmp.b 505(a4),d0 ;beyond last block?? blt.s plr_nolstblk ;no.. moveq #0,d0 ;play block 0 ENDC plr_nolstblk: move.b d0,3(a3) ;store pblock plr_nonewseq: clr.b nextblock ;clear this if F00 set it plr_nochgblock: move.w d1,(a3) ;set new pline movea.l 16(a6),a0 move.w 2(a3),d0 ;pblock lsl.w #2,d0 movea.l 0(a0,d0.w),a2 ;block... move.b (a2),d7 ;# of tracks move.w (a3),d0 ;play line move.w d0,d1 add.w d0,d0 ;d0 * 2 add.w d1,d0 ;+ d0 = d0 * 3 mulu d7,d0 lea 2(a2,d0.w),a2 move.b 769(a4),d3 ;interrupts/note lea trackdataptrs(pc),a0 subq.b #1,d7 plr_chkhold: movea.l (a0)+,a1 ;track data tst.b trk_noteoffcnt(a1) ;hold?? bmi.s plr_holdend ;no. move.b (a2),d1 ;get the 1st byte.. bne.s plr_hold1 move.b 1(a2),d1 and.b #$f0,d1 beq.s plr_holdend ;don't hold bra.s plr_hold2 plr_hold1: and.b #$3f,d1 ;note?? beq.s plr_hold2 ;no, cont hold.. move.b 1(a2),d1 and.b #$0f,d1 ;get cmd subq.b #3,d1 ;is there command 3 (slide) bne.s plr_holdend ;no -> end holding plr_hold2: add.b d3,trk_noteoffcnt(a1) ;continue holding... plr_holdend: addq.l #3,a2 ;next note dbf d7,plr_chkhold btst #5,767(a4) ;FLAG_STSLIDE?? bne.w plr_endfx ;yes, no effects this time... moveq #0,d3 ;counter = 0!!! nonewnote: ; *********************** This code produces the effects ** moveq #0,d7 ;clear track count moveq #0,d6 lea trackdataptrs(pc),a2 plr_loop1: movea.l (a2)+,a5 moveq #0,d5 moveq #0,d4 move.b trk_cmd(a5),d6 ;get the fx number move.b trk_cmdqual(a5),d4 ;and the last 2 #'s IFNE MIDI tst.b trk_prevmidin(a5) ;first: is it MIDI?? bne.w midifx ENDC cmp.w #4,d7 bge.w endl ;no non-MIDI effects in tracks 4 - 15 tst.b trk_noteoffcnt(a5) bmi.s plr_nowaitoff subq.b #1,trk_noteoffcnt(a5) bpl.s plr_nowaitoff IFNE SYNTH tst.b trk_synthtype(a5) ;synth/hybrid?? beq.s plr_nosyndec move.b trk_decay(a5),trk_volcmd+1(a5) ;set volume command poi nter clr.b trk_volwait(a5) ;abort WAI move.l trk_synthptr(a5),d0 bra.s plr_gosynth ENDC plr_nosyndec: move.b trk_decay(a5),trk_fadespd(a5) ;set fade... bne.s plr_nowaitoff ;if > 0, don't stop sound bset d7,d5 move.w d5,$dff096 ;stop DMA... moveq #0,d5 plr_nowaitoff: IFNE SYNTH move.l trk_synthptr(a5),d0 beq.s plr_nosynth plr_gosynth: move.l d0,a0 bsr.w synth_start ENDC plr_nosynth: move.b trk_fadespd(a5),d0 ;fade?? beq.s plr_nofade ;no. sub.b d0,trk_prevvol(a5) bpl.s plr_nofade2low clr.b trk_prevvol(a5) clr.b trk_fadespd(a5) ;fade no more plr_nofade2low: movea.l trk_audioaddr(a5),a1 move.b trk_prevvol(a5),ac_vol+1(a1) plr_nofade: add.b d6,d6 ;* 2 move.w fx_table(pc,d6.w),d0 jmp fxs(pc,d0.w) fx_table: dc.w fx_00-fxs,fx_01-fxs,fx_02-fxs,fx_03-fxs,fx_04-fxs dc.w fx_05-fxs,fx_xx-fxs,fx_xx-fxs,fx_xx-fxs,fx_xx-fxs dc.w fx_0a-fxs,fx_xx-fxs,fx_0c-fxs,fx_0d-fxs,fx_xx-fxs dc.w fx_0f-fxs fxs: ; **************************************** Effect 01 ****** fx_01: sub.w d4,trk_prevper(a5) ;slide it up!!! move.w trk_prevper(a5),d5 cmp.w #113,d5 ;too high??? bge newvals move.w #113,d5 ;yes, too high!!! move.w d5,trk_prevper(a5) bra newvals ; **************************************** Effect 02 ****** fx_02: add.w d4,trk_prevper(a5) ;slide it down!!!!!!!!! move.w trk_prevper(a5),d5 bra.w newvals ; **************************************** Effect 00 ****** fx_00: tst.b d4 ;both fxqualifiers are 0s: no arpeggio!! beq.w endl move.l d3,d0 divu #3,d0 swap d0 tst.w d0 bne.s fx_arp12 and.b #$0f,d4 add.b (a5),d4 bra.s fx_doarp fx_arp12: subq.b #1,d0 bne.s fx_arp2 lsr.b #4,d4 add.b (a5),d4 bra.s fx_doarp fx_arp2: move.b (a5),d4 fx_doarp: subq.b #1,d4 ;-1 to make it 0 - 127 add.b 766(a4),d4 ;add play transpose add.b trk_stransp(a5),d4 ;add instrument transpose lsl.b #1,d4 ;shift to make index for UWORD movea.l trk_periodtbl(a5),a1 move.w 0(a1,d4.w),d5 bra.w newvals ; **************************************** Effect 0D/0A *** fx_0a: fx_0d: move.b d4,d1 move.b trk_prevvol(a5),d0 ;move previous vol to d0 and.b #$f0,d1 bne.s crescendo sub.b d4,d0 ;sub from prev. vol bpl.s novolund0 moveq #0,d0 ;volumes under zero not accepted!!! novolund0: move.b d0,trk_prevvol(a5) ;put new vol back move.b d0,d1 bra dispvolchng crescendo: lsr.b #4,d1 add.b d1,d0 cmp.b #64,d0 ble.s novolover64 moveq #64,d0 novolover64: move.b d0,trk_prevvol(a5) move.b d0,d1 bra.w dispvolchng ; **************************************** Effect 05 ****** fx_05: move.w trk_prevper(a5),d5 ;this is very simple: get the old pe riod cmp.b #3,d3 ;and.. bge.w newvals ;if counter < 3 sub.w d4,d5 ;subtract effect qualifier bra.w newvals ; **************************************** Effect 03 ****** fx_03: move.w trk_porttrgper(a5),d0 ;d0 = target period beq.w newvals ;no target period specified move.w trk_prevper(a5),d1 ;d1 = curr. period move.b trk_prevportspd(a5),d4 ;get prev. speed cmp.w d0,d1 bhi.s subper ;curr. period > target period add.w d4,d1 ;add the period cmp.w d0,d1 bge.s targreached bra.s targnreach subper: sub.w d4,d1 ;subtract cmp.w d0,d1 ;compare current period to target period bhi.s targnreach targreached: move.w trk_porttrgper(a5),d1 ;eventually push target period clr.w trk_porttrgper(a5) ;now we can forget everything targnreach: move.w d1,trk_prevper(a5) move.w d1,d5 bra.w newvals ; ********************************************************* fx_0c: cmp.b #1,d3 bne.w newvals move.b trk_prevvol(a5),d1 dispvolchng: bra.w newvals ; **************************************** Effect 04 ****** fx_04: tst.b d4 beq.s nonvib move.b d4,trk_vibrspdsz(a5) nonvib: move.b trk_vibroffs(a5),d0 lsr.b #2,d0 and.w #$1f,d0 moveq #0,d1 lea sinetable(pc),a0 move.b 0(a0,d0.w),d5 ext.w d5 move.b trk_vibrspdsz(a5),d0 and.w #$000f,d0 muls d0,d5 asr.w #5,d5 add.w trk_prevper(a5),d5 move.b trk_vibrspdsz(a5),d0 lsr.b #3,d0 and.b #$3e,d0 add.b d0,trk_vibroffs(a5) bra.s newvals ; **************************************** Effect 0F ****** fx_0f: cmp.b #$f1,d4 bne.s no0ff1 cmp.b #3,d3 bne.w endl bra.s playfxnote no0ff1: cmp.b #$f2,d4 bne.s no0ff2 cmp.b #3,d3 bne.w endl bra.s playfxnote no0ff2: cmp.b #$f3,d4 bne.s no0ff3 move.b d3,d0 and.b #2+4,d0 ;is 2 or 4 beq.w endl playfxnote: move.w d7,d0 ;track # to d0... moveq #0,d1 move.b (a5),d1 ;get note # of previous note moveq #0,d2 move.b trk_prevvol(a5),d2 ;get previous volume move.w d3,-(sp) moveq #0,d3 move.b trk_previnstr(a5),d3 ;and prev. sample # movea.l trk_previnstra(a5),a3 bsr _PlayNote move.w (sp)+,d3 bra.s endl no0ff3: cmp.b #$f8,d4 ;f8 = filter off beq.s plr_filteroff cmp.b #$f9,d4 ;f9 = filter on bne.s endl bclr #1,$bfe001 bra.s endl plr_filteroff: bset #1,$bfe001 bra.s endl ; ********************************************************* newvals: movea.l trk_audioaddr(a5),a1 ;get channel address tst.w d5 ;now: do the effects!!! beq.s plr_oldper plr_newper: move.w d5,ac_per(a1) ;push period plr_oldper: move.b trk_prevvol(a5),ac_vol+1(a1) ;get volume & push it fx_xx: endl: addq.b #1,d7 ;increment channel number cmp.w numtracks(pc),d7 ;all channels done??? blt.w plr_loop1 ;not yet!!! plr_endfx: bsr _StartDMA ;turn on DMA plr_exit: movem.l (sp)+,d2-d7/a2-a5 rts pline: dc.w 0 pblock: dc.w 0 pseq: dc.w 0 pstate: dc.w 0 _SetTempo: IFNE CIAB cmp.b #10,d0 ;If tempo <= 10, use SoundTracker tempo bhi.s calctempo subq.b #1,d0 lsl.w #1,d0 move.w sttempo+2(pc,d0.w),d1 bra.s pushtempo calctempo: move.l timerdiv(pc),d1 divu d0,d1 pushtempo: movea.l craddr+4(pc),a0 move.b d1,(a0) ;and set the CIA timer lsr.w #8,d1 movea.l craddr+8(pc),a0 move.b d1,(a0) ENDC rts ; vv-- These values are the SoundTracker tempos (approx.) sttempo: dc.w $0f00 IFNE CIAB dc.w 2417,4833,7250,9666,12083,14500,16916,19332,21436,24163 timerdiv: dc.l 470000 ;this value for CIA freq 709 379 Hz (PAL..) ENDC IFNE MIDI midifx: tst.b trk_noteoffcnt(a5) bmi.s midi_nowaitoff subq.b #1,trk_noteoffcnt(a5) bpl.s midi_nowaitoff move.l a5,a1 move.b trk_prevmidin(a5),d1 beq.s midi_nowaitoff ;no note bsr.w choff_midi midi_nowaitoff: cmp.b #1,d6 bne.s nomidi01fx lea prevmidipbend(pc),a0 moveq #0,d1 move.b trk_prevmidich(a5),d1 ;get previous midi channel lsl.w #1,d1 ;UWORD index tst.b d4 ;x100?? beq.s resetpbend move.w 0(a0,d1.w),d0 ;get previous pitch bend lsl.w #3,d4 ;multiply bend value by 8 add.w d4,d0 cmp.w #$3fff,d0 bls.s bendpitch move.w #$3fff,d0 bendpitch: move.w d0,0(a0,d1.w) ;save current pitch bend lsr.b #1,d1 ;back to UBYTE or.b #$e0,d1 lea noteondata(pc),a0 move.b d1,(a0) ;midi command & channel move.b d0,1(a0) ;lower value and.b #$7f,1(a0) ;clear bit 7 lsr.w #7,d0 and.b #$7f,d0 ;clr bit 7 move.b d0,2(a0) ;higher 7 bits moveq #3,d0 bsr.w _AddMIDIData bra.w endl nomidi01fx: cmp.b #2,d6 bne.s nomidi02fx lea prevmidipbend(pc),a0 moveq #0,d1 move.b trk_prevmidich(a5),d1 lsl.w #1,d1 tst.b d4 beq.s resetpbend ;x200?? move.w 0(a0,d1.w),d0 lsl.w #3,d4 sub.w d4,d0 bpl.s bendpitch ;not under 0 moveq #0,d0 bra.s bendpitch resetpbend: tst.b d3 ;d3 = counter (remember??) bne.w endl move.w #$2000,d0 bra.s bendpitch nomidi02fx: cmp.b #$04,d6 bne.s nomidi04fx moveq #$01,d0 bra.s pushctrldata nomidi04fx: cmp.b #$0e,d6 ;with MIDI, this is "pan", when bne.s nomidi0efx ;values are 0 - $7f moveq #$0a,d0 pushctrldata: tst.b d3 ;do it only once in a note bne.w endl ;(when counter = 0) lea noteondata(pc),a0 ;push "control change" data, move.b trk_prevmidich(a5),(a0) ;d0 = 1. databyte, d4 = 2. db or.b #$b0,(a0) move.b d0,1(a0) move.b d4,2(a0) bmi.w endl ;I said 0 - $7f!!! (for future compatibility) moveq #3,d0 bsr.w _AddMIDIData bra.w endl nomidi0efx: cmp.b #$0f,d6 bne.w endl cmp.b #$fa,d4 ;hold pedal ON bne.s nomffa moveq #$40,d0 moveq #$7f,d4 bra.s pushctrldata nomffa: cmp.b #$fb,d4 ;hold pedal OFF bne.s nomffb moveq #$40,d0 moveq #$00,d4 bra.s pushctrldata nomffb: bra.w fx_0f _ResetMIDI: movem.l d2/a2,-(sp) lea prevmidicpres(pc),a0 clr.l (a0)+ ;force presets to be set again clr.l (a0)+ ;(clear prev. preset numbers) clr.l (a0)+ clr.l (a0)+ clr.b lastcmdbyte lea midiresd(pc),a2 move.b #$e0,(a2) ;reset pitchbenders & mod. wheel move.b #$b0,3(a2) moveq #15,d2 respbendl: movea.l a2,a0 moveq #6,d0 bsr.w _AddMIDIData addq.b #1,(a2) addq.b #1,3(a2) dbf d2,respbendl lea prevmidipbend(pc),a2 moveq #15,d2 resprevpbends: move.w #$2000,(a2)+ dbf d2,resprevpbends movem.l (sp)+,d2/a2 rts midiresd: dc.b $e0,$00,$40,$b0,$01,$00 ENDC getinsdata: moveq #0,d2 IFNE IFF53 move.w 4(a0),d0 ;Soitin-struct in a0 bne.s iff5or3oct ;note # in d1 (0 - ...) ENDC move.l a0,d0 lea _periods(pc),a0 move.l a0,trk_periodtbl(a5) lsl.b #1,d1 move.w 0(a0,d1.w),d5 ;put period to d5 move.l d0,a0 addq.l #6,d0 ;Skip structure move.l (a0),d1 ;length move.w (a3),d2 move.w 2(a3),d3 rts IFNE IFF53 iff5or3oct: movem.l d6-d7,-(sp) moveq #0,d7 move.w d1,d7 divu #12,d7 ;octave # move.l d7,d5 swap d5 ;note number in this oct (0-11) is in d5 move.l (a0),d1 cmp.b #2,d0 bne.s no3oct addq.l #6,d7 divu #7,d1 ;get length of the 1st octave bra.s no5oct no3oct: divu #31,d1 ;get length of the 1st octave (5 octaves) no5oct: move.l d1,d0 ;d0 and d1 = length of the 1st oct move.w (a3),d2 move.w 2(a3),d3 moveq #0,d6 move.b shiftcnt(pc,d7.w),d6 lsl.w d6,d2 lsl.w d6,d3 lsl.w d6,d1 move.b mullencnt(pc,d7.w),d6 mulu d6,d0 ;offset of this oct from 1st oct add.l a0,d0 ;add base address to offset addq.l #6,d0 ;skip structure lea _periods(pc),a1 add.b octstart(pc,d7.w),d5 lsl.b #1,d5 move.w 0(a1,d5.w),d5 movem.l (sp)+,d6-d7 rts ;returns period in d5 shiftcnt: dc.b 4,3,2,1,1,0,2,2,1,1,0,0 mullencnt: dc.b 15,7,3,1,1,0,3,3,1,1,0,0 octstart: dc.b 12,12,12,12,24,24,0,12,12,24,24,36 ENDC xdef _startmusic xdef _endmusic relocentr: tst.l (a0) beq.s norel add.l d1,(a0) norel: addq.l #4,a0 rts _RelocModule: movem.l a2-a3/d2-d3,-(sp) movea.l a0,a2 move.l a2,d1 ;d1 = ptr to start of module lea 8(a2),a0 bsr.s relocentr ;reloc song ptr addq.l #4,a0 bsr.s relocentr ;reloc blockarr ptr addq.l #4,a0 bsr.s relocentr ;reloc smplarr ptr addq.l #4,a0 bsr.s relocentr ;reloc expdata ptr movea.l 24(a2),a0 movea.l 8(a2),a1 moveq #0,d0 move.b 787(a1),d0 ;number of samples subq.b #1,d0 relocs: bsr.s relocentr move.l -4(a0),d3 ;sample ptr beq.s nosyn move.l d3,a3 tst.w 4(a3) bpl.s nosyn ;type >= 0 move.w 20(a3),d2 ;number of waveforms lea 278(a3),a3 ;ptr to wf ptrs subq.w #1,d2 relsyn: add.l d3,(a3)+ dbf d2,relsyn nosyn: dbf d0,relocs movea.l 16(a2),a0 move.w 504(a1),d0 subq.b #1,d0 relocb: bsr.s relocentr dbf d0,relocb move.l 32(a2),d0 ;extension struct beq.s rel_ex move.l d0,a0 addq.l #4,a0 ;pass "reserved" bsr.s relocentr ;InstrExt... addq.l #4,a0 ;pass sizes of InstrExt bsr.s relocentr ;annotxt rel_ex movem.l (sp)+,d2-d3/a2-a3 rts _InitModule: movem.l a2-a3/d2,-(sp) move.l a0,d0 beq.s IM_exit ;0 => xit IFNE RELVOL movea.l 8(a0),a1 ;MMD0song move.b 786(a1),d0 ;d0 = mastervol ext.w d0 lea 770(a1),a1 ;a1 = trkvol lea trackdataptrs(pc),a2 moveq #15,d1 IM_loop0 move.b (a1)+,d2 ;get vol... ext.w d2 move.l (a2)+,a3 ;pointer to track data mulu d0,d2 ;mastervol * trackvol lsr.w #4,d2 move.w d2,trk_trackvol(a3) dbf d1,IM_loop0 ENDC lea holdvals(pc),a2 move.l 32(a0),d0 ;expdata... beq.s IM_clrhlddec ;none here move.l d0,a1 move.l 4(a1),d0 ;exp_smp beq.s IM_clrhlddec ;again.. nothing move.l d0,a0 ;InstrExt... move.w 8(a1),d2 ;# of entries beq.s IM_clrhlddec subq.w #1,d2 ;- 1 (for dbf) move.w 10(a1),d0 ;entry size IM_loop1 move.b 1(a0),63(a2) ;InstrExt.decay ->decay move.b (a0),(a2)+ ;InstrExt.hold -> holdvals adda.w d0,a0 ;ptr to next InstrExt dbf d2,IM_loop1 bra.s IM_exit IM_clrhlddec moveq #62,d0 ;no InstrExt => clear holdvals/decays IM_loop2 clr.w (a2)+ dbf d0,IM_loop2 IM_exit movem.l (sp)+,a2-a3/d2 rts _startmusic: IFNE MIDI bsr.w _GetSerial tst.l d0 bne.s IP_error ENDC bsr.w _AudioInit tst.l d0 bne.s IP_error moveq #33,d0 bsr.w _SetTempo ;set default tempo lea _playmodule,a0 bsr.w _RelocModule lea _playmodule,a0 bra.s _PlayModule IP_error bsr.w _endmusic moveq #-1,d0 rts _endmusic: move.b _timeropen(pc),d0 beq.s RP_notimer ;timer is not ours bsr.s _StopPlayer RP_notimer: bsr.w _AudioRem IFNE MIDI bsr.w _FreeSerial ENDC rts _PlayModule: move.l a0,-(sp) bsr _InitModule move.l (sp)+,a0 move.b _timeropen(pc),d0 beq.s PM_end ;resource allocation failure move.l a0,d0 beq.s PM_end ;module failure IFNE CIAB movea.l craddr(pc),a1 bclr #0,(a1) ;stop timer... ENDC clr.l _module IFNE MIDI clr.b lastcmdbyte ENDC move.l a0,-(sp) movea.l 8(a0),a0 ;song move.b 769(a0),_counter ;init counter btst #0,767(a0) bne.s PM_filon bset #1,$bfe001 bra.s PM_filset PM_filon bclr #1,$bfe001 PM_filset move.w 764(a0),d0 ;get default tempo moveq #0,d1 move.b 508(a0),d1 ;get first playseq entry lea pline(pc),a0 ;clear pline etc.. move.l d1,(a0)+ moveq #2,d1 move.l d1,(a0) ;pstate = 2 (play song) move.l (sp)+,_module IFNE CIAB bsr.w _SetTempo ;set default tempo bset #0,(a1) ;start timer => PLAY!! ENDC PM_end rts _StopPlayer: move.b _timeropen(pc),d0 beq.s SP_end ;res. alloc fail. IFNE CIAB movea.l craddr(pc),a0 bclr #0,(a0) ;stop timer ENDC clr.w pstate IFNE MIDI clr.b lastcmdbyte ENDC bsr.w SoundOff SP_end rts ; ************************************************************************* _AudioInit: movem.l a4/a6/d2-d3,-(sp) moveq #0,d2 movea.l 4,a6 ; +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ alloc signal bit IFNE AUDDEV addq.l #1,d2 moveq #-1,d0 jsr -$14a(a6) ;AllocSignal() tst.b d0 bmi.w initerr move.b d0,sigbitnum ; +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ prepare IORequest lea allocport(pc),a1 move.b d0,15(a1) ;set mp_SigBit move.l a1,-(sp) suba.l a1,a1 jsr -$126(a6) ;FindTask(0) move.l (sp)+,a1 move.l d0,16(a1) ;set mp_SigTask lea reqlist(pc),a0 move.l a0,(a0) ;NEWLIST begins... addq.l #4,(a0) clr.l 4(a0) move.l a0,8(a0) ;NEWLIST ends... ; +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ open audio.device addq.l #1,d2 lea allocreq(pc),a1 lea audiodevname(pc),a0 moveq #0,d0 moveq #0,d1 jsr -$1bc(a6) ;OpenDevice() tst.b d0 bne.w initerr st.b audiodevopen ; +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ open ciaa.resource addq.l #1,d2 ENDC IFNE CIAB cmp.b #50,$212(a6) ;ExecBase->VBlankFrequency beq.s init_pal move.l #474326,timerdiv ;Assume that CIA freq is 715 909 Hz init_pal: moveq #0,d0 lea ciabname(pc),a1 jsr -$1f2(a6) ;OpenResource() tst.l d0 beq.s initerr move.l d0,_ciaresource ; +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ attach interrupt addq.l #1,d2 moveq #2,d3 lea craddr(pc),a4 move.l #$bfdf00,(a4)+ ;Initialize Timer B addresses move.l #$bfd600,(a4)+ move.l #$bfd700,(a4) move.l d0,a6 lea timerinterrupt(pc),a1 ;Attempt to get timer B clr.l -4(a1) ;clear module pointer moveq #1,d0 ;Bit number 1: Timer B jsr -$6(a6) ;AddICRVector tst.l d0 beq.s gotTimerB ;succeeded!! moveq #1,d3 lea timerinterrupt(pc),a1 ;no. Get timer A then... moveq #0,d0 ;Bit number 0: Timer A jsr -$6(a6) tst.l d0 bne.s initerr move.l #$bfd500,(a4) ;Set Timer A addresses move.l #$bfd400,-(a4) move.l #$bfde00,-(a4) gotTimerB: movea.l craddr(pc),a0 ;get Control Register address and.b #%10000000,(a0) ;clear CtrlReg bits 0 - 6 move.b d3,_timeropen ;d3: 1 = TimerA 2 = TimerB ENDC IFNE VBLANK moveq #5,d0 ;INTB_VERTB lea timerinterrupt(pc),a1 jsr -$a8(a6) ;AddIntServer st _timeropen ENDC clr.w pstate moveq #0,d0 initret: movem.l (sp)+,a4/a6/d2-d3 rts initerr: move.l d2,d0 bra.s initret _AudioRem: move.l a6,-(sp) moveq #0,d0 lea _timeropen(pc),a0 move.b (a0),d0 beq.s rem1 ; +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ remove interrupt clr.b (a0) IFNE CIAB move.l _ciaresource(pc),a6 lea timerinterrupt(pc),a1 subq.b #1,d0 jsr -$c(a6) ;RemICRVector ENDC IFNE VBLANK movea.l 4,a6 lea timerinterrupt(pc),a1 moveq #5,d0 jsr -$ae(a6) ;RemIntServer ENDC rem1: IFNE AUDDEV movea.l 4,a6 lea audiodevopen(pc),a0 tst.b (a0) beq.s rem2 move.w #$000f,$dff096 ;stop audio DMA ; +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ close audio.device clr.b (a0) lea allocreq(pc),a1 jsr -$1c2(a6) ;CloseDevice() rem2: moveq #0,d0 move.b sigbitnum(pc),d0 bmi.s rem3 ; +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ free signal bit jsr -$150(a6) ;FreeSignal() ENDC rem3: move.l (sp)+,a6 rts IFNE MIDI _GetSerial: move.l a6,-(sp) ;Get serial port for MIDI bsr.s GetSer2 tst.l d0 ;got the port?? beq.s rgser ;yes movea.l 4,a6 ;no..try to flush serial.device: jsr -$84(a6) ;Forbid lea $15e(a6),a0 ;ExecBase->DeviceList lea serdev(pc),a1 ;"serial.device" jsr -$114(a6) ;FindName tst.l d0 beq.s serdnotf ;no serial.device!! move.l d0,a1 jsr -$1b6(a6) ;RemDevice serdnotf: jsr -$8a(a6) ;and Permit bsr.s GetSer2 ;now try it again... rgser: move.l (sp)+,a6 rts GetSer2: movea.l 4,a6 moveq #0,d0 lea miscresname(pc),a1 jsr -$1f2(a6) ;OpenResource() move.l d0,miscresbase tst.l d0 beq.s gserror move.l d0,a6 lea medname(pc),a1 moveq #0,d0 ;serial port jsr -$6(a6) ;AllocMiscResource() tst.l d0 bne.s gserror st.b serportalloc clr.w intrson move.w $dff01c,d0 btst #0,d0 sne intrson moveq #0,d0 ;TBE lea serinterrupt(pc),a1 move.l 4,a6 jsr -$a2(a6) ;SetIntVector() move.l d0,prevtbe move.w #$8001,$dff09a ;TBE on!! move.w #114,$dff032 ;set baud rate (SERPER) moveq #0,d0 rts gserror: moveq #-1,d0 rts intrson: dc.b 0,0 _FreeSerial: move.l a6,-(sp) tst.l miscresbase beq.s retfs tst.b serportalloc beq.s retfs move.w #$0001,$dff09a ;disable TBE movea.l prevtbe(pc),a1 moveq #0,d0 movea.l 4,a6 jsr -$a2(a6) ;SetIntVector() tst.b intrson beq.s nofsstbe move.w #$8001,$dff09a nofsstbe: movea.l miscresbase(pc),a6 moveq #0,d0 ;serial port jsr -$c(a6) ;FreeMiscResource() clr.b serportalloc clr.b lastcmdbyte retfs: move.l (sp)+,a6 rts prevtbe: dc.l 0 SerIntHandler: move.w #$4000,$9a(a0) ;disable... addq.b #1,$126(a6) move.w #1,$9c(a0) ;clear intreq bit move.b bytesinbuff(pc),d0 beq.s exsih ;buffer empty movea.l 4(a1),a5 ;get buffer read pointer move.w #$100,d1 ;Stop bit move.b (a5),d1 ;get byte move.w d1,$30(a0) ;and push it out!! (SERDAT) addq.l #1,a5 ;add 1 cmpa.l a1,a5 ;shall we reset ptr?? bne.s norrbuffptr ;not yet.. lea sendbuffer(pc),a5 norrbuffptr: subq.b #1,d0 ;one less bytes in buffer move.b d0,bytesinbuff ;remember it move.l a5,4(a1) ;push new read pointer back exsih: subq.b #1,$126(a6) bge.s exsih0 move.w #$c000,$9a(a0) exsih0: rts _AddMIDIData: tst.b serportalloc beq.s retamd movem.l a2/a6,-(sp) movea.l 4,a6 move.w #$4000,$dff09a ;Disable interrupts addq.b #1,$126(a6) ;ExecBase->IDNestCnt move.b bytesinbuff(pc),d1 bne.s noTBEreq move.w #$8001,$dff09c ;request TBE noTBEreq: lea buffptr(pc),a2 ;end of buffer (ptr) movea.l (a2),a1 ;buffer pointer adddataloop: move.b (a0)+,d1 ;get byte bpl.s norscheck ;this isn't a status byte cmp.b #$ef,d1 ;forget system messages bhi.s norscheck cmp.b lastcmdbyte(pc),d1 ;same as previos status byte?? beq.s samesb ;yes, skip move.b d1,lastcmdbyte ;no, don't skip but remember!! norscheck: move.b d1,(a1)+ ;push it to midi send buffer addq.b #1,bytesinbuff samesb: cmpa.l a2,a1 ;end of buffer?? bne.s noresbuffptr ;no, no!! lea sendbuffer(pc),a1 ;better reset it to avoid trashing noresbuffptr: subq.b #1,d0 bne.s adddataloop move.l a1,(a2) ;push new buffer ptr back overflow: subq.b #1,$126(a6) bge.s retamd1 move.w #$c000,$dff09a ;enable interrupts again retamd1: movem.l (sp)+,a2/a6 retamd: rts sendbuffer: ds.b 128 buffptr: dc.l sendbuffer readbuffptr: dc.l sendbuffer miscresbase: dc.l 0 lastcmdbyte: dc.b 0 ENDC even IFNE MIDI preschgdata: dc.b 0,0 noteondata: dc.l 0 serportalloc: dc.b 0 bytesinbuff: dc.b 0 ENDC IFNE AUDDEV audiodevopen: dc.b 0 sigbitnum: dc.b -1 ENDC _timeropen: dc.b 0 even IFNE CIAB _ciaresource: dc.l 0 craddr: dc.l 0 dc.l 0 ;tloaddr dc.l 0 ;thiaddr ENDC _module: dc.l 0 timerinterrupt: dc.w 0,0,0,0,0 dc.l timerintname,0,_IntHandler IFNE MIDI serinterrupt: dc.w 0,0,0,0,0 dc.l serintname,buffptr,SerIntHandler ENDC IFNE AUDDEV allocport: dc.l 0,0 ;succ, pred dc.b 4,0 ;NT_MSGPORT dc.l 0 ;name dc.b 0,0 ;flags = PA_SIGNAL dc.l 0 ;task reqlist: dc.l 0,0,0 ;list head, tail and tailpred dc.b 5,0 allocreq: dc.l 0,0 dc.b 5,127 ;NT_MESSAGE, use maximum priority (127) dc.l 0,allocport ;name, replyport dc.w 68 ;length dc.l 0 ;io_Device dc.l 0 ;io_Unit dc.w 0 ;io_Command dc.b 0,0 ;io_Flags, io_Error dc.w 0 ;ioa_AllocKey dc.l sttempo ;ioa_Data dc.l 1 ;ioa_Length dc.w 0,0,0 ;ioa_Period, Volume, Cycles dc.w 0,0,0,0,0,0,0,0,0,0 ;ioa_WriteMsg audiodevname: dc.b 'audio.device',0 ENDC IFNE CIAB ciabname: dc.b 'ciab.resource',0 ENDC timerintname: dc.b 'MEDTimerInterrupt',0 IFNE MIDI serintname: dc.b 'MEDSerialInterrupt',0 ENDC IFNE MIDI miscresname: dc.b 'misc.resource',0 serdev: dc.b 'serial.device',0 ENDC medname: dc.b 'MED - Player routine',0 ;yeah, our name even IFNE SYNTH _synthper: dc.w 3424,3232,3048,2880,2712,2560,2416,2280,2152,2032 dc.w 1920,1812,1712,1616,1524,1440,1356,1280,1208,1140 dc.w 1076,1016,960,906 ENDC _periods: dc.w 856,808,762,720,678,640,604,570,538,508,480,453 dc.w 428,404,381,360,339,320,302,285,269,254,240,226 dc.w 214,202,190,180,170,160,151,143,135,127,120,113 dc.w 214,202,190,180,170,160,151,143,135,127,120,113 dc.w 214,202,190,180,170,160,151,143,135,127,120,113 dc.w 214,202,190,180,170,160,151,143,135,127,120,113 holdvals: ds.b 63 decays: ds.b 63 section "chipdata",data_c _chipzero: dc.l 0 _playmodule: incbin "module" ;; <<< MODULE NAME HERE ; the track-data structure definition: rsreset trk_prevnote rs.b 1 ;previous note number trk_previnstr rs.b 1 ;previous instrument number trk_prevvol rs.b 1 ;previous volume trk_prevmidich rs.b 1 ;previous MIDI channel trk_cmd rs.b 1 ;command (the 3rd number from right) trk_cmdqual rs.b 1 ;command qualifier (infobyte, databyte..) trk_prevmidin rs.b 1 ;previous MIDI note trk_noteoffcnt rs.b 1 ;note-off counter (hold) trk_inithold rs.b 1 ;default hold for this instrument trk_initdecay rs.b 1 ;default decay for.... trk_stransp rs.b 1 ;instrument transpose trk_pad rs.b 1 trk_previnstra rs.l 1 ;address of the previous instrument data trk_trackvol rs.w 1 ;relative volume of track (premultiplied) ; the following data only on tracks 0 - 3 trk_prevper rs.w 1 ;previous period trk_audioaddr rs.l 1 ;hardware audio channel base address trk_sampleptr rs.l 1 ;pointer to sample trk_samplelen rs.w 1 ;length (>> 1) trk_porttrgper rs.w 1 ;portamento (cmd 3) target period trk_vibroffs rs.b 1 ;vibrato table offset trk_vibrspdsz rs.b 1 ;vibrato speed/size (cmd 4 qualifier) trk_synthptr rs.l 1 ;pointer to synthetic/hybrid instrument trk_arpgoffs rs.w 1 ;SYNTH: current arpeggio offset trk_arpsoffs rs.w 1 ;SYNTH: arpeggio restart offset trk_volxcnt rs.b 1 ;SYNTH: volume execute counter trk_wfxcnt rs.b 1 ;SYNTH: waveform execute counter trk_volcmd rs.w 1 ;SYNTH: volume command pointer trk_wfcmd rs.w 1 ;SYNTH: waveform command pointer trk_volwait rs.b 1 ;SYNTH: counter for WAI (volume list) trk_wfwait rs.b 1 ;SYNTH: counter for WAI (waveform list) trk_synthvibspd rs.w 1 ;SYNTH: vibrato speed trk_wfchgspd rs.w 1 ;SYNTH: period change trk_volchgspd rs.b 1 ;SYNTH: volume change trk_prevnote2 rs.b 1 ;SYNTH: previous note trk_initvolxspd rs.b 1 ;SYNTH: volume execute speed trk_initwfxspd rs.b 1 ;SYNTH: waveform execute speed trk_perchg rs.w 1 ;SYNTH: curr. period change from trk_prevper trk_synviboffs rs.w 1 ;SYNTH: vibrato pointer trk_synvibdep rs.w 1 ;SYNTH: vibrato depth trk_synvol rs.b 1 ;SYNTH: current volume trk_synthtype rs.b 1 ;>0 = synth, -1 = hybrid, 0 = no synth trk_periodtbl rs.l 1 ;pointer to period table trk_prevportspd rs.w 1 ;portamento (cmd 3) speed trk_decay rs.b 1 ;decay trk_fadespd rs.b 1 ;decay speed end