;	mod8player.a - OctaMED V1.00 8-channel player routine
;	Written by Teijo Kinnunen

ac_ptr	EQU	$00
ac_len	EQU	$04
ac_per	EQU	$06
ac_vol	EQU	$08
ac_end	EQU	$0C
ac_rest	EQU	$10
T03SZ	EQU	72

		section	"text",code
		EVEN
sinetable:	dc.b	0,25,49,71,90,106,117,125,127,125,117,106,90,71,49
		dc.b	25,0,-25,-49,-71,-90,-106,-117,-125,-127,-125,-117
		dc.b	-106,-90,-71,-49,-25,0

nextblock:	dc.b	0
numtracks:	dc.w	0
numlines:	dc.w	0
_counter:	dc.w	0

_Wait1line:	move.l	d0,-(sp)
		moveq	#$79,d0
wl0:		move.b	$dff007,d1
wl1:		cmp.b	$dff007,d1
		beq.s	wl1
		dbf	d0,wl0
		move.l	(sp)+,d0
		rts

pline:		dc.w	0
pblock:		dc.w	0
pseq:		dc.w	0

		even
getinsdata:	moveq	#0,d2
		move.w	4(a0),d0	;Soitin-struct in a0
		bne.s	iff5or3oct	;note # in d1 (0 - ...)
		move.l	a0,d0
		lea	_periods(pc),a0
		move.l	a0,trk_periodtbl(a5)
		lsl.b	#1,d1
		move.w	0(a0,d1.w),d5 ;put period to d5
		move.l	d0,a0
		addq.l	#6,d0		;Skip structure
		move.l	(a0),d1		;length
		move.w	(a3),d2
		move.w	2(a3),d3
		rts
iff5or3oct:	movem.l	d6-d7,-(sp)
		moveq	#0,d7
		move.w	d1,d7
		divu	#12,d7	;octave #
		move.l	d7,d5
		swap	d5	;note number in this oct (0-11) is in d5
		move.l	(a0),d1
		cmp.b	#2,d0
		bne.s	no3oct
		addq.l	#6,d7
		divu	#7,d1	;get length of the 1st octave
		bra.s	no5oct
no3oct:		divu	#31,d1	;get length of the 1st octave (5 octaves)
no5oct:		move.l	d1,d0		;d0 and d1 = length of the 1st oct
		move.w	(a3),d2
		move.w	2(a3),d3
		moveq	#0,d6
		move.b	shiftcnt(pc,d7.w),d6
		lsl.w	d6,d2
		lsl.w	d6,d3
		lsl.w	d6,d1
		move.b	mullencnt(pc,d7.w),d6
		mulu	d6,d0		;offset of this oct from 1st oct
		add.l	a0,d0		;add base address to offset
		addq.l	#6,d0		;skip structure
		lea	_periods(pc),a1
		add.b	octstart(pc,d7.w),d5
		lsl.b	#1,d5
		move.w	0(a1,d5.w),d5
		movem.l	(sp)+,d6-d7
		rts	;returns period in d5
shiftcnt:	dc.b	4,3,2,1,1,0,2,2,1,1,0,0
mullencnt:	dc.b	15,7,3,1,1,0,3,3,1,1,0,0
octstart:	dc.b	12,12,12,12,24,24,0,12,12,24,24,36

;**************************************************************************
;*
;*		8 CHANNEL PLAY ROUTINE
;*
;**************************************************************************

_ChannelO8:	cmp.b	#8,d0
		bge.s	xco8
		lea	trackdata8(pc),a1
		lsl.b	#2,d0
		adda.w	d0,a1
		movea.l	(a1),a1
		movea.l	trk_audioaddr(a1),a1
		clr.w	ac_per(a1)
xco8		rts

_PlayNote8:	;d0(w) = trk #, d1 = note #, d2 = vol, d3(w) = instr # a3 = addr of instr
		movem.l d3-d7,-(sp) ;All right, let's start!!
		movea.l	24(a6),a0
		move.w	d3,d7
		add.w	d3,d3			;d3 = instr.num << 2
		add.w	d3,d3
		move.l	0(a0,d3.w),d5		;get address of instrument
		beq.s	retsn28
inmem8:		add.b	766(a4),d1	;add play transpose
		add.b	7(a3),d1	;and instr. transpose
		tst.b	4(a3)
		bne.s	retsn28		;MIDI
		cmp.w	#8,d0		;track > 7???
		bge.s	retsn28		;no Amiga instruments here!!!
		clr.b	trk_vibroffs(a5)	;clr vibrato offset
		move.l	d5,a0
		subq.b	#1,d1
		tst.w	4(a0)
		bmi.s	retsn28		;Synth
tlwtst08:	tst.b	d1
		bpl.s	notenot2low8
		add.b	#12,d1	;note was too low, octave up
		bra.s	tlwtst08
notenot2low8:	cmp.b	#62,d1
		ble.s	endpttest8
		sub.b	#12,d1	;note was too high, octave down
endpttest8:	move.w	d0,d4	;d4 = track number
		bsr.w	getinsdata
		movea.l	trk_audioaddr(a5),a1 ;base of this channel's regs
		add.w	d4,d4	;d4 = trk << 1
		move.w	d4,d6
		add.w	d6,d6	;d6 = trk << 2
		move.l	d0,(a1)		;put it in ac_ptr
		cmp.w	#1,d3
		bhi.s	repeat8

		add.l	d1,d0
		subq.l	#1,d0
		move.l	d0,ac_end(a1)
		clr.l	ac_rest(a1)
		bra.s	retsn18

repeat8:	lsl.l	#1,d2		;shift
		lsl.l	#1,d3
		add.l	d2,d0
		move.l	d0,ac_rest(a1)
		add.l	d3,d0
		subq.l	#1,d0
		move.l	d0,ac_end(a1)
				
retsn18:	move.w	d5,ac_per(a1)	;getinsdata puts period to d5
		move.w	d5,trk_prevper(a5)
retsn28:	movem.l	(sp)+,d3-d7
		rts

t038:		ds.b	18
		dc.l	track0hw
		ds.b	50+18
		dc.l	track1hw
		ds.b	50+18
		dc.l	track2hw
		ds.b	50+18
		dc.l	track3hw
		ds.b	50
t4158:		ds.b	18
		dc.l	track4hw
		ds.b	50+18
		dc.l	track5hw
		ds.b	50+18
		dc.l	track6hw
		ds.b	50+18
		dc.l	track7hw
		ds.b	50
		
trackdata8:	dc.l	t038,t038+T03SZ,t038+2*T03SZ,t038+3*T03SZ
		dc.l	t4158,t4158+T03SZ,t4158+2*T03SZ,t4158+3*T03SZ

_IntHandler8:	movem.l	d2-d7/a2-a5,-(sp)
; ================ 8 channel handling (buffer swap) ======
		move.w	#800,d0
		not.b	whichbuff	;swap buffer
		bne.s	usebuff1
		move.l	a1,$a0(a0)
		lea	800(a1),a5
		move.l	a5,$b0(a0)
		adda.w	d0,a5
		move.l	a5,$c0(a0)
		adda.w	d0,a5
		move.l	a5,$d0(a0)
		bra.s	buffset
usebuff1:	lea	400(a1),a1
		move.l	a1,$a0(a0)
		lea	800(a1),a5
		move.l	a5,$b0(a0)
		adda.w	d0,a5
		move.l	a5,$c0(a0)
		adda.w	d0,a5
		move.l	a5,$d0(a0)
buffset:	move.w	#1<<7,$9c(a0)	;clear interrupt
; ============== fill buffers ============
		lea	track0hw(pc),a6
		bsr.s	fillbuf
		adda.w	#400,a1
		lea	track1hw(pc),a6
		bsr.s	fillbuf
		adda.w	#400,a1
		lea	track2hw(pc),a6
		bsr.s	fillbuf
		adda.w	#400,a1
		lea	track3hw(pc),a6
		bsr.s	fillbuf
		bra.w	do_play8
; =========================================================
;calculate channel A period
fillbuf:	move.l	#3719168,d7
		move.w	ac_per(a6),d6
		beq.s	setpzero0
		move.l	d7,d2
		divu 	d6,d2
		moveq	#0,d1
		move.w	d2,d1
		add.l	d1,d1
		add.l	d1,d1
;get channel A addresses
		move.l	ac_end(a6),a5
		move.l	(a6),d0
		beq.s	setpzero0
chA_dfnd	move.l	d0,a3	;a3 = start address, a5 = end address
;calc bytes before end
		mulu	#1600,d2
		clr.w	d2
		swap	d2
; d2 = # of bytes/fill
		add.l	a3,d2	;d2 = end position after this fill
		sub.l	a5,d2	;subtract sample end
		bmi.s	norestart0
		move.l	ac_rest(a6),d0
		beq.s	rst0end
		move.l	d0,(a6)
		move.l	d0,a3
		bra.s	norestart0
rst0end		clr.l	(a6)
setpzero0	lea	zerodata(pc),a3
		moveq	#0,d1
norestart0
;channel B period
		move.w	SIZE4TRKHW+ac_per(a6),d6
		beq.s	setpzero0b
		divu	d6,d7
		moveq	#0,d2
		move.w	d7,d2
		add.l	d2,d2
		add.l	d2,d2
;channel B addresses
		move.l	SIZE4TRKHW+ac_end(a6),a5
		move.l	SIZE4TRKHW(a6),d0
		beq.s	setpzero0b
		move.l	d0,a4
		mulu	#1600,d7
		clr.w	d7
		swap	d7
		add.l	a4,d7
		sub.l	a5,d7
		bmi.s	norestart0b
		move.l	SIZE4TRKHW+ac_rest(a6),d0
		beq.s	rst0endb
		move.l	d0,SIZE4TRKHW(a6)
		move.l	d0,a4
		bra.s	norestart0b
rst0endb	clr.l	SIZE4TRKHW(a6)
setpzero0b	lea	zerodata(pc),a4
		moveq	#0,d2
norestart0b	moveq	#0,d6
		moveq	#0,d7
		REPT	400
		swap	d6
		swap	d7
		move.b	0(a3,d6.w),d0
		add.b	0(a4,d7.w),d0
		move.b	d0,(a1)+
		swap	d6
		swap	d7
		add.l	d1,d6
		add.l	d2,d7
		ENDR
		clr.w	d6
		clr.w	d7
		swap	d6
		swap	d7
		add.l	d6,(a6)
		add.l	d7,SIZE4TRKHW(a6)
		rts
; ========== player starts here...
do_play8	move.l	_module(pc),d0
		beq.w	plr_exit8
		move.l	d0,a6
		move.l	8(a6),a4
		moveq	#0,d3
		lea	_counter(pc),a0
		move.b	(a0),d3
		addq.b	#1,d3
		cmp.b	769(a4),d3
		bge.s	plr_pnewnote8	;play new note
		move.b	d3,(a0)
		bra.w	nonewnote8	;do just fx
; --- new note!! first get address of current block
plr_pnewnote8:	clr.b	(a0)
; --- now start to play it
		move.w	pblock(pc),d0
		movea.l	16(a6),a0
		lsl.w	#2,d0
		movea.l	0(a0,d0.w),a2	;block...
		move.b	(a2)+,numtracks+1
		move.b	(a2)+,numlines+1
		move.w	pline(pc),d0
		move.w	d0,d1
		add.w	d0,d0	;d0 * 2
		add.w	d1,d0	;+ d0 = d0 * 3
		mulu	numtracks(pc),d0
		adda.w	d0,a2		;address of current note
		moveq	#0,d7		;number of track
		pea	trackdata8(pc)
plr_loop08:	moveq	#0,d5
		move.l	(sp),a1
		movea.l	(a1)+,a5	;get address of this track's struct
		move.l	a1,(sp)
		cmp.b	#8,d7
		bge.w	plr_endloop08
; ---------------- get the note numbers
		move.b	(a2)+,d5	;get the number of this note
		move.b	(a2)+,d6	;and the 4 numbers containing fx
		move.b	(a2)+,trk_cmdqual(a5)	;get & save the fx numbers
; ---------------- clear some instrument # flags
		moveq	#0,d4		;d4 is a flag: if set, instr. is
		moveq	#0,d3		;in range G-V. If clr, it's 1-F.
; ---------------- and set them, if needed
		bclr	#7,d5		;d3 is also a flag. If it's set,
		sne	d4		;the instr. is in range 10 - 1V
		bclr	#6,d5
		sne	d3
; ---------------- check if there's an instrument number
		move.b	d6,d0
		and.w	#$f0,d0		;d0 now contains only the # of instr
		bne.s	instnum8	;instrument number is not 0
		tst.b	d4		;maybe it's G (instr. #0, d4 set)
		bne.s	instnum8	;yes, it really was G!!
		tst.b	d3
		beq.s	noinstnum8	;it wasn't 10 - 1V either..
; ---------------- if there was, GET IT!!
instnum8:	lsr.b	#4,d0		;shift it right to get number 0-F
		tst.b	d4
		beq.s	nogtov28
		add.w	#16,d0		;if G-V, add 16 to the number
nogtov28:	tst.b	d3
		beq.s	no10to1v8
		add.w	#32,d0
; ---------------- finally, save the number
no10to1v8:	subq.b	#1,d0
		move.b	d0,trk_previnstr(a5) ;remember instr. number!
		lea	holdvals(pc),a0
		move.b	0(a0,d0.w),trk_inithold(a5) ;rem. hold
; ---------------- get the pointer of data's of this sample in Song-struct
		lsl.w	#3,d0
		lea	0(a4,d0.w),a3
		move.l	a3,trk_previnstra(a5)
; ---------------- set volume to 64
		movea.l	trk_audioaddr(a5),a0
		movea.l	ac_vol(a0),a0	;ptr to volume hardware register
		moveq	#64,d1
		move.b	d1,(a0)
		move.b	d1,trk_prevvol(a5)
; ---------------- remember the some values of this instrument
		move.b	7(a3),trk_stransp(a5)	;rem. transpose
		moveq	#0,d0
; ---------------- check the commands
noinstnum8:	move.b	d6,d0		;effect again...
		and.b	#$0f,d0		;now check only the effect part
		move.b	d0,trk_cmd(a5)	;save the effect number
		beq.w	plr_nocmd8	;no effect
		move.b	trk_cmdqual(a5),d6	;get qualifier...
; ---------------- there was a command (effect), but which one??
		cmp.b	#$0f,d0		;yes effect...is it Tempo???
		bne.s	not0f8		;not Tempo
; ---------------- it was tempo (F)
		tst.b	d6		;test effect qual..
		beq.s	fx0fchgblck8	;if effect qualifier (last 2 #'s)..
		cmp.b	#$f0,d6		;..is zero, go to next block
		bls.w	plr_nocmd8	;01 - F0, not supported on 8 channels
; ---------------- no, it was FFx, something special will happen!!
fx0fspecial8:	cmp.b	#$f2,d6	; | rest - play | SpecialFX#2: no note..yet
		bne.s	isfxfe8	;not SpecFX2
; ---------------- it was FF2, nothing to do now
		move.b	d5,(a5)	;Yes!!! Save the note number
		moveq	#0,d5	; clear the number for awhile
		bra.w	plr_nocmd8
isfxfe8:	cmp.b	#$fe,d6
		bne.s	notcmdfe8
; ---------------- it was FFE, stop playing
		bsr.w	_End8Play
		bra.w	plr_nocmd8
notcmdfe8:	cmp.b	#$fd,d6 ;change period
		bne.s	isfxff8
; ---------------- FFD, change the period, don't replay the note
		cmp.w	#8,d7 ;no tracks 8 - 15, thank you!!
		bge.w	plr_nocmd8
		movea.l	trk_periodtbl(a5),a0	;period table
		subq.b	#1,d5    ;sub 1 to make "real" note number
		bmi.w	plr_endloop08	;under zero, do nothing
		add.b	d5,d5
		move.w	0(a0,d5.w),d0 ;get the period
		movea.l	trk_audioaddr(a5),a0
		move.w	d0,ac_per(a0) ;push the period
		moveq	#0,d5 ;and clear it so that it won't be replayed
		bra.w	plr_nocmd8	;done!!
isfxff8:	cmp.b	#$ff,d6		;note off??
		bne.w	plr_nocmd8
		move.w	d7,d0
		bsr.w	_ChannelO8
		bra.w	plr_nocmd8
; ---------------- F00, called Pattern Break in ST
fx0fchgblck8:	addq.b	#1,nextblock	;next block????...YES!!!! (F00)
		bra.w	plr_nocmd8
; ---------------- was not Fxx, then it's something else!!
not0f8:		cmp.b	#$0e,d0
		bne.s	not0e8
		move.b	d6,trk_wfcmd+1(a5) ;set waveform command position ptr
		bra.w	plr_nocmd8
not0e8:		cmp.b	#$0c,d0		;new volume???
		bne.s	not0c8		;NO!!!!!!!!!!!!!!!!!!!!!!
; ---------------- change volume
		move.b	d6,d0
		btst	#4,767(a4)	;look at flags
		bne.s	volhex8
		lsr.b	#4,d0		;get number from left
		mulu	#10,d0		;number of tens
		move.b	d6,d1		;get again
		and.b	#$0f,d1		;this time don't get tens
		add.b	d1,d0		;add them
volhex8		cmp.b	#64,d0
		bls.s	novolov648
		moveq	#64,d0
novolov648:	movea.l	trk_audioaddr(a5),a0
		movea.l	ac_vol(a0),a0
		move.b	d0,(a0)
		move.b	d0,trk_prevvol(a5)
		bra.s	plr_nocmd8
; ---------------- tempo2 change??
not0c8:		cmp.b	#$09,d0
		bne.s	not098
		and.b	#$1F,d6
		bne.s	fx9chk8
		moveq	#$20,d6
fx9chk8		move.b	d6,769(a4)
		bra.s	plr_nocmd8
; ---------------- note off time set??
not098:		cmp.b	#$08,d0
		bne.s	not088
		and.b	#$0f,d6		; extract right only
		move.b	d6,trk_inithold(a5)	;right = hold
		bra.s	plr_nocmd8
; ---------------- cmd Bxx, "position jump", like Goto, yäk!!
not088		cmp.b	#$0b,d0
		bne.s	not0b8
		move.w	d6,d0
		and.w	#$00ff,d0
		cmp.w	506(a4),d0	;test the song length
		bhi.s	plr_nocmd8
		move.w	d0,pseq
		st	nextblock	; = 1
		bra.s	plr_nocmd8
; ---------------- try portamento (3)
not0b8:		cmp.b	#$03,d0
		bne.s	plr_nocmd8
		subq.b	#1,d5		;subtract note number
		bpl.s	plr_fx3note8	;there's a note...
		tst.b	d6		;qual??
		beq.s	plr_endloop08	;0 -> do nothing
		bra.s	plr_setfx3spd8	;not 0 -> set new speed
plr_fx3note8:	cmp.w	#8,d7
		bge.s	plr_endloop08	;hey, what are you trying to do??
		movea.l	trk_periodtbl(a5),a0
		add.b	766(a4),d5	;play transpose
		add.b	trk_stransp(a5),d5 ;and instrument transpose
		bmi.s	plr_endloop08	;again.. too low
		add.w	d5,d5
		move.w	0(a0,d5.w),trk_porttrgper(a5) ;period of this note is the target
plr_setfx3spd8:	move.b	d6,trk_prevportspd(a5)	;remember size
		moveq	#0,d5	;don't play this one
; ---------------- everything is checked now: play or not to play??
plr_nocmd8:	tst.b	d5	;Now we'll check if we have to play a note
		beq.s	plr_endloop08	;no.
; ---------------- we decided to play
		move.b	d5,(a5)
		move.w	d7,d0
		move.w	d5,d1
		moveq	#0,d2
		moveq	#0,d3
		move.b	trk_previnstr(a5),d3	;instr #
		movea.l	trk_previnstra(a5),a3	;instr data address
; ---------------- does this instrument have holding??
		move.b	trk_inithold(a5),trk_noteoffcnt(a5) ;initialize hold
		bne.s	plr_holdok8	;not 0 -> OK
		st	trk_noteoffcnt(a5)	;0 -> hold = 0xff (-1)
; ---------------- and finally:
plr_holdok8:
		bsr	_PlayNote8	;play it!!!!!!!!!!!
; ---------------- end of loop: handle next track, or quit
plr_endloop08:	addq.b	#1,d7
		cmp.w	numtracks(pc),d7
		blt.w	plr_loop08
		addq.l	#4,sp		;trackdata8

; and advance song pointers
		lea	pline(pc),a3
		move.w	(a3),d1		;pline
		addq.w	#1,d1		;very important!!! advance line!!
		cmp.w	numlines(pc),d1	;important too!!! advance block??
		bgt.s	plr_chgblock8	;yes!!!
		tst.b	nextblock	;command F00 ??
		beq.s	plr_nochgblock8	;no, don't change block
plr_chgblock8:	moveq	#0,d1		;clear line number
		move.w	4(a3),d0	;get play sequence number
		tst.b	nextblock
		bmi.s	plr_noadvseq8	;Bxx sets nextblock to 0xff (= neg)
		addq.w	#1,d0		;advance sequence number
plr_noadvseq8:	cmp.w	506(a4),d0	;is this the highest seq number??
		blt.s	plr_notagain8	;no.
		moveq	#0,d0		;yes: play song again
		moveq	#0,d1			;...forever!!!
plr_notagain8:	move.b	d0,5(a3)	;remember new playseq-#
		lea	508(a4),a0	;offset of sequence table
		move.b	0(a0,d0.w),d0	;get number of the block
		cmp.b	505(a4),d0	;beyond last block??
		blt.s	plr_nolstblk8	;no..
		moveq	#0,d0		;play block 0
plr_nolstblk8:	move.b	d0,3(a3)	;store pblock
plr_nonewseq8:	clr.b	nextblock	;clear this if F00 set it
plr_nochgblock8	move.w	d1,(a3)		;set new pline
		movea.l	16(a6),a0
		move.w	2(a3),d0	;pblock
		lsl.w	#2,d0
		movea.l	0(a0,d0.w),a2	;block...
		move.b	(a2),d7		;# of tracks
		move.w	(a3),d0		;play line
		move.w	d0,d1
		add.w	d0,d0	;d0 * 2
		add.w	d1,d0	;+ d0 = d0 * 3
		mulu	d7,d0
		lea	2(a2,d0.w),a2
		move.b	769(a4),d3	;interrupts/note
		lea	trackdata8(pc),a0
		subq.b	#1,d7
plr_chkhold8:	movea.l	(a0)+,a1	;track data
		tst.b	7(a1)		;hold??
		bmi.s	plr_holdend8	;no.
		move.b	(a2),d1		;get the 1st byte..
		bne.s	plr_hold18
		move.b	1(a2),d1
		and.b	#$f0,d1
		beq.s	plr_holdend8	;don't hold
		bra.s	plr_hold28
plr_hold18:	and.b	#$3f,d1		;note??
		beq.s	plr_hold28	;no, cont hold..
		move.b	1(a2),d1
		and.b	#$0f,d1		;get cmd
		subq.b	#3,d1		;is there command 3 (slide)
		bne.s	plr_holdend8	;no -> end holding
plr_hold28:	add.b	d3,7(a1)	;continue holding...
plr_holdend8:	addq.l	#3,a2		;next note
		dbf	d7,plr_chkhold8
		btst	#5,767(a4)	;FLAG_STSLIDE??
		bne.w	plr_endfx8	;yes, no effects this time...
		moveq	#0,d3		;counter = 0!!!
nonewnote8:

;	*********************** This code produces the effects **
		moveq	#0,d7	;clear track count
		moveq	#0,d6
		lea	trackdata8(pc),a2
plr_loop18:	movea.l	(a2)+,a5
		moveq	#0,d5
		moveq	#0,d4
		move.b	trk_cmd(a5),d6	;get the fx number
		move.b	trk_cmdqual(a5),d4	;and the last 2 #'s
		cmp.w	#8,d7
		bge.w	endl8	;no non-MIDI effects in tracks 8 - 15
		tst.b	trk_noteoffcnt(a5)
		bmi.s	plr_nowaitoff8
		subq.b	#1,trk_noteoffcnt(a5)
		bpl.s	plr_nowaitoff8
		movea.l	trk_audioaddr(a5),a1
		clr.w	ac_per(a1)	;if ac_per is 0, sound'll be stopped
plr_nowaitoff8:	add.b	d6,d6	;* 2
		move.w	fx_table8(pc,d6.w),d0
		jmp	fxs8(pc,d0.w)
fx_table8:	dc.w	fx_008-fxs8,fx_018-fxs8,fx_028-fxs8,fx_038-fxs8,fx_048-fxs8
		dc.w	fx_058-fxs8,fx_xx8-fxs8,fx_xx8-fxs8,fx_xx8-fxs8,fx_xx8-fxs8
		dc.w	fx_0a8-fxs8,fx_xx8-fxs8,fx_xx8-fxs8,fx_0d8-fxs8,fx_xx8-fxs8
		dc.w	fx_0f8-fxs8
fxs8:
;	**************************************** Effect 01 ******
fx_018:		sub.w	d4,trk_prevper(a5)	;slide it up!!!
		move.w	trk_prevper(a5),d5
		cmp.w	#113,d5		;too high???
		bge	newvals8
		move.w	#113,d5		;yes, too high!!!
		move.w	d5,trk_prevper(a5)
		bra	newvals8
;	**************************************** Effect 02 ******
fx_028:		add.w	d4,trk_prevper(a5)	;slide it down!!!!!!!!!
		move.w	trk_prevper(a5),d5
		bra.w	newvals8
;	**************************************** Effect 00 ******
fx_008:		tst.b	d4	;both fxqualifiers are 0s: no arpeggio!!
		beq.w	endl8
		move.l	d3,d0
		divu	#3,d0
		swap	d0
		tst.w	d0
		bne.s	fx_arp128
		and.b	#$0f,d4
		add.b	(a5),d4
		bra.s	fx_doarp8
fx_arp128:	subq.b	#1,d0
		bne.s	fx_arp28
		lsr.b	#4,d4
		add.b	(a5),d4
		bra.s	fx_doarp8
fx_arp28:	move.b	(a5),d4
fx_doarp8:	subq.b	#1,d4		;-1 to make it 0 - 127
		add.b	766(a4),d4	;add play transpose
		add.b	trk_stransp(a5),d4	;add instrument transpose
		lsl.b	#1,d4		;shift to make index for UWORD
		movea.l	trk_periodtbl(a5),a1
		move.w	0(a1,d4.w),d5
		bra.w	newvals8
;	**************************************** Effect 0D/0A ***
fx_0a8:
fx_0d8:		move.b	d4,d1
		move.b	trk_prevvol(a5),d0	;move previous vol to d0
		and.b	#$f0,d1
		bne.s	crescendo8
		sub.b	d4,d0	;sub from prev. vol
		bpl.s	novolund08
		moveq	#0,d0	;volumes under zero not accepted!!!
novolund08:	move.b	d0,trk_prevvol(a5)	;put new vol back
		bra.s	setvc8
crescendo8:	lsr.b	#4,d1
		add.b	d1,d0
		cmp.b	#64,d0
		ble.s	novolover648
		moveq	#64,d0
novolover648:	move.b	d0,trk_prevvol(a5)
setvc8		movea.l	trk_audioaddr(a5),a0
		movea.l	ac_vol(a0),a0
		move.b	d0,(a0)
		bra.w	fx_xx8
;	**************************************** Effect 05 ******
fx_058:		move.w	trk_prevper(a5),d5 ;this is very simple: get the old period
		cmp.b	#3,d3		;and..
		bge.w	newvals8		;if counter < 3
		sub.w	d4,d5	;subtract effect qualifier
		bra.w	newvals8
;	**************************************** Effect 03 ******
fx_038:		move.w	trk_porttrgper(a5),d0	;d0 = target period
		beq.w	newvals8	;no target period specified
		move.w	trk_prevper(a5),d1	;d1 = curr. period
		move.b	trk_prevportspd(a5),d4	;get prev. speed
		cmp.w	d0,d1
		bhi.s	subper8	;curr. period > target period
		add.w	d4,d1	;add the period
		cmp.w	d0,d1
		bge.s	targreached8
		bra.s	targnreach8
subper8:	sub.w	d4,d1	;subtract
		cmp.w	d0,d1	;compare current period to target period
		bhi.s	targnreach8
targreached8:	move.w	trk_porttrgper(a5),d1 ;eventually push target period
		clr.w	trk_porttrgper(a5) ;now we can forget everything
targnreach8:	move.w	d1,trk_prevper(a5)
		move.w	d1,d5
		bra.w	newvals8
;	**************************************** Effect 04 ******
fx_048:		tst.b	d4
		beq.s	nonvib8
		move.b	d4,trk_vibrspdsz(a5)
nonvib8:	move.b	trk_vibroffs(a5),d0
		lsr.b	#2,d0
		and.w	#$1f,d0
		moveq	#0,d1
		lea	sinetable(pc),a0
		move.b	0(a0,d0.w),d5
		ext.w	d5
		move.b	trk_vibrspdsz(a5),d0
		and.w	#$000f,d0
		muls	d0,d5
		asr.w	#5,d5
		add.w	trk_prevper(a5),d5
		move.b	trk_vibrspdsz(a5),d0
		lsr.b	#3,d0
		and.b	#$3e,d0
		add.b	d0,trk_vibroffs(a5)
		bra.w	newvals8
;	**************************************** Effect 0F ******
fx_0f8:		cmp.b	#$f1,d4
		bne.s	no0ff18
		cmp.b	#3,d3
		bne.w	endl8
		bra.s	playfxnote8
no0ff18:	cmp.b	#$f2,d4
		bne.s	no0ff28
		cmp.b	#3,d3
		bne.w	endl8
		bra.s	playfxnote8
no0ff28:	cmp.b	#$f3,d4
		bne.s	no0ff38
		move.b	d3,d0
		and.b	#2+4,d0		;is 2 or 4
		beq.w	endl8
playfxnote8:	move.w	d7,d0		;track # to d0...
		moveq	#0,d1
		move.b	(a5),d1		;get note # of previous note
		move.w	d3,-(sp)
		moveq	#0,d3
		move.b	trk_previnstr(a5),d3	;and prev. sample #
		movea.l	trk_previnstra(a5),a3
		bsr	_PlayNote8
		move.w	(sp)+,d3
		bra.s	endl8
no0ff38:	cmp.b	#$f8,d4		;f8 = filter off
		beq.s	plr_filteroff8
		cmp.b	#$f9,d4		;f9 = filter on
		bne.s	endl8
		bclr	#1,$bfe001
		bra.s	plr_xff38
plr_filteroff8:	bset	#1,$bfe001
plr_xff38:	bra.s	endl8
;	*********************************************************
newvals8	movea.l	trk_audioaddr(a5),a1	;get channel address
		tst.w	d5	;now: do the effects!!!
		beq.s	fx_xx8
plr_newper8:	move.w	d5,ac_per(a1)	;push period
fx_xx8:
endl8:		addq.b	#1,d7	;increment channel number
		cmp.w	numtracks(pc),d7	;all channels done???
		blt.w	plr_loop18	;not yet!!!
plr_endfx8:
plr_exit8:	movem.l	(sp)+,d2-d7/a2-a5
		rts

zerodata	dc.w	0

whichbuff:	dc.w	0

track0hw:	dc.l	0,0,$dff0a9,0,0
track1hw:	dc.l	0,0,$dff0b9,0,0
track2hw:	dc.l	0,0,$dff0c9,0,0
track3hw:	dc.l	0,0,$dff0d9,0,0
track4hw:	dc.l	0,0,$dff0a9,0,0
track5hw:	dc.l	0,0,$dff0b9,0,0
track6hw:	dc.l	0,0,$dff0c9,0,0
track7hw:	dc.l	0,0,$dff0d9,0,0
SIZE4TRKHW	equ	4*$14

audintname:	dc.b	'OctaMED AudioInterrupt',0
		even
audiointerrupt:	dc.w	0,0,0,0,0
		dc.l	audintname,_audiobuff,_IntHandler8
prevaud:	dc.l	0
play8:		dc.b	0
eightrkon:	dc.b	0

_Init8chan:	move.l	a6,-(sp)
		move.w	#1<<7,$dff09a
		moveq	#7,d0		;Audio channel 0 interrupt
		lea	audiointerrupt(pc),a1
		move.l	4,a6
		jsr	-$a2(a6)	;SetIntVector()
		move.l	d0,prevaud
		st	eightrkon
		move.l	(sp)+,a6
		rts

_Rem8chan:	move.l	a6,-(sp)
		move.b	eightrkon(pc),d0
		beq.s	no8init
		clr.b	eightrkon
		move.w	#1<<7,$dff09a
		moveq	#7,d0
		move.l	prevaud(pc),a1
		move.l	4,a6
		jsr	-$a2(a6)
no8init		move.l	(sp)+,a6
		rts

_Start8Play:	;d1 = pstate
		lea	_audiobuff,a0
		move.w	#799,d1
clrbuffloop:	clr.l	(a0)+		;clear track buffers
		dbf	d1,clrbuffloop
		lea	$dff000,a0
		move.w	#227,d1
		move.w	d1,$a6(a0)
		move.w	d1,$b6(a0)
		move.w	d1,$c6(a0)
		move.w	d1,$d6(a0)
		move.w	#200,d1
		move.w	d1,$a4(a0)
		move.w	d1,$b4(a0)
		move.w	d1,$c4(a0)
		move.w	d1,$d4(a0)
		move.l	#_audiobuff,$a0(a0)
		move.l	#_audiobuff+800,$b0(a0)
		move.l	#_audiobuff+1600,$c0(a0)
		move.l	#_audiobuff+2400,$d0(a0)
		moveq	#64,d1
		move.w	d1,$a8(a0)
		move.w	d1,$b8(a0)
		move.w	d1,$c8(a0)
		move.w	d1,$d8(a0)
		clr.b	whichbuff
		move.w	#$4000,$9a(a0)
		lea	track0hw,a1
		moveq	#7,d1
clrtrkloop	clr.l	(a1)
		clr.w	ac_per(a1)
		adda.w	#$14,a1
		dbf	d1,clrtrkloop
		move.w	#$F,$dff096	;audio DMA off
		bsr.w	_Wait1line	;wait until all stopped
		st	play8
		move.w	#$c080,$9a(a0)
		move.w	#$800F,$96(a0)
		rts

_End8Play:	move.b	play8(pc),d0
		beq.s	noend8play
		move.w	#1<<7,$dff09a
		move.w	#$F,$dff096
		clr.b	play8
noend8play	rts

_AudioInit:	movem.l	a4/a6/d2-d3,-(sp)
		moveq	#0,d2
		movea.l	4,a6
;	+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ alloc signal bit
		addq.l	#1,d2
		moveq	#-1,d0
		jsr	-$14a(a6)	;AllocSignal()
		tst.b	d0
		bmi.w	initerr
		move.b	d0,sigbitnum
;	+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ prepare IORequest
		lea	allocport(pc),a1
		move.b	d0,15(a1)	;set mp_SigBit
		move.l	a1,-(sp)
		suba.l	a1,a1
		jsr	-$126(a6)	;FindTask(0)
		move.l	(sp)+,a1
		move.l	d0,16(a1)	;set mp_SigTask
		lea	reqlist(pc),a0
		move.l	a0,(a0)		;NEWLIST begins...
		addq.l	#4,(a0)
		clr.l	4(a0)
		move.l	a0,8(a0)	;NEWLIST ends...
;	+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ open audio.device
		addq.l	#1,d2
		lea	allocreq(pc),a1
		lea	audiodevname(pc),a0
		moveq	#0,d0
		moveq	#0,d1
		movea.l	4,a6
		jsr	-$1bc(a6)	;OpenDevice()
		tst.b	d0
		bne.w	initerr
		st.b	audiodevopen
;	+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ open ciaa.resource
		bsr.w	_Init8chan
		moveq	#0,d0
initret:	movem.l	(sp)+,a4/a6/d2-d3
		rts
initerr:	move.l	d2,d0
		bra.s	initret

_AudioRem:	move.l	a6,-(sp)
		bsr.w	_Rem8chan
		movea.l	4,a6
		tst.b	audiodevopen
		beq.s	rem2
		move.w	#$000f,$dff096	;stop audio DMA
;	+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ close audio.device
		lea	allocreq(pc),a1
		jsr	-$1c2(a6)	;CloseDevice()
rem2:		moveq	#0,d0
		move.b	sigbitnum(pc),d0
		bmi.s	rem3
;	+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ free signal bit
		jsr	-$150(a6)	;FreeSignal()
rem3:		move.l	(sp)+,a6
		rts
; *************************************************************************
; *************************************************************************
; ***********          P U B L I C   F U N C T I O N S          ***********
; *************************************************************************
; *************************************************************************

		xdef	_InitModule8,_PlayModule8,_PlayModule82
		xdef	_InitPlayer8,_RemPlayer8,_StopPlayer8

; *************************************************************************
; InitModule8(a0 = module) -- extract expansion data etc.. from V3.00 module
; *************************************************************************

_InitModule8:	movem.l	a2-a3/d2,-(sp)
		move.l	a0,d0
		beq.s	IM_exit		;0 => xit
		lea	holdvals(pc),a2
		move.l	32(a0),d0	;expdata...
		beq.s	IM_clrhlddec	;none here
		move.l	d0,a1
		move.l	4(a1),d0	;exp_smp
		beq.s	IM_clrhlddec	;again.. nothing
		move.l	d0,a0		;InstrExt...
		move.w	8(a1),d2	;# of entries
		beq.s	IM_clrhlddec
		subq.w	#1,d2		;- 1 (for dbf)
		move.w	10(a1),d0	;entry size
IM_loop1	move.b	(a0),(a2)+	;InstrExt.hold -> holdvals
		adda.w	d0,a0		;ptr to next InstrExt
		dbf	d2,IM_loop1
		bra.s	IM_exit
IM_clrhlddec	moveq	#62,d0		;no InstrExt => clear holdvals/decays
IM_loop2	clr.w	(a2)+
		dbf	d0,IM_loop2
IM_exit		movem.l	(sp)+,a2-a3/d2
		rts
; *************************************************************************
; InitPlayer8() -- allocate interrupt, audio, serial port etc...
; *************************************************************************
_InitPlayer8:	bsr.w	_AudioInit
		tst.l	d0
		bne.s	IP_error
		moveq	#0,d0
		rts
IP_error	bsr.w	_RemPlayer8
		moveq	#-1,d0
		rts
; *************************************************************************
; RemPlayer8() -- free interrupt, audio, serial port etc..
; *************************************************************************
_RemPlayer8:	bsr.s	_StopPlayer8
		bsr.w	_AudioRem
PM_end		rts
; *************************************************************************
; PlayModule(a0 = module)  -- initialize & play it!!
; PlayModule2(a0 = module) -- play module (must be initialized)
; *************************************************************************
_PlayModule8:	move.l	a0,-(sp)
		bsr	_InitModule8
		move.l	(sp)+,a0
_PlayModule82:	move.b	audiodevopen(pc),d0
		beq.s	PM_end		;resource allocation failure
		move.l	a0,d0
		beq.s	PM_end		;module failure
		clr.l	_module
		move.l	a0,-(sp)
		movea.l	8(a0),a0	;song
		move.b	769(a0),_counter	;init counter
		btst	#0,767(a0)
		bne.s	PM_filon
		bset	#1,$bfe001
		bra.s	PM_filset
PM_filon	bclr	#1,$bfe001
PM_filset	moveq	#0,d1
		move.b	508(a0),d1	;get first playseq entry
		lea	pline(pc),a0	;clear pline etc..
		move.l	d1,(a0)+
		clr.w	(a0)
		move.l	(sp)+,_module
		bra.w	_Start8Play
; *************************************************************************
; StopPlayer() -- stop music
; *************************************************************************
_StopPlayer8:	clr.l	_module
		bra.w	_End8Play
; *************************************************************************


audiodevopen	dc.b	0
sigbitnum	dc.b	-1
allocmsk	dc.b	$0f,$00
		even
allocport	dc.l	0,0	;succ, pred
		dc.b	4,0	;NT_MSGPORT
		dc.l	0	;name
		dc.b	0,0	;flags = PA_SIGNAL
		dc.l	0	;task
reqlist		dc.l	0,0,0	;list head, tail and tailpred
		dc.b	5,0
allocreq	dc.l	0,0
		dc.b	5,127	;NT_MESSAGE, use maximum priority (127)
		dc.l	0,allocport	;name, replyport
		dc.w	68		;length
		dc.l	0	;io_Device
		dc.l	0	;io_Unit
		dc.w	0	;io_Command
		dc.b	0,0	;io_Flags, io_Error
		dc.w	0	;ioa_AllocKey
		dc.l	allocmsk ;ioa_Data
		dc.l	1	;ioa_Length
		dc.w	0,0,0	;ioa_Period, Volume, Cycles
		dc.w	0,0,0,0,0,0,0,0,0,0	;ioa_WriteMsg
audiodevname	dc.b	'audio.device',0

		even
_periods:	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
		dc.w 214,202,190,180,170,160,151,143,135,127,120,113
		dc.w 214,202,190,180,170,160,151,143,135,127,120,113
		dc.w 214,202,190,180,170,160,151,143,135,127,120,113

_module		dc.l	0
holdvals:	ds.b 63

	IFND	__G2
		section "datachip",data,chip ;for A68k
	ENDC
	IFD	__G2
		section "datachip",bss_c ;this is for Devpac 2
	ENDC
_audiobuff:	ds.w	200*8

; the track-data structure definition:
		rsreset
trk_prevnote	rs.b	1	;previous note number
trk_previnstr	rs.b	1	;previous instrument number
trk_prevvol	rs.b	1	;previous volume
trk_prevmidich	rs.b	1	;previous MIDI channel
trk_cmd		rs.b	1	;command (the 3rd number from right)
trk_cmdqual	rs.b	1	;command qualifier (infobyte, databyte..)
trk_prevmidin	rs.b	1	;previous MIDI note
trk_noteoffcnt	rs.b	1	;note-off counter (hold)
trk_inithold	rs.b	1	;default hold for this instrument
trk_initdecay	rs.b	1	;default decay for....
trk_stransp	rs.b	1	;instrument transpose
trk_pad		rs.b	1
trk_previnstra	rs.l	1	;address of the previous instrument data
;	the following data only on tracks 0 - 3
trk_prevper	rs.w	1	;previous period
trk_audioaddr	rs.l	1	;hardware audio channel base address
trk_sampleptr	rs.l	1	;pointer to sample
trk_samplelen	rs.w	1	;length (>> 1)
trk_porttrgper	rs.w	1	;portamento (cmd 3) target period
trk_vibroffs	rs.b	1	;vibrato table offset
trk_vibrspdsz	rs.b	1	;vibrato speed/size (cmd 4 qualifier)
trk_synthptr	rs.l	1	;pointer to synthetic/hybrid instrument
trk_arpgoffs	rs.w	1	;SYNTH: current arpeggio offset
trk_arpsoffs	rs.w	1	;SYNTH: arpeggio restart offset
trk_volxcnt	rs.b	1	;SYNTH: volume execute counter
trk_wfxcnt	rs.b	1	;SYNTH: waveform execute counter
trk_volcmd	rs.w	1	;SYNTH: volume command pointer
trk_wfcmd	rs.w	1	;SYNTH: waveform command pointer
trk_volwait	rs.b	1	;SYNTH: counter for WAI (volume list)
trk_wfwait	rs.b	1	;SYNTH: counter for WAI (waveform list)
trk_synthvibspd	rs.w	1	;SYNTH: vibrato speed
trk_wfchgspd	rs.w	1	;SYNTH: period change
trk_volchgspd	rs.b	1	;SYNTH: volume change
trk_prevnote2	rs.b	1	;SYNTH: previous note
trk_initvolxspd	rs.b	1	;SYNTH: volume execute speed
trk_initwfxspd	rs.b	1	;SYNTH: waveform execute speed
trk_perchg	rs.w	1	;SYNTH: curr. period change from trk_prevper
trk_synviboffs	rs.w	1	;SYNTH: vibrato pointer
trk_synvibdep	rs.w	1	;SYNTH: vibrato depth
trk_synvol	rs.b	1	;SYNTH: current volume
trk_synthtype	rs.b	1	;>0 = synth, -1 = hybrid, 0 = no synth
trk_periodtbl	rs.l	1	;pointer to period table
trk_prevportspd	rs.w	1	;portamento (cmd 3) speed
trk_decay	rs.b	1	;decay
trk_fadespd	rs.b	1	;decay speed
		end
