****************************************************************************
*									   *	
*									   *
*		  Sound Abspiel Routine zu Sound FX		   	   *
*									   *
*		       © 1988 LINEL Switzerland				   *	
*									   *	
*									   *	
*									   *
****************************************************************************

;Einbau:
;	1.Sound FX laden
;	2.Song schreiben oder einladen
;	3.Sounddaten mit Save Datas abspeichern 
;	4.Sound FX verlassen und Seka laden
;	5.Source des Programms laden in den wir den Song einbauen
;	  wollen.
;	6.Am Anfang des Programms 'bsr StartSound' einsetzen
;	7.Am Schluss des Programms 'bsr StopSound' einsetzen
;	8.Diesen AbspielSource laden
;	9.Das Label 'Laenge' auf die Laenge des Datenfiles setzen
;	10.Assemblieren
;	11.DatenFile mit 'ri' nach 'datas' laden
;	12.fertig

;------------------------------------------------------------------------


	Laenge = 60000			;Länge der Sounddaten

;------------------------------------------------------------------------
Beispiel:
	bsr.s	StartSound
	bsr.s	Intro
	bsr.L	StopSound
	moveq	#0,d0
	rts
;-------------------------------------------------------------------------

Intro:
	move	d0,$dff180
	addq	#1,d0
	btst	#6,$bfe001
	bne.s	Intro
	rts

;---------------------------------------------------------------------------

;Ergebis in D0           0=alles Ok.        $ff = Timer nicht bekommen

StartSound:
	movem.l	d1-d7/a0-a6,-(SP)
	clr	ChipFlag		;Flag fuer Daten im Chip
	move.l	4,a6			;ExecBase
	move.l	SongPointer(pc),a1	;Zeiger auf Daten
	jsr	-534(A6)		;TypeOfMem()
	btst	#1,d0			;Sind Daten im Chip ?
	bne.s	ChipOK			;ja ->
	st	ChipFlag		;Flag fuer AllocMemChip setzen
	move.l	#Laenge,d0		;wenn Fast Memory ->
	addq.l	#4,d0			
	moveq	#2,d1			;Chip Speicher
	jsr	-198(a6)		;reservieren
	moveq	#-1,d5			;Fehlerflag
	tst.l	d0			;Speicher bekommen ?
	beq.L	EndStart		;wenn nicht -> Ende
	move.l	d0,a1			;Ziel
	move.l	SongPointer(pc),a0	;Quelle
	move.l	#Laenge,d1
	addq.l	#4,d1			;Reserve
	lsr.l	#2,d1			;divu #4
	subq	#1,d1			;für dbf
CopyMem:
	move.l	(a0)+,(A1)+		;Daten ins ChipMem
	move	(a0),$dff180
	dbf	d1,CopyMem		;kopieren
	move.l	d0,SongPointer

ChipOk:
	move.l	SongPointer(pc),a0	;Zeiger auf SongDaten
	add	#60,a0			;Laengentabelle ueberspringen
	move.b	470(a0),AnzPat+1	;Laenge des Sounds
	move	4(A0),DelayValue 	;Geschwindigkeit
	bsr	SongLen			;Länge der Songdaten berechnen
	add.l	d0,a0			;Zur Adresse der Songstr.
	add.w	#600,a0			;Laenge der SongStr.
	move.l	SongPointer(pc),a2
	lea	Instruments(pc),a1	;Tabelle auf Samples
	moveq	#14,d7			;15 Instrumente
CalcIns:
	move.l	a0,(A1)+		;Startadresse des Instr.
	add.l	(a2)+,a0		;berechnen un speichern
	dbf	d7,CalcIns

	lea	CiaaResource(pc),a1	;'ciaa.resource'
	moveq	#0,d0			;Version egal
	jsr	-498(A6)		;OpenResource()
	move.l	d0,CiaaBase		;Resource Base speichern
	move.l	d0,a6
	bsr	PlayDisable		;Sound DMA abschalten
	lea	Interrupt(pc),a1	;Sound Interupt Structure
	moveq	#0,d0			;TimerA
	jsr	-6(A6)			;installieren
	move.l	d0,d5			;ergebnis speichern
	bsr	PlayInit		;Loop Bereich setzen
	bsr	PlayEnable		;Player erlauben
	bsr	InitTimer		;Timer starten
	moveq	#0,d0			;Ergebnisregister loeschen
EndStart:
	tst.l	d5			;ergebnis von Resource
	sne	d0			;ergebnis in d0 setzen 
	movem.l	(SP)+,d1-d7/a0-a6
	rts

;---------------------------------------------------------------------------

StopSound:
	movem.l	d1-d7/a0-a6,-(SP)
	move.l	4,a6			;ExecBase
	tst	ChipFlag		;mussten wir Speicher reservieren ?
	beq.s	NoFreeSong		
	move.l	#Laenge,d0		;Länge der Daten
	addq.l	#4,d0			;Reserve
	move.l	SongPointer(pc),a1	;Zeiger auf Daten
	jsr	-210(a6)		;FreeMem()
NoFreeSong:
	move.l	CiaaBase(pc),a6		;Zeiger auf Ciaa Resource
	lea	Interrupt(pc),a1	;Zeiger auf Int. Strukture
	moveq	#0,d0			;Timer A
	jsr	-12(A6)			;Interupt entfernen
	bsr	PlayDisable		;Player sperren
	moveq	#0,d0			;Alles Ok	
	movem.l	(SP)+,d1-d7/a0-a6
	rts
;---------------------------------------------------------------------------

SongLen:
	movem.l	d1-d7/a0-a6,-(SP)
	move.l	SongPointer,a0
	lea	532(A0),a0
	move	AnzPat(pc),d2		;wieviel Positions
	subq	#1,d2			;für dbf
	moveq	#0,d1
	moveq	#0,d0
SongLenLoop:
	move.b	(a0)+,d0		;Patternnummer holen
	cmp.b	d0,d1			;ist es die höchste ?
	bhi.s	LenHigher		;nein!
	move.b	d0,d1			;ja
LenHigher:
	dbf	d2,SongLenLoop
	move.l	d1,d0			;Hoechste BlockNummer nach d0
	addq	#1,d0			;plus 1
	mulu	#1024,d0		;Laenge eines Block
	movem.l	(SP)+,d1-d7/a0-a6
	rts

;--------------------------------------------------------------------	


Interrupt:
	dc.l	0			;letzter Node
	dc.l	0			;nächster Node
	dc.b	2			;Node Type = Interrupt
	dc.b	0 			;Priorität
	dc.l	InterruptName		;Name
	dc.l	0			;Zeiger auf Daten
	dc.l	IntCode			;Interrupt Routine

;-------------------------------------------------------------------

InitTimer:
	move.b	#%10000001,$bfee01	;Timer starten
	lea	DelayValue(pc),a1
	move.b	1(a1),$bfe401		;Timer A low
	move.b	0(a1),$bfe501		;Timer A high
	rts

;--------------------------------------------------------------------

PlayInit:
	lea	Instruments(pc),a0	;Zeiger auf instr.Tabelle
	moveq	#14,d7			;15 Instrumente
InitLoop:	
	move.l	(A0)+,a1		;Zeiger holen
	clr.l	(A1)			;erstes Longword löschen
	dbf	d7,InitLoop
	rts

;-----------------------------------------------------------------------

PlayEnable:
	lea	$dff000,a0		;AMIGA
	move.w	#-1,PlayLock		;player zulassen
	clr	$a8(A0)			;Alle Voloumenregs. auf 0
	clr	$b8(A0)
	clr	$c8(a0)
	clr	$d8(a0)
	clr.w	Timer			;zahler auf 0
	clr.l	TrackPos		;zeiger auf pos
	clr.l	PosCounter		;zeiger innehalb des pattern
	rts
;----------------------------------------------------------------------

PlayDisable:
	lea	$dff000,a0		;AMIGA
	clr.w	PlayLock		;player sperren
	clr	$a8(a0)			;volumen auf 0
	clr	$b8(a0)
	clr	$c8(a0)
	clr	$d8(a0)
	move.w	#$f,$96(A0)		;dma sperren
	rts

;---------------------------------------------------------------------

IntCode:
	bsr	PlaySong		;Note spielen
	moveq	#0,d0			;kein Fehler
	rts

;----------------------------------------------------------------------


;hier werden 5 * effekte gespielt und einmal der song

PlaySong:				;HauptAbspielRoutine
	movem.l	d0-d7/a0-a6,-(SP)
	addq.w	#1,Timer		;zähler erhöhen
	cmp.w	#6,Timer		;schon 6?
	bne.s	CheckEffects		;wenn nicht -> effekte
	clr.w	Timer			;sonst zähler löschen
	bsr 	PlaySound		;und sound spielen
NoPlay:	movem.l	(SP)+,d0-d7/a0-a6
	rts

;-------------------------------------------------------------------
CheckEffects:
	moveq	#3,d7			;4 kanäle
	lea	StepControl0,a4
	lea	ChannelData0(pc),a6	;zeiger auf daten für 0
	lea	$dff0a0,a5		;Kanal 0
EffLoop:
	movem.l	d7/a5,-(SP)
	bsr.s	MakeEffekts		;Effekt spielen
	movem.l	(Sp)+,d7/a5
NoEff:
	add	#8,a4
	add	#$10,a5			;nächster Kanal
	add	#22,a6			;Nächste KanalDaten
	dbf	d7,EffLoop
	movem.l	(a7)+,d0-d7/a0-a6
	rts

MakeEffekts:
	move	(A4),d0
	beq.s	NoStep
	bmi.s	StepItUp
	add	d0,2(A4)
	move	2(A4),d0
	move	4(A4),d1
	cmp	d0,d1
	bhi.s	StepOk
	move	d1,d0
StepOk:
	move	d0,6(a5)
	MOVE	D0,2(A4)
	rts

StepItUp:
	add	d0,2(A4)
	move	2(A4),d0
	move	4(A4),d1
	cmp	d0,d1
	blt.s	StepOk
	move	d1,d0
	bra.s	StepOk



NoStep:
	move.b	2(a6),d0
	and.b	#$0f,d0
	cmp.b	#1,d0
	beq	appreggiato
	cmp.b	#2,d0
	beq	pitchbend
	cmp.b	#3,d0
	beq	LedOn
	cmp.b	#4,d0
	beq	LedOff
	cmp.b	#7,d0
	beq.s	SetStepUp
	cmp.b	#8,d0
	beq.s	SetStepDown
	rts

LedOn:
	bset	#1,$bfe001
	rts
LedOff:
	bclr	#1,$bfe001
	rts

SetStepUp:
	moveq	#0,d4
StepFinder:
	clr	(a4)
	move	(A6),2(a4)
	moveq	#0,d2
	move.b	3(a6),d2
	and	#$0f,d2
	tst	d4
	beq.s	NoNegIt
	neg	d2
NoNegIt:	
	move	d2,(a4)
	moveq	#0,d2
	move.b	3(a6),d2
	lsr	#4,d2
	move	(a6),d0
	lea	NoteTable,a0

StepUpFindLoop:
	move	(A0),d1
	cmp	#-1,d1
	beq.s	EndStepUpFind
	cmp	d1,d0
	beq.s	StepUpFound
	addq	#2,a0
	bra.s	StepUpFindLoop
StepUpFound:
	lsl	#1,d2
	tst	d4
	bne.s	NoNegStep
	neg	d2
NoNegStep:
	move	(a0,d2.w),d0
	move	d0,4(A4)
	rts

EndStepUpFind:
	move	d0,4(A4)
	rts
	
SetStepDown:
	st	d4
	bra.s	StepFinder


StepControl0:
	dc.l	0,0
StepControl1:
	dc.l	0,0
StepControl2:
	dc.l	0,0
StepControl3:
	dc.l	0,0


appreggiato:
	lea	ArpeTable,a0
	moveq	#0,d0
	move	Timer,d0
	subq	#1,d0
	lsl	#2,d0
	move.l	(A0,d0.l),a0
	jmp	(A0)

Arpe4:	lsl.l	#1,d0
	clr.l	d1
	move.w	16(a6),d1
	lea.l	NoteTable,a0
Arpe5:	move.w	(a0,d0.l),d2
	cmp.w	(a0),d1
	beq.s	Arpe6
	addq.l	#2,a0
	bra.s	Arpe5



Arpe1:	clr.l	d0
	move.b	3(a6),d0
	lsr.b	#4,d0
	bra.s	Arpe4


Arpe2:	clr.l	d0
	move.b	3(a6),d0
	and.b	#$0f,d0
	bra.s	Arpe4

Arpe3:	move.w	16(a6),d2
	
Arpe6:	move.w	d2,6(a5)
	rts


pitchbend:
	clr.l	d0
	move.b	3(a6),d0
	lsr.b	#4,d0
	tst.b	d0
	beq.s	pitch2
	add.w	d0,(a6)
	move.w	(a6),6(a5)
	rts
pitch2:	clr.l	d0
	move.b	3(a6),d0
	and.b	#$0f,d0
	tst.b	d0
	beq.s	pitch3
	sub.w	d0,(a6)
	move.w	(a6),6(a5)
pitch3:	rts


;--------------------------------------------------------------------

PlaySound:
	move.l	SongPointer(pc),a0	;Zeiger auf SongFile
	add	#60,a0			;Laengentabelle ueberspringen
	move.l	a0,a3
	move.l	a0,a2
	lea	600(A0),a0		;Zeiger auf BlockDaten
	add	#472,a2			;zeiger auf Patterntab.
	add	#12,a3			;zeiger auf Instr.Daten
	move.l	TrackPos(pc),d0		;Postionzeiger
	clr.l	d1
	move.b	(a2,d0.l),d1		;dazugehörige PatternNr. holen
	moveq	#10,d7
	lsl.l	d7,d1			;*1024 / länge eines Pattern
	add.l	PosCounter,d1		;Offset ins Pattern
	clr.w	DmaCon
	lea	StepControl0,a4
	lea	$dff0a0,a5		;Zeiger auf Kanal0
	lea	ChannelData0(pc),a6	;Daten für Kanal0
	moveq	#3,d7			;4 Kanäle
SoundHandleLoop:
	bsr	PlayNote		;aktuelle Note spielen
	add	#8,a4
	add.l	#$10,a5			;nächster Kanal
	add.l	#22,a6			;nächste Daten
	dbf	d7,SoundHandleLoop	;4*
	
	move	DmaCon(pc),d0		;DmaBits
	bset	#15,d0			;Clear or Set Bit setzen
	move.w	d0,$dff096		;DMA ein!

	move	#300,d0			;Verzögern (genug für MC68030)
Delay2:
	dbf	d0,Delay2

	lea	ChannelData3(pc),a6
	lea	$dff0d0,a5
	moveq	#3,d7
SetRegsLoop:
	move.l	10(A6),(a5)		;Adresse
	move	14(A6),4(A5)		;länge
NoSetRegs:
	sub	#22,a6			;nächste Daten
	sub	#$10,a5			;nächster Kanal
	dbf	d7,SetRegsLoop
	tst	PlayLock
	beq.s	NoEndPattern
	add.l	#16,PosCounter		;PatternPos erhöhen
	cmp.l	#1024,PosCounter	;schon Ende ?
	blt.s	NoEndPattern

	clr.l	PosCounter		;PatternPos löschen
	addq.l	#1,TrackPos		;Position erhöhen
NoAddPos:
	move.w	Anzpat(pc),d0		;AnzahlPosition
	move.l	TrackPos(pc),d1		;Aktuelle Pos
	cmp.w	d0,d1			;Ende?
	bne.s	NoEndPattern		;nein!
	clr.l	TrackPos		;ja/ Sound von vorne
NoEndPattern:
	rts



PlayNote:
	clr.l	(A6)
	tst	PlayLock		;Player zugellassen ?
	beq.s	NoGetNote		;
	move.l	(a0,d1.l),(a6)		;Aktuelle Note holen
NoGetNote:
	addq.l	#4,d1			;PattenOffset + 4
	clr.l	d2
	cmp	#-3,(A6)		;Ist Note = 'PIC' ?
	beq	NoInstr2		;wenn ja -> ignorieren
	move.b	2(a6),d2		;Instr Nummer holen	
	and.b	#$f0,d2			;ausmaskieren
	lsr.b	#4,d2			;ins untere Nibble
	tst.b	d2			;kein Intrument ?
	beq.L	NoInstr2		;wenn ja -> überspringen
	
	clr.l	d3
	lea.l	Instruments(pc),a1	;Instr. Tabelle
	move.l	d2,d4			;Instrument Nummer
	subq	#1,d2
	lsl	#2,d2			;Offset auf akt. Instr.
	mulu	#30,d4			;Offset Auf Instr.Daten
	move.l	(a1,d2.w),4(a6)		;Zeiger auf akt. Instr.
	move.w	(a3,d4.l),8(a6)		;Instr.Länge
	move.w	2(a3,d4.l),18(a6)	;Volume
	move.w	4(a3,d4.l),d3		;Repeat
	tst	d3			;kein Repeat?
	beq.s	NoRepeat		;Nein!
					;Doch!
	
	move.l	4(a6),d2		;akt. Instr.
	add.l	d3,d2			;Repeat dazu
	move.l	d2,10(a6)		;Repeat Instr.
	move.w	6(a3,d4),14(a6)		;rep laenge
	move.w	18(a6),d3 		;Volume in HardReg.
	bra.s	NoInstr

NoRepeat:
	move.l	4(a6),d2		;Instrument	
	add.l	d3,d2			;rep Offset
	move.l	d2,10(a6)		;in Rep. Pos.
	move.w	6(a3,d4.l),14(a6)	;rep Laenge
	move.w	18(a6),d3 		;Volume in Hardware

CheckPic:
NoInstr:
	move.b	2(A6),d2
	and	#$0f,d2
	cmp.b	#5,d2
	beq.s	ChangeUpVolume
	cmp.b	#6,d2
	bne.L	SetVolume2
	moveq	#0,d2
	move.b	3(A6),d2
	sub	d2,d3		
	tst	d3
	bpl	SetVolume2	
	clr	d3
	bra.L	SetVolume2
ChangeUpVolume:
	moveq	#0,d2
	move.b	3(A6),d2
	add	d2,d3
	tst	d3
	cmp	#64,d3
	ble.L	SetVolume2
	move	#64,d3
SetVolume2:
	move	d3,8(A5)
	
NoInstr2:
	cmp	#-3,(A6)		;Ist Note = 'PIC' ?
	bne.s	NoPic		
	clr	2(A6)			;wenn ja -> Note auf 0 setzen
	bra.s	NoNote	
NoPic:
	tst	(A6)			;Note ?
	beq.s	NoNote			;wenn 0 -> nicht spielen
	
	clr	(a4)
	move.w	(a6),16(a6)		;eintragen
	move.w	20(a6),$dff096		;dma abschalten
	move.l	d7,-(SP)
	move	#300,d7			;genug für MC68030
Delay1:
	dbf	d7,Delay1		;delay
	move.l	(SP)+,d7
	cmp	#-2,(A6)		;Ist es 'STP'
	bne.s	NoStop			;Nein!
	clr	8(A5)
	bra	Super
NoStop:
	move.l	4(a6),0(a5)		;Intrument Adr.
	move.w	8(a6),4(a5)		;Länge
	move.w	0(a6),6(a5)		;Period
Super:
	move.w	20(a6),d0		;DMA Bit
	or.w	d0,DmaCon		;einodern
NoNote:
	rts

;--------------------------------------------------------------------
ArpeTable:
	dc.l	Arpe1
	dc.l	Arpe2
	dc.l	Arpe3
	dc.l	Arpe2
	dc.l	Arpe1


ChannelData0:
	blk.l	5,0			;Daten für Note
	dc.w	1			;DMA - Bit
ChannelData1:	
	blk.l	5,0			;u.s.w
	dc.w	2
ChannelData2:	
	blk.l	5,0			;etc.
	dc.w	4
ChannelData3:	
	blk.l	5,0			;a.s.o
	dc.w	8
Instruments:
	blk.l	15,0			;Zeiger auf die 15 Instrumente
PosCounter:
	dc.l	0			;Offset ins Pattern
TrackPos:
	dc.l	0			;Position Counter
Timer:
	dc.w	0			;Zähler 0-5
DmaCon:
	dc.w	0			;Zwischenspeicher für DmaCon
AnzPat:
	dc.w	1			;Anzahl Positions
PlayLock:
	dc.w	0			;Flag fuer 'Sound erlaubt'
DelayValue:
	dc.w	14565
SongPointer:
	dc.l	LenghtTable
ChipFlag:
	dc.w	0
CiaaBase:
	dc.l	0
InterruptName:
	dc.b	"Chris's SoundInterrupt",0
CiaaResource:
	dc.b	'ciaa.resource',0


even

Reserve:
	dc.w	856,856,856,856,856,856,856,856,856,856,856,856
NoteTable:
	dc.w	856,808,762,720,678,640,604,570,538,508,480,453   ;1.Okt
	dc.w	428,404,381,360,339,320,302,285,269,254,240,226   ;2.Okt
	dc.w	214,202,190,180,170,160,151,143,135,127,120,113	  ;3.Okt
	dc.w	113,113,113,113,113,113,113,113,113,113,113,113	  ;Reserve
	dc.w	-1

LenghtTable:

Datas:
	Songstr = lenghttable+60
	
	blk.b	Laenge,0		;hierhin die Daten!!!

EndSongStr:

