;-------------------------------------------------------------------------------
; GoatTracker V1.4 standard playroutine with 1-frame hardrestart
;
; NOTE: This playroutine source code does not fall under the GPL license!
; Use it freely for any purpose, commercial or noncommercial.
;-------------------------------------------------------------------------------

                processor 6502
                org $1000

        ; "Virtual addresses" for relocation

mt_instad       = $4000
mt_instsr       = $4001
mt_instpulse    = $4002
mt_instpulsespd = $4003
mt_instpulselow = $4004
mt_instpulsehigh = $4005
mt_instfilter   = $4006
mt_instwave     = $4007
mt_wavetbl      = $4100
mt_notetbl      = $4200
mt_songtbllo    = $4300
mt_songtblhi    = $4400
mt_patttbllo    = $4500
mt_patttblhi    = $4600
mt_filttbl      = $4700

        ; Defines for the music data.
        ; Patterndata 1st byte: repeat prefix or note

C0              = $00
CIS0            = $01
D0              = $02
DIS0            = $03
E0              = $04
F0              = $05
FIS0            = $06
G0              = $07
GIS0            = $08
A0              = $09
B0              = $0a
H0              = $0b
C1              = $0c
CIS1            = $0d
D1              = $0e
DIS1            = $0f
E1              = $10
F1              = $11
FIS1            = $12
G1              = $13
GIS1            = $14
A1              = $15
B1              = $16
H1              = $17
C2              = $18
CIS2            = $19
D2              = $1a
DIS2            = $1b
E2              = $1c
F2              = $1d
FIS2            = $1e
G2              = $1f
GIS2            = $20
A2              = $21
B2              = $22
H2              = $23
C3              = $24
CIS3            = $25
D3              = $26
DIS3            = $27
E3              = $28
F3              = $29
FIS3            = $2a
G3              = $2b
GIS3            = $2c
A3              = $2d
B3              = $2e
H3              = $2f
C4              = $30
CIS4            = $31
D4              = $32
DIS4            = $33
E4              = $34
F4              = $35
FIS4            = $36
G4              = $37
GIS4            = $38
A4              = $39
B4              = $3a
H4              = $3b
C5              = $3c
CIS5            = $3d
D5              = $3e
DIS5            = $3f
E5              = $40
F5              = $41
FIS5            = $42
G5              = $43
GIS5            = $44
A5              = $45
B5              = $46
H5              = $47
C6              = $48
CIS6            = $49
D6              = $4a
DIS6            = $4b
E6              = $4c
F6              = $4d
FIS6            = $4e
G6              = $4f
GIS6            = $50
A6              = $51
B6              = $52
H6              = $53
C7              = $54
CIS7            = $55
D7              = $56
DIS7            = $57
E7              = $58
F7              = $59
FIS7            = $5a
G7              = $5b
GIS7            = $5c
A7              = $5d
KEYOFF          = $5e           ;Keyoff clears the gatebit
REST            = $5f
NOCMD           = $60           ;Instrument & command byte do not exist
FIRSTPACKEDREST = $c0
PACKEDREST      = $00
ENDPATT         = $ff

        ; Patterndata 2nd byte: Instrument number and command ORed with each
        ; other

INST0           = $00           ;There's a maximum of 32 instruments
INST1           = $08
INST2           = $10
INST3           = $18
INST4           = $20
INST5           = $28
INST6           = $30
INST7           = $38
INST8           = $40
INST9           = $48
INST10          = $50
INST11          = $58
INST12          = $60
INST13          = $68
INST14          = $70
INST15          = $78
INST16          = $80
INST17          = $88
INST18          = $90
INST19          = $98
INST20          = $a0
INST21          = $a8
INST22          = $b0
INST23          = $b8
INST24          = $c0
INST25          = $c8
INST26          = $d0
INST27          = $d8
INST28          = $e0
INST29          = $e8
INST30          = $f0
INST31          = $f8

        ; Orderlist commands

REPEAT          = $d0
TRANSDOWN       = $e0
TRANSUP         = $f0
LOOPSONG        = $ff

        ; Used zeropage temp variables

mt_temp1        = $fc
mt_temp2        = $fd
mt_temp3        = $fe
mt_temp4        = $ff

                jmp init
                jmp play

init:           sta init_adc+1
                asl
init_adc:       adc #$00
                sta mt_chnloop+1
                rts

mt_tick0cmdtbllo:dc.b <mt_cmddowavearp
                dc.b <mt_cmddowavearp
                dc.b <mt_cmddowavearp
                dc.b <mt_starttoneportamento
                dc.b <mt_cmddowavearp
                dc.b <mt_setfilter
                dc.b <mt_setsustain
                dc.b <mt_settempo

mt_contcmdtbllo:dc.b <mt_portaup
                dc.b <mt_portadown
                dc.b <mt_toneportamento
                dc.b <mt_vibrato
                dc.b <mt_cmddonothing
                dc.b <mt_cmddonothing
                dc.b <mt_cmddonothing

                ds.b 33,0

play:           ldx #$00
mt_chnloop:     lda #$00
                bmi mt_noinit
                sta mt_chnsongnum,x
                inc mt_chnloop+1
                lda #$00
                sta mt_chnwave,x
                sta mt_chntrans,x
                sta mt_chnwavetbl,x
                sta mt_chnsongptr,x
                sta mt_chnpackrest,x
                sta mt_chnrepeat,x
                lda #$05
                sta mt_chntick,x
                sta mt_chntempo,x
                lda #ENDPATT
                sta mt_chnnewnote,x
                sta mt_chnpattptr,x
                txa
                bne mt_nofiltinit
                sta $d415                       ;Reset cutoff lowbyte
                jsr mt_setfiltersub             ;Set filter 0
mt_nofiltinit:  jmp mt_nextchn
mt_noinit:      ldy mt_chntick,x
                bne mt_nonewnotes
                jmp mt_newnotes
mt_nonewnotes:  bpl mt_noreload
mt_inittick:    ldy mt_chntempo,x
                cpy #$02
                bcs mt_noreload
                tya
                eor #$01
                sta mt_chntempo,x
                lda mt_funktable,y
                tay
mt_noreload:    dey
                tya
                sta mt_chntick,x
                lda mt_chnpattptr,x             ;Check for pattern end
                cmp #ENDPATT
                bcc mt_effects
mt_nextpatt:    ldy mt_chnsongnum,x
mt_songtblloaccess:
                lda mt_songtbllo,y
                sta mt_temp1
mt_songtblhiaccess:
                lda mt_songtblhi,y
                sta mt_temp2
                ldy mt_chnsongptr,x
mt_nextpattloop:lda (mt_temp1),y
                iny
                cmp #REPEAT
                bcc mt_noloopsong
                cmp #TRANSDOWN
                bcc mt_repeat
                cmp #LOOPSONG
                bcc mt_trans
mt_loopsong:    lda (mt_temp1),y
                tay
                bcs mt_nextpattloop
mt_repeat:      sbc #REPEAT-1
                sta mt_chnrepeat,x
                jmp mt_nextpattloop
mt_trans:       sbc #TRANSUP-1
                sta mt_chntrans,x
                jmp mt_nextpattloop
mt_noloopsong:  sta mt_chnpattnum,x
                lda mt_chnrepeat,x
                beq mt_norepeat
                dec mt_chnrepeat,x
                dey
mt_norepeat:    tya
                sta mt_chnsongptr,x
                inc mt_chnpattptr,x
                lda mt_chnnewnote,x
                bpl mt_effects2
                jmp mt_cmddowavearp2

mt_effects:     lda mt_chnnewnote,x
                bmi mt_pulsemod
mt_effects2:    ;dec mt_chnnewnote,x
                ;bne mt_newnoteinit
                ;beq mt_pulseok                    ;Start note now

mt_newnoteinit: lda #$00
                sta mt_chnarpcount,x
                sta mt_chnnewnote,x
                ldy mt_chninstnum,x
                lda mt_instpulse,y
                beq mt_skippulse
                pha
                and #$f0
                sta mt_chnpulsedir,x
                pla
                and #$0f
                sta mt_chnpulse,x
mt_skippulse:   lda mt_instwave,y
                sta mt_chnwavetbl,x
                lda mt_instfilter,y
                beq mt_nofilterchange
                jsr mt_setfiltersub
mt_nofilterchange:
                jmp mt_pulseok

mt_pulsemod:    ldy mt_chninstnum,x
                lda mt_instpulsespd,y
                and #$fe
                beq mt_pulseok
                sta mt_temp1
                lda mt_chnpulsedir,x
                lsr
                bcs mt_pulsesub
mt_pulseadd:    asl
                adc mt_temp1
                pha
                lda mt_chnpulse,x
                adc #$00
                sta mt_chnpulse,x
                cmp mt_instpulsehigh,y
                jmp mt_pulsedone
mt_pulsesub:    asl
                sec
                sbc mt_temp1
                pha
                lda mt_chnpulse,x
                sbc #$00
                sta mt_chnpulse,x
                cmp mt_instpulselow,y
mt_pulsedone:   pla
                adc #$00
                sta mt_chnpulsedir,x
mt_pulseok:     ldy mt_chnwavetbl,x
                bne mt_dowavetbl
                ldy mt_chncommand,x             ;Do only continuous effects
                beq mt_arpeggio                 ;Arpeggio is most common so
                lda #$00                        ;optimize for it
                sta mt_temp2                    ;Clear speed highbyte
                lda mt_contcmdtbllo-1,y
                sta mt_contjump+1
                lda mt_chncmddata,x
mt_contjump:    jmp mt_portaup

mt_dowavetbl:   jsr mt_dowavesub
                jmp mt_nextchn

mt_arpeggio:    lda mt_chncmddata,x
                beq mt_nextchn
                asl
                lda mt_chnarpcount,x
                pha
                adc #$01
                cmp #$06
                bcc mt_arpnotover
                lda #$00
mt_arpnotover:  sta mt_chnarpcount,x
                pla
                lsr
                cmp #$01
                bcc mt_arp1
                bne mt_arp0
mt_arp2:        lda mt_chncmddata,x
                and #$0f
                bpl mt_arpfreq2
mt_arp0:        lda #$00
                bpl mt_arpfreq2
mt_arp1:        lda mt_chncmddata,x
                and #$70
                lsr
                lsr
                lsr
                lsr
mt_arpfreq2:    clc
                adc mt_chnnote,x
                tay
mt_arpfreq:     lda mt_freqtbllo,y
                sta mt_chnfreqlo,x
                lda mt_freqtblhi,y
                sta mt_chnfreqhi,x

mt_nextchn:     lda mt_chnfreqlo,x
                sta $d400,x
                lda mt_chnfreqhi,x
                sta $d401,x
                lda mt_chnwave,x
                sta $d404,x
                lda mt_chnnewnote,x
                bne mt_noadsr
                ldy mt_chninstnum,x
                lda mt_instad,y
                sta $d405,x
                lda mt_instsr,y
                ldy mt_chncommand,x
                cpy #$06
                bne mt_defaultsr
                lda mt_chncmddata,x
mt_defaultsr:   sta $d406,x
                dec mt_chnnewnote,x
mt_noadsr:      lda mt_chnpulsedir,x
                sta $d402,x
                lda mt_chnpulse,x
                sta $d403,x
                cpx #14
                bcs mt_alldone
                txa
                adc #$07
                tax
                jmp mt_chnloop
mt_alldone:     lda #$ff
                sta mt_chnloop+1

mt_filtstep:    lda #$00
                beq mt_filtskip
mt_filttime:    ldy #$00
                bne mt_filtmod
                jsr mt_setfiltersub
                jmp mt_filtskip
mt_filtmod:     dec mt_filttime+1
mt_filtcutoff:  lda #$00
mt_filtcutoffadd:sbc #$00
                sta mt_filtcutoff+1
                sta $d416
mt_filtskip:
mt_d418reg:     lda #$00
mt_volume:      and #$ff
                sta $d418
                rts

mt_newpackedrest:
                inc mt_chnpattptr,x
                sta mt_chnpackrest,x
                bne mt_packedrestcommon
mt_newnotes:    dec mt_chntick,x
                lda mt_chnpackrest,x
                beq mt_nopackedrest
mt_packedrestcommon:
                inc mt_chnpackrest,x
                jmp mt_rest
mt_nopackedrest:ldy mt_chnpattnum,x
mt_patttblloaccess:
                lda mt_patttbllo,y
                sta mt_temp1
mt_patttblhiaccess:
                lda mt_patttblhi,y
                sta mt_temp2
                ldy mt_chnpattptr,x
                lda (mt_temp1),y
                iny
                cmp #NOCMD                      ;Do command+databytes exist?
                bcc mt_cmd
                cmp #FIRSTPACKEDREST
                bcs mt_newpackedrest
                sbc #NOCMD-1
                sta mt_temp3
                bcs mt_nocmd
mt_cmd:         sta mt_temp3
                lda (mt_temp1),y
                and #$f8
                beq mt_skipinst                 ;Instrument number 0 lets
                sta mt_chninstnum,x             ;the instrument stay the same
mt_skipinst:    lda (mt_temp1),y
                and #$07
                sta mt_chncommand,x
                iny
                lda (mt_temp1),y
                sta mt_chncmddata,x
                iny
mt_nocmd:       lda (mt_temp1),y
                cmp #ENDPATT
                beq mt_endpatt
                tya
mt_endpatt:     sta mt_chnpattptr,x
                ldy mt_chncommand,x
                lda mt_temp3
                cmp #KEYOFF                   ;Keyoff or rest?
                beq mt_keyoff
                bcs mt_rest
mt_normalnote:  adc mt_chntrans,x
                sta mt_setnote+1
                cpy #$03                      ;Or toneportamento?
                beq mt_setnote
                ldy mt_chninstnum,x
                lda mt_instpulsespd,y         ;Pulsespd bit 0: hardrestart
                lsr                           ;flag
                lda #$02
                sta mt_chnnewnote,x
                bcs mt_nohardrestart
                dc.b $ff,$00                  ;Hardrestart AD parameter
                sta $d405,x
                lda #$00
                sta $d406,x
mt_nohardrestart:
                ldy mt_chnwavetbl,x
                beq mt_keyoff2
                jsr mt_dowavesub              ;Exec wavetable also here
mt_keyoff2:	clc
mt_keyoff:      lda mt_chnwave,x              ;Keyoff: reset gatebit
                and #$fe
                sta mt_chnwave,x
                bcs mt_rest
mt_setnote:	lda #$00
                sta mt_chnnote,x
mt_rest:        ldy mt_chncommand,x
                lda mt_tick0cmdtbllo,y
                sta mt_tick0jump+1
                lda mt_chncmddata,x
mt_tick0jump:   jmp mt_setfilter

mt_freqadd:     lda mt_chnfreqlo,x
                adc mt_temp1
                sta mt_chnfreqlo,x
                lda mt_chnfreqhi,x
                adc mt_temp2
                sta mt_chnfreqhi,x
                rts

mt_freqsub:     lda mt_chnfreqlo,x
                sbc mt_temp1
                sta mt_chnfreqlo,x
                lda mt_chnfreqhi,x
                sbc mt_temp2
                sta mt_chnfreqhi,x
                rts

mt_cmpfreq:     lda mt_chnfreqhi,x
                cmp mt_freqtblhi,y
                bne mt_cmpfreqdone
                lda mt_chnfreqlo,x
                cmp mt_freqtbllo,y
mt_cmpfreqdone: rts

mt_setfiltersub:tay
                lda mt_filttbl,y
                beq mt_sfsmod
                sta $d417
                lda mt_filttbl+1,y
                sta mt_d418reg+1
                lda mt_filttbl+2,y
                beq mt_sfscutoffskip
                sta mt_filtcutoff+1
                sta $d416
mt_sfscutoffskip:
                lda #$00
                beq mt_sfscommon
mt_sfsmod:      lda mt_filttbl+2,y
                sta mt_filtcutoffadd+1
                lda mt_filttbl+1,y
mt_sfscommon:   sta mt_filttime+1
                lda mt_filttbl+3,y
                sta mt_filtstep+1
                rts

mt_makespeed:   asl
                rol mt_temp2
                asl
                rol mt_temp2
                sta mt_temp1
                rts

        ;Effects. All entrypoints must reside on the same page!

mt_cmddonothing:jmp mt_nextchn

mt_cmddowavearp:lda mt_chnnewnote,x
                bpl mt_cmddonothing
mt_cmddowavearp2:ldy mt_chnwavetbl,x
                beq mt_cdwa_nowave
                jmp mt_dowavetbl
mt_cdwa_nowave: ldy mt_chncommand,x
                bne mt_cmddonothing
                jmp mt_arpeggio

mt_starttoneportamento:
                lda #$00
                sta mt_chnarpcount,x
                beq mt_cmddowavearp

mt_setfilter:   jsr mt_setfiltersub
                jmp mt_cmddowavearp

mt_setsustain:  ldy mt_chnnewnote,x
                bpl mt_cmddonothing
                sta $d406,x
                jmp mt_cmddowavearp2

mt_settempo:    bmi mt_settempoone
                sta mt_chntempo
                sta mt_chntempo+7
                sta mt_chntempo+14
                bpl mt_cmddowavearp
mt_settempoone: cmp #$c0
                bcs mt_timingmark
                and #$7f
                sta mt_chntempo,x
                bpl mt_cmddowavearp
mt_timingmark:  sta mt_temp4
                bcs mt_cmddowavearp

mt_portaup:     jsr mt_makespeed
                clc
mt_dofreqadd:   jsr mt_freqadd
                jmp mt_nextchn
mt_portadown:   jsr mt_makespeed
                sec
mt_dofreqsub:   jsr mt_freqsub
                jmp mt_nextchn

mt_vibrato:     sta mt_temp1
                and #$0f
                sta mt_temp3
                lda mt_chnarpcount,x
                bmi mt_novibdir
                cmp mt_temp3
                bcc mt_novibdir2
                beq mt_novibdir
                eor #$ff
mt_novibdir:    clc
mt_novibdir2:   adc #$02
mt_vibdone:     sta mt_chnarpcount,x
                lsr
                bcc mt_dofreqadd
                bcs mt_dofreqsub

mt_toneportamento:
                ldy mt_chnnote,x
                pha
                jsr mt_makespeed
                pla
                beq mt_tpfound
                jsr mt_cmpfreq
                beq mt_tpalldone
                bcs mt_tpdown
mt_tpup:        jsr mt_freqadd
                jsr mt_cmpfreq
                bcs mt_tpfound
mt_tpalldone:
mt_tpnotyet:    jmp mt_nextchn
mt_tpdown:      jsr mt_freqsub
                jsr mt_cmpfreq
                bcs mt_tpnotyet
mt_tpfound:     jmp mt_arpfreq

mt_dowavesub:
mt_wavetblaccess2:
                lda mt_wavetbl,y
                beq mt_skipwave
                sta mt_chnwave,x
mt_skipwave:
mt_notetblaccess:
                lda mt_notetbl,y
                bmi mt_wavetblabs
                clc
                adc mt_chnnote,x
mt_wavetblabs:  and #$7f
                sta mt_temp1
mt_wavetblplusaccess:
                lda mt_wavetbl+1,y
                cmp #$ff
                bcc mt_nowaveend
mt_notetblplusaccess:
                lda mt_notetbl+1,y
                beq mt_nowaveloop
                ldy mt_chninstnum,x
                adc mt_instwave,y             ;Carry is cleared
                adc #$fe
                bne mt_nowaveloop
mt_nowaveend:   iny
                tya
mt_nowaveloop:  sta mt_chnwavetbl,x
                ldy mt_temp1
                lda mt_freqtbllo,y
                sta mt_chnfreqlo,x
                lda mt_freqtblhi,y
                sta mt_chnfreqhi,x
                rts


                if (mt_cmddonothing & $ff00)!=(mt_toneportamento & $ff00)
                err
                endif

mt_funktable:   dc.b 0,0

mt_chnnote:     dc.b 0
mt_chnfreqlo:   dc.b 0
mt_chnfreqhi:   dc.b 0
mt_chnnewnote:  dc.b 0
mt_chncommand:  dc.b 0
mt_chncmddata:  dc.b 0
mt_chninstnum:  dc.b INST1

                dc.b 0,0,0,0,0,0,INST1
                dc.b 0,0,0,0,0,0,INST1

mt_chnwave:     dc.b 0
mt_chnwavetbl:  dc.b 0
mt_chnpulse:    dc.b 0
mt_chnpulsedir: dc.b 0
mt_chnarpcount: dc.b 0
mt_chnpackrest: dc.b 0
mt_chnrepeat:   dc.b 0

                dc.b 0,0,0,0,0,0,0
                dc.b 0,0,0,0,0,0,0

mt_chnsongptr:  dc.b 0
mt_chnsongnum:  dc.b 0
mt_chnpattptr:  dc.b 0
mt_chnpattnum:  dc.b 0
mt_chntick:     dc.b 0
mt_chntempo:    dc.b 0
mt_chntrans:    dc.b 0

                dc.b 0,0,0,0,0,0,0
                dc.b 0,0,0,0,0,0,0

mt_freqtblhi:   dc.b $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$02
                dc.b $02,$02,$02,$02,$02,$02,$03,$03,$03,$03,$03,$04
                dc.b $04,$04,$04,$05,$05,$05,$06,$06,$06,$07,$07,$08
                dc.b $08,$09,$09,$0a,$0a,$0b,$0c,$0c,$0d,$0e,$0f,$10
                dc.b $11,$12,$13,$14,$15,$17,$18,$19,$1b,$1d,$1e,$20
                dc.b $22,$24,$26,$29,$2b,$2e,$30,$33,$36,$3a,$3d,$41
                dc.b $45,$49,$4d,$52,$57,$5c,$61,$67,$6d,$74,$7b,$82
                dc.b $8a,$92,$9b,$a4,$ae,$b8,$c3,$cf,$db,$e8,$f6,$ff

mt_freqtbllo:   dc.b $15,$25,$36,$49,$5c,$71,$87,$9f,$b7,$d1,$ed,$0a
                dc.b $2a,$4a,$6d,$92,$b9,$e3,$0f,$3e,$6f,$a3,$db,$15
                dc.b $54,$95,$db,$25,$73,$c7,$1e,$7c,$de,$47,$b6,$2b
                dc.b $a8,$2b,$b7,$4b,$e7,$8e,$3d,$f8,$bd,$8e,$6c,$57
                dc.b $50,$57,$6e,$96,$cf,$1c,$7b,$f0,$7b,$1d,$d8,$ae
                dc.b $a0,$af,$dd,$2d,$9f,$38,$f7,$e0,$f6,$3b,$b1,$5d
                dc.b $40,$5e,$bb,$5a,$3f,$70,$ef,$c1,$ed,$76,$63,$ba
                dc.b $80,$bc,$76,$b4,$7f,$e0,$de,$83,$da,$ed,$c7,$ff


