;------------------------------------------------------------------------------
;
;	ProTracker playroutine, revision 2C
;
;	Coded by Lars "ZAP" Hamre
;	Updated by Håvard "HOWARD/MENTAL DISEASES" Pedersen
;
;REVISION 2B:
;* Mastervolume. (Separate left and right for balance controls.)
;* Overstep for players using program mode.
;* Rewind/fast forward. (One pattern)
;* Easy CIA-utilising.
;* DMAWait solved. (Finally!)
;* No longer clears one word behind end of module if instrument 31 is unused.
;* F00 really halts module now. (Was ignored)
;* Slower than before. =(
;* Various small optimisations in attempt to compensate. (Bugs may appear!)
;* PT_End now stops module, even if music is still called.
;* Added (selectable) 68020+ optimisations. (Not powerful yet...) 8(
;
;REVISION 2C:
;* Overstep didn't trig on halting the module. Now does.
;* Made overstep selectable.
;* Added NoiseTracker compatibility. (Selectable through PT__NTComp) Both NTs
;  vibrato and loop-position is supported. PT modules are still played as before.
;* Fixed some ucase/lcase confusion on some labels.
;* CIA-tempo wasn't reset upon PT_Init(). Now is.
;
;DMAWAIT ISSUES:
;Commodore states that atleast one rasterline must pass before rewriting to
;the audio hardware after disabling the DMA. A DMA-delay of 80 will wait
;_exactly_ one line. Still, OctaMed uses a delay equal to 64, and it sounds OK.
;Still, delays which sounds fine on your computer _may_ turn up bad on faster
;machines, so please add some extra for margins.
;
;PLEASE NOTE!
;This replay assumes exclusive access to audio hardware and OCS/ECS/AGA or
;compatible audio chips. This behaviour is _NOT_ encouraged by Commodore!
;
;------------------------------------------------------------------------------

	IFND	PT_MasterVol			; Mastervolume ON/OFF
PT_MasterVol	=	0			; Call PT_SetMasterVol with
	ENDC					; Left in D0 and Right in D1
						; ( 0 - 64 )

	IFND	PT__OverStep			; Signal when module is
PT__OverStep	=	0			; finished. PT_OverStep
	ENDC					; contains 1 if end has reached
						; and -1 if rewound past
						; beginning.

	IFND	PT__WindFuncs			; Fast forward / Rewind
PT__WindFuncs	=	0
	ENDC

	IFND	PT__CIA				; CIA Support
PT__CIA		=	0			; Supply PT_SetIntRate with
	ENDC					; rate (32-255) in D0. Do not
						; trash registers.

	IFND	PT__DMADelay			; 80 is Commodore's
PT__DMADelay	=	80			; recommendation.
	ENDC

	IFND	PT__68020			; 68020+ optimisations
PT__68020	=	0
	ENDC

	IFND	PT__NTComp			; Noisetracker compatibility
PT__NTComp	=	1
	ENDC

n_note		equ	0	; w
n_cmd		equ	2	; w
n_cmdlo		equ	3	; b
n_start		equ	4	; l
n_length	equ	8	; w
n_loopstart	equ	10	; l
n_replen	equ	14	; w
n_period	equ	16	; w
n_finetune	equ	18	; b
n_volume	equ	19	; b
n_dmabit	equ	20	; w
n_toneportdirec	equ	22	; b
n_toneportspeed	equ	23	; b
n_wantedperiod	equ	24	; w
n_vibratocmd	equ	26	; b
n_vibratopos	equ	27	; b
n_tremolocmd	equ	28	; b
n_tremolopos	equ	29	; b
n_wavecontrol	equ	30	; b
n_glissfunk	equ	31	; b
n_sampleoffset	equ	32	; b
n_pattpos	equ	33	; b
n_loopcount	equ	34	; b
n_funkoffset	equ	35	; b
n_wavestart	equ	36	; l
n_reallength	equ	40	; w

;------------------------------------------------------------------------------
;Macro:		PT_WaitDMA()
;Purpose:	Waits for the DMA-delay to expire before continuing.
;------------------------------------------------------------------------------
PT_WaitDMA	macro
		move.w	$dff006,d1
		moveq.l	#PT__DMADelay-1,d0
.dmaloop1	cmp.w	$dff006,d1
		beq.s	.dmaloop1
		move.w	$dff006,d1
.dmaloop2	cmp.w	$dff006,d1
		beq.s	.dmaloop2
		move.w	$dff006,d1
   		dbf	d0,.dmaloop1
		endm

;------------------------------------------------------------------------------
;Function:	PT_Init(Module)(A0)
;Purpose:	Initializes the play-routine and restarts the module if it's
;		already playing. The module may have up to 256 patterns.
;------------------------------------------------------------------------------
PT_Init		move.b	#0,PT_speed
		move.l	a0,PT_songdataptr
		move.l	a0,a1
		lea.l	952(a1),a1
		moveq.l	#128-1,d0
		moveq.l	#0,d1
PT_loop		move.l	d1,d2
		subq.w	#1,d0
PT_loop2	move.b	(a1)+,d1
		cmp.b	d2,d1
		bgt.s	PT_loop
		dbra	d0,PT_loop2
		addq.b	#1,d2

		lea.l	PT_samplestarts(pc),a1
		lsl.l	#8,d2
		lsl.l	#2,d2
		add.l	#1084,d2
		add.l	a0,d2
		move.l	d2,a2
		moveq.l	#31-1,d0
PT_loop3	cmp.l	a2,a3
		blt.s	.range

		clr.l	(a2)
.range		move.l	a2,(a1)+
		moveq.l	#0,d1
		move.w	42(a0),d1		; sample length
		beq.b	.nosample
		lsl.l	#1,d1
		add.l	d1,a2
.nosample	add.l	#30,a0
		dbra	d0,PT_loop3

	ifne PT__NTComp
		move.b	951(a0),d1
		cmp.b	#$7f,d1
		beq.s	.NotNT		; ProTracker module
		cmp.b	#$78,d1
		beq.w	.NotNT		; Old 15-instrument converted

.WasNT		move.l	#6,PT_vibshift
		move.b	d1,PT_looppos
		bra.s	.GotFormat

.NotNT		move.l	#7,PT_vibshift
		clr.b	PT_looppos
.GotFormat
	endc

	ifne PT__CIA
		moveq.l	#125,d0
		bsr.s	PT_SetIntRate
	endc

		or.b	#2,$bfe001
		clr.b	PT_counter
		clr.b	PT_songpos
		clr.w	PT_patternpos
		bsr.s	PT_KillSound
		move.b	#6,PT_speed
		rts

;------------------------------------------------------------------------------
;Function:	PT_End()
;Purpose:	Stops the music.
;------------------------------------------------------------------------------
PT_End		move.b	#0,PT_speed

PT_KillSound	lea.l	$dff000,a0
		clr.w	$a8(a0)
		clr.w	$b8(a0)
		clr.w	$c8(a0)
		clr.w	$d8(a0)
		move.w	#$f,$dff096
		rts

;------------------------------------------------------------------------------
;Function:	PT_SetMasterVol(Left,Right)(D0,D1)
;Purpose:	Sets left and right mastervolume.
;------------------------------------------------------------------------------
	ifne	PT_MasterVol
PT_SetMasterVol
		move.w	d0,PT_lmastervol
		move.w	d1,PT_rmastervol
		lea.l	$dff000,a5

		moveq.l	#0,d0
		lea.l	PT_chan1temp(pc),a0
		move.b	n_volume(a0),d0
		mulu.w	PT_lmastervol,d0
		lsr.w	#6,d0
		move.b	d0,$a8(a5)

		moveq.l	#0,d0
		lea.l	PT_chan2temp(pc),a0
		move.b	n_volume(a0),d0
		mulu.w	PT_rmastervol,d0
		lsr.w	#6,d0
		move.b	d0,$b8(a5)

		moveq.l	#0,d0
		lea.l	PT_chan3temp(pc),a0
		move.b	n_volume(a0),d0
		mulu.w	PT_rmastervol,d0
		lsr.w	#6,d0
		move.b	d0,$c8(a5)

		moveq.l	#0,d0
		lea.l	PT_chan4temp(pc),a0
		move.b	n_volume(a0),d0
		mulu.w	PT_lmastervol,d0
		lsr.w	#6,d0
		move.b	d0,$d8(a5)

		moveq.l	#0,d0
		rts
	endc

PT_calcvol	macro

	ifne	PT_MasterVol

		move.l	a5,d5
		subq.l	#1,d5
		and.w	#32,d5
		beq.s	.left

.right		mulu.w	PT_rmastervol(pc),d0
		bra.s	.volend

.left		mulu.w	PT_lmastervol(pc),d0
.volend		lsr.w	#6,d0
	endc

	endm

;------------------------------------------------------------------------------
;Function:	PT_Rewind()
;Purpose:	Jumps one pattern backwards.
;------------------------------------------------------------------------------
	ifne	PT__WindFuncs
PT_Rewind	bsr.w	PT_KillSound

		move.b	PT_songpos,d0
		tst.b	d0
		bne.s	.not0

		move.l	PT_songdataptr,a0	; get moduleptr.
		move.b	950(a0),PT_songpos
		sub.b	#2,PT_songpos
	ifne PT__OverStep
		move.w	#-1,PT_OverStep
	endc
		bra.s	.ok

.not0		cmp.b	#1,d0
		bne.s	.not1

		move.l	PT_songdataptr,a0	; get moduleptr.
		move.b	950(a0),PT_songpos
		sub.b	#1,PT_songpos
		bra.s	.ok

.not1
		sub.b	#2,PT_songpos
.ok		move.w	#63*16,PT_patternpos
		move.b	PT_speed(pc),PT_counter

		moveq.l	#0,d0
		rts

;------------------------------------------------------------------------------
;Function:	PT_Forward()
;Purpose:	Jumps one pattern forwards.
;------------------------------------------------------------------------------
PT_Forward	bsr.w	PT_KillSound
		move.w	#63*16,PT_patternpos
		move.b	PT_speed(pc),PT_counter

		moveq.l	#0,d0
		rts
	endc

;------------------------------------------------------------------------------
;Function:	PT_Music()
;Purpose:	Updates the music. Call with steady intervals, preferably 50
;		times a second.
;------------------------------------------------------------------------------
PT_Music	addq.b	#1,PT_counter
		move.b	PT_counter(pc),d0
		move.b	PT_speed(pc),d1
		beq.w	PT_return
		cmp.b	d1,d0
		blo.s	PT_nonewnote
		clr.b	PT_counter
		tst.b	PT_pattdeltime2
		beq.w	PT_getnewnote
		bsr.s	PT_nonewallchannels
		bra.w	PT_dskip

PT_nonewnote	bsr.s	PT_nonewallchannels
		bra.w	PT_nonewposyet

PT_nonewallchannels
		lea.l	$dff0a0,a5
		lea.l	PT_chan1temp(pc),a6
		bsr.w	PT_checkefx
		lea.l	$dff0b0,a5
		lea.l	PT_chan2temp(pc),a6
		bsr.w	PT_checkefx
		lea.l	$dff0c0,a5
		lea.l	PT_chan3temp(pc),a6
		bsr.w	PT_checkefx
		lea.l	$dff0d0,a5
		lea.l	PT_chan4temp(pc),a6
		bra.w	PT_checkefx

PT_getnewnote	move.l	PT_songdataptr(pc),a0
		lea.l	12(a0),a3
		lea.l	952(a0),a2		;pattpo
		lea.l	1084(a0),a0		;patterndata
		moveq.l	#0,d0
		moveq.l	#0,d1
		move.b	PT_songpos(pc),d0
		move.b	(a2,d0.w),d1
		lsl.l	#8,d1
		lsl.l	#2,d1
		add.w	PT_patternpos(pc),d1
		clr.w	PT_dmacontemp

		lea.l	$dff0a0,a5
		lea.l	PT_chan1temp(pc),a6
		bsr.s	PT_playvoice

		lea.l	$dff0b0,a5
		lea.l	PT_chan2temp(pc),a6
		bsr.s	PT_playvoice

		lea.l	$dff0c0,a5
		lea.l	PT_chan3temp(pc),a6
		bsr.s	PT_playvoice

		lea.l	$dff0d0,a5
		lea.l	PT_chan4temp(pc),a6
		bsr.s	PT_playvoice

		bra.w	PT_setdma

PT_playvoice	tst.w	(a6)
		bne.s	PT_plvskip
		bsr.w	PT_pernop

PT_plvskip	move.l	(a0,d1.l),(a6)
		addq.l	#4,d1
		moveq.l	#0,d2
		move.b	n_cmd(a6),d2
		and.b	#$f0,d2
		lsr.b	#4,d2
		move.b	(a6),d0
		and.b	#$f0,d0
		or.b	d0,d2
		tst.b	d2
		beq.w	PT_setregs
		moveq.l	#0,d3
		lea.l	PT_samplestarts(pc),a1
		move.w	d2,d4
		subq.l	#1,d2

		mulu.w	#30,d4

	ifeq	PT__68020
		lsl.l	#2,d2
		move.l	(a1,d2.l),n_start(a6)
	else
		move.l	(a1,d2.l*4),n_start(a6)
	endc

		move.w	(a3,d4.l),n_length(a6)
		move.w	(a3,d4.l),n_reallength(a6)
		move.b	2(a3,d4.l),n_finetune(a6)
		move.b	3(a3,d4.l),n_volume(a6)
		move.w	4(a3,d4.l),d3		; get repeat
		tst.w	d3
		beq.s	PT_noloop
		move.l	n_start(a6),d2		; get start
		add.w	d3,d3
		add.l	d3,d2			; add repeat
		move.l	d2,n_loopstart(a6)
		move.l	d2,n_wavestart(a6)
		move.w	4(a3,d4.l),d0		; get repeat
		add.w	6(a3,d4.l),d0		; add replen
		move.w	d0,n_length(a6)
		move.w	6(a3,d4.l),n_replen(a6)	; save replen
		moveq.l	#0,d0
		move.b	n_volume(a6),d0

		PT_calcvol

		move.w	d0,8(a5)		; set volume
		bra.s	PT_setregs

PT_noloop	move.l	n_start(a6),d2
		add.l	d3,d2
		move.l	d2,n_loopstart(a6)
		move.l	d2,n_wavestart(a6)
		move.w	6(a3,d4.l),n_replen(a6)	; save replen
		moveq.l	#0,d0
		move.b	n_volume(a6),d0

		PT_calcvol

		move.w	d0,8(a5)		; set volume

PT_setregs	move.w	(a6),d0
		and.w	#$0fff,d0
		beq.w	PT_checkmoreefx		; if no note
		move.w	2(a6),d0
		and.w	#$0ff0,d0
		cmp.w	#$0e50,d0
		beq.s	PT_dosetfinetune
		move.b	2(a6),d0
		and.b	#$0f,d0
		cmp.b	#3,d0			; toneportamento
		beq.s	PT_chktoneporta
		cmp.b	#5,d0
		beq.s	PT_chktoneporta
		cmp.b	#9,d0			; sample offset
		bne.s	PT_setperiod
		bsr.w	PT_checkmoreefx
		bra.s	PT_setperiod

PT_dosetfinetune
		bsr.w	PT_setfinetune
		bra.s	PT_setperiod

PT_chktoneporta
		bsr.w	PT_settoneporta
		bra.w	PT_checkmoreefx

PT_setperiod	move.w	(a6),d7
		and.w	#$0fff,d7
		lea.l	PT_periodtable(pc),a4
		moveq.l	#0,d0
		moveq.l	#36,d2
PT_ftuloop	cmp.w	(a4,d0.w),d7
		bhs.s	PT_ftufound
		addq.l	#2,d0
		dbra	d2,PT_ftuloop
PT_ftufound	moveq.l	#0,d7
		move.b	n_finetune(a6),d7
		mulu.w	#36*2,d7
		add.l	d7,a4
		move.w	(a4,d0.w),n_period(a6)

		move.w	2(a6),d0
		and.w	#$0ff0,d0
		cmp.w	#$0ed0,d0		; notedelay
		beq.w	PT_checkmoreefx

		move.w	n_dmabit(a6),$dff096
		btst	#2,n_wavecontrol(a6)
		bne.s	PT_vibnoc
		clr.b	n_vibratopos(a6)

PT_vibnoc	btst	#6,n_wavecontrol(a6)
		bne.s	PT_trenoc
		clr.b	n_tremolopos(a6)

PT_trenoc	move.l	n_start(a6),(a5)	; set start
		move.w	n_length(a6),4(a5)	; set length
		move.w	n_period(a6),d0
		move.w	d0,6(a5)		; set period
		move.w	n_dmabit(a6),d0
		or.w	d0,PT_dmacontemp
		bra.w	PT_checkmoreefx

PT_setdma	lea.l	$bfe000,a5

		PT_WaitDMA

PT_dummy001	move.w	PT_dmacontemp(pc),d0
		or.w	#$8000,d0
		move.w	d0,$dff096

		PT_WaitDMA

		lea.l	$dff000,a5
		lea.l	PT_chan4temp(pc),a6
		move.l	n_loopstart(a6),$d0(a5)
		move.w	n_replen(a6),$d4(a5)
		lea.l	PT_chan3temp(pc),a6
		move.l	n_loopstart(a6),$c0(a5)
		move.w	n_replen(a6),$c4(a5)
		lea.l	PT_chan2temp(pc),a6
		move.l	n_loopstart(a6),$b0(a5)
		move.w	n_replen(a6),$b4(a5)
		lea.l	PT_chan1temp(pc),a6
		move.l	n_loopstart(a6),$a0(a5)
		move.w	n_replen(a6),$a4(a5)

PT_dskip	add.w	#16,PT_patternpos
		move.b	PT_pattdeltime(pc),d0
		beq.s	PT_dskc
		move.b	d0,PT_pattdeltime2
		clr.b	PT_pattdeltime

PT_dskc		tst.b	PT_pattdeltime2
		beq.s	PT_dska
		subq.b	#1,PT_pattdeltime2
		beq.s	PT_dska
		sub.w	#16,PT_patternpos

PT_dska		tst.b	PT_pbreakflag
		beq.s	PT_nnpysk
		sf	PT_pbreakflag
		moveq.l	#0,d0
		move.b	PT_pbreakpos(pc),d0
		clr.b	PT_pbreakpos

		lsl.w	#4,d0
		move.w	d0,PT_patternpos

PT_nnpysk	cmp.w	#1024,PT_patternpos
		blo.s	PT_nonewposyet

PT_nextposition
		moveq.l	#0,d0
		move.b	PT_pbreakpos(pc),d0
		move.w	d0,PT_patternpos
		clr.b	PT_pbreakpos
		clr.b	PT_posjumpflag
		addq.b	#1,PT_songpos
		and.b	#$7f,PT_songpos
		move.b	PT_songpos(pc),d1

		move.l	PT_songdataptr(pc),a0
		cmp.b	950(a0),d1
		blo.s	PT_nonewposyet
	ifne PT__OverStep
		move.w	#1,PT_OverStep
	endc
	ifne PT__NTComp
		move.b	PT_looppos,PT_songpos
	else
		clr.b	PT_songpos
	endc

PT_nonewposyet
		tst.b	PT_posjumpflag
		bne.s	PT_nextposition
PT_return	rts

PT_checkefx	bsr.w	PT_updatefunk
		move.w	n_cmd(a6),d0
		and.w	#$0fff,d0
		beq.s	PT_pernop
		move.b	n_cmd(a6),d0
		and.b	#$0f,d0
		beq.s	PT_arpeggio
		cmp.b	#1,d0
		beq.w	PT_portaup
		cmp.b	#2,d0
		beq.w	PT_portadown
		cmp.b	#3,d0
		beq.w	PT_toneportamento
		cmp.b	#4,d0
		beq.w	PT_vibrato
		cmp.b	#5,d0
		beq.w	PT_toneplusvolslide
		cmp.b	#6,d0
		beq.w	PT_vibratoplusvolslide
		cmp.b	#$e,d0
		beq.w	PT_e_commands
setback		move.w	n_period(a6),6(a5)
		cmp.b	#7,d0
		beq.w	PT_tremolo
		cmp.b	#$a,d0
		beq.w	PT_volumeslide
		rts

PT_pernop	move.w	n_period(a6),6(a5)
		rts

PT_arpeggio	moveq.l	#0,d0
		move.b	PT_counter(pc),d0
		divs.w	#3,d0
		swap.w	d0
		tst.w	d0
		beq.s	PT_arpeggio2
		cmp.w	#2,d0
		beq.s	PT_arpeggio1
		moveq.l	#0,d0
		move.b	n_cmdlo(a6),d0
		lsr.b	#4,d0
		bra.s	PT_arpeggio3

PT_arpeggio1	moveq.l	#0,d0
		move.b	n_cmdlo(a6),d0
		and.b	#15,d0
		bra.s	PT_arpeggio3

PT_arpeggio2	move.w	n_period(a6),d2
		bra.s	PT_arpeggio4

PT_arpeggio3	add.w	d0,d0
		moveq.l	#0,d1
		move.b	n_finetune(a6),d1
		mulu.w	#36*2,d1
		lea.l	PT_periodtable(pc),a0
		add.l	d1,a0
		moveq.l	#0,d1
		move.w	n_period(a6),d1
		moveq.l	#36,d3

PT_arploop	move.w	(a0,d0.w),d2
		cmp.w	(a0),d1
		bhs.s	PT_arpeggio4
		addq.l	#2,a0
		dbra	d3,PT_arploop
		rts

PT_arpeggio4	move.w	d2,6(a5)
		rts

PT_fineportaup	tst.b	PT_counter
		bne.w	PT_return
		move.b	#$0f,PT_lowmask

PT_portaup	moveq.l	#0,d0
		move.b	n_cmdlo(a6),d0
		and.b	PT_lowmask(pc),d0
		move.b	#$ff,PT_lowmask
		sub.w	d0,n_period(a6)
		move.w	n_period(a6),d0
		and.w	#$0fff,d0
		cmp.w	#113,d0
		bpl.s	PT_portauskip
		and.w	#$f000,n_period(a6)
		or.w	#113,n_period(a6)

PT_portauskip	move.w	n_period(a6),d0
		and.w	#$0fff,d0
		move.w	d0,6(a5)
		rts

PT_fineportadown
		tst.b	PT_counter
		bne.w	PT_return
		move.b	#$0f,PT_lowmask

PT_portadown	clr.w	d0
		move.b	n_cmdlo(a6),d0
		and.b	PT_lowmask(pc),d0
		move.b	#$ff,PT_lowmask
		add.w	d0,n_period(a6)
		move.w	n_period(a6),d0
		and.w	#$0fff,d0
		cmp.w	#856,d0
		bmi.s	PT_portadskip
		and.w	#$f000,n_period(a6)
		or.w	#856,n_period(a6)

PT_portadskip	move.w	n_period(a6),d0
		and.w	#$0fff,d0
		move.w	d0,6(a5)
		rts

PT_settoneporta
		move.l	a0,-(sp)
		move.w	(a6),d2
		and.w	#$0fff,d2
		moveq.l	#0,d0
		move.b	n_finetune(a6),d0
		mulu.w	#37*2,d0
		lea.l	PT_periodtable(pc),a0
		add.l	d0,a0
		moveq.l	#0,d0

PT_stploop	cmp.w	(a0,d0.w),d2
		bhs.s	PT_stpfound
		addq.w	#2,d0
		cmp.w	#37*2,d0
		blo.s	PT_stploop
		moveq.l	#35*2,d0

PT_stpfound	move.b	n_finetune(a6),d2
		and.b	#8,d2
		beq.s	PT_stpgoss
		tst.w	d0
		beq.s	PT_stpgoss
		subq.w	#2,d0

PT_stpgoss	move.w	(a0,d0.w),d2
		move.l	(sp)+,a0
		move.w	d2,n_wantedperiod(a6)
		move.w	n_period(a6),d0
		clr.b	n_toneportdirec(a6)
		cmp.w	d0,d2
		beq.s	PT_cleartoneporta
		bge.w	PT_return
		move.b	#1,n_toneportdirec(a6)
		rts

PT_cleartoneporta
		clr.w	n_wantedperiod(a6)
		rts

PT_toneportamento
		move.b	n_cmdlo(a6),d0
		beq.s	PT_toneportnochange
		move.b	d0,n_toneportspeed(a6)
		clr.b	n_cmdlo(a6)

PT_toneportnochange
		tst.w	n_wantedperiod(a6)
		beq.w	PT_return
		moveq.l	#0,d0
		move.b	n_toneportspeed(a6),d0
		tst.b	n_toneportdirec(a6)
		bne.s	PT_toneportaup

PT_toneportadown
		add.w	d0,n_period(a6)
		move.w	n_wantedperiod(a6),d0
		cmp.w	n_period(a6),d0
		bgt.s	PT_toneportasetper
		move.w	n_wantedperiod(a6),n_period(a6)
		clr.w	n_wantedperiod(a6)
		bra.s	PT_toneportasetper

PT_toneportaup
		sub.w	d0,n_period(a6)
		move.w	n_wantedperiod(a6),d0
		cmp.w	n_period(a6),d0
		blt.s	PT_toneportasetper
		move.w	n_wantedperiod(a6),n_period(a6)
		clr.w	n_wantedperiod(a6)

PT_toneportasetper
		move.w	n_period(a6),d2
		move.b	n_glissfunk(a6),d0
		and.b	#$0f,d0
		beq.s	PT_glissskip
		moveq.l	#0,d0
		move.b	n_finetune(a6),d0
		mulu.w	#36*2,d0
		lea.l	PT_periodtable(pc),a0
		add.l	d0,a0
		moveq.l	#0,d0

PT_glissloop	cmp.w	(a0,d0.w),d2
		bhs.s	PT_glissfound
		addq.w	#2,d0
		cmp.w	#36*2,d0
		blo.s	PT_glissloop
		moveq.l	#35*2,d0

PT_glissfound	move.w	(a0,d0.w),d2

PT_glissskip	move.w	d2,6(a5)		; set period
		rts

PT_vibrato	move.b	n_cmdlo(a6),d0
		beq.s	PT_vibrato2
		move.b	n_vibratocmd(a6),d2
		and.b	#$0f,d0
		beq.s	PT_vibskip
		and.b	#$f0,d2
		or.b	d0,d2

PT_vibskip	move.b	n_cmdlo(a6),d0
		and.b	#$f0,d0
		beq.s	PT_vibskip2
		and.b	#$0f,d2
		or.b	d0,d2

PT_vibskip2	move.b	d2,n_vibratocmd(a6)

PT_vibrato2	move.b	n_vibratopos(a6),d0
		lea.l	PT_vibratotable(pc),a4
		lsr.w	#2,d0
		and.w	#$001f,d0
		moveq.l	#0,d2
		move.b	n_wavecontrol(a6),d2
		and.b	#$03,d2
		beq.s	PT_vib_sine
		lsl.b	#3,d0
		cmp.b	#1,d2
		beq.s	PT_vib_rampdown
		move.b	#255,d2
		bra.s	PT_vib_set

PT_vib_rampdown
		tst.b	n_vibratopos(a6)
		bpl.s	PT_vib_rampdown2
		move.b	#255,d2
		sub.b	d0,d2
		bra.s	PT_vib_set

PT_vib_rampdown2
		move.b	d0,d2
		bra.s	PT_vib_set

PT_vib_sine	move.b	(a4,d0.w),d2

PT_vib_set	move.b	n_vibratocmd(a6),d0
		and.w	#15,d0
		mulu.w	d0,d2
	ifne PT__NTComp
		move.l	PT_vibshift,d0
		lsr.w	d0,d2
	else
		lsr.w	#7,d2
	endc
		move.w	n_period(a6),d0
		tst.b	n_vibratopos(a6)
		bmi.s	PT_vibratoneg
		add.w	d2,d0
		bra.s	PT_vibrato3

PT_vibratoneg	sub.w	d2,d0

PT_vibrato3	move.w	d0,6(a5)
		move.b	n_vibratocmd(a6),d0
		lsr.w	#2,d0
		and.w	#$003c,d0
		add.b	d0,n_vibratopos(a6)
		rts

PT_toneplusvolslide
		bsr.w	PT_toneportnochange
		bra.w	PT_volumeslide

PT_vibratoplusvolslide
		bsr.s	PT_vibrato2
		bra.w	PT_volumeslide

PT_tremolo	move.b	n_cmdlo(a6),d0
		beq.s	PT_tremolo2
		move.b	n_tremolocmd(a6),d2
		and.b	#$0f,d0
		beq.s	PT_treskip
		and.b	#$f0,d2
		or.b	d0,d2

PT_treskip	move.b	n_cmdlo(a6),d0
		and.b	#$f0,d0
		beq.s	PT_treskip2
		and.b	#$0f,d2
		or.b	d0,d2

PT_treskip2	move.b	d2,n_tremolocmd(a6)

PT_tremolo2	move.b	n_tremolopos(a6),d0
		lea.l	PT_vibratotable(pc),a4
		lsr.w	#2,d0
		and.w	#$001f,d0
		moveq.l	#0,d2
		move.b	n_wavecontrol(a6),d2
		lsr.b	#4,d2
		and.b	#$03,d2
		beq.s	PT_tre_sine
		lsl.b	#3,d0
		cmp.b	#1,d2
		beq.s	PT_tre_rampdown
		move.b	#255,d2
		bra.s	PT_tre_set

PT_tre_rampdown
		tst.b	n_vibratopos(a6)
		bpl.s	PT_tre_rampdown2
		move.b	#255,d2
		sub.b	d0,d2
		bra.s	PT_tre_set

PT_tre_rampdown2
		move.b	d0,d2
		bra.s	PT_tre_set

PT_tre_sine	move.b	(a4,d0.w),d2

PT_tre_set	move.b	n_tremolocmd(a6),d0
		and.w	#15,d0
		mulu.w	d0,d2
		lsr.w	#6,d2
		moveq.l	#0,d0
		move.b	n_volume(a6),d0
		tst.b	n_tremolopos(a6)
		bmi.s	PT_tremoloneg
		add.w	d2,d0
		bra.s	PT_tremolo3

PT_tremoloneg	sub.w	d2,d0

PT_tremolo3	bpl.s	PT_tremoloskip
		clr.w	d0

PT_tremoloskip
		cmp.w	#$40,d0
		bls.s	PT_tremolook
		move.w	#$40,d0

PT_tremolook	PT_calcvol

		move.w	d0,8(a5)
		move.b	n_tremolocmd(a6),d0
		lsr.w	#2,d0
		and.w	#$003c,d0
		add.b	d0,n_tremolopos(a6)
		rts

PT_sampleoffset
		moveq.l	#0,d0
		move.b	n_cmdlo(a6),d0
		beq.s	PT_sononew
		move.b	d0,n_sampleoffset(a6)

PT_sononew	move.b	n_sampleoffset(a6),d0
		lsl.w	#7,d0
		cmp.w	n_length(a6),d0
		bge.s	PT_sofskip
		sub.w	d0,n_length(a6)
		add.w	d0,d0
		add.l	d0,n_start(a6)
		rts

PT_sofskip	move.w	#$0001,n_length(a6)
		rts

PT_volumeslide
		moveq.l	#0,d0
		move.b	n_cmdlo(a6),d0
		lsr.b	#4,d0
		tst.b	d0
		beq.s	PT_volslidedown

PT_volslideup	add.b	d0,n_volume(a6)
		cmp.b	#$40,n_volume(a6)
		bmi.s	PT_vsuskip
		move.b	#$40,n_volume(a6)

PT_vsuskip	move.b	n_volume(a6),d0

		PT_calcvol

		move.w	d0,8(a5)
		rts

PT_volslidedown
		moveq.l	#0,d0
		move.b	n_cmdlo(a6),d0
		and.b	#$0f,d0

PT_volslidedown2
		sub.b	d0,n_volume(a6)
		bpl.s	PT_vsdskip
		clr.b	n_volume(a6)

PT_vsdskip	moveq.l	#0,d0
		move.b	n_volume(a6),d0

		PT_calcvol

		move.w	d0,8(a5)
		rts

PT_positionjump
		move.b	n_cmdlo(a6),d0
		subq.b	#1,d0
		move.b	d0,PT_songpos
	ifne PT__OverStep
		move.w	#1,PT_OverStep
	endc
PT_pj2		clr.b	PT_pbreakpos
		st	PT_posjumpflag
		rts

PT_volumechange
		moveq.l	#0,d0
		move.b	n_cmdlo(a6),d0
		cmp.b	#$40,d0
		bls.s	PT_volumeok
		moveq.l	#$40,d0

PT_volumeok	move.b	d0,n_volume(a6)

		PT_calcvol

		move.w	d0,8(a5)
		rts

PT_patternbreak
		moveq.l	#0,d0
		move.b	n_cmdlo(a6),d0
		move.l	d0,d2
		lsr.b	#4,d0
		mulu.w	#10,d0
		and.b	#$0f,d2
		add.b	d2,d0
		cmp.b	#63,d0
		bhi.s	PT_pj2
		move.b	d0,PT_pbreakpos
		st	PT_posjumpflag
		rts

PT_setspeed
		moveq.l	#0,d0
		move.b	3(a6),d0

	ifne PT__OverStep
		bne.s	.wasnotzero
		move.w	#1,PT_OverStep
.wasnotzero
	endc

	ifne	PT__CIA
		cmp.b	#32,d0
		blo.s	.setspeed

.settempo	bsr.w	PT_SetIntRate
		rts

	endc

.setspeed	clr.b	PT_counter
		move.b	d0,PT_speed
		rts

PT_checkmoreefx
		bsr.w	PT_updatefunk
		move.b	2(a6),d0
		and.b	#$0f,d0
		cmp.b	#$9,d0
		beq.w	PT_sampleoffset
		cmp.b	#$b,d0
		beq.w	PT_positionjump
		cmp.b	#$d,d0
		beq.w	PT_patternbreak
		cmp.b	#$e,d0
		beq.s	PT_e_commands
		cmp.b	#$f,d0
		beq.w	PT_setspeed
		cmp.b	#$c,d0
		beq.w	PT_volumechange
		bra.w	PT_pernop

PT_e_commands	move.b	n_cmdlo(a6),d0
		and.b	#$f0,d0
		lsr.b	#4,d0
		beq.s	PT_filteronoff
		cmp.b	#1,d0
		beq.w	PT_fineportaup
		cmp.b	#2,d0
		beq.w	PT_fineportadown
		cmp.b	#3,d0
		beq.s	PT_setglisscontrol
		cmp.b	#4,d0
		beq.w	PT_setvibratocontrol
		cmp.b	#5,d0
		beq.w	PT_setfinetune
		cmp.b	#6,d0
		beq.w	PT_jumploop
		cmp.b	#7,d0
		beq.w	PT_settremolocontrol
		cmp.b	#9,d0
		beq.w	PT_retrignote
		cmp.b	#$a,d0
		beq.w	PT_volumefineup
		cmp.b	#$b,d0
		beq.w	PT_volumefinedown
		cmp.b	#$c,d0
		beq.w	PT_notecut
		cmp.b	#$d,d0
		beq.w	PT_notedelay
		cmp.b	#$e,d0
		beq.w	PT_patterndelay
		cmp.b	#$f,d0
		beq.w	PT_funkit
		rts

PT_filteronoff
		move.b	n_cmdlo(a6),d0
		and.b	#1,d0
		add.b	d0,d0
		and.b	#$fd,$bfe001
		or.b	d0,$bfe001
		rts	

PT_setglisscontrol
		move.b	n_cmdlo(a6),d0
		and.b	#$0f,d0
		and.b	#$f0,n_glissfunk(a6)
		or.b	d0,n_glissfunk(a6)
		rts

PT_setvibratocontrol
		move.b	n_cmdlo(a6),d0
		and.b	#$0f,d0
		and.b	#$f0,n_wavecontrol(a6)
		or.b	d0,n_wavecontrol(a6)
		rts

PT_setfinetune
		move.b	n_cmdlo(a6),d0
		and.b	#$0f,d0
		move.b	d0,n_finetune(a6)
		rts

PT_jumploop	tst.b	PT_counter
		bne.w	PT_return
		move.b	n_cmdlo(a6),d0
		and.b	#$0f,d0
		beq.s	PT_setloop
		tst.b	n_loopcount(a6)
		beq.s	PT_jumpcnt
		subq.b	#1,n_loopcount(a6)
		beq.w	PT_return

PT_jmploop	move.b	n_pattpos(a6),PT_pbreakpos
		st	PT_pbreakflag
		rts

PT_jumpcnt	move.b	d0,n_loopcount(a6)
		bra.s	PT_jmploop

PT_setloop	move.w	PT_patternpos(pc),d0
		lsr.w	#4,d0
		move.b	d0,n_pattpos(a6)
		rts

PT_settremolocontrol
		move.b	n_cmdlo(a6),d0
		and.b	#$0f,d0
		lsl.b	#4,d0
		and.b	#$0f,n_wavecontrol(a6)
		or.b	d0,n_wavecontrol(a6)
		rts

PT_retrignote	move.l	d1,-(sp)
		moveq.l	#0,d0
		move.b	n_cmdlo(a6),d0
		and.b	#$0f,d0
		beq.s	PT_rtnend
		moveq.l	#0,d1
		move.b	PT_counter(pc),d1
		bne.s	PT_rtnskp
		move.w	(a6),d1
		and.w	#$0fff,d1
		bne.s	PT_rtnend
		moveq.l	#0,d1
		move.b	PT_counter(pc),d1

PT_rtnskp	divu.w	d0,d1
		swap.w	d1
		tst.w	d1
		bne.s	PT_rtnend

PT_doretrig	move.w	n_dmabit(a6),$dff096	; channel dma off
		move.l	n_start(a6),(a5)	; set sampledata pointer
		move.w	n_length(a6),4(a5)	; set length
		move.w	#300,d0

PT_rtnloop1	dbra	d0,PT_rtnloop1
		move.w	n_dmabit(a6),d0
		bset	#15,d0
		move.w	d0,$dff096
		move.w	#300,d0

PT_rtnloop2	dbra	d0,PT_rtnloop2
		move.l	n_loopstart(a6),(a5)
		move.l	n_replen(a6),4(a5)

PT_rtnend	move.l	(sp)+,d1
		rts

PT_volumefineup
		tst.b	PT_counter
		bne.w	PT_return
		moveq.l	#0,d0
		move.b	n_cmdlo(a6),d0
		and.b	#$f,d0
		bra.w	PT_volslideup

PT_volumefinedown
		tst.b	PT_counter
		bne.w	PT_return
		moveq.l	#0,d0
		move.b	n_cmdlo(a6),d0
		and.b	#$0f,d0
		bra.w	PT_volslidedown2

PT_notecut	moveq.l	#0,d0
		move.b	n_cmdlo(a6),d0
		and.b	#$0f,d0
		cmp.b	PT_counter(pc),d0
		bne.w	PT_return
		clr.b	n_volume(a6)
		move.w	#0,8(a5)
		rts

PT_notedelay	moveq.l	#0,d0
		move.b	n_cmdlo(a6),d0
		and.b	#$0f,d0
		cmp.b	PT_counter(pc),d0
		bne.w	PT_return
		move.w	(a6),d0
		beq.w	PT_return
		move.l	d1,-(sp)
		bra.w	PT_doretrig

PT_patterndelay
		tst.b	PT_counter
		bne.w	PT_return
		moveq.l	#0,d0
		move.b	n_cmdlo(a6),d0
		and.b	#$0f,d0
		tst.b	PT_pattdeltime2
		bne.w	PT_return
		addq.b	#1,d0
		move.b	d0,PT_pattdeltime
		rts

PT_funkit	tst.b	PT_counter
		bne.w	PT_return
		move.b	n_cmdlo(a6),d0
		and.b	#$0f,d0
		lsl.b	#4,d0
		and.b	#$0f,n_glissfunk(a6)
		or.b	d0,n_glissfunk(a6)
		tst.b	d0
		beq.w	PT_return

PT_updatefunk	movem.l	a0/d1,-(sp)
		moveq.l	#0,d0
		move.b	n_glissfunk(a6),d0
		lsr.b	#4,d0
		beq.s	PT_funkend
		lea.l	PT_funktable(pc),a0
		move.b	(a0,d0.w),d0
		add.b	d0,n_funkoffset(a6)
		btst	#7,n_funkoffset(a6)
		beq.s	PT_funkend
		clr.b	n_funkoffset(a6)

		move.l	n_loopstart(a6),d0
		moveq.l	#0,d1
		move.w	n_replen(a6),d1
		add.l	d1,d0
		add.l	d1,d0
		move.l	n_wavestart(a6),a0
		addq.l	#1,a0
		cmp.l	d0,a0
		blo.s	PT_funkok
		move.l	n_loopstart(a6),a0

PT_funkok	move.l	a0,n_wavestart(a6)
		moveq.l	#-1,d0
		sub.b	(a0),d0
		move.b	d0,(a0)

PT_funkend	movem.l	(sp)+,a0/d1
		rts

PT_funktable	dc.b	0,5,6,7,8,10,11,13,16,19,22,26,32,43,64,128

PT_vibratotable	
		dc.b	000,024,049,074,097,120,141,161
		dc.b	180,197,212,224,235,244,250,253
		dc.b	255,253,250,244,235,224,212,197
		dc.b	180,161,141,120,097,074,049,024

PT_periodtable
; tuning 0, normal
		dc.w	856,808,762,720,678,640,604,570,538,508,480,453
		dc.w	428,404,381,360,339,320,302,285,269,254,240,226
		dc.w	214,202,190,180,170,160,151,143,135,127,120,113
; tuning 1
		dc.w	850,802,757,715,674,637,601,567,535,505,477,450
		dc.w	425,401,379,357,337,318,300,284,268,253,239,225
		dc.w	213,201,189,179,169,159,150,142,134,126,119,113
; tuning 2
		dc.w	844,796,752,709,670,632,597,563,532,502,474,447
		dc.w	422,398,376,355,335,316,298,282,266,251,237,224
		dc.w	211,199,188,177,167,158,149,141,133,125,118,112
; tuning 3
		dc.w	838,791,746,704,665,628,592,559,528,498,470,444
		dc.w	419,395,373,352,332,314,296,280,264,249,235,222
		dc.w	209,198,187,176,166,157,148,140,132,125,118,111
; tuning 4
		dc.w	832,785,741,699,660,623,588,555,524,495,467,441
		dc.w	416,392,370,350,330,312,294,278,262,247,233,220
		dc.w	208,196,185,175,165,156,147,139,131,124,117,110
; tuning 5
		dc.w	826,779,736,694,655,619,584,551,520,491,463,437
		dc.w	413,390,368,347,328,309,292,276,260,245,232,219
		dc.w	206,195,184,174,164,155,146,138,130,123,116,109
; tuning 6
		dc.w	820,774,730,689,651,614,580,547,516,487,460,434
		dc.w	410,387,365,345,325,307,290,274,258,244,230,217
		dc.w	205,193,183,172,163,154,145,137,129,122,115,109
; tuning 7
		dc.w	814,768,725,684,646,610,575,543,513,484,457,431
		dc.w	407,384,363,342,323,305,288,272,256,242,228,216
		dc.w	204,192,181,171,161,152,144,136,128,121,114,108
; tuning -8
		dc.w	907,856,808,762,720,678,640,604,570,538,508,480
		dc.w	453,428,404,381,360,339,320,302,285,269,254,240
		dc.w	226,214,202,190,180,170,160,151,143,135,127,120
; tuning -7
		dc.w	900,850,802,757,715,675,636,601,567,535,505,477
		dc.w	450,425,401,379,357,337,318,300,284,268,253,238
		dc.w	225,212,200,189,179,169,159,150,142,134,126,119
; tuning -6
		dc.w	894,844,796,752,709,670,632,597,563,532,502,474
		dc.w	447,422,398,376,355,335,316,298,282,266,251,237
		dc.w	223,211,199,188,177,167,158,149,141,133,125,118
; tuning -5
		dc.w	887,838,791,746,704,665,628,592,559,528,498,470
		dc.w	444,419,395,373,352,332,314,296,280,264,249,235
		dc.w	222,209,198,187,176,166,157,148,140,132,125,118
; tuning -4
		dc.w	881,832,785,741,699,660,623,588,555,524,494,467
		dc.w	441,416,392,370,350,330,312,294,278,262,247,233
		dc.w	220,208,196,185,175,165,156,147,139,131,123,117
; tuning -3
		dc.w	875,826,779,736,694,655,619,584,551,520,491,463
		dc.w	437,413,390,368,347,328,309,292,276,260,245,232
		dc.w	219,206,195,184,174,164,155,146,138,130,123,116
; tuning -2
		dc.w	868,820,774,730,689,651,614,580,547,516,487,460
		dc.w	434,410,387,365,345,325,307,290,274,258,244,230
		dc.w	217,205,193,183,172,163,154,145,137,129,122,115
; tuning -1
		dc.w	862,814,768,725,684,646,610,575,543,513,484,457
		dc.w	431,407,384,363,342,323,305,288,272,256,242,228
		dc.w	216,203,192,181,171,161,152,144,136,128,121,114

PT_chan1temp	dc.l	0,0,0,0,0,$00010000,0,  0,0,0,0
PT_chan2temp	dc.l	0,0,0,0,0,$00020000,0,  0,0,0,0
PT_chan3temp	dc.l	0,0,0,0,0,$00040000,0,  0,0,0,0
PT_chan4temp	dc.l	0,0,0,0,0,$00080000,0,  0,0,0,0

	cnop	0,4
PT_samplestarts
		dc.l	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
		dc.l	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

PT_songdataptr	dc.l	0

PT_speed	dc.b	0
PT_counter	dc.b	0
PT_songpos	dc.b	0
PT_pbreakpos	dc.b	0
PT_posjumpflag	dc.b	0
PT_pbreakflag	dc.b	0
PT_lowmask	dc.b	0
PT_pattdeltime	dc.b	0
PT_pattdeltime2	dc.b	0
	ifne PT__NTComp
PT_looppos	dc.b	0
	endc
	even
PT_patternpos	dc.w	0
PT_dmacontemp	dc.w	0
	ifne PT__OverStep
PT_OverStep	dc.w	0
	endc
	ifne PT__NTComp
PT_vibshift	dc.l	0
	endc
PT_lmastervol	dc.w	64
PT_rmastervol	dc.w	64

