; Compile with WLA-6510 (http://www.hut.fi/~vhelin/wla.html) .INCLUDE "mem.inc" .DEFINE COMPILE_PLAYER 0 ; 0 to compile editor (with normal player) ; 1 to compile normal player only ; 2 to compile packed block data player .DEFINE reloc $1000 ; note that seq data must be relocated too! .DEFINE zeropage $40 ;***** PLAYER DEFINES **************************************************** .DEFINE FreqTab $0358+reloc .DEFINE Music $0400+reloc .DEFINE VibTab $0400+reloc .DEFINE SoundTab $0420+reloc .DEFINE FilTab $0500+reloc .DEFINE WaveTab $0540+reloc .DEFINE ArpTab $0580+reloc .DEFINE Sequencer $05c0+reloc .DEFINE BlockData $0600+reloc .ENUM zeropage cmdtick DB fbase DB c1hold DB c2hold DB c3hold DB count DB speed DB seqpos DB step DB block DB vibpos DB mod DB modh DB .ENDE .ORG $0 .SECTION "BANKHEADER" ; CBM binary load address .IF COMPILE_PLAYER == 0 .dw $0801 .ELSE .dw reloc .ENDIF .ENDS .IF COMPILE_PLAYER == 0 ;***** EDITOR ************************************************************ .ORG $0801 .SECTION "run" FORCE .DB $0b,$08,$01,$00,$9e,$32,$30,$36,$31 ;"1 sys2061" .DB $00,$00,$00 Go: jmp Editor ;----- save executable player .DEFINE xpchar $fb ; scroll pos addr .DEFINE xpfin $fc ; scroll fine addr .DEFINE xpscry $40 ; scroll y-line sei jsr $1000 lda #$16 sta $d018 ldy #$00 sty $d020 sty $d021 _xpinit: lda.w xptoptext,y sta $0400,y lda.w xpbottext,y sta $0700,y lda #$20 sta $0500,y sta $0600,y lda #$01 sta $d800,y lda #$0f sta $d900,y sta $da00,y sta $db00,y iny bne _xpinit lda #$90 ; 3 second delay before playing sta count lda #$00 sta xpchar lda #$07 sta xpfin xpframeloop: lda #$08 sta $d016 lda xpfin ldx #xpscry _xpscrtop: cpx $d012 bne _xpscrtop sta $d016 lda #$08 ldx #xpscry+16 _xpscrbot: cpx $d012 bne _xpscrbot sta $d016 dec xpfin bpl _xpnonewchar lda #$07 sta xpfin ldy #$00 _xpleap: lda.w $0401+$28*2,y sta.w $0400+$28*2,y iny cpy #$27 bne _xpleap ldy xpchar lda.w xpscroller,y sta.w $0427+$28*2 iny cpy #$60 bne _xpnowrap ldy #$00 _xpnowrap: sty xpchar _xpnonewchar: lda #$0c ldx #$fc _xpwaitfc: cpx $d012 bne _xpwaitfc sta $d020 jsr $1003 lda #$00 sta $d020 jmp xpframeloop xpscroller: .DSB $60 $20 xptoptext: .DSB $100 $20 xpbottext: .INCBIN "xpbottext.bin" .ENDS .INCLUDE "editor.asm" .ENDIF ;***** JSR $1000 TO INITIALIZE PLAYER ************************************ .ORG reloc .SECTION "johnplayer" FORCE jmp Initialize ;***** CHANNEL 1 ********************************************************* c1: ldy c1hold ; 0 = no hold, other = sound offset beq c1sndp_ ldx #$ff stx.w c1gate_+1 ; key down - gate on inx stx c1hold ; voice reset lda SoundTab,y ; $00: attack/decay sta $d405 lda SoundTab+1,y ; $01: sustain/release sta $d406 lda #$09 ; testbit+gate to stabilize adsr sta $d404 lda SoundTab+2,y ; $02: sound pos sta.w c1sndp_+1 lda SoundTab+3,y ; $03: sound end sta.w c1snde_+1 lda SoundTab+4,y ; $04: sound loop sta.w c1snd8_+1 lda SoundTab+9,y ; $09: resonance/filt. ch select sta $d417 lda SoundTab+10,y ; $0a: filt. type/master volume sta $d418 lda SoundTab+5,y ; $05: pw init ($00 = no init) beq c1done sta.w c1pwhi_+1 lda SoundTab+6,y ; $06: pw mod rate sta.w c1pwal_+1 lda SoundTab+7,y ; $07: pw mod top sta.w c1ptop_+1 lda SoundTab+8,y ; $08: pw mod bottom sta.w c1pbot_+1 c1nopwmini: jmp c1done c1sndp_: ldy #$00 ; c1 snd pos beq c1done c1snde_: cpy #$00 ; c1 snd end bne c1nolp c1snd8_: ldy #$00 ; c1 snd loop c1nolp: lda FilTab,y ; c1 controls filter clc adc fbase ; filter base (from note data) sta $d416 ; set filter cutoff lda WaveTab,y ; get waveform c1gate_: and #$fe ; gate on/off sta $d404 ; set channel waveform c1pwlo_: lda #$00 c1pwal_: adc #$02 tax c1pwhi_: lda #$08 c1pwah_: adc #$00 c1ptop_: cmp #$10 ; pulse width top limit beq c1pwrev c1pbot_: cmp #$00 ; pulse width bottom limit beq c1pwrev stx.w c1pwlo_+1 stx $d402 ; set c1 pulse width lo sta.w c1pwhi_+1 sta $d403 ; set c1 pulse width hi bne c1pwdone ; always branch c1pwrev: lda.w c1pwal_+0 ; reverse pulse direction eor #$80 ; morph between adc/sbc sta.w c1pwal_+0 sta.w c1pwah_+0 c1pwdone: lda ArpTab,y ; get arpeggio byte asl bcs c1abs ; absolute freq hi value (for drums) c1note_: adc #$00 ; add to channel note tax lda FreqTab,x c1modulatel_: cmp #mod ; ($c9=cmp#=vib off/ $65=adc=vib on) sta $d400 ; set c1 frequency lo lda FreqTab+1,x c1modulateh_: cmp #modh ; ($c9=cmp#=vib off/ $65=adc=vib on) c1abs: sta $d401 ; set c1 frequency hi iny sty.w c1sndp_+1 ; advance c1 snd pos c1done: ;***** CHANNEL 2 ********************************************************* c2: ldy c2hold ; 0 = no hold, other = sound offset beq c2sndp_ ldx #$ff stx.w c2gate_+1 ; key down - gate on inx stx c2hold ; voice reset lda SoundTab,y ; $00: attack/decay sta $d405+7 lda SoundTab+1,y ; $01: sustain/release sta $d406+7 lda #$09 sta $d404+7 ; testbit+gate to stabilize adsr lda SoundTab+2,y ; $02: sound pos sta.w c2sndp_+1 lda SoundTab+3,y ; $03: sound end sta.w c2snde_+1 lda SoundTab+4,y ; $04: sound loop sta.w c2snd8_+1 lda SoundTab+5,y ; $05: pw init ($00 = no init) beq c2done sta.w c2pwhi_+1 lda SoundTab+6,y ; $06: pw mod rate sta.w c2pwal_+1 lda SoundTab+7,y ; $07: pw mod top sta.w c2ptop_+1 lda SoundTab+8,y ; $08: pw mod bottom sta.w c2pbot_+1 jmp c2done c2sndp_: ldy #$00 ; c2 snd pos beq c2done c2snde_: cpy #$00 ; c2 snd end bne c2nolp c2snd8_: ldy #$00 ; c2 snd loop c2nolp: lda WaveTab,y ; get waveform c2gate_: and #$fe ; gate on/off sta $d404+7 ; set channel waveform c2pwlo_: lda #$55 c2pwal_: sbc #$02 tax c2pwhi_: lda #$08 c2pwah_: sbc #$00 c2ptop_: cmp #$10 ; pulse width top limit beq c2pwrev c2pbot_: cmp #$00 ; pulse width bottom limit beq c2pwrev stx.w c2pwlo_+1 stx $d402+7 ; set c2 pulse width lo sta.w c2pwhi_+1 sta $d403+7 ; set c2 pulse width hi bne c2pwdone ; always branch c2pwrev: lda.w c2pwal_+0 ; reverse pulse direction eor #$80 ; morph between adc/sbc sta.w c2pwal_+0 sta.w c2pwah_+0 c2pwdone: lda ArpTab,y ; get arpeggio byte asl bcs c2abs ; absolute freq hi value (for drums) c2note_: adc #$00 ; add to channel note tax lda FreqTab,x c2modulatel_: cmp #mod ; ($c9=cmp#=vib off/ $65=adc=vib on) sta $d400+7 ; set c2 frequency lo lda FreqTab+1,x c2modulateh_: cmp #modh ; ($c9=cmp#=vib off/ $65=adc=vib on) c2abs: sta $d401+7 ; set c2 frequency hi iny sty.w c2sndp_+1 ; advance c2 snd pos c2done: ;***** CHANNEL 3 ********************************************************* c3: ldy c3hold ; 0 = no hold, other = sound offset beq c3sndp_ ldx #$ff stx.w c3gate_+1 ; key down - gate on inx stx c3hold ; voice reset lda SoundTab,y ; $00: attack/decay sta $d405+14 lda SoundTab+1,y ; $01: sustain/release sta $d406+14 lda #$09 sta $d404+14 ; testbit+gate to stabilize adsr lda SoundTab+2,y ; $02: sound pos sta.w c3sndp_+1 lda SoundTab+3,y ; $03: sound end sta.w c3snde_+1 lda SoundTab+4,y ; $04: sound loop sta.w c3snd8_+1 lda SoundTab+5,y ; $05: pw init ($00 = no init) beq c3done sta.w c3pwhi_+1 lda SoundTab+6,y ; $06: pw mod rate sta.w c3pwal_+1 lda SoundTab+7,y ; $07: pw mod top sta.w c3ptop_+1 lda SoundTab+8,y ; $08: pw mod bottom sta.w c3pbot_+1 jmp c3done c3sndp_: ldy #$00 ; c3 snd pos beq c3done c3snde_: cpy #$00 ; c3 snd end + 1 bne c3nolp c3snd8_: ldy #$00 ; c3 snd loop c3nolp: lda WaveTab,y ; get waveform c3gate_: and #$fe ; gate on/off sta $d404+14 ; set channel waveform c3pwlo_: lda #$aa c3pwal_: adc #$02 tax c3pwhi_: lda #$08 c3pwah_: adc #$00 c3ptop_: cmp #$10 ; pulse width top limit beq c3pwrev c3pbot_: cmp #$00 ; pulse width bottom limit beq c3pwrev stx.w c3pwlo_+1 stx $d402+14 ; set c3 pulse width lo sta.w c3pwhi_+1 sta $d403+14 ; set c3 pulse width hi bne c3pwdone ; always branch c3pwrev: lda.w c3pwal_+0 ; reverse pulse direction eor #$80 ; morph between adc/sbc sta.w c3pwal_+0 sta.w c3pwah_+0 c3pwdone: lda ArpTab,y ; get arpeggio byte asl bcs c3abs ; absolute freq hi value (for drums) c3note_: adc #$00 ; add to channel note tax lda FreqTab,x c3modulatel_: cmp #mod ; ($c9=cmp#=vib off/ $65=adc=vib on) sta $d400+14 ; set c3 frequency lo lda FreqTab+1,x c3modulateh_: cmp #modh ; ($c9=cmp#=vib off/ $65=adc=vib on) c3abs: sta $d401+14 ; set c3 frequency hi iny sty.w c3sndp_+1 ; write back c3done: ;***** SEQUENCER ********************************************************* seq: dec count beq nextstep lda speed lsr cmp count beq halfstep lda cmdtick beq nocmdtick ;***** CHECK AND EXECUTE COMMAND ***************************************** getcmd: ldy #$00 ; clear cmdtick sty cmdtick lda (step),y ; get cmd beq nocmd tax lda.w cmdjmpL-1,x sta.w docmd_+1 iny lda (step),y ; get cmd parameter tax .IF COMPILE_PLAYER == 2 lda step ; advance note pointer clc adc #$02 sta step bcc _chnoc inc block _chnoc: docmd_: jmp setfilter nocmd: inc step bne _cnonoc inc block _cnonoc: nocmdtick: .ELSE lda step ; advance to next step clc adc #$08 sta step docmd_: jmp setfilter nocmd: lda step ; advance to next step clc adc #$08 sta step nocmdtick: .ENDIF ;***** VIBRATO *********************************************************** vibrate_: lda #$00 beq _vibdone clc adc vibpos and #$0f sta vibpos tay lda.w VibTab,y vibwidth_: cmp #$ea sta mod lda.w VibTab+$10,y sta modh _vibdone: rts ;***** NEXT STEP ********************************************************* nextstep: lda speed sta count halfstep: ;***** GET NOTES ********************************************************* .IF COMPILE_PLAYER == 2 getnotes: getc1: ldy #$00 ; get c1 note lda (step),y beq getc1done ; no note bpl c1notie ; normal note cmp #$ff ; check if 'no notes on line' beq packedreadok sta.w c1gate_+1 ; must be $fe (gate off mask) bne getc1done ; always branch c1notie: sta.w c1note_+1 ; set c1 note iny lda (step),y ; get c1 sound beq getc1done ; tied note, no trig sta c1hold ; init sound next frame lda #$08 ; reset channel sta $d404 getc1done: getc2: iny ; get c2 note lda (step),y beq getc2done ; no note bpl c2notie ; normal note cmp #$ff ; check if 'no more notes on line' beq packedreadok sta.w c2gate_+1 ; must be $fe (gate off mask) bne getc2done ; always branch c2notie: sta.w c2note_+1 ; set c2 note iny lda (step),y ; get c2 sound beq getc2done ; tied note, no trig sta c2hold ; init sound next frame lda #$08 sta $d404+7 ; reset channel getc2done: getc3: iny ; get c3 note lda (step),y beq getc3done ; no note bpl c3notie ; normal note sta.w c3gate_+1 ; must be $fe (gate off mask) bmi getc3done ; always branch c3notie: sta.w c3note_+1 ; set c3 note iny lda (step),y ; get c3 sound beq getc3done ; tied note, no trig sta c3hold ; init sound next frame lda #$08 sta $d404+14 ; reset channel getc3done: packedreadok: iny sty cmdtick ; (set flag: process cmd next frame) tya clc adc step ; advance note pointer sta step bcc _prnoc inc block _prnoc: rts .ELSE getnotes: getc1: ldy #$02 ; get c1 note sty cmdtick ; (set flag: process cmd next frame) lda (step),y beq getc1done ; no note bpl c1notie ; normal note sta.w c1gate_+1 ; must be $fe (gate off mask) bmi getc1done ; always branch c1notie: sta.w c1note_+1 ; set c1 note iny lda (step),y ; get c1 sound beq getc1done ; tied note, no trig sta c1hold ; init sound next frame lda #$08 ; reset channel sta $d404 getc1done: getc2: ldy #$04 ; get c2 note lda (step),y beq getc2done ; no note bpl c2notie ; normal note sta.w c2gate_+1 ; must be $fe (gate off mask) bmi getc2done ; always branch c2notie: sta.w c2note_+1 ; set c2 note iny lda (step),y ; get c2 sound beq getc2done ; tied note, no trig sta c2hold ; init sound next frame lda #$08 sta $d404+7 ; reset channel getc2done: getc3: ldy #$06 ; get c3 note lda (step),y beq getc3done ; no note bpl c3notie ; normal note sta.w c3gate_+1 ; must be $fe (gate off mask) rts c3notie: sta.w c3note_+1 ; set c3 note iny lda (step),y ; get c3 sound beq getc3done ; tied note, no trig sta c3hold ; init sound next frame lda #$08 sta $d404+14 ; reset channel getc3done: rts .ENDIF ;***** BLOCK COMMANDS **************************************************** ;----- entries must be located on the same memory page setfilter: stx fbase ; cmd 3: Flt - set cutoff base rts setspeed: stx speed ; cmd 4: Tmp - set speed dex ; i got rhythm stx count rts blockbreak: lda #$00 ; cmd 2: Brk - block break sta step offchannel: ; cmd 8: Off - channel mod off lda #$c9 ; $c9=cmp#=vib off bmi _modchange modchannel: ; cmd 7: Mod - channel mod on lda #$65 ; $65=adc=vib on _modchange: cpx #$03 beq setc3mod dex beq setc1mod bne setc2mod setvibwidth: cpx #$03 ; cmd 5: Ini - init vibrato bcs _badinit lda.w vibwidthcmd,x ; set vibrato width sta.w vibwidth_ lda.w vibwidthcmd+1,x sta.w vibwidth_+1 ldx #$00 stx mod stx modh stx vibpos ; retrig vibrato ; setvibrate: ;--- stx.w vibrate_+1 ; cmd 6: Vib - set vibrato rate _badinit: rts .IF COMPILE_PLAYER == 2 nextblock: ldy seqpos iny iny _regetblock: sty seqpos firstblockentry: lda Sequencer,y ; get block address high byte bne newblock lda Sequencer+1,y ; sequencer loop tay bpl _regetblock newblock: sta block lda Sequencer+1,y ; get block address low byte sta step rts .ELSE nextblock: inc seqpos ; cmd 1: End - normal block firstblockentry: ldy seqpos lda Sequencer,y ; get block number bne newblock ldx Sequencer+1,y ; sequencer loop stx seqpos lda Sequencer,x newblock: clc adc #(BlockData/256)-1 sta block rts .ENDIF setc1mod: sta.w c1modulatel_ sta.w c1modulateh_ rts setc2mod: sta.w c2modulatel_ sta.w c2modulateh_ rts setc3mod: sta.w c3modulatel_ sta.w c3modulateh_ rts ;***** INITIALIZE ******************************************************** .IF COMPILE_PLAYER == 2 Initialize: lda #$00 ldx #$0c _clr: sta cmdtick,x ; clear zero page variables sta $d400,x sta $d40b,x dex bpl _clr lda #$0f sta $d418 ; set volume lda #$0c sta speed ; initial song speed ldy #$01 sty count ; first step next frame dey jmp firstblockentry ; prepare first block .ELSE Initialize: lda #$00 sta.w c1sndp_+1 ; channels off sta.w c2sndp_+1 sta.w c3sndp_+1 sta.w vibrate_+1 ; vibrato lfo off ldx #$0c _clr: sta cmdtick,x ; clear zero page variables dex bpl _clr ldy #$17 _sid: sta $d400,y dey bpl _sid lda #$0f sta $d418 ; set volume lda #$0c sta speed ; initial song speed lda #$01 sta count ; first step next frame lda #$c9 jsr setc1mod ; vibrato off jsr setc2mod jsr setc3mod jmp firstblockentry ; prepare first block .ENDIF vibwidthcmd: ; x vi,x vi+1,x width multiplier .DB $c9 ; 0 cmp# nop x1 .DB $ea ; 1 nop asl x2 asl ; 2 asl asl x4 asl cmdjmpL: .DB nextblock&255 .DB blockbreak&255 .DB setfilter&255 .DB setspeed&255 .DB setvibwidth&255 .DB setvibrate&255 .DB modchannel&255 .DB offchannel&255 .ENDS ;***** FREQUENCY TABLE *************************************************** .ORG FreqTab .SECTION "freqtable" FORCE .INCBIN "64freq.bin" .ENDS ;***** MUSIC DATA ******************************************************** .ORG Music .SECTION "music" FORCE ;VibTab: ; .DB 0,24,45,59,63,59,45,24 ; .DB 0,-25,-46,-60,-64,-60,-46,-25 ; .DB $00,$00,$00,$00,$00,$00,$00,$00 ; .DB $00,$ff,$ff,$ff,$ff,$ff,$ff,$ff .IF COMPILE_PLAYER == 2 .INCBIN "packtest.bin" .ELSE .INCBIN "default.bin" .ENDIF .ENDS