• File: player.asm
  • Full Path: /srv/http/kleku/pub/c64/rulez-games-cd/j/ai200-jinn/player.asm
  • Date Modified: 2010-03-23 11:00:00
  • File size: 25.53 KB
  • MIME-type: text/plain
  • Charset: utf-8
 
Open Back
;       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