                  ****************************************
                  *** JamCrackerPro V1.0a play-routine ***
                  ***   Originally coded by M. Gemmel  ***
                  ***           Code optimised         ***
                  ***         by Xag of Betrayal       ***
                  ***    See docs for important info   ***
                  ****************************************
	incdir	"tom:asm/"
	include	inc/macro.s

; !!!!
	XDEF	_pp_init,_pp_music,_pp_end
	XREF	_adr_data
; !!!!

DMAWAIT		EQU	$12C	;Change to suit

	*** Relative offset definitions ***

		RSRESET		;Instrument info structure
it_name		RS.B	31
it_flags		RS.B	1
it_size		RS.L	1
it_address	RS.L	1
it_sizeof		RS.W	0

		RSRESET		;Pattern info structure
pt_size		RS.W	1
pt_address	RS.L	1
pt_sizeof		RS.W	0

		RSRESET		;Note info structure
nt_period		RS.B	1
nt_instr		RS.B	1
nt_speed		RS.B	1
nt_arpeggio	RS.B	1
nt_vibrato	RS.B	1
nt_phase		RS.B	1
nt_volume		RS.B	1
nt_porta		RS.B	1
nt_sizeof		RS.W	0

		RSRESET		;Voice info structure
pv_waveoffset	RS.W	1
pv_dmacon		RS.W	1
pv_custbase	RS.L	1
pv_inslen		RS.W	1
pv_insaddress	RS.L	1
pv_peraddress	RS.L	1
pv_pers		RS.W	3
pv_por		RS.W	1
pv_deltapor	RS.W	1
pv_porlevel	RS.W	1
pv_vib		RS.W	1
pv_deltavib	RS.W	1
pv_vol		RS.W	1
pv_deltavol	RS.W	1
pv_vollevel	RS.W	1
pv_phase		RS.W	1
pv_deltaphase	RS.W	1
pv_vibcnt		RS.B	1
pv_vibmax		RS.B	1
pv_flags		RS.B	1
pv_sizeof		RS.W	0

		SECTION	BTLCRACKER_PLAYER,CODE_C


	*** Initialise routine ***

_pp_init:
; !!!!
		push.l	d0-d7/a0-a6
		move.l	_adr_data,a0
; !!!!
		addq.w	#4,a0
		move.w	(a0)+,d0
		move.w	d0,d1
		move.l	a0,instable
		mulu	#it_sizeof,d0
		add.w	d0,a0

		move.w	(a0)+,d0
		move.w	d0,d2
		move.l	a0,patttable
		mulu	#pt_sizeof,d0
		add.w	d0,a0

		move.w	(a0)+,d0
		move.w	d0,songlen
		move.l	a0,songtable
		add.w	d0,d0
		add.w	d0,a0

		move.l	patttable(PC),a1
		move.w	d2,d0
		subq.w	#1,d0
.l0:		move.l	a0,pt_address(a1)
		move.w	(a1),d3		;pt_size
		mulu	#nt_sizeof*4,d3
		add.w	d3,a0
		addq.w	#pt_sizeof,a1
		dbra	d0,.l0

		move.l	instable(PC),a1
		move.w	d1,d0
		subq.w	#1,d0
.l1:		move.l	a0,it_address(a1)
		move.l	it_size(a1),d2
		add.l	d2,a0
		add.w	#it_sizeof,a1
		dbra	d0,.l1

		move.l	songtable(PC),pp_songptr
		move.w	songlen(PC),pp_songcnt
		move.l	pp_songptr(PC),a0
		move.w	(a0),d0
		mulu	#pt_sizeof,d0
		add.l	patttable(PC),d0
		move.l	d0,a0
		move.l	a0,pp_pattentry
		move.b	pt_size+1(a0),pp_notecnt
		move.l	pt_address(a0),pp_address
		move.b	#6,pp_wait
		move.b	#1,pp_waitcnt
		clr.w	pp_nullwave
		move.w	#$000F,$DFF096

		lea	pp_variables(PC),a0
		lea	$DFF0A0,a1
		moveq	#1,d1
		move.w	#$80,d2
		moveq	#4-1,d0
.l2:		move.w	#0,8(a1)
		move.w	d2,(a0)		;pv_waveoffset
		move.w	d1,pv_dmacon(a0)
		move.l	a1,pv_custbase(a0)
		move.l	#pp_periods,pv_peraddress(a0)
		move.w	#1019,pv_pers(a0)
		clr.w	pv_pers+2(a0)
		clr.w	pv_pers+4(a0)
		clr.l	pv_por(a0)
		clr.w	pv_porlevel(a0)
		clr.l	pv_vib(a0)
		clr.l	pv_vol(a0)
		move.w	#$40,pv_vollevel(a0)
		clr.l	pv_phase(a0)
		clr.w	pv_vibcnt(a0)
		clr.b	pv_flags(a0)
		add.w	#pv_sizeof,a0
		add.w	#$10,a1
		add.w	d1,d1
		add.w	#$40,d2
		dbra	d0,.l2

		bset	#1,$BFE001
; !!!!
		pop.l	d0-d7/a0-a6
; !!!!
		rts

	*** Clean-up routine ***

_pp_end:
; !!!!
		push.l	d0-d7/a0-a6
; !!!!
		moveq	#0,d0
		lea	$DFF000,a0
		move.w	d0,$A8(a0)
		move.w	d0,$B8(a0)
		move.w	d0,$C8(a0)
		move.w	d0,$D8(a0)
		move.w	#$000F,$96(a0)
;		bclr	#1,$BFE001
; !!!!
	pop.l	d0-d7/a0-a6
	moveq.l	#0,d0
; !!!!
		rts
_pp_music:
	push.l	d0-d7/a0-a6
	bsr.s	pp_play
	pop.l	d0-d7/a0-a6
	rts

	*** Play routine ***

pp_play:		lea	$DFF000,a6
		subq.b	#1,pp_waitcnt
		bne.s	.l0
		bsr	pp_nwnt
		move.b	pp_wait(PC),pp_waitcnt

.l0:		lea	pp_variables(PC),a1
		bsr.s	pp_uvs
		lea	pp_variables+pv_sizeof(PC),a1
		bsr.s	pp_uvs
		lea	pp_variables+2*pv_sizeof(PC),a1
		bsr.s	pp_uvs
		lea	pp_variables+3*pv_sizeof(PC),a1

pp_uvs:		move.l	pv_custbase(a1),a0

.l0:		move.w	pv_pers(a1),d0
		bne.s	.l1
		bsr	pp_rot
		bra.s	.l0
.l1:		add.w	pv_por(a1),d0
		tst.w	pv_por(a1)
		beq.s	.l1c
		bpl.s	.l1a
		cmp.w	pv_porlevel(a1),d0
		bge.s	.l1c
		bra.s	.l1b
.l1a:		cmp.w	pv_porlevel(a1),d0
		ble.s	.l1c
.l1b:		move.w	pv_porlevel(a1),d0

.l1c:		add.w	pv_vib(a1),d0
		cmp.w	#135,d0
		bge.s	.l1d
		move.w	#135,d0
		bra.s	.l1e
.l1d:		cmp.w	#1019,d0
		ble.s	.l1e
		move.w	#1019,d0
.l1e:		move.w	d0,6(a0)
		bsr	pp_rot

		move.w	pv_deltapor(a1),d0
		add.w	d0,pv_por(a1)
		cmp.w	#-1019,pv_por(a1)
		bge.s	.l3
		move.w	#-1019,pv_por(a1)
		bra.s	.l5
.l3:		cmp.w	#1019,pv_por(a1)
		ble.s	.l5
		move.w	#1019,pv_por(a1)

.l5:		tst.b	pv_vibcnt(a1)
		beq.s	.l7
		move.w	pv_deltavib(a1),d0
		add.w	d0,pv_vib(a1)
		subq.b	#1,pv_vibcnt(a1)
		bne.s	.l7
		neg.w	pv_deltavib(a1)
		move.b	pv_vibmax(a1),pv_vibcnt(a1)

.l7:		move.w	pv_dmacon(a1),d0
		move.w	pv_vol(a1),8(a0)
		move.w	pv_deltavol(a1),d0
		add.w	d0,pv_vol(a1)
		tst.w	pv_vol(a1)
		bpl.s	.l8
		clr.w	pv_vol(a1)
		bra.s	.la
.l8:		cmp.w	#$40,pv_vol(a1)
		ble.s	.la
		move.w	#$40,pv_vol(a1)

.la:		btst	#1,pv_flags(a1)
		beq.s	.l10
		tst.w	pv_deltaphase(a1)
		beq.s	.l10
		bpl.s	.sk
		clr.w	pv_deltaphase(a1)
.sk:		move.l	pv_insaddress(a1),a0
		move.w	(a1),d0		;pv_waveoffset
		neg.w	d0
		lea	(a0,d0.w),a2
		move.l	a2,a3
		move.w	pv_phase(a1),d0
		lsr.w	#2,d0
		add.w	d0,a3

		moveq	#$40-1,d0
.lb:		move.b	(a2)+,d1
		ext.w	d1
		move.b	(a3)+,d2
		ext.w	d2
		add.w	d1,d2
		asr.w	#1,d2
		move.b	d2,(a0)+
		dbra	d0,.lb

		move.w	pv_deltaphase(a1),d0
		add.w	d0,pv_phase(a1)
		cmp.w	#$100,pv_phase(a1)
		blt.s	.l10
		sub.w	#$100,pv_phase(a1)

.l10:		rts

pp_rot:		move.w	pv_pers(a1),d0
		move.w	pv_pers+2(a1),pv_pers(a1)
		move.w	pv_pers+4(a1),pv_pers+2(a1)
		move.w	d0,pv_pers+4(a1)
		rts

pp_nwnt:		move.l	pp_address(PC),a0
		add.l	#4*nt_sizeof,pp_address
		subq.b	#1,pp_notecnt
		bne.s	.l5

.l0:		addq.l	#2,pp_songptr
		subq.w	#1,pp_songcnt
		bne.s	.l1
		move.l	songtable(PC),pp_songptr
		move.w	songlen(PC),pp_songcnt
.l1:		move.l	pp_songptr(PC),a1
		move.w	(a1),d0
		mulu	#pt_sizeof,d0
		add.l	patttable(PC),d0
		move.l	d0,a1
		move.b	pt_size+1(a1),pp_notecnt
		move.l	pt_address(a1),pp_address

.l5:		clr.w	pp_tmpdmacon
		lea	pp_variables(PC),a1
		bsr	pp_nnt
		addq.w	#nt_sizeof,a0
		lea	pp_variables+pv_sizeof(PC),a1
		bsr	pp_nnt
		addq.w	#nt_sizeof,a0
		lea	pp_variables+2*pv_sizeof(PC),a1
		bsr	pp_nnt
		addq.w	#nt_sizeof,a0
		lea	pp_variables+3*pv_sizeof(PC),a1
		bsr	pp_nnt

		move.w	pp_tmpdmacon(PC),$96(a6)

		move.w	#DMAWAIT-1,d0
.loop1:		dbra	d0,.loop1

		lea	pp_variables(PC),a1
		bsr.s	pp_scr
		lea	pp_variables+pv_sizeof(PC),a1
		bsr.s	pp_scr
		lea	pp_variables+2*pv_sizeof(PC),a1
		bsr.s	pp_scr
		lea	pp_variables+3*pv_sizeof(PC),a1
		bsr.s	pp_scr

		bset	#7,pp_tmpdmacon
		move.w	pp_tmpdmacon(PC),$96(a6)

		move.w	#DMAWAIT-1,d0
.loop2:		dbra	d0,.loop2

		move.l	pp_variables+pv_insaddress(PC),$A0(a6)
		move.w	pp_variables+pv_inslen(PC),$A4(a6)
		move.l	pp_variables+pv_sizeof+pv_insaddress(PC),$B0(a6)
		move.w	pp_variables+pv_sizeof+pv_inslen(PC),$B4(a6)
		move.l	pp_variables+2*pv_sizeof+pv_insaddress(PC),$C0(a6)
		move.w	pp_variables+2*pv_sizeof+pv_inslen(PC),$C4(a6)
		move.l	pp_variables+3*pv_sizeof+pv_insaddress(PC),$D0(a6)
		move.w	pp_variables+3*pv_sizeof+pv_inslen(PC),$D4(a6)

		rts

pp_scr:		move.w	pp_tmpdmacon(PC),d0
		and.w	pv_dmacon(a1),d0
		beq.s	.l5

		move.l	pv_custbase(a1),a0
		move.l	pv_insaddress(a1),(a0)
		move.w	pv_inslen(a1),4(a0)
		move.w	pv_pers(a1),6(a0)
		btst	#0,pv_flags(a1)
		bne.s	.l5
		move.l	#pp_nullwave,pv_insaddress(a1)
		move.w	#1,pv_inslen(a1)

.l5:		rts

pp_nnt:		move.b	(a0),d1		;nt_period
		beq	.l5

		and.l	#$000000FF,d1
		add.w	d1,d1
		add.l	#pp_periods-2,d1
		move.l	d1,a2

		btst	#6,nt_speed(a0)
		beq.s	.l2
		move.w	(a2),pv_porlevel(a1)
		bra.s	.l5

.l2:		move.w	pv_dmacon(a1),d0
		or.w	d0,pp_tmpdmacon

		move.l	a2,pv_peraddress(a1)
		move.w	(a2),pv_pers(a1)
		move.w	(a2),pv_pers+2(a1)
		move.w	(a2),pv_pers+4(a1)

		clr.w	pv_por(a1)

		move.b	nt_instr(a0),d0
		ext.w	d0
		mulu	#it_sizeof,d0
		add.l	instable(PC),d0
		move.l	d0,a2
		tst.l	it_address(a2)
		bne.s	.l1
		move.l	#pp_nullwave,pv_insaddress(a1)
		move.w	#1,pv_inslen(a1)
		clr.b	pv_flags(a1)
		bra.s	.l5

.l1:		move.l	it_address(a2),a3
		btst	#1,it_flags(a2)
		bne.s	.l0a
		move.l	it_size(a2),d0
		lsr.l	#1,d0
		move.w	d0,pv_inslen(a1)
		bra.s	.l0
.l0a:		move.w	(a1),d0		;pv_waveoffset
		add.w	d0,a3
		move.w	#$20,pv_inslen(a1)
.l0:		move.l	a3,pv_insaddress(a1)
		move.b	it_flags(a2),pv_flags(a1)
		move.w	pv_vollevel(a1),pv_vol(a1)

.l5:		move.b	nt_speed(a0),d0
		and.b	#$0F,d0
		beq.s	.l6
		move.b	d0,pp_wait

.l6:		move.l	pv_peraddress(a1),a2
		move.b	nt_arpeggio(a0),d0
		beq.s	.l9
		cmp.b	#$FF,d0
		bne.s	.l7
		move.w	(a2),pv_pers(a1)
		move.w	(a2),pv_pers+2(a1)
		move.w	(a2),pv_pers+4(a1)
		bra.s	.l9

.l7:		and.b	#$0F,d0
		add.b	d0,d0
		ext.w	d0
		move.w	(a2,d0.w),pv_pers+4(a1)
		move.b	nt_arpeggio(a0),d0
		lsr.b	#4,d0
		add.b	d0,d0
		ext.w	d0
		move.w	(a2,d0.w),pv_pers+2(a1)
		move.w	(a2),pv_pers(a1)

.l9:		move.b	nt_vibrato(a0),d0
		beq.s	.ld
		cmp.b	#$FF,d0
		bne.s	.la
		clr.l	pv_vib(a1)
		clr.b	pv_vibcnt(a1)
		bra.s	.ld
.la:		clr.w	pv_vib(a1)
		and.b	#$0F,d0
		ext.w	d0
		move.w	d0,pv_deltavib(a1)
		move.b	nt_vibrato(a0),d0
		lsr.b	#4,d0
		move.b	d0,pv_vibmax(a1)
		lsr.b	#1,d0
		move.b	d0,pv_vibcnt(a1)

.ld:		move.b	nt_phase(a0),d0
		beq.s	.l10
		cmp.b	#$FF,d0
		bne.s	.le
		clr.w	pv_phase(a1)
		move.w	#$FFFF,pv_deltaphase(a1)
		bra.s	.l10
.le:		and.b	#$0F,d0
		ext.w	d0
		move.w	d0,pv_deltaphase(a1)
		clr.w	pv_phase(a1)

.l10:		move.b	nt_volume(a0),d0
		bne.s	.l10a
		btst	#7,nt_speed(a0)
		beq.s	.l16
		bra.s	.l11a
.l10a:		cmp.b	#$FF,d0
		bne.s	.l11
		clr.w	pv_deltavol(a1)
		bra.s	.l16
.l11:		btst	#7,nt_speed(a0)
		beq.s	.l12
.l11a:		move.b	d0,pv_vol+1(a1)
		move.b	d0,pv_vollevel+1(a1)
		clr.w	pv_deltavol(a1)
		bra.s	.l16
.l12:		bclr	#7,d0
		beq.s	.l13
		neg.b	d0
.l13:		ext.w	d0
		move.w	d0,pv_deltavol(a1)

.l16:		move.b	nt_porta(a0),d0
		beq.s	.l1a
		cmp.b	#$FF,d0
		bne.s	.l17
		clr.l	pv_por(a1)
		bra.s	.l1a
.l17:		clr.w	pv_por(a1)
		btst	#6,nt_speed(a0)
		beq.s	.l17a
		move.w	pv_porlevel(a1),d1
		cmp.w	pv_pers(a1),d1
		bgt.s	.l17c
		neg.b	d0
		bra.s	.l17c

.l17a:		bclr	#7,d0
		bne.s	.l18
		neg.b	d0
		move.w	#135,pv_porlevel(a1)
		bra.s	.l17c

.l18:		move.w	#1019,pv_porlevel(a1)
.l17c:		ext.w	d0
.l18a:		move.w	d0,pv_deltapor(a1)

.l1a:		rts

	*** Data section ***

pp_periods:	DC.W	1019,962,908,857,809,763,720,680,642,606,572,540
		DC.W	509,481,454,428,404,381,360,340,321,303,286,270
		DC.W	254,240,227,214,202,190,180,170,160,151,143,135
		DC.W	135,135,135,135,135,135,135,135,135
		DC.W	135,135,135,135,135,135

songlen:		DS.W	1
songtable:	DS.L	1
instable:		DS.L	1
patttable:	DS.L	1

pp_wait:		DS.B	1
pp_waitcnt:	DS.B	1
pp_notecnt:	DS.B	1
pp_address:	DS.L	1
pp_songptr:	DS.L	1
pp_songcnt:	DS.W	1
pp_pattentry:	DS.L	1
pp_tmpdmacon:	DS.W	1
pp_variables:	DS.B	4*48

pp_nullwave:	DS.W	1

