***********  PLAY MODULE **********
******** ©1990 Dan Lennard ********     

; This is the source code for the Play routine. Once you have
; loaded your samples and music, use ' jsr Play ' to start
; your music running. Once 'Play' has been called the music is
; maintained by the Vertical Blanking interrupt. 


; INSTRUCTIONS:
;
; To incorporate the Play subroutine in your program:
;
; Once you have changed the Tempo, MusicStart and MusicEnd
; Registers to reflect where you have loaded
; your music and its Length, change the components
; of the SampleBase to the start addresses of your samples and
; their respective Lengths in words.
; If you are using a Quartet created 'Set', the first sample
; starts 354 bytes after the address where you load the
; set; this is due to the set header. The samples then occur in
; Three contiguous blocks of four 10K samples, followed by One
; block of four 5K samples = 16 samples in total. In my code
; the set is Stored from $3a560 so the first sample is at:
; $3a560+354   = $3a6c2 , the next at:
; $3a6c2+10000 = $3cdd2 etc etc...
;
; Calling the Play subroutine starts the interrupt driven music.
; Firstly it installs the interrupts, including the Vertical
; Blanking interrupt Server, which is removed when the music stops.
; If you have an existing Vertical Blanking interrupt in your code,
; delete the indicated lines in the 'InterruptsSetup' subroutine, and
; call the subroutine 'GetNextBeat' from your interrupt.
;
; Once you've loaded a set and some music (see the DemoPlayModule 
; if in doubt), you can run this from DevPac2 by selecting the Run
; option, and from Seka with 'g Start (BreakPoint = end)'.
;
; The 'Start' subroutine is for test purposes. It starts the music
; by calling 'Play' and then waits until you press the mouse button
; or the music reaches its specified end, whereupon the subroutine
; 'UnloadInterrupt' is executed. If you don't use 'Start', then
; call this subroutine yourself to remove the vertical blanking
; interrupt installed by 'InterruptsSetup'.
;
;
; In summary, assuming you don't have another Vert-Blank interrupt, just
; call the 'Play' subroutine to play the music (having loaded your
; samples and music).


; For the interested, each note is stored as two consecutive bytes;

;			01 111111    0001 0003 
;			/    |         |    \
;		  Octave   Volume    Sample  Note Value
;		  (0-2)    (0-63)    (0-15)   (1-12 = F#-G)




*        ------------------------------------------------	 *
*			CODE STARTS HERE			 * 
*        ------------------------------------------------	 *


	bra Start	; This is for the benefit of a DevPac2 Run.


Tempo = $5d000		; This should be set to the music start
			; address, since the tempo is Stored
			; in the first byte of the music file.

MusicStart:
	dc.l $5d002	; This long word should contain the music
			; start address + 2.
MusicEnd:
	dc.l $5f000	; When the music pointers reach this value
			; the sound channels are disabled.

SampleBase:
***         Block 1          ***
	dc.l $3a6c2,5000	;The long word after each
	dc.l $3cdd2,5000	;sample address, is the Length
	dc.l $3f4e2,5000	;in words of that sample.
	dc.l $41bf2,5000
***         Block 2          ***
	dc.l $44302,5000
	dc.l $46a12,5000
	dc.l $49122,5000
	dc.l $4b832,5000
***         Block 3          ***
	dc.l $4df42,5000
	dc.l $50652,5000
	dc.l $52d62,5000
	dc.l $55472,5000
***         Block 4          ***
	dc.l $57b82,2500
	dc.l $58f0a,2500
	dc.l $5a292,2500
	dc.l $5b61a,2500

Play:	
	bsr InterruptsSetup

** The following 8 instructions force the sound channels
** to their idling state, ready to be re-started.

	move.w #$000f,$dff096
	move.w #12,$dff0a6
	move.w #12,$dff0b6
	move.w #12,$dff0c6
	move.w #12,$dff0d6
	move.w #$8780,$dff09c
	move.w #$8780,$dff09a
	

	move.w #288,ChzeroPer		; Initialize Sound
	move.w #288,ChOnePer		; variables.
	move.w #288,ChTwoPer
	move.w #288,ChThreePer
	clr.w ChzeroVol
	clr.w ChOneVol
	clr.w ChTwoVol
	clr.w ChThreeVol
		
	move.l MusicStart,AudPos	;Initialize Music Pointer.	
			
	move.w #$8780,$dff09a		; Enable all sound
					; interrupts.	
	move.w #$800f,$dff096		; Enable audio DMA.

	bset #0,PlaySignal		; Enable 'GetNextBeat'.
	rts

InterruptsSetup:		; Sets up sound and Vert-Blank
	move.w #$0780,$dff09a	; interrupts.
	move.l $004,a6
	move.l #7,d0		; Channel 0 int number.
	lea Data0,a1		; Channel 0 int data.
	jsr -162(a6)		; SetIntVector().
	move.l #8,d0		; Channel 1 int number.
	lea Data1,a1		; etc etc.
	jsr -162(a6)
	move.l #9,d0
	lea Data2,a1
	jsr -162(a6)
	move.l #10,d0
	lea Data3,a1
	jsr -162(a6)
	move.l #5,d0		; REMOVE THESE THREE LINES
	lea Data4,a1		; IF USING EXISTING VERT-BLANKING
	jsr -168(a6)		; INTERRUPT.
	rts

; The Interrupt Data.	

Data0:
	dc.l $00000902
	dc.w 0,0,0,0,0,0,0
	dc.l AudIntzero

Data1:
	dc.l $00000902,0,0,0
	dc.w 0
	dc.l AudIntOne

Data2:
	dc.l $00000902,0,0,0
	dc.w 0
	dc.l AudIntTwo

Data3:
	dc.l $00000902,0,0,0
	dc.w 0
	dc.l AudIntThree

Data4:
	dc.l $00000902,0,0,0
	dc.w 0
	dc.l GetNextBeat

TempoCounter:
	dc.b 1

Ch0Buffer:
	dc.b 0
Ch1Buffer:
	dc.b 0
Ch2Buffer:
	dc.b 0
Ch3Buffer:
	dc.b 0

	even

GetNextBeat:			; This subroutine should be called
	move.w #$0020,$dff09c	; once every Vertical Blank.
	btst #0,PlaySignal
	bne DoPlay
	rts
DoPlay:	
	subq.b #1,TempoCounter
	beq NextNotePeriod
	rts

NextNotePeriod:
	movem.l a3/a1/a0/d7/d3/d0,-(a7)
	move.l AudPos,a1
	cmpa.l MusicEnd,a1	; Have we reached end of music?
	bge TurnOffMusic
	move.b Tempo,TempoCounter	; Reset Tempo counter.
	move.l AudPos,a1
	tst.b (a1)
	beq TryNewNoteCh1
GetNewNoteCh0:
	move.w #2,$dff0a6
	move.w #$0001,$dff096
	move.w #$8080,$dff09c
	bsr GetNextNoteChannel0
	bclr #0,SignalCh0
	move.l ChzeroLoc,$dff0a0
	move.w ChzeroLen,$dff0a4
	move.w ChzeroPer,$dff0a6
	move.w ChzeroVol,$dff0a8
	move.w #$8001,$dff096

TryNewNoteCh1:
	tst.b 2(a1)
	beq TryNewNoteCh2
GetNewNoteCh1:
	move.w #2,$dff0b6
	move.w #$0002,$dff096
	move.w #$8100,$dff09c
	bsr GetNextNoteChannel1
	bclr #0,SignalCh1
	move.l ChOneLoc,$dff0b0
	move.w ChOneLen,$dff0b4
	move.w ChOnePer,$dff0b6
	move.w ChOneVol,$dff0b8
	move.w #$8002,$dff096

TryNewNoteCh2:
	tst.b 4(a1)
	beq TryNewNoteCh3 
GetNewNoteCh2:
	move.w #2,$dff0c6
	move.w #$0004,$dff096
	move.w #$8200,$dff09c
	bsr GetNextNoteChannel2
	move.l ChTwoLoc,$dff0c0
	move.w ChTwoLen,$dff0c4
	move.w ChTwoPer,$dff0c6
	move.w ChTwoVol,$dff0c8
	bclr #0,SignalCh2
	move.w #$8004,$dff096

TryNewNoteCh3:
	tst.b 6(a1)
	beq Vertblend
GetNewNoteCh3:
	move.w #2,$dff0d6
	move.w #$0008,$dff096
	move.w #$8400,$dff09c
	bsr GetNextNoteChannel3
	move.l ChThreeLoc,$dff0d0
	move.w ChThreeLen,$dff0d4
	move.w ChThreePer,$dff0d6
	move.w ChThreeVol,$dff0d8
	bclr #0,SignalCh3
	move.w #$8008,$dff096

Vertblend:
	addq.l #8,AudPos
	movem.l (a7)+,a3/a1/a0/d7/d3/d0
	rts
TurnOffMusic:
	clr.w $dff0a8
	clr.w $dff0b8
	clr.w $dff0c8
	clr.w $dff0d8
	move.w #$000f,$dff096
	bclr #0,PlaySignal
	bra Vertblend
	


	
AudIntzero:			; Interrupt for Channel 0.
	move.w #$0080,$dff09c
	btst #0,SignalCh0
	beq donowt0
	clr.w $dff0a8
donowt0:
	bset #0,SignalCh0
	rts

AudIntOne:			; Interrupt for Channel 0.
	move.w #$0100,$dff09c
	btst #0,SignalCh1
	beq donowt1
	clr.w $dff0b8
donowt1:
	bset #0,SignalCh1
	rts

AudIntTwo:			; Interrupt for Channel 0.
	move.w #$0200,$dff09c
	btst #0,SignalCh2
	beq donowt2
	clr.w $dff0c8
donowt2:
	bset #0,SignalCh2
	rts

AudIntThree:			; Interrupt for Channel 0.
	move.w #$0400,$dff09c
	btst #0,SignalCh3
	beq donowt3
	clr.w $dff0d8
donowt3:
	bset #0,SignalCh3
	rts

SignalCh0:
	dc.b 0
SignalCh1:
	dc.b 0
SignalCh2:
	dc.b 0
SignalCh3:
	dc.b 0

Delay:
	move.w #$150,d0
Delay2:
	dbf d0,Delay2
	rts

GetNextNoteChannel0:
	bsr Delay
	moveq #0,d0
	move.b (a1),d7		; a1 contains AudPos
	andi.l #$3f,d7
	addq.b #1,d7
	move.w d7,ChzeroVol
	move.b 1(a1),d7
	andi.l #$f0,d7		; Extract Sample Number.
	lsr.b #1,d7
	lea SampleBase,a3
	move.l (a3,d7),ChzeroLoc
	move.w 6(a3,d7),ChzeroLen
	move.b 1(a1),d7
	and.b #$0f,d7		; Extract note.
	subq.b #1,d7
	asl.b #1,d7
	lea Periodbase,a3
	move.w (a3,d7),d3	; D3 contains Period.
	move.b (a1),d7
	lsr.b #6,d7		; Extract Octave.
	btst #1,d7
	beq lat0
	lsr.w #1,d3
lat0:	
	tst.b d7
	bne Leave0
	asl.w #1,d3
Leave0:
	move.w d3,ChzeroPer	; Octave modulated Period.
	rts

GetNextNoteChannel1:
	bsr Delay
	moveq #2,d0
	move.b (a1,d0),d7
	andi.l #$3f,d7
	addq.b #1,d7
	move.w d7,ChOneVol
	move.b 1(a1,d0),d7
	andi.l #$f0,d7		; Extract Sample.
	lsr.b #1,d7
	lea SampleBase,a3
	move.l (a3,d7),ChOneLoc
	move.w 6(a3,d7),ChOneLen
	move.b 1(a1,d0),d7
	and.b #$0f,d7		; Extract note.
	subq.b #1,d7
	asl.b #1,d7
	lea Periodbase,a3
	move.w (a3,d7),d3	; D3 contains Period.
	move.b (a1,d0),d7
	lsr.b #6,d7		; Extract Octave.
	btst #1,d7
	beq lat1
	lsr.w #1,d3
lat1:	
	tst.b d7
	bne Leave1
	asl.w #1,d3
Leave1:
	move.w d3,ChOnePer	; Octave modulated Period.
	rts


GetNextNoteChannel2:
	bsr Delay
	moveq #4,d0
	move.b (a1,d0),d7
	andi.l #$3f,d7
	addq.b #1,d7
	move.w d7,ChTwoVol
	move.b 1(a1,d0),d7
	andi.l #$f0,d7		; Extract Sample.
	lsr.b #1,d7
	lea SampleBase,a3
	move.l (a3,d7),ChTwoLoc
	move.w 6(a3,d7),ChTwoLen
	move.b 1(a1,d0),d7
	and.b #$0f,d7		; Extract note.
	subq.b #1,d7
	asl.b #1,d7
	lea Periodbase,a3
	move.w (a3,d7),d3	; D3 contains Period.
	move.b (a1,d0),d7
	lsr.b #6,d7		; Extract Octave.
	btst #1,d7
	beq lat2
	lsr.w #1,d3
lat2:	
	tst.w d7
	bne Leave2
	asl.w #1,d3
Leave2:
	move.w d3,ChTwoPer	; Octave modulated Period.
	rts

GetNextNoteChannel3:
	bsr Delay
	moveq #6,d0
	move.b (a1,d0),d7
	andi.l #$3f,d7
	addq.b #1,d7
	move.w d7,ChThreeVol
	move.b 1(a1,d0),d7
	andi.l #$f0,d7		; Extract Sample.
	lsr.b #1,d7
	lea SampleBase,a3
	move.l (a3,d7),ChThreeLoc
	move.w 6(a3,d7),ChThreeLen
	move.b 1(a1,d0),d7
	and.b #$0f,d7		; Extract note.
	subq.b #1,d7
	asl.b #1,d7
	lea Periodbase,a3
	move.w (a3,d7),d3	; D3 contains Period.
	move.b (a1,d0),d7
	lsr.b #6,d7		; Extract Octave.
	btst #1,d7
	beq lat3
	lsr.w #1,d3
lat3:	
	tst.w d7
	bne Leave3
	asl.w #1,d3
Leave3:
	move.w d3,ChThreePer	; Octave modulated Period.
	rts

Periodbase:
	dc.w 204,216,229,242,257,272,288,305,323,343,363,384
	dc.b 102,108,114,121,128,136,144,153,162,171,181,192
AudPos:
	dc.l 0
ChzeroLoc:
	dc.l 0
ChzeroLen:
	dc.w 0
ChzeroPer:
	dc.w 0
ChzeroVol:
	dc.w 0
ChOneLoc:
	dc.l 0
ChOneLen:
	dc.w 0
ChOnePer:
	dc.w 0
ChOneVol:	
	dc.w 0
ChTwoLoc:
	dc.l 0
ChTwoLen:
	dc.w 0
ChTwoPer:
	dc.w 0
ChTwoVol:
	dc.w 0
ChThreeLoc:
	dc.l 0
ChThreeLen:
	dc.w 0
ChThreePer:
	dc.w 0
ChThreeVol:
	dc.w 0

ChzeroLocStore:
	dc.l 0
ChzeroLenStore:
	dc.w 0
ChzeroPerStore:
	dc.w 0
ChzeroVolStore:
	dc.w 0
ChOneLocStore:
	dc.l 0
ChOneLenStore:
	dc.w 0
ChOnePerStore:
	dc.w 0
ChOneVolStore:	
	dc.w 0
ChTwoLocStore:
	dc.l 0
ChTwoLenStore:
	dc.w 0
ChTwoPerStore:
	dc.w 0
ChTwoVolStore:
	dc.w 0
ChThreeLocStore:
	dc.l 0
ChThreeLenStore:
	dc.w 0
ChThreePerStore:
	dc.w 0
ChThreeVolStore:
	dc.w 0
PlaySignal:
	dc.b 0,0



UnloadInterrupt:
	Move.w #$000f,$dff096		; Turn-off Audio DMA.
	move.w #$0780,$dff09a		; Disable Sound Interrupts.
	move.l $04,a6			; Remove Vert-B Int Server.
	move.l #5,d0
	lea Data4,a1
	jsr -174(a6)
end:
	rts

Start:	
	bsr Play
Waitbutton:	
	btst #6,$bfe0ff			; Is mouse button pressed.
	bne Waitbutton			; If not loop.
	bra UnloadInterrupt		; If so then Exit.







