Login

Subversion Repositories NedoOS

Rev

Rev 1637 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

;Universal PT2'n'PT3 Turbo Sound player for ZX Spectrum
;(c)2004-2007 S.V.Bulba <vorobey@mail.khstu.ru>
;Specially for AlCo
;http://bulba.untergrund.net/ (http://bulba.at.kz/)

;Release number
Release EQU "0"

;Conditional assembly
;1) Current position counters at (Vars1+0) and (Vars2+0)
CurPosCounter=1
;2) Allow channels allocation bits at (START+10)
ACBBAC=0
;3) Allow loop checking and disabling
LoopChecker=1
;4) Insert official identificator
Id=0
;5) Set IY for correct return to ZX Basic
Basic=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).
;-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.
;-See also notes at the end of this source code.

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

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

;Call MUTE or INIT one more time to mute sound after stopping
;playing

;Test codes (commented)
;       LD A,32 ;SinglePT3(TS if TSPT3.7),ABC,Looped
;       LD (START+10),A
;       LD HL,#8000 ;Mod1
;       LD DE,#A000 ;Mod2 (optional)
;       CALL START+3
;       EI
;_LP    HALT
;       CALL START+5
;       XOR A
;       IN A,(#FE)
;       CPL
;       AND 15
;       JR Z,_LP
;       JR START+8

TonA    EQU 0
TonB    EQU 2
TonC    EQU 4
Noise   EQU 6
Mixer   EQU 7
AmplA   EQU 8
AmplB   EQU 9
AmplC   EQU 10
Env     EQU 11
EnvTp   EQU 13

;Entry and other points
;START initialize playing of modules at MDLADDR (single module)
;START+3 initialization with module address in HL and DE (TS)
;START+5 play one quark
;START+8 mute
;START+10 setup and status flags

START
        LD HL,MDLADDR ;DE - address of 2nd module for TS
        JR INIT
        JP PLAY
        JR MUTE
SETUP   DB 0 ;set bit0, if you want to play without looping
             ;(optional);
             ;set bit1 for PT2 and reset for PT3 before
             ;calling 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).

;Identifier
        IF Id
        DB "=UniPT2/PT3/TS-Player r.",Release,"="
        ENDIF

        IF LoopChecker
CHECKLP LD HL,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

MUTE    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 ROUT

INIT
;HL - AddressOfModule
;DE - AddresOf2ndModule
        PUSH DE
        PUSH HL
        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
        LD A,(START+10)
        AND 2
        JP NZ,I_PT2

        CALL INITPT3
        LD HL,(e_-SamCnv-2)*256+#18
        LD (SamCnv),HL
        LD A,#BA
        LD (OrnCP),A
        LD (SamCP),A
        LD A,#7B
        LD (OrnLD),A
        LD (SamLD),A
        LD A,#87
        LD (SamClc2),A
        POP HL
        ;Use version and ton table of 1st module
        LD A,(IX+13-100) ;EXTRACT VERSION NUMBER
        SUB #30
        JR C,L20
        CP 10
        JR C,L21
L20     LD A,6
L21     LD (Version),A
        PUSH AF ;VolTable version
        CP 4
        LD A,(IX+99-100) ;TONE TABLE NUMBER
        RLA
        AND 7
        PUSH AF ;NoteTable number

        LD IY,VARS2+100
        LD A,(START+10)
        AND 48
        JR Z,NOTS
        CP 16
        JR Z,TwoPT3s
        LD A,(Version)
        CP 7
        JR C,NOTS
        LD A,(IX+98-100) ;ALCO TS MARKER
        CP #20
        JR Z,NOTS
        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 BC,PT3PD
        LD HL,0
        LD DE,PT3EMPTYORN
        JR INITCOMMON

I_PT2   CALL INITPT2
        LD HL,#51CB
        LD (SamCnv),HL
        LD A,#BB
        LD (OrnCP),A
        LD (SamCP),A
        LD A,#7A
        LD (OrnLD),A
        LD (SamLD),A
        LD A,#80
        LD (SamClc2),A
        POP HL
        LD A,5
        LD (Version),A
        PUSH AF
        LD A,2
        PUSH AF

        LD A,(START+10)
        AND 48
        JR Z,NOTS2

        LD IY,VARS2+100
        LD A,1
        LD (is_ts),A
        SET 0,(IY-100+VRS.ModNum)
        CALL INITPT2

NOTS2   LD BC,PT2PD
        LD HL,#8687
        LD DE,PT2EMPTYORN

INITCOMMON

        IF Basic
        LD IY,#5C3A
        ENDIF

        LD (PTDEC),BC
        LD (PsCalc),HL
        PUSH DE

;note table data depacker
;(c) Ivan Roshin
        LD DE,T_PACK
        LD BC,T1_+(2*49)-1
TP_0    LD A,(DE)
        INC DE
        CP 15*2
        JR NC,TP_1
        LD H,A
        LD A,(DE)
        LD L,A
        INC DE
        JR TP_2
TP_1    PUSH DE
        LD D,0
        LD E,A
        ADD HL,DE
        ADD HL,DE
        POP DE
TP_2    LD A,H
        LD (BC),A
        DEC BC
        LD A,L
        LD (BC),A
        DEC BC
        SUB (#F8*2)&0xff
        JR NZ,TP_0

        INC A
        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
        POP HL
        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

        POP AF

;NoteTableCreator (c) Ivan Roshin
;A - NoteTableNumber*2+VersionForNoteTable
;(xx1b - 3.xx..3.4r, xx0b - 3.4x..3.6x..VTII1.0)

        LD HL,NT_DATA
        LD D,0
        ADD A,A
        LD E,A
        ADD HL,DE
        LD E,(HL)
        INC HL
        SRL E
        SBC A,A
        AND #A7 ;#00 (NOP) or #A7 (AND A)
        LD (L3),A
        EX DE,HL
        LD BC,T1_
        ADD HL,BC

        LD A,(DE)
        ADD A,T_%256
        LD C,A
        ADC A,T_/256
        SUB C
        LD B,A
        PUSH BC
        LD DE,NT_
        PUSH DE

        LD B,12
L1      PUSH BC
        LD C,(HL)
        INC HL
        PUSH HL
        LD B,(HL)

        PUSH DE
        EX DE,HL
        LD DE,23
        LD IXH,8

L2      SRL B
        RR C
L3      DB #19  ;AND A or NOP
        LD A,C
        ADC A,D ;=ADC 0
        LD (HL),A
        INC HL
        LD A,B
        ADC A,D
        LD (HL),A
        ADD HL,DE
        DEC IXH
        JR NZ,L2

        POP DE
        INC DE
        INC DE
        POP HL
        INC HL
        POP BC
        DJNZ L1

        POP HL
        POP DE

        LD A,E
        CP TCOLD_1&0xff
        JR NZ,CORR_1
        LD A,#FD
        LD (NT_+#2E),A

CORR_1  LD A,(DE)
        AND A
        JR Z,TC_EXIT
        RRA
        PUSH AF
        ADD A,A
        LD C,A
        ADD HL,BC
        POP AF
        JR NC,CORR_2
        DEC (HL)
        DEC (HL)
CORR_2  INC (HL)
        AND A
        SBC HL,BC
        INC DE
        JR CORR_1

TC_EXIT

        POP AF

;VolTableCreator (c) Ivan Roshin
;A - VersionForVolumeTable (0..4 - 3.xx..3.4x;
                           ;5.. - 2.x,3.5x..3.6x..VTII1.0)

        CP 5
        LD HL,#11
        LD D,H
        LD E,H
        LD A,#17
        JR NC,M1
        DEC L
        LD E,L
        XOR A
M1      LD (M2),A

        LD IX,VT_+16

        LD C,#F
INITV2  PUSH HL

        ADD HL,DE
        EX DE,HL
        SBC HL,HL

        LD B,#10
INITV1  LD A,L
M2      DB #7D
        LD A,H
        ADC A,0
        LD (IX),A
        INC IX
        ADD HL,DE
        DJNZ INITV1

        POP HL
        LD A,E
        CP #77
        JR NZ,M3
        INC E
M3      DEC C
        JR NZ,INITV2

        JP ROUT

INITPT3 CALL SETMDAD
        PUSH HL
        LD DE,100
        ADD HL,DE
        LD A,(HL)
        LD (IY-100+VRS.Delay),A
        PUSH HL
        POP IX
        ADD HL,DE
        CALL SETCPPT
        LD E,(IX+102-100)
        INC HL

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

        ADD HL,DE
        CALL SETLPPT
        POP DE
        LD L,(IX+103-100)
        LD H,(IX+104-100)
        ADD HL,DE
        CALL SETPTPT
        LD HL,169
        ADD HL,DE
        CALL SETORPT
        LD HL,105
        ADD HL,DE

SETSMPT LD (IY-100+VRS.SamPtrs),L
        LD (IY-100+VRS.SamPtrs+1),H
        RET

INITPT2 LD A,(HL)
        LD (IY-100+VRS.Delay),A
        PUSH HL
        PUSH HL
        PUSH HL
        INC HL
        INC HL
        LD A,(HL)
        INC HL
        CALL SETSMPT
        LD E,(HL)
        INC HL
        LD D,(HL)
        POP HL
        AND A
        SBC HL,DE
        CALL SETMDAD
        POP HL
        LD DE,67
        ADD HL,DE
        CALL SETORPT
        LD E,32
        ADD HL,DE
        LD C,(HL)
        INC HL
        LD B,(HL)
        LD E,30
        ADD HL,DE
        CALL SETCPPT
        LD E,A
        INC HL

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

        ADD HL,DE
        CALL SETLPPT
        POP HL
        ADD HL,BC

SETPTPT LD (IY-100+VRS.PatsPtr),L
        LD (IY-100+VRS.PatsPtr+1),H
        RET

SETMDAD LD (IY-100+VRS.MODADDR),L
        LD (IY-100+VRS.MODADDR+1),H
        RET

SETORPT LD (IY-100+VRS.OrnPtrs),L
        LD (IY-100+VRS.OrnPtrs+1),H
        RET

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

SETLPPT LD (IY-100+VRS.LPosPtr),L
        LD (IY-100+VRS.LPosPtr+1),H
        RET

SETENBS LD (IY-100+VRS.EnvBase),L
        LD (IY-100+VRS.EnvBase+1),H
        RET

SETESLD LD (IY-100+VRS.CurESld),L
        LD (IY-100+VRS.CurESld+1),H
        RET

GETIX   PUSH IY
        POP IX
        ADD IX,DE
        RET

PTDECOD CALL GETIX
PTDEC   EQU $+1
        JP #C3C3

;PT2 pattern decoder
PD2_SAM CALL SETSAM
        JR PD2_LOOP

PD2_EOff LD (IX-12+CHP.Env_En),A
        JR PD2_LOOP

PD2_ENV LD (IX-12+CHP.Env_En),16
        LD (IY-100+VRS.AYREGS+EnvTp),A
        LD A,(BC)
        INC BC
        LD L,A
        LD A,(BC)
        INC BC
        LD H,A
        CALL SETENBS
        JR PD2_LOOP

PD2_ORN CALL SETORN
        JR PD2_LOOP

PD2_SKIP INC A
        LD (IX-12+CHP.NNtSkp),A
        JR PD2_LOOP

PD2_VOL RRCA
        RRCA
        RRCA
        RRCA
        LD (IX-12+CHP.Volume),A
        JR PD2_LOOP

PD2_DEL CALL C_DELAY
        JR PD2_LOOP

PD2_GLIS SET 2,(IX-12+CHP.Flags)
        INC A
        LD (IX-12+CHP.TnSlDl),A
        LD (IX-12+CHP.TSlCnt),A
        LD A,(BC)
        INC BC
        LD (IX-12+CHP.TSlStp),A
        ADD A,A
        SBC A,A
        LD (IX-12+CHP.TSlStp+1),A
        SCF
        JR PD2_LP2

PT2PD   AND A

PD2_LP2 EX AF,AF'

PD2_LOOP LD A,(BC)
        INC BC
        ADD A,#20
        JR Z,PD2_REL
        JR C,PD2_SAM
        ADD A,96
        JR C,PD2_NOTE
        INC A
        JR Z,PD2_EOff
        ADD A,15
        JP Z,PD_FIN
        JR C,PD2_ENV
        ADD A,#10
        JR C,PD2_ORN
        ADD A,#40
        JR C,PD2_SKIP
        ADD A,#10
        JR C,PD2_VOL
        INC A
        JR Z,PD2_DEL
        INC A
        JR Z,PD2_GLIS
        INC A
        JR Z,PD2_PORT
        INC A
        JR Z,PD2_STOP
        LD A,(BC)
        INC BC
        LD (IX-12+CHP.CrNsSl),A
        JR PD2_LOOP

PD2_PORT RES 2,(IX-12+CHP.Flags)
        LD A,(BC)
        INC BC
        INC BC ;ignoring precalc delta to right sound
        INC BC
        SCF
        JR PD2_LP2

PD2_STOP LD (IX-12+CHP.TSlCnt),A
        JR PD2_LOOP

PD2_REL LD (IX-12+CHP.Flags),A
        JR PD2_EXIT

PD2_NOTE LD L,A
        LD A,(IX-12+CHP.Note)
        LD (PrNote+1),A
        LD (IX-12+CHP.Note),L
        XOR A
        LD (IX-12+CHP.TSlCnt),A
        SET 0,(IX-12+CHP.Flags)
        EX AF,AF'

        JR NC,NOGLIS2
        BIT 2,(IX-12+CHP.Flags)
        JR NZ,NOPORT2
        LD (LoStep),A
        ADD A,A
        SBC A,A
        EX AF,AF'
        LD H,A
        LD L,A
        INC A
        CALL SETPORT
NOPORT2 LD (IX-12+CHP.TSlCnt),1
NOGLIS2 XOR A


PD2_EXIT LD (IX-12+CHP.PsInSm),A
        LD (IX-12+CHP.PsInOr),A
        LD (IX-12+CHP.CrTnSl),A
        LD (IX-12+CHP.CrTnSl+1),A
        JP PD_FIN

;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)&0xffff
        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  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,#3131

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-
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
        CALL SETENBS
        XOR A
        LD (IX-12+CHP.PsInOr),A
        LD (IY-100+VRS.CurEDel),A
        LD H,A
        LD L,A
        JP SETESLD

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 DW C_NOP
        DW C_GLISS
        DW C_PORTM
        DW C_SMPOS
        DW C_ORPOS
        DW C_VIBRT
        DW C_NOP
        DW C_NOP
        DW C_ENGLS
        DW C_DELAY
        DW C_NOP
        DW C_NOP
        DW C_NOP
        DW C_NOP
        DW C_NOP
        DW C_NOP

CHREGS  CALL GETIX
        XOR A
        LD (Ampl),A
        BIT 0,(IX+CHP.Flags)
        PUSH HL
        JP Z,CH_EXIT
        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
                ;PT2    PT3
OrnCP   INC A   ;CP E   CP D
        JR C,CH_ORPS
OrnLD   DB 1    ;LD A,D 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 ;or ADD A,B for PT2
        LD L,A
        ADD HL,SP
        LD SP,HL
        LD A,B
        INC A
                ;PT2    PT3
SamCP   INC A   ;CP E   CP D
        JR C,CH_SMPS
SamLD   DB 1    ;LD A,D LD A,E
CH_SMPS LD (IX+CHP.PsInSm),A
        POP BC
        POP HL

;Convert PT2 sample to PT3
                ;PT2            PT3
SamCnv  POP HL  ;BIT 2,C        JR e_
        POP HL 
        LD H,B
        JR NZ,$+8
        EX DE,HL
        AND A
        SBC HL,HL
        SBC HL,DE
        LD D,C
        RR C
        SBC A,A
        CPL
        AND #3E
        RR C
        RR B
        AND C
        LD C,A
        LD A,B
        RRA
        RRA
        RR D
        RRA
        AND #9F
        LD B,A

e_      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_%256
        LD L,A
        ADC A,NT_/256
        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,#3131
        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_%256
        LD L,A
        ADC A,VT_/256
        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   CALL SETCPPT
        DEC A
        BIT 1,(IY-100+VRS.ModNum)
        JR Z,NoAlCo
TSSub   EQU $+1
        SUB #D6
        CPL
NoAlCo
                ;PT2            PT3
PsCalc  DEC A   ;ADD A,A        NOP
        DEC A   ;ADD A,(HL)     NOP
        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)
        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,#3131
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
        JP SETESLD

PLAY    LD IY,VARS1+100
        CALL PLAY_
        LD A,(is_ts)
        AND A
        JR Z,PL_nts
        LD IY,VARS2+100
        CALL PLAY_
PL_nts
        IF Basic
        LD IY,#5C3A
        ENDIF

ROUT    LD BC,#FFFD
        LD A,(is_ts)
        AND A
        JR Z,r_nts ;keep old standard
        OUT (C),B
r_nts   EX AF,AF'


        IF ACBBAC
        LD IX,VARS1+VRS.AYREGS
        ELSE
        LD HL,VARS1+VRS.AYREGS
        ENDIF

        CALL ROUT_
        EX AF,AF'
        RET Z
        LD B,D
        CPL
        OUT (C),A

        IF ACBBAC
        LD IX,VARS2+VRS.AYREGS
        ELSE
        LD HL,VARS2+VRS.AYREGS
        ENDIF

ROUT_
        IF ACBBAC
        LD A,(SETUP)
        AND 12
        JR Z,ABC
        ADD A,CHTABLE
        LD E,A
        ADC A,CHTABLE/256
        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   DB #E6
        AND %010010
        OR E
        LD E,A
        LD A,(IX+Mixer)
        AND %010010
RxCA2   OR E
        OR E
        LD (IX+Mixer),A
ABC
        ENDIF

        XOR A
        LD DE,#FFBF

        IF ACBBAC
        LD BC,#FFFD
        PUSH IX
        POP HL
        ENDIF

LOUT    OUT (C),A
        LD B,E
        OUTI
        LD B,D
        INC A
        CP 13
        JR NZ,LOUT
        OUT (C),A
        LD A,(HL)
        AND A
        RET M
        LD B,E
        OUT (C),A
        RET

        IF ACBBAC
CHTABLE EQU $-4
        DB 4,5,15,%001001,0,7,7,%100100
        ENDIF

NT_DATA DB (T_NEW_0-T1_)*2
        DB TCNEW_0-T_
        DB (T_OLD_0-T1_)*2+1
        DB TCOLD_0-T_
        DB (T_NEW_1-T1_)*2+1
        DB TCNEW_1-T_
        DB (T_OLD_1-T1_)*2+1
        DB TCOLD_1-T_
        DB (T_NEW_2-T1_)*2
        DB TCNEW_2-T_
        DB (T_OLD_2-T1_)*2
        DB TCOLD_2-T_
        DB (T_NEW_3-T1_)*2
        DB TCNEW_3-T_
        DB (T_OLD_3-T1_)*2
        DB TCOLD_3-T_

T_

TCOLD_0 DB #00+1,#04+1,#08+1,#0A+1,#0C+1,#0E+1,#12+1,#14+1
        DB #18+1,#24+1,#3C+1,0
TCOLD_1 DB #5C+1,0
TCOLD_2 DB #30+1,#36+1,#4C+1,#52+1,#5E+1,#70+1,#82,#8C,#9C
        DB #9E,#A0,#A6,#A8,#AA,#AC,#AE,#AE,0
TCNEW_3 DB #56+1
TCOLD_3 DB #1E+1,#22+1,#24+1,#28+1,#2C+1,#2E+1,#32+1,#BE+1,0
TCNEW_0 DB #1C+1,#20+1,#22+1,#26+1,#2A+1,#2C+1,#30+1,#54+1
        DB #BC+1,#BE+1,0
TCNEW_1 EQU TCOLD_1
TCNEW_2 DB #1A+1,#20+1,#24+1,#28+1,#2A+1,#3A+1,#4C+1,#5E+1
        DB #BA+1,#BC+1,#BE+1,0

PT3EMPTYORN EQU $-1
        DB 1,0

;first 12 values of tone tables (packed)

T_PACK  DB #06EC*2/256,#06EC*2%256
        DB #0755-#06EC
        DB #07C5-#0755
        DB #083B-#07C5
        DB #08B8-#083B
        DB #093D-#08B8
        DB #09CA-#093D
        DB #0A5F-#09CA
        DB #0AFC-#0A5F
        DB #0BA4-#0AFC
        DB #0C55-#0BA4
        DB #0D10-#0C55
        DB #066D*2/256,#066D*2%256
        DB #06CF-#066D
        DB #0737-#06CF
        DB #07A4-#0737
        DB #0819-#07A4
        DB #0894-#0819
        DB #0917-#0894
        DB #09A1-#0917
        DB #0A33-#09A1
        DB #0ACF-#0A33
        DB #0B73-#0ACF
        DB #0C22-#0B73
        DB #0CDA-#0C22
        DB #0704*2/256,#0704*2%256
        DB #076E-#0704
        DB #07E0-#076E
        DB #0858-#07E0
        DB #08D6-#0858
        DB #095C-#08D6
        DB #09EC-#095C
        DB #0A82-#09EC
        DB #0B22-#0A82
        DB #0BCC-#0B22
        DB #0C80-#0BCC
        DB #0D3E-#0C80
        DB #07E0*2/256,#07E0*2%256
        DB #0858-#07E0
        DB #08E0-#0858
        DB #0960-#08E0
        DB #09F0-#0960
        DB #0A88-#09F0
        DB #0B28-#0A88
        DB #0BD8-#0B28
        DB #0C80-#0BD8
        DB #0D60-#0C80
        DB #0E10-#0D60
        DB #0EF8-#0E10

;vars from here can be stripped
;you can move VARS to any other address

VARS

is_ts   DB 0

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

OnOffD  DB 0

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

        STRUCT  VRS

;IF not works in STRUCT in SjASM :(
;       IF CurPosCounter
CurPos  DB 0
PosSub  DB 0
;       ENDIF

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

ChanA   DS CHP
ChanB   DS CHP
ChanC   DS CHP

;GlobalVars
MODADDR DW 0
OrnPtrs DW 0
SamPtrs DW 0
PatsPtr DW 0
AdInPtA DW 0
AdInPtB DW 0
AdInPtC DW 0
CrPsPtr DW 0
LPosPtr DW 0
Delay   DB 0
DelyCnt DB 0
ESldAdd DW 0
CurESld DW 0
Env_Del DB 0
CurEDel DB 0
Ns_Base DB 0
AddToNs DB 0
AddToEn DB 0
EnvBase DW 0
AYREGS  DS 14
        ENDS

VARS1   DS VRS
VARS2   DS VRS

VT_     EQU $-16
        DS 256-16 ;CreatedVolumeTableAddress

T1_     EQU VT_+16 ;Tone tables data depacked here

T_OLD_1 EQU T1_
T_OLD_2 EQU T_OLD_1+24
T_OLD_3 EQU T_OLD_2+24
T_OLD_0 EQU T_OLD_3+2
T_NEW_0 EQU T_OLD_0
T_NEW_1 EQU T_OLD_1
T_NEW_2 EQU T_NEW_0+24
T_NEW_3 EQU T_OLD_3

PT2EMPTYORN EQU VT_+31 ;1,0,0 sequence

NT_     DS 192 ;CreatedNoteTableAddress

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

VARSEND EQU $

MDLADDR EQU 0x8000

;Release 0 steps:
;04/21/2007
;Works start (PTxPlay adaptation); first beta.
;04/22/2007
;Job finished; beta-testing.
;04/23/2007
;PT v3.7 TS mode corrected (after AlCo remarks).
;04/29/2007
;Added 1.XX and 2.XX special commands interpretation for PT3
;modules of v3.7+.

;Size (minimal build for ZX Spectrum):
;Code block #908 bytes
;Variables #2BF bytes (can be stripped)
;Total size #908+#2BF=#BC7 (3015) bytes