;Universal PT2 player for ZX Spectrum and MSX computers
;(c)2005 S.V.Bulba <vorobey@mail.khstu.ru> http://bulba.at.kz

;Release number
Release EQU "1"

;Improvements
;-max number of patterns: 85
;-correct tone portamento (see Epilogue.pt2 by Nik-O)
;-note range checking like in PT3
;-supports all PT2 subversions including PT2.4PF
;-PT2.4PF modules can be loaded to any address

;Limitations
;-run in RAM only (self-modified code)
;-modules like GUEST4.pt2 by Alex Job (with incorrect
; note+ornament values) sound differ than with standard
; PT2-player

	ORG #C000

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 initialization
;START+3 initialization with module address in HL
;START+5 play one quark
;START+8 mute
;START+10 setup and status flags
;START+11 pointer to current position value in PT2 module;
;After INIT (START+11) points to Postion0-1 (optimization)

START
	LD HL,MDLADDR
	JR INIT
	JP PLAY
	JR MUTE
SETUP	DB 0 ;set bit0 to 1, if you want to play without looping
	     ;bit7 is set each time, when loop point is passed
CrPsPtr	DW 0 

;Identifier
	DB "=Uni PT2 Player r.",Release,"="

CHECKLP	LD HL,SETUP
	SET 7,(HL)
	BIT 0,(HL)
	RET Z
	POP HL
	LD HL,DelyCnt
	INC (HL)
	LD HL,ChanA+CHP.SkpCnt
	INC (HL)
MUTE	XOR A
	LD H,A
	LD L,A
	LD (AYREGS+AmplA),A
	LD (AYREGS+AmplB),HL
	JP ROUT

INIT
;HL - AddressOfModule

	LD A,(HL)
	LD (Delay),A
	PUSH HL
	PUSH HL
	PUSH HL
	INC HL
	INC HL
	LD A,(HL)
	INC HL
	LD (SamPtrs),HL
	LD E,(HL)
	INC HL
	LD D,(HL)
	POP HL
	AND A
	SBC HL,DE
	LD (MODADDR),HL
	LD (MDADDR2),HL
	POP HL
	LD DE,67
	ADD HL,DE
	LD (OrnPtrs),HL
	LD E,32
	ADD HL,DE
	LD C,(HL)
	INC HL
	LD B,(HL)
	LD E,30
	ADD HL,DE
	LD (CrPsPtr),HL
	INC HL
	LD E,A
	ADD HL,DE
	LD (LPosPtr),HL
	POP HL
	ADD HL,BC
	LD (PatsPtr),HL
	LD HL,VARS
	LD (HL),D
	LD DE,VARS+1
	LD BC,VAR0END-VARS-1
	LDIR
	LD (AdInPtA),HL ;ptr to zero
	
	LD HL,#F01 ;H - CHP.Volume, L - CHP.SkpCnt
	LD (ChanA+CHP.SkpCnt),HL
	LD (ChanB+CHP.SkpCnt),HL
	LD (ChanC+CHP.SkpCnt),HL
	LD A,L
	LD (DelyCnt),A

	LD HL,EMPTYORN
	LD (ChanA+CHP.OrnPtr),HL
	LD (ChanB+CHP.OrnPtr),HL
	LD (ChanC+CHP.OrnPtr),HL

;note table creator (same as PT3 table #1)
;(c)Ivan Roshin

	LD HL,NTBL

	LD DE,NT_

	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 A,8

L2	SRL B
	RR C
	LD (HL),C
	INC HL
	LD (HL),B
	ADD HL,DE
	DEC A
	JR NZ,L2

	POP DE
	INC DE
	INC DE
	POP HL
	INC HL
	POP BC
	DJNZ L1

	LD A,#FD
	LD (NT_+#2E),A
	LD A,#0A
	LD (NT_+#5C),A

;vol table creator (same as PT3.5+ table)
;(c)Ivan Roshin

	LD HL,#11
	LD D,H
	LD E,H

	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
	RLA
	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

PD_SAM	ADD A,A
	LD E,A
	LD D,0
SamPtrs EQU $+1
	LD HL,#2121
	ADD HL,DE
	LD E,(HL)
	INC HL
	LD D,(HL)
MODADDR EQU $+1
	LD HL,#2121
	ADD HL,DE
	LD (IX+CHP.SamPtr),L
	LD (IX+CHP.SamPtr+1),H
	JR PD_LOOP

PD_EOff	LD (IX+CHP.Env_En),A
	JR PD_LOOP

PD_ENV	LD (IX+CHP.Env_En),16
	LD (AYREGS+EnvTp),A
	LD A,(BC)
	INC BC
	LD L,A
	LD A,(BC)
	INC BC
	LD H,A
	LD (AYREGS+Env),HL
	JR PD_LOOP

PD_ORN	ADD A,A
	LD E,A
	LD D,0
OrnPtrs	EQU $+1
	LD HL,#2121
	ADD HL,DE
	LD E,(HL)
	INC HL
	LD D,(HL)
MDADDR2	EQU $+1
	LD HL,#2121
	ADD HL,DE
	LD (IX+CHP.OrnPtr),L
	LD (IX+CHP.OrnPtr+1),H
	JR PD_LOOP

PD_SKIP	INC A
	LD (IX+CHP.Skip),A
	JR PD_LOOP

PD_VOL	LD (IX+CHP.Volume),A
	JR PD_LOOP

PD_DEL	LD A,(BC)
	INC BC
	LD (Delay),A
	JR PD_LOOP

PD_GLIS	SET 2,(IX+CHP.Flags)
	SET 1,(IX+CHP.Flags)
	LD A,(BC)
	INC BC
        LD (IX+CHP.TSlStp),A
	SCF
	JR PD_LP2

PTDECOD AND A

PD_LP2	EX AF,AF'

PD_LOOP	LD A,(BC)
	INC BC
	ADD A,#20
	JR Z,PD_REL
	JR C,PD_SAM
	ADD A,96
	JR C,PD_NOTE
	INC A
	JR Z,PD_EOff
	ADD A,15
	JP Z,PD_QUIT
	JR C,PD_ENV
	ADD A,#10
	JR C,PD_ORN
	ADD A,#40
	JR C,PD_SKIP
	ADD A,#10
	JR C,PD_VOL
	INC A
	JR Z,PD_DEL
	INC A
	JR Z,PD_GLIS
	INC A
	JR Z,PD_PORT
	INC A
	JR Z,PD_STOP
	LD A,(BC)
	INC BC
	LD (IX+CHP.AddToN),A
	JR PD_LOOP

PD_PORT RES 2,(IX+CHP.Flags)
	SET 1,(IX+CHP.Flags)
	LD A,(BC)
	INC BC
	INC BC ;ignoring precalc delta to right sound
	INC BC
	SCF
	JR PD_LP2

PD_STOP	RES 1,(IX+CHP.Flags)
	JR PD_LOOP

PD_REL	LD (IX+CHP.Flags),A
	JR PD_EXIT

PD_NOTE	LD L,A
	SET 0,(IX+CHP.Flags)
	EX AF,AF'
	JR NC,NOGLISS
	BIT 2,(IX+CHP.Flags)
	JR NZ,NOPORT
	LD (IX+CHP.SlToNt),L

	PUSH BC
	LD DE,NT_
	SLA L
	LD H,0
	ADD HL,DE
	LD C,(HL)
	INC HL
	LD B,(HL)
	LD L,(IX+CHP.Note)
	SLA L
	LD H,0
	ADD HL,DE
	LD E,(HL)
	INC HL
	LD D,(HL)
	LD L,C
	LD H,B
	SBC HL,DE
	LD (IX+CHP.TnDelt),L
	LD (IX+CHP.TnDelt+1),H
	JP P,DELTP
	AND A
	JP M,SET_STP
	JR NEG_STP
DELTP	AND A
	JP P,SET_STP
NEG_STP	NEG
SET_STP	LD (IX+CHP.TSlStp),A
	POP BC
	JR PD_EX1

NOGLISS	RES 1,(IX+CHP.Flags)
NOPORT	LD (IX+CHP.Note),L


PD_EX1	XOR A

PD_EXIT	LD (IX+CHP.PsInSm),A
	LD (IX+CHP.PsInOr),A
	LD (IX+CHP.CrTnSl),A
	LD (IX+CHP.CrTnSl+1),A
PD_QUIT	LD A,(IX+CHP.Skip)
	LD (IX+CHP.SkpCnt),A
	RET

CHREGS	XOR A
	LD (Ampl),A
	PUSH HL
	BIT 0,(IX+CHP.Flags)
	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
	CP E
	JR C,CH_ORPS
	LD A,D
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
	ADD A,B
	LD L,A
	ADD HL,SP
	LD SP,HL
	LD A,B
	INC A
	CP E
	JR C,CH_SMPS
	LD A,D
CH_SMPS	LD (IX+CHP.PsInSm),A
	POP BC
	POP DE
	LD D,B
	EX AF,AF'

;ASM by AIG cannot compile
;	ADD A,NT_ ;7
;	LD L,A ;4
;	ADC A,'NT_ ;7
;	SUB L ;4
;	LD H,A ;4

;Temporary
	LD L,A ;4
	LD H,0 ;7
	LD SP,NT_ ;10
	ADD HL,SP ;11

	LD SP,HL
	POP HL
CSP_	LD SP,#3131
	BIT 2,C
	JR Z,TSUB
	ADD HL,DE
	ADD HL,DE
TSUB	AND A
	SBC HL,DE
	LD E,(IX+CHP.CrTnSl)
	LD D,(IX+CHP.CrTnSl+1)
	ADD HL,DE
	EX (SP),HL

	BIT 1,(IX+CHP.Flags)
	JR Z,CH_AMP
	LD A,(IX+CHP.TSlStp)
	LD L,A
	ADD A,A
	SBC A,A
	LD H,A
	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
	LD (IX+CHP.Flags),1
	XOR A
	LD (IX+CHP.CrTnSl),A
	LD (IX+CHP.CrTnSl+1),A

CH_AMP	LD A,B
	AND #F0
CH_VOL	OR (IX+CHP.Volume)
	RRCA
	RRCA
	RRCA
	RRCA
	LD L,A
	LD H,0
	LD DE,VT_
	ADD HL,DE
	LD A,(HL)
	OR (IX+CHP.Env_En)
	LD (Ampl),A
	RRC C
	SBC A,A
	AND #40
	JR NZ,NONS
	LD A,C
	RRCA
	RRCA
	ADD A,(IX+CHP.AddToN)
	LD (AYREGS+Noise),A
	XOR A
NONS	RRC C
	JR NC,CH_EXIT
	OR 8

CH_EXIT	LD HL,AYREGS+Mixer
	OR (HL)
	RRCA
	LD (HL),A
	POP HL
	RET

PLAY    XOR A
	LD (AYREGS+Mixer),A
	DEC A
	LD (AYREGS+EnvTp),A
	LD HL,DelyCnt
	DEC (HL)
	JR NZ,PL2
	LD HL,ChanA+CHP.SkpCnt
	DEC (HL)
	JR NZ,PL1B
AdInPtA	EQU $+1
	LD BC,#0101
	LD A,(BC)
	AND A
	JR NZ,PL1A
	LD D,A
	LD HL,(CrPsPtr)
	INC HL
	LD A,(HL)
	ADD A,A
	JR NC,PLNLP
	CALL CHECKLP
LPosPtr	EQU $+1
	LD HL,#2121
	LD A,(HL)
	ADD A,A
PLNLP	LD (CrPsPtr),HL
	ADD A,(HL)
	ADD A,A
	LD E,A
	RL D
PatsPtr	EQU $+1
	LD HL,#2121
	ADD HL,DE
	LD DE,(MODADDR)
	LD (PSP_+1),SP
	LD SP,HL
	POP HL
	ADD HL,DE
	LD B,H
	LD C,L
	POP HL
	ADD HL,DE
	LD (AdInPtB),HL
	POP HL
	ADD HL,DE
	LD (AdInPtC),HL
PSP_	LD SP,#3131
PL1A	LD IX,ChanA
	CALL PTDECOD
	LD (AdInPtA),BC

PL1B	LD HL,ChanB+CHP.SkpCnt
	DEC (HL)
	JR NZ,PL1C
	LD IX,ChanB
AdInPtB	EQU $+1
	LD BC,#0101
	CALL PTDECOD
	LD (AdInPtB),BC

PL1C	LD HL,ChanC+CHP.SkpCnt
	DEC (HL)
	JR NZ,PL1D
	LD IX,ChanC
AdInPtC	EQU $+1
	LD BC,#0101
	CALL PTDECOD
	LD (AdInPtC),BC

Delay	EQU $+1
PL1D	LD A,#3E
	LD (DelyCnt),A

PL2	LD IX,ChanA
	LD HL,(AYREGS+TonA)
	CALL CHREGS
	LD (AYREGS+TonA),HL
	LD A,(Ampl)
	LD (AYREGS+AmplA),A
	LD IX,ChanB
	LD HL,(AYREGS+TonB)
	CALL CHREGS
	LD (AYREGS+TonB),HL
	LD A,(Ampl)
	LD (AYREGS+AmplB),A
	LD IX,ChanC
	LD HL,(AYREGS+TonC)
	CALL CHREGS
	LD (AYREGS+TonC),HL

ROUT	XOR A
	LD DE,#FFBF
	LD BC,#FFFD
	LD HL,AYREGS
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

;MSX version of ROUT (c)Dioniso
;ROUT	XOR A
;	LD C,#A0
;	LD HL,AYREGS
;LOUT	OUT (C),A
;	INC C
;	OUTI 
;	DEC C
;	INC A
;	CP 13
;	JR NZ,LOUT
;	OUT (C),A
;	LD A,(HL)
;	AND A
;	RET M
;	INC C
;	OUT (C),A
;	RET

NTBL	DW #0EF8*2,#0E10*2,#0D60*2,#0C80*2,#0BD8*2,#0B28*2
	DW #0A88*2,#09F0*2,#0960*2,#08E0*2,#0858*2,#07E0*2

;vars from here can be stripped
;you can move VARS to any other address

VARS

DelyCnt	DB 0

struc	CHP
.PsInSm	RESB 1
.PsInOr	RESB 1
.CrTnSl RESW 1

.Flags	RESB 1
.TSlStp	RESB 1
.SamPtr	RESW 1
.OrnPtr	RESW 1
.SlToNt	RESB 1
.Note	RESB 1
.TnDelt RESW 1
.Skip	RESB 1
.SkpCnt	RESB 1
.Volume	RESB 1
.Env_En	RESB 1
.AddToN RESB 1
.Size	RESB 1
endstruc

ChanA	RESB CHP.Size
ChanB	RESB CHP.Size
ChanC	RESB CHP.Size

AYREGS	RESB 14

Ampl	EQU AYREGS+AmplC

VT_	RESB 256

VAR0END	EQU VT_+16 ;zeroing area end

EMPTYORN EQU VT_+31 ;1,0,0 sequence

NT_	RESB 192

VARSEND

MDLADDR	EQU $

;Release 0 steps
;7.Feb.2005
;note table creator; volume table creator; pattern decoder
;8.Feb.2005
;CHREGS subroutine
;9.Feb.2005
;no code
;10.Feb.2005
;PLAY subroutine
;11.Feb.2005
;INIT subroutine; first run; debug
;12.Feb.2005
;debug; test; first release!
;Release 1 steps
;13.Feb.2005
;TSlStp word become byte; CHREGS slightly reorganized
;18.Feb.2005
;ntcreator: XH changed to A (-2 bytes, thx to Spectre);
;vtcreator: optimized loops (-3 bytes, thx to Spectre)
;26.Feb.2005
;PT2PF converter is removed and changed to simple MODADDR
;corrector (50 bytes shorter)

;Tests in IMMATION TESTER V1.0 by Andy Man/POS
;Module name/author	Min tacts	Max tacts	Average
;Epilogue/Nik-O		3000		8200		5000
;NY tHEMEs/zHenYa	3000		7704		5000
;GUEST 4/Alex Job	2232		7640		5000
;KickDB/Fatal Snipe	1464		8456		5000

;Size:
;Code block #3F1 bytes
;Variables #208 bytes (can be stripped)
;Size in RAM #3F1+#208=#5F9 (1529) bytes
