; ---------------------------------------------------------------------------
; PT3 Turbo Sound player for Amstrad CPC
; (c) 2013 SyX
; Based in the Universal PT2'n'PT3 Turbo Sound player for ZX Spectrum
; (c)2004-2007 S.V.Bulba <vorobey@mail.khstu.ru>
; http://bulba.untergrund.net/ (http://bulba.at.kz/)
; and the Amstrad CPC/Plus Port by Grim/Arkos^Semilanceata 
; ---------------------------------------------------------------------------

; Variables for the conditional assembly
; 1) Current position counters at (Vars1 + 0) and (Vars2 + 0)
CurPosCounter=0

; 2) Allow channels allocation bits at TS_SETUP
ACBBAC=0

; 3) Allow loop checking and disabling
LoopChecker=0

; 4) Use CPC frequencies or ZX
CPCFreq=0

; Features
; --------
; - Can be compiled at any address (i.e. no need rounding ORG
;    address).
; - Variables (VARS) can be located at any address (not only after
;    code block).
; - TS_Init subprogram checks PT3-module version and rightly
;    generates both note and volume tables outside of code block
;    (in VARS).
; - Two portamento (spc. command 3xxx) algorithms (depending of
;    PT3 module version).
; - New 1.XX and 2.XX special command behaviour (only for PT v3.7
;    and higher).
; - Any Tempo value are accepted (including Tempo=1 and Tempo=2).
; - TS modes: 2xPT3, 2xPT2 and PT v3.7 TS standard.
; - Fully compatible with Ay_Emul PT3 and PT2 players codes.

; Limitations
; -----------
; - Can run in RAM only (self-modified code is used).
; - PT2 position list must be end by $FF marker only.

; Warning!!! TS_Play subprogram can crash if no module are loaded
; into RAM or TS_Init subprogram was not called before.

; Call TS_Stop or TS_Init one more time to mute sound after stopping
; playing 

; Constants with the AY/YM registers 
TonA        EQU 0            ; Channel A Frequency
TonB        EQU 2            ; Channel B Frequency
TonC        EQU 4            ; Channel C Frequency
Noise       EQU 6            ; Noise Frequency 
Mixer       EQU 7            ; PSG/YM Control 
AmplA       EQU 8            ; Channel A Amplitude
AmplB       EQU 9            ; Channel B Amplitude
AmplC       EQU 10           ; Channel C Amplitude
Env         EQU 11           ; Envelope Frequency
EnvTp       EQU 13           ; Envelope Shape/Cycle

TS_SETUP    DEFB 0  ; set bit0, if you want to play without looping (optional)
                    ; set bit1 for PT2 and reset for PT3 before calling TS_Init
                    ; bits2-3: %00-ABC, %01-ACB, %10-BAC (optional)
                    ; bits4-5: %00-no TS, %01-2 modules TS, %10-
                    ;           autodetect PT3 TS-format by AlCo (PT 3.7+);
                    ;           Remark: old PT3 TS-format by AlCo (PT 3.6) is not
                    ;           documented and must be converted to new standard.
                    ; bit6 is set each time, when loop point of 2nd TS
                    ;          module is passed (optional).
                    ; bit7 is set each time, when loop point of 1st TS
                    ;          or of single module is passed (optional).

    IF LoopChecker
CHECKLP
    LD   HL,TS_SETUP
    BIT  0,(IY-100+VRS.ModNum)
    JR   Z,CHL1
    SET  6,(HL)
    JR   CHL2
CHL1
    SET  7,(HL)
CHL2
    BIT  0,(HL)
    RET  Z
    POP  HL
    INC  (IY-100+VRS.DelyCnt)
    INC  (IY-100+VRS.ChanA+CHP.NtSkCn)
    XOR  A
    LD   (IY-100+VRS.AYREGS+AmplA),A
    LD   (IY-100+VRS.AYREGS+AmplB),A
    LD   (IY-100+VRS.AYREGS+AmplC),A
    RET
    ENDIF

; ---------------------------------------------------------------------------
; Stop the music and silent the channels.
; ---------------------------------------------------------------------------
TS_Stop
    XOR  A
    LD   H,A
    LD   L,A
    LD   (VARS1+VRS.AYREGS+AmplA),A
    LD   (VARS1+VRS.AYREGS+AmplB),HL
    LD   (VARS2+VRS.AYREGS+AmplA),A
    LD   (VARS2+VRS.AYREGS+AmplB),HL
    JP   TS_Play.ROUT

; ---------------------------------------------------------------------------
; Initialize the song
; ENTRIES:
;     HL = Address of module or first module in case of split songs
;     DE = Address of second module in case of split songs (optional)
; ---------------------------------------------------------------------------
TS_Init
    PUSH DE
    PUSH HL
    
    ; Clean VARS1 + VARS2
    LD   HL,VARS
    LD   (HL),0
    LD   DE,VARS+1
    LD   BC,VAR0END-VARS-1
    LDIR
 
    INC  HL
    LD   (VARS1+VRS.AdInPtA),HL ; ptr to zero
    LD   (VARS2+VRS.AdInPtA),HL

    POP  HL
    LD   IY,VARS1+100
    CALL INITPT3

    POP  HL
    ; Use version and tone table of 1st module *** REVIEW for reduce size in files ***
    LD   A,(IX+13-100)  ; Extract version number
    SUB  $30            ; A - '0'
    JR   C,.L20
    CP   10             ; < 10?
    JR   C,.L21
.L20
    LD   A,6            ; Version 6 for unknown type
.L21
    LD   (Version),A    ; Set version for the song

    ; Test for Turbo-Sound
    LD   IY,VARS2+100
    LD   A,(TS_SETUP)
    AND  %00110000      ; Mask bits 5-4
    RET  Z              ; (JR Z,NOTS) %00 = No Turbo Sound
    CP   16
    JR   Z,TwoPT3s      ; %01 = 2 x PT3 songs
    LD   A,(Version)
    CP   7
    RET  C              ; (JR C,NOTS) Song version <> 7, then no Turbo Sound
    LD   A,(IX+98-100)  ; Get Alco Turbo Sound marker
    CP   32             ; is space?
    RET  Z              ; (JR Z,NOTS) Yes, then NO Turbo Sound
    ; It's a Turbo Sound song, copy VARS1 to VARS2
    LD   HL,VARS1
    LD   DE,VARS2
    LD   BC,VRS
    LDIR

    SET  1,(IY-100+VRS.ModNum)
    LD   C,A
    ADD  A,A
    ADD  A,C
    SUB  2
    LD   (TSSub),A
    JR   AlCoTS_
TwoPT3s
    CALL INITPT3
AlCoTS_
    LD   A,1
    LD   (is_ts),A
    SET  0,(IY-100+VRS.ModNum)

NOTS
    LD   A,1
    LD   (VARS1+VRS.DelyCnt),A
    LD   (VARS2+VRS.DelyCnt),A

    LD   HL,$F001       ; H - CHP.Volume, L - CHP.NtSkCn
    LD   (VARS1+VRS.ChanA+CHP.NtSkCn),HL
    LD   (VARS1+VRS.ChanB+CHP.NtSkCn),HL
    LD   (VARS1+VRS.ChanC+CHP.NtSkCn),HL
    LD   (VARS2+VRS.ChanA+CHP.NtSkCn),HL
    LD   (VARS2+VRS.ChanB+CHP.NtSkCn),HL
    LD   (VARS2+VRS.ChanC+CHP.NtSkCn),HL

    LD   HL,PT3EMPTYORN
    LD   (VARS1+VRS.ChanA+CHP.OrnPtr),HL
    LD   (VARS1+VRS.ChanB+CHP.OrnPtr),HL
    LD   (VARS1+VRS.ChanC+CHP.OrnPtr),HL
    LD   (VARS2+VRS.ChanA+CHP.OrnPtr),HL
    LD   (VARS2+VRS.ChanB+CHP.OrnPtr),HL
    LD   (VARS2+VRS.ChanC+CHP.OrnPtr),HL

    JP   TS_Play.ROUT

    ; Initialize variables for PT3 song
INITPT3
    ; Set song pointer
    LD   (IY-100+VRS.MODADDR),L
    LD   (IY-100+VRS.MODADDR+1),H

    PUSH HL
    LD   DE,100
    ADD  HL,DE
    LD   A,(HL)             ; Get song Tempo
    LD   (IY-100+VRS.Delay),A
    PUSH HL
    POP  IX
    ADD  HL,DE

    ; SETCPPT
    LD   (IY-100+VRS.CrPsPtr),L
    LD   (IY-100+VRS.CrPsPtr+1),H

    LD   E,(IX+102-100)
    INC  HL

    IF CurPosCounter
    LD   (IY-100+VRS.PosSub),L
    ENDIF

    ADD  HL,DE
    ; SETLPPT
    LD   (IY-100+VRS.LPosPtr),L
    LD   (IY-100+VRS.LPosPtr+1),H
    POP  DE
    LD   L,(IX+103-100)
    LD   H,(IX+104-100)
    ADD  HL,DE
    ; SETPTPT
    LD   (IY-100+VRS.PatsPtr),L
    LD   (IY-100+VRS.PatsPtr+1),H
    LD   HL,169
    ADD  HL,DE
    ; SETORPT
    LD   (IY-100+VRS.OrnPtrs),L
    LD   (IY-100+VRS.OrnPtrs+1),H
    LD   HL,105
    ADD  HL,DE
    ; SETSMPT
    LD   (IY-100+VRS.SamPtrs),L
    LD   (IY-100+VRS.SamPtrs+1),H
    RET

PTDECOD
    PUSH IY
    POP  IX
    ADD  IX,DE
    PUSH IY
    POP  IX
    ADD  IX,DE
    JP   PT3PD

; PT3 pattern decoder
PD_OrSm
    LD   (IX-12+CHP.Env_En),0
    CALL SETORN
PD_SAM_
    LD   A,(BC)
    INC  BC
    RRCA

PD_SAM
    CALL SETSAM
    JR   PD_LOOP

PD_VOL
    RRCA
    RRCA
    RRCA
    RRCA
    LD   (IX-12+CHP.Volume),A
    JR   PD_LP2
    
PD_EOff
    LD   (IX-12+CHP.Env_En),A
    LD   (IX-12+CHP.PsInOr),A
    JR   PD_LP2

PD_SorE
    DEC  A
    JR   NZ,PD_ENV
    LD   A,(BC)
    INC  BC
    LD   (IX-12+CHP.NNtSkp),A
    JR   PD_LP2

PD_ENV
    CALL SETENV
    JR   PD_LP2

PD_ORN
    CALL SETORN
    JR   PD_LOOP

PD_ESAM
    LD   (IX-12+CHP.Env_En),A
    LD   (IX-12+CHP.PsInOr),A
    CALL NZ,SETENV
    JR   PD_SAM_

PT3PD
    LD   A,(IX-12+CHP.Note)
    LD   (PrNote+1),A
    LD   L,(IX-12+CHP.CrTnSl)
    LD   H,(IX-12+CHP.CrTnSl+1)
    LD   (PrSlide+1),HL

PD_LOOP
    LD   DE,$2010
PD_LP2
    LD   A,(BC)
    INC  BC
    ADD  A,E
    JR   C,PD_OrSm
    ADD  A,D
    JR   Z,PD_FIN
    JR   C,PD_SAM
    ADD  A,E
    JR   Z,PD_REL
    JR   C,PD_VOL
    ADD  A,E
    JR   Z,PD_EOff
    JR   C,PD_SorE
    ADD  A,96
    JR   C,PD_NOTE
    ADD  A,E
    JR   C,PD_ORN
    ADD  A,D
    JR   C,PD_NOIS
    ADD  A,E
    JR   C,PD_ESAM
    ADD  A,A
    LD   E,A
    LD   HL,SPCCOMS+$FF20-$2000
    ADD  HL,DE
    LD   E,(HL)
    INC  HL
    LD   D,(HL)
    PUSH DE
    JR   PD_LOOP

PD_NOIS
    LD   (IY-100+VRS.Ns_Base),A
    JR   PD_LP2

PD_REL
    RES  0,(IX-12+CHP.Flags)
    JR   PD_RES

PD_NOTE
    LD   (IX-12+CHP.Note),A
    SET  0,(IX-12+CHP.Flags)
    XOR  A

PD_RES
    DI                  ; Disable Ints for SP
    LD   (PDSP_ + 1),SP
    LD   SP,IX
    LD   H,A
    LD   L,A
    PUSH HL
    PUSH HL
    PUSH HL
    PUSH HL
    PUSH HL
    PUSH HL
PDSP_
    LD   SP,$0000
    EI                  ; Enable Ints

PD_FIN
    LD   A,(IX-12+CHP.NNtSkp)
    LD   (IX-12+CHP.NtSkCn),A
    RET

C_PORTM
    LD   A,(BC)
    INC  BC
    ; Skip precalculated tone delta (because cannot be right after pt3 compilation)
    INC  BC
    INC  BC
    EX   AF,AF'
    LD   A,(BC)         ; Signed tone step
    INC  BC
    LD   (LoStep),A
    LD   A,(BC)
    INC  BC
    AND  A
    EX   AF,AF'
    LD   L,(IX-12+CHP.CrTnSl)
    LD   H,(IX-12+CHP.CrTnSl+1)

; Set portamento variables
; A - Delay; A' - Hi(Step); ZF' - (A'=0); HL - CrTnSl
SETPORT
    RES  2,(IX-12+CHP.Flags)
    LD   (IX-12+CHP.TnSlDl),A
    LD   (IX-12+CHP.TSlCnt),A
    PUSH HL
    LD   DE,NT_
    LD   A,(IX-12+CHP.Note)
    LD   (IX-12+CHP.SlToNt),A
    ADD  A,A
    LD   L,A
    LD   H,0
    ADD  HL,DE
    LD   A,(HL)
    INC  HL
    LD   H,(HL)
    LD   L,A
    PUSH HL
PrNote
    LD   A,$3E
    LD   (IX-12+CHP.Note),A
    ADD  A,A
    LD   L,A
    LD   H,0
    ADD  HL,DE
    LD   E,(HL)
    INC  HL
    LD   D,(HL)
    POP  HL
    SBC  HL,DE
    LD   (IX-12+CHP.TnDelt),L
    LD   (IX-12+CHP.TnDelt+1),H
    POP  DE
Version  EQU $+1
    LD   A,$3E
    CP   6
    JR   C,OLDPRTM  ; Old 3xxx for PT v3.5- *** Review ***
PrSlide
    LD   DE,$1111
    LD   (IX-12+CHP.CrTnSl),E
    LD   (IX-12+CHP.CrTnSl+1),D
LoStep   EQU $+1
OLDPRTM
    LD   A,$3E
    EX   AF,AF'
    JR   Z,NOSIG
    EX   DE,HL
NOSIG
    SBC  HL,DE
    JP   P,SET_STP
    CPL
    EX   AF,AF'
    NEG
    EX   AF,AF'
SET_STP
    LD   (IX-12+CHP.TSlStp+1),A
    EX   AF,AF'
    LD   (IX-12+CHP.TSlStp),A
    LD   (IX-12+CHP.COnOff),0
    RET

C_GLISS
    SET  2,(IX-12+CHP.Flags)
    LD   A,(BC)
    INC  BC
    LD   (IX-12+CHP.TnSlDl),A
    AND  A
    JR   NZ,GL36
    LD   A,(Version)    ; AlCo PT3.7+
    CP   7
    SBC  A,A
    INC  A
GL36
    LD   (IX-12+CHP.TSlCnt),A
    LD   A,(BC)
    INC  BC
    EX   AF,AF'
    LD   A,(BC)
    INC  BC
    JR   SET_STP

C_SMPOS
    LD   A,(BC)
    INC  BC
    LD   (IX-12+CHP.PsInSm),A
    RET

C_ORPOS
    LD   A,(BC)
    INC  BC
    LD   (IX-12+CHP.PsInOr),A
    RET

C_VIBRT
    LD   A,(BC)
    INC  BC
    LD   (IX-12+CHP.OnOffD),A
    LD   (IX-12+CHP.COnOff),A
    LD   A,(BC)
    INC  BC
    LD   (IX-12+CHP.OffOnD),A
    XOR  A
    LD   (IX-12+CHP.TSlCnt),A
    LD   (IX-12+CHP.CrTnSl),A
    LD   (IX-12+CHP.CrTnSl+1),A
    RET

C_ENGLS
    LD   A,(BC)
    INC  BC
    LD   (IY-100+VRS.Env_Del),A
    LD   (IY-100+VRS.CurEDel),A
    LD   A,(BC)
    INC  BC
    LD   L,A
    LD   A,(BC)
    INC  BC
    LD   H,A
    LD   (IY-100+VRS.ESldAdd),L
    LD   (IY-100+VRS.ESldAdd+1),H
    RET

C_DELAY
    LD   A,(BC)
    INC  BC
    LD   (IY-100+VRS.Delay),A
    LD   HL,VARS2+VRS.ModNum    ; if AlCo_TS
    BIT  1,(HL)
    RET  Z
    LD   (VARS1+VRS.Delay),A
    LD   (VARS1+VRS.DelyCnt),A
    LD   (VARS2+VRS.Delay),A
    RET
    
SETENV
    LD   (IX-12+CHP.Env_En),E
    LD   (IY-100+VRS.AYREGS+EnvTp),A
    LD   A,(BC)
    INC  BC
    LD   H,A
    LD   A,(BC)
    INC  BC
    LD   L,A
    ; SETENBS
    LD   (IY-100+VRS.EnvBase),L
    LD   (IY-100+VRS.EnvBase+1),H
    XOR  A
    LD   (IX-12+CHP.PsInOr),A
    LD   (IY-100+VRS.CurEDel),A
    LD   H,A
    LD   L,A
    ; SETESLD
    LD   (IY-100+VRS.CurESld),L
    LD   (IY-100+VRS.CurESld+1),H
    RET

SETORN
    ADD  A,A
    LD   E,A
    LD   D,0
    LD   (IX-12+CHP.PsInOr),D
    LD   L,(IY-100+VRS.OrnPtrs)
    LD   H,(IY-100+VRS.OrnPtrs+1)
    ADD  HL,DE
    LD   E,(HL)
    INC  HL
    LD   D,(HL)
    LD   L,(IY-100+VRS.MODADDR)
    LD   H,(IY-100+VRS.MODADDR+1)
    ADD  HL,DE
    LD   (IX-12+CHP.OrnPtr),L
    LD   (IX-12+CHP.OrnPtr+1),H
C_NOP
    RET

SETSAM
    ADD  A,A
    LD   E,A
    LD   D,0
    LD   L,(IY-100+VRS.SamPtrs)
    LD   H,(IY-100+VRS.SamPtrs+1)
    ADD  HL,DE
    LD   E,(HL)
    INC  HL
    LD   D,(HL)
    LD   L,(IY-100+VRS.MODADDR)
    LD   H,(IY-100+VRS.MODADDR+1)
    ADD  HL,DE
    LD   (IX-12+CHP.SamPtr),L
    LD   (IX-12+CHP.SamPtr+1),H
    RET

; All 16 addresses to protect from broken pt3 modules
SPCCOMS
    DEFW C_NOP
    DEFW C_GLISS
    DEFW C_PORTM
    DEFW C_SMPOS
    DEFW C_ORPOS
    DEFW C_VIBRT
    DEFW C_NOP
    DEFW C_NOP
    DEFW C_ENGLS
    DEFW C_DELAY
    DEFW C_NOP
    DEFW C_NOP
    DEFW C_NOP
    DEFW C_NOP
    DEFW C_NOP
    DEFW C_NOP

CHREGS
    PUSH IY
    POP  IX
    ADD  IX,DE
    XOR  A
    LD   (Ampl),A
    BIT  0,(IX+CHP.Flags)
    PUSH HL
    JP   Z,CH_EXIT
    DI                      ; Disable Ints for SP
    LD   (CSP_+1),SP
    LD   L,(IX+CHP.OrnPtr)
    LD   H,(IX+CHP.OrnPtr+1)
    LD   SP,HL
    POP  DE
    LD   H,A
    LD   A,(IX+CHP.PsInOr)
    LD   L,A
    ADD  HL,SP
    INC  A
OrnCP
    CP   D
    JR   C,CH_ORPS
OrnLD
    LD   A,E
CH_ORPS
    LD   (IX+CHP.PsInOr),A
    LD   A,(IX+CHP.Note)
    ADD  A,(HL)
    JP   P,CH_NTP
    XOR  A
CH_NTP
    CP   96
    JR   C,CH_NOK
    LD   A,95
CH_NOK
    ADD  A,A
    EX   AF,AF'
    LD   L,(IX+CHP.SamPtr)
    LD   H,(IX+CHP.SamPtr+1)
    LD   SP,HL
    POP  DE
    LD   H,0
    LD   A,(IX+CHP.PsInSm)
    LD   B,A
    ADD  A,A
SamClc2
    ADD  A,A
    LD   L,A
    ADD  HL,SP
    LD   SP,HL
    LD   A,B
    INC  A
SamCP
    CP   D
    JR   C,CH_SMPS
SamLD
    LD   A,E
CH_SMPS
    LD   (IX+CHP.PsInSm),A
    POP  BC
    POP  HL
    LD   E,(IX+CHP.TnAcc)
    LD   D,(IX+CHP.TnAcc+1)
    ADD  HL,DE
    BIT  6,B
    JR   Z,CH_NOAC
    LD   (IX+CHP.TnAcc),L
    LD   (IX+CHP.TnAcc+1),H
CH_NOAC
    EX   DE,HL
    EX   AF,AF'
    ADD  A,<NT_
    LD   L,A
    ADC  A,>NT_
    SUB  L
    LD   H,A
    LD   SP,HL
    POP  HL
    ADD  HL,DE
    LD   E,(IX+CHP.CrTnSl)
    LD   D,(IX+CHP.CrTnSl+1)
    ADD  HL,DE
CSP_
    LD   SP,$0000
    EI                      ; Enable Ints
    EX   (SP),HL
    XOR  A
    OR   (IX+CHP.TSlCnt)
    JR   Z,CH_AMP
    DEC  (IX+CHP.TSlCnt)
    JR   NZ,CH_AMP
    LD   A,(IX+CHP.TnSlDl)
    LD   (IX+CHP.TSlCnt),A
    LD   L,(IX+CHP.TSlStp)
    LD   H,(IX+CHP.TSlStp+1)
    LD   A,H
    ADD  HL,DE
    LD   (IX+CHP.CrTnSl),L
    LD   (IX+CHP.CrTnSl+1),H
    BIT  2,(IX+CHP.Flags)
    JR   NZ,CH_AMP
    LD   E,(IX+CHP.TnDelt)
    LD   D,(IX+CHP.TnDelt+1)
    AND  A
    JR   Z,CH_STPP
    EX   DE,HL
CH_STPP
    SBC  HL,DE
    JP   M,CH_AMP
    LD   A,(IX+CHP.SlToNt)
    LD   (IX+CHP.Note),A
    XOR  A
    LD   (IX+CHP.TSlCnt),A
    LD   (IX+CHP.CrTnSl),A
    LD   (IX+CHP.CrTnSl+1),A
CH_AMP
    LD   A,(IX+CHP.CrAmSl)
    BIT  7,C
    JR   Z,CH_NOAM
    BIT  6,C
    JR   Z,CH_AMIN
    CP   15
    JR   Z,CH_NOAM
    INC  A
    JR   CH_SVAM
CH_AMIN
    CP   -15
    JR   Z,CH_NOAM
    DEC  A
CH_SVAM
    LD   (IX+CHP.CrAmSl),A
CH_NOAM
    LD   L,A
    LD   A,B
    AND  15
    ADD  A,L
    JP   P,CH_APOS
    XOR  A
CH_APOS
    CP   16
    JR   C,CH_VOL
    LD   A,15
CH_VOL
    OR   (IX+CHP.Volume)
    ADD  A,<VT_
    LD   L,A
    ADC  A,>VT_
    SUB  L
    LD   H,A
    LD   A,(HL)
CH_ENV
    BIT  0,C
    JR   NZ,CH_NOEN
    OR   (IX+CHP.Env_En)
CH_NOEN
    LD   (Ampl),A
    BIT  7,B
    LD   A,C
    JR   Z,NO_ENSL
    RLA
    RLA
    SRA  A
    SRA  A
    SRA  A
    ADD  A,(IX+CHP.CrEnSl)      ; See comment below
    BIT  5,B
    JR   Z,NO_ENAC
    LD   (IX+CHP.CrEnSl),A
NO_ENAC
    ADD  A,(IY-100+VRS.AddToEn) ; Bug in PT3 - Need word here
    LD   (IY-100+VRS.AddToEn),A
    JR   CH_MIX
NO_ENSL
    RRA
    ADD  A,(IX+CHP.CrNsSl)
    LD   (IY-100+VRS.AddToNs),A
    BIT  5,B
    JR   Z,CH_MIX
    LD   (IX+CHP.CrNsSl),A
CH_MIX
    LD   A,B
    RRA
    AND  $48
CH_EXIT
    OR   (IY-100+VRS.AYREGS+Mixer)
    RRCA
    LD   (IY-100+VRS.AYREGS+Mixer),A
    POP  HL
    XOR  A
    OR   (IX+CHP.COnOff)
    RET  Z
    DEC  (IX+CHP.COnOff)
    RET  NZ
    XOR  (IX+CHP.Flags)
    LD   (IX+CHP.Flags),A
    RRA
    LD   A,(IX+CHP.OnOffD)
    JR   C,CH_ONDL
    LD   A,(IX+CHP.OffOnD)
CH_ONDL
    LD   (IX+CHP.COnOff),A
    RET

PLAY_
    XOR  A
    LD   (IY-100+VRS.AddToEn),A
    LD   (IY-100+VRS.AYREGS+Mixer),A
    DEC  A
    LD   (IY-100+VRS.AYREGS+EnvTp),A
    DEC  (IY-100+VRS.DelyCnt)
    JP   NZ,PL2
    DEC  (IY-100+VRS.ChanA+CHP.NtSkCn)
    JR   NZ,PL1B
    LD   C,(IY-100+VRS.AdInPtA)
    LD   B,(IY-100+VRS.AdInPtA+1)
    LD   A,(BC)
    AND  A
    JR   NZ,PL1A
    LD   D,A
    LD   (IY-100+VRS.Ns_Base),A
    LD   L,(IY-100+VRS.CrPsPtr)
    LD   H,(IY-100+VRS.CrPsPtr+1)
    INC  HL
    LD   A,(HL)
    INC  A
    JR   NZ,PLNLP

    IF LoopChecker
    CALL CHECKLP
    ENDIF

    LD   L,(IY-100+VRS.LPosPtr)
    LD   H,(IY-100+VRS.LPosPtr+1)
    LD   A,(HL)
    INC  A
PLNLP
    ; SETCPPT
    LD   (IY-100+VRS.CrPsPtr),L
    LD   (IY-100+VRS.CrPsPtr+1),H
    DEC  A
    BIT  1,(IY-100+VRS.ModNum)
    JR   Z,NoAlCo
TSSub    EQU $+1
    SUB  $D6
    CPL
NoAlCo
    ADD  A,A
    LD   E,A
    RL   D

    IF CurPosCounter
    LD   A,L
    SUB  (IY-100+VRS.PosSub)
    LD   (IY-100+VRS.CurPos),A
    ENDIF

    LD   L,(IY-100+VRS.PatsPtr)
    LD   H,(IY-100+VRS.PatsPtr+1)
    ADD  HL,DE
    LD   E,(IY-100+VRS.MODADDR)
    LD   D,(IY-100+VRS.MODADDR+1)
    DI                      ; Disable Ints for SP
    LD   (PSP_+1),SP
    LD   SP,HL
    POP  HL
    ADD  HL,DE
    LD   B,H
    LD   C,L
    POP  HL
    ADD  HL,DE
    LD   (IY-100+VRS.AdInPtB),L
    LD   (IY-100+VRS.AdInPtB+1),H
    POP  HL
    ADD  HL,DE
    LD   (IY-100+VRS.AdInPtC),L
    LD   (IY-100+VRS.AdInPtC+1),H
PSP_
    LD   SP,$0000
    EI                      ; Enable Ints
PL1A
    LD   DE,VRS.ChanA+12-100
    CALL PTDECOD
    LD   (IY-100+VRS.AdInPtA),C
    LD   (IY-100+VRS.AdInPtA+1),B

PL1B
    DEC  (IY-100+VRS.ChanB+CHP.NtSkCn)
    JR   NZ,PL1C
    LD   DE,VRS.ChanB+12-100
    LD   C,(IY-100+VRS.AdInPtB)
    LD   B,(IY-100+VRS.AdInPtB+1)
    CALL PTDECOD
    LD   (IY-100+VRS.AdInPtB),C
    LD   (IY-100+VRS.AdInPtB+1),B

PL1C
    DEC  (IY-100+VRS.ChanC+CHP.NtSkCn)
    JR   NZ,PL1D
    LD   DE,VRS.ChanC+12-100
    LD   C,(IY-100+VRS.AdInPtC)
    LD   B,(IY-100+VRS.AdInPtC+1)
    CALL PTDECOD
    LD   (IY-100+VRS.AdInPtC),C
    LD   (IY-100+VRS.AdInPtC+1),B

PL1D
    LD   A,(IY-100+VRS.Delay)
    LD   (IY-100+VRS.DelyCnt),A

PL2
    LD   DE,VRS.ChanA-100
    LD   L,(IY-100+VRS.AYREGS+TonA)
    LD   H,(IY-100+VRS.AYREGS+TonA+1)
    CALL CHREGS
    LD   (IY-100+VRS.AYREGS+TonA),L
    LD   (IY-100+VRS.AYREGS+TonA+1),H
Ampl     EQU $+1
    LD   A,$3E
    LD   (IY-100+VRS.AYREGS+AmplA),A
    LD   DE,VRS.ChanB-100
    LD   L,(IY-100+VRS.AYREGS+TonB)
    LD   H,(IY-100+VRS.AYREGS+TonB+1)
    CALL CHREGS
    LD   (IY-100+VRS.AYREGS+TonB),L
    LD   (IY-100+VRS.AYREGS+TonB+1),H
    LD   A,(Ampl)
    LD   (IY-100+VRS.AYREGS+AmplB),A
    LD   DE,VRS.ChanC-100
    LD   L,(IY-100+VRS.AYREGS+TonC)
    LD   H,(IY-100+VRS.AYREGS+TonC+1)
    CALL CHREGS
    LD   (IY-100+VRS.AYREGS+TonC),L
    LD   (IY-100+VRS.AYREGS+TonC+1),H
    LD   A,(Ampl)
    LD   (IY-100+VRS.AYREGS+AmplC),A

    LD   A,(IY-100+VRS.Ns_Base)
    ADD  (IY-100+VRS.AddToNs)
    LD   (IY-100+VRS.AYREGS+Noise),A

    LD   A,(IY-100+VRS.AddToEn)
    LD   E,A
    ADD  A,A
    SBC  A,A
    LD   D,A
    LD   L,(IY-100+VRS.EnvBase)
    LD   H,(IY-100+VRS.EnvBase+1)
    ADD  HL,DE
    LD   E,(IY-100+VRS.CurESld)
    LD   D,(IY-100+VRS.CurESld+1)
    ADD  HL,DE
    LD   (IY-100+VRS.AYREGS+Env),L
    LD   (IY-100+VRS.AYREGS+Env+1),H

    XOR  A
    OR   (IY-100+VRS.CurEDel)
    RET  Z
    DEC  (IY-100+VRS.CurEDel)
    RET  NZ
    LD   A,(IY-100+VRS.Env_Del)
    LD   (IY-100+VRS.CurEDel),A
    LD   L,(IY-100+VRS.ESldAdd)
    LD   H,(IY-100+VRS.ESldAdd+1)
    ADD  HL,DE
    ; SETESLD
    LD   (IY-100+VRS.CurESld),L
    LD   (IY-100+VRS.CurESld+1),H
    RET

; ---------------------------------------------------------------------------
; Play sound
; ---------------------------------------------------------------------------
TS_Play
    ; Proccess Music
    ; 1.- YM LEFT
    LD   IY,VARS1+100
    CALL PLAY_

    ; 2.- PSG
    LD   IY,VARS2+100
    CALL PLAY_

.ROUT
    ; Send datas to the audio chips
.ROUT_YM_LEFT
    ; 1.- YMZ Left
    IF ACBBAC
        LD   IX,VARS1+VRS.AYREGS
        CALL alloc_channels
        PUSH IX
        POP  HL
    ELSE
        LD   HL,VARS1+VRS.AYREGS
    ENDIF
    LD   BC,YMZ_SELECT_LEFT

    XOR  A                      ; YM Register 0
    ; Send Registers 0 - 12
    REPT 13
        OUT  (C),A              ; Register
        OUTI                    ; Value
        INC  B
        INC  A                  ; Increase Register
    ENDR

    ; Send Register 13 (Envelope Shape/Cycle)
    OUT  (C),A
    LD   A,(HL)
    AND  A
    JP   M,.ROUT_YM_RIGHT       ; RET     M
    OUTI                        ; Value

.ROUT_YM_RIGHT
    ; 2.- YM Right
    IF ACBBAC
        LD   IX,VARS2+VRS.AYREGS
        CALL alloc_channels
        PUSH IX
        POP  HL
    ELSE
        LD   HL,VARS2+VRS.AYREGS
    ENDIF

    LD   BC,YMZ_SELECT_RIGHT

    XOR  A                      ; YM Register 0
    ; Send Registers 0 - 12
    REPT 13
        OUT  (C),A              ; Register
        OUTI                    ; Value
        INC  B
        INC  A                  ; Increase Register
    ENDR

    ; Send Register 13 (Envelope Shape/Cycle)
    OUT  (C),A
    LD   A,(HL)
    AND  A
    RET  M
    OUTI                        ; Value

    RET

; ---------------------------------------------------------------------------
    IF ACBBAC
alloc_channels
    LD   A,(TS_SETUP)
    AND  12
    JR   Z,ABC
    ADD  A,<CHTABLE
    LD   E,A
    ADC  A,>CHTABLE
    SUB  E
    LD   D,A
    LD   B,0
    PUSH IX
    POP  HL
    LD   A,(DE)
    INC  DE
    LD   C,A
    ADD  HL,BC
    LD   A,(IX+TonB)
    LD   C,(HL)
    LD   (IX+TonB),C
    LD   (HL),A
    INC  HL
    LD   A,(IX+TonB+1)
    LD   C,(HL)
    LD   (IX+TonB+1),C
    LD   (HL),A
    LD   A,(DE)
    INC  DE
    LD   C,A
    ADD  HL,BC
    LD   A,(IX+AmplB)
    LD   C,(HL)
    LD   (IX+AmplB),C
    LD   (HL),A
    LD   A,(DE)
    INC  DE
    LD   (RxCA1),A
    XOR  8
    LD   (RxCA2),A
    LD   A,(DE)
    AND  (IX+Mixer)
    LD   E,A
    LD   A,(IX+Mixer)
RxCA1
    DEFB $E6
    AND  %010010
    OR   E
    LD   E,A
    LD   A,(IX+Mixer)
    AND  %010010
RxCA2
    OR   E
    OR   E
    LD   (IX+Mixer),A
ABC
    RET

CHTABLE  EQU $-4
    DEFB 4,5,15,%001001,0,7,7,%100100
    ENDIF

PT3EMPTYORN EQU $-1
    DEFB 1,0

; vars from here can be stripped (you can move VARS to any other address)
VARS
is_ts   DEFB 0          ; 0 = No Turbo Sound | 1 = Turbo Sound

; ChannelsVars
    STRUCT CHP
; reset group
PsInOr  DEFB 0
PsInSm  DEFB 0
CrAmSl  DEFB 0
CrNsSl  DEFB 0
CrEnSl  DEFB 0
TSlCnt  DEFB 0
CrTnSl  DEFW 0
TnAcc   DEFW 0
COnOff  DEFB 0
; reset group

OnOffD  DEFB 0

; IX for PTDECOD here (+12)
OffOnD  DEFB 0
OrnPtr  DEFW 0
SamPtr  DEFW 0
NNtSkp  DEFB 0
Note    DEFB 0
SlToNt  DEFB 0
Env_En  DEFB 0
Flags   DEFB 0
; Enabled - 0, SimpleGliss - 2
TnSlDl  DEFB 0
TSlStp  DEFW 0
TnDelt  DEFW 0
NtSkCn  DEFB 0
Volume  DEFB 0
    ENDSTRUCT

    STRUCT VRS
    IF CurPosCounter
CurPos  DEFB 0
PosSub  DEFB 0
    ENDIF

ModNum  DEFB 0          ; bit0: ChipNum
                        ; bit1: 1-reversed patterns order (AlCo TS)

ChanA   DEFS CHP
ChanB   DEFS CHP
ChanC   DEFS CHP

; GlobalVars
MODADDR DEFW 0          ; Pointer to the song
OrnPtrs DEFW 0
SamPtrs DEFW 0
PatsPtr DEFW 0
AdInPtA DEFW 0
AdInPtB DEFW 0
AdInPtC DEFW 0
CrPsPtr DEFW 0
LPosPtr DEFW 0
Delay   DEFB 0          ; Song Tempo
DelyCnt DEFB 0
ESldAdd DEFW 0
CurESld DEFW 0
Env_Del DEFB 0
CurEDel DEFB 0
Ns_Base DEFB 0
AddToNs DEFB 0
AddToEn DEFB 0
EnvBase DEFW 0
AYREGS  DEFS 14
    ENDSTRUCT

VARS1   DEFS VRS
VARS2   DEFS VRS

VT_     EQU $-16
    ; Volume table
    DEFB $00,$00,$00,$00,$00,$00,$00,$00,$01,$01,$01,$01,$01,$01,$01,$01
    DEFB $00,$00,$00,$00,$01,$01,$01,$01,$01,$01,$01,$01,$02,$02,$02,$02
    DEFB $00,$00,$00,$01,$01,$01,$01,$01,$02,$02,$02,$02,$02,$03,$03,$03
    DEFB $00,$00,$01,$01,$01,$01,$02,$02,$02,$02,$03,$03,$03,$03,$04,$04
    DEFB $00,$00,$01,$01,$01,$02,$02,$02,$03,$03,$03,$04,$04,$04,$05,$05
    DEFB $00,$00,$01,$01,$02,$02,$02,$03,$03,$04,$04,$04,$05,$05,$06,$06
    DEFB $00,$00,$01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07
    DEFB $00,$01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08
    DEFB $00,$01,$01,$02,$02,$03,$04,$04,$05,$05,$06,$07,$07,$08,$08,$09
    DEFB $00,$01,$01,$02,$03,$03,$04,$05,$05,$06,$07,$07,$08,$09,$09,$0A
    DEFB $00,$01,$01,$02,$03,$04,$04,$05,$06,$07,$07,$08,$09,$0A,$0A,$0B
    DEFB $00,$01,$02,$02,$03,$04,$05,$06,$06,$07,$08,$09,$0A,$0A,$0B,$0C
    DEFB $00,$01,$02,$03,$03,$04,$05,$06,$07,$08,$09,$0A,$0A,$0B,$0C,$0D
    DEFB $00,$01,$02,$03,$04,$05,$06,$07,$07,$08,$09,$0A,$0B,$0C,$0D,$0E
    DEFB $00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0A,$0B,$0C,$0D,$0E,$0F

NT_
    IF   CPCFreq
    ; CPC Note table (it's valid for ST, too)
    DEFW 3822,3608,3405,3214,3034,2863,2703,2551,2408,2273,2145,2025
    DEFW 1911,1804,1703,1607,1517,1432,1351,1276,1204,1136,1073,1012
    DEFW 956,902,851,804,758,716,676,638,602,568,536,506
    DEFW 478,451,426,402,379,358,338,319,301,284,268,253
    DEFW 239,225,213,201,190,179,169,159,150,142,134,127
    DEFW 119,113,106,100,95,89,84,80,75,71,67,63
    DEFW 60,56,53,50,47,45,42,40,38,36,34,32
    DEFW 30,28,27,25,24,22,21,20,19,18,17,16
;    DEFW 15,14,13,13,12,11,11,10,9,9,8,8
;    DEFW 7,7,7,6,6,6,5,5,5,4,4,4
;    DEFW 4,4,3,3,3,3,3,2,2,2,2,2
;    DEFW 2,2,2,2,1,1,1,1,1,1,1,1
    ELSE
    ; ZX Note table
;    DEFW 4095,4095,4095,4095,4095,4095,4095,4095,4095,3977,3754,3543
    DEFW 3344,3157,2980,2812,2655,2506,2365,2232,2107,1989,1877,1772
    DEFW 1672,1578,1490,1406,1327,1253,1182,1116,1053,994,939,886
    DEFW 836,789,745,703,664,626,591,558,527,497,469,443
    DEFW 418,395,372,352,332,313,296,279,263,249,235,221
    DEFW 209,197,186,176,166,157,148,140,132,124,117,111
    DEFW 105,99,93,88,83,78,74,70,66,62,59,55
    DEFW 52,49,47,44,41,39,37,35,33,31,29,28
    DEFW 26,25,23,22,21,20,18,17,16,15,14,13 ; the last 3 should be 16, 15 and 14
;    DEFW 13,12,12,11,10,10,9,9,8,8,7,7
;    DEFW 7,6,6,5,5,5,5,4,4,4,4,3
;    DEFW 3,3,3,3,3,2,2,2,2,2,2,2
    ENDIF

VAR0END EQU VT_+16              ; TS_Init zeroes from VARS to VAR0END-1
; ---------------------------------------------------------------------------
