*
* 6581.asm - SID-Emulation
*
* Copyright (C) 1994-1996 by Christian Bauer
*

*
* Anmerkungen:
* ------------
*
* Funktionsprinzip:
*  - Es bestehen vier Möglichkeiten zur SID-Emulation, die über das
*    Flag UseSIDCard ausgewählt werden: Keine Emulation (d.h. kein Ton),
*    die Verwendung der SID-Karte, die "6581sid.library" und die
*    "playsid.library"
*
* SID-Karte:
*  - Die SID-Karte enthält einen echten C64-SID, der im Adreßraum
*    $a00001-$a00039 (Register an den ungeraden Adressen) eingeblendet
*    ist (z.B. auf einer Zorro-II-Karte)
*  - Wegen Timing-Problemen mit der Karte werden Lesezugriffe aus
*    SID-Registern nur simuliert
*  - Die Power-LED ist mit dem Reset-Eingang des SID verbunden,
*    d.h. ein kurzes Aus- und Einschalten der LED setzt den SID
*    zurück
*
* PlaySID:
*  - Da die playsid.library keinen Befehl zum direkten Setzen von SID-
*    Registern hat, wird ein Trick angewendet: Es wird ein Dummy-Modul
*    installiert, dessen Play-Routine nur die Aufgabe hat, ein einziges
*    SID-Register zu setzen. Schreibt der Prozessor in ein SID-Register,
*    werden Registernummer und Wert in die Play-Routine im C64-RAM der
*    playsid.library geschrieben und über ForwardSong ein Aufruf der
*    Play-Routine erzwungen
*

		MACHINE	68020

		INCLUDE	"exec/types.i"
		INCLUDE	"exec/macros.i"
		INCLUDE	"exec/nodes.i"
		INCLUDE	"libraries/playsidbase.i"
		INCLUDE	"libraries/playsid_lib.i"
		INCLUDE	"hardware/intbits.i"

		XREF	_SysBase
		XREF	_GfxBase
		XREF	_SID6581Base
		XREF	_PlaySidBase

		XREF	Random

		XDEF	Reset6581
		XDEF	ReadFrom6581
		XDEF	WriteTo6581
		XDEF	_PauseSound
		XDEF	_ResumeSound
		XDEF	_GetSIDDump
		XDEF	InitSID
		XDEF	ExitSID
		XDEF	ChangedSIDPrefs
		XDEF	SIDType		;Prefs

		SECTION	"text",CODE

*
* Definitionen
*

; SIDType
SIDTYPE_OFF	= 0	;Audio-Ausgabe aus
SIDTYPE_CARD	= 1	;SID-Karte
SIDTYPE_A64	= 2	;6581sid.library
SIDTYPE_PSID	= 3	;playsid.library

; 6581sid.library
SID_AllocSID	= -30
SID_FreeSID	= -36
SID_Interrupt	= -42
SID_Initialize	= -48
SID_ResetSID	= -54
SID_IRQOnOff	= -60
SID_ReadReg	= -72
SID_WriteReg	= -78

; SID Handle Struktur
sid_Enabled	= 4
sid_Filter	= 5
sid_60Hz	= 6
sid_RingQual	= 7
sid_SyncQual	= 8
sid_ADSRQual	= 13
sid_IRQRate	= 18


**
** SID-Emulation starten
**

InitSID		move.w	SIDType,CurrentSIDType
		bsr	OpenSID
		st.b	SIDInited
		rts


**
** SID-Emulation beenden
**

ExitSID		tst.b	SIDInited
		beq	1$
		bsr	CloseSID
1$		rts


**
** SID-Einstellungen wurde verändert
**

; Schließen und neu öffnen, wenn sich die Einstellungen geändert haben
ChangedSIDPrefs	move.w	SIDType,d0
		cmp.w	CurrentSIDType,d0
		beq	1$

		bsr	CloseSID
		move.w	SIDType,CurrentSIDType
		bsr	OpenSID

		moveq	#0,d2
2$		move.w	d2,d0
		move.b	(Registers,d0.w),d1
		bsr	WriteTo6581	;SID-Register neu laden
		addq.w	#1,d2
		cmp.w	#24,d2
		bne	2$
1$		rts


*
* Alles für SID-Emulation mit CurrentSIDType vorbereiten
*

OpenSID		cmp.w	#SIDTYPE_A64,CurrentSIDType
		beq	OpenA64
		cmp.w	#SIDTYPE_PSID,CurrentSIDType
		beq	OpenPSID
		rts

OpenA64		move.l	_SID6581Base,d0		;6581sid.library offen?
		beq	1$

		move.l	a6,-(sp)		;Ja, SIDHandle holen
		move.l	d0,a6
		jsr	SID_AllocSID(a6)
		move.l	d0,SIDHandle
		beq	2$

		move.l	d0,a1			;Parameter setzen
		st.b	sid_Enabled(a1)
		clr.b	sid_Filter(a1)
		st.b	sid_60Hz(a1)
		move.b	#48,sid_RingQual(a1)
		move.b	#48,sid_SyncQual(a1)
		move.b	#48,sid_ADSRQual(a1)
		move.w	#60,sid_IRQRate(a1)
		jsr	SID_Initialize(a6)

		move.l	SIDHandle,a1		;SID anschalten
		moveq	#0,d0
		jsr	SID_IRQOnOff(a6)

		move.l	_SysBase,a6		;VBI einrichten
		lea	VBInterrupt,a1
		moveq	#INTB_VERTB,d0
		JSRLIB	AddIntServer

2$		move.l	(sp)+,a6
1$		rts

OpenPSID	clr.b	PSIDOpened

		move.l	_PlaySidBase,d0		;playsid.library offen?
		beq	1$

		move.l	a6,-(sp)
		move.l	d0,a6			;Ja, Resourcen belegen
		jsr	_LVOAllocEmulResource(a6)
		tst.l	d0
		bne	2$

		lea	PSIDHeader,a0		;"Modul" setzen
		move.l	a0,a1
		move.l	#PSIDEnd-PSIDHeader,d0
		jsr	_LVOSetModule(a6)

		moveq	#1,d0			;Und starten
		jsr	_LVOStartSong(a6)
		tst.l	d0
		beq	3$

		jsr	_LVOFreeEmulResource(a6) ;Nicht gelungen
		bra	2$

3$		bset	#1,$bfe001		;Filter aus
		st.b	PSIDOpened

2$		move.l	(sp)+,a6
1$		rts


*
* SID-Emulation mit CurrentSIDType beenden
*

CloseSID	cmp.w	#SIDTYPE_A64,CurrentSIDType
		beq	CloseA64
		cmp.w	#SIDTYPE_PSID,CurrentSIDType
		beq	ClosePSID
		rts

CloseA64	move.l	SIDHandle,d0
		beq	1$

		move.l	a6,-(sp)
		move.l	_SID6581Base,a6		;SID abschalten
		move.l	d0,a1
		moveq	#-1,d0
		jsr	SID_IRQOnOff(a6)

		move.l	_SysBase,a6		;VBI entfernen
		lea	VBInterrupt,a1
		moveq	#INTB_VERTB,d0
		JSRLIB	RemIntServer

		move.l	_SID6581Base,a6
		move.l	SIDHandle,a1		;Und SIDHandle freigeben
		jsr	SID_FreeSID(a6)
		clr.l	SIDHandle
		move.l	(sp)+,a6
1$		rts

ClosePSID	tst.b	PSIDOpened
		beq	1$

		move.l	a6,-(sp)
		move.l	_PlaySidBase,a6
		jsr	_LVOStopSong(a6)	;Alles freigeben
		jsr	_LVOFreeEmulResource(a6)
		bclr	#1,$bfe001		;Filter an
		clr.b	PSIDOpened
		move.l	(sp)+,a6
1$		rts


*
* Interrupt-Routine
* a1: SIDHandle
*

IntProc		move.l	_SID6581Base,a6
		jsr	SID_Interrupt(a6)
		moveq	#0,d0			;Nächsten Server aufrufen
		rts


**
** Sound stummschalten
**

_PauseSound	cmp.w	#SIDTYPE_A64,CurrentSIDType
		beq	PauseA64
		cmp.w	#SIDTYPE_PSID,CurrentSIDType
		beq	PausePSID
		rts

PauseA64	move.l	SIDHandle,d0
		beq	1$
		move.l	a6,-(sp)
		move.l	_SID6581Base,a6
		move.l	d0,a1
		moveq	#-1,d0
		jsr	SID_IRQOnOff(a6)
		move.l	(sp)+,a6
1$		rts

PausePSID	tst.b	PSIDOpened
		beq	1$
		move.l	a6,-(sp)
		move.l	_PlaySidBase,a6
		jsr	_LVOPauseSong(a6)
		move.l	(sp)+,a6
1$		rts


**
** Sound wieder laut machen
**

_ResumeSound	cmp.w	#SIDTYPE_A64,CurrentSIDType
		beq	ResumeA64
		cmp.w	#SIDTYPE_PSID,CurrentSIDType
		beq	ResumePSID
		rts

ResumeA64	move.l	SIDHandle,d0
		beq	1$
		move.l	a6,-(sp)
		move.l	_SID6581Base,a6
		move.l	d0,a1
		moveq	#0,d0
		jsr	SID_IRQOnOff(a6)
		move.l	(sp)+,a6
1$		rts

ResumePSID	tst.b	PSIDOpened
		beq	1$
		move.l	a6,-(sp)
		move.l	_PlaySidBase,a6
		jsr	_LVOContinueSong(a6)
		move.l	(sp)+,a6
1$		rts


**
** SID zurücksetzen
**

Reset6581	lea	Registers,a0
		moveq	#32/4-1,d0
1$		clr.l	(a0)+
		dbra	d0,1$

		cmp.w	#SIDTYPE_CARD,CurrentSIDType
		beq	ResetCard
		cmp.w	#SIDTYPE_A64,CurrentSIDType
		beq	ResetA64
		cmp.w	#SIDTYPE_PSID,CurrentSIDType
		beq	ResetPSID
		rts

ResetCard	move.l	a6,-(sp)
		move.l	_GfxBase,a6
		JSRLIB	WaitTOF
		bset	#1,$bfe001	;LED aus
		JSRLIB	WaitTOF
		bclr	#1,$bfe001	;LED an
		move.l	(sp)+,a6
		rts

ResetA64	move.l	SIDHandle,d0
		beq	1$
		move.l	a6,-(sp)
		move.l	_SID6581Base,a6
		move.l	d0,a1
		jsr	SID_ResetSID(a6)
		move.l	(sp)+,a6
1$		rts

ResetPSID	tst.b	PSIDOpened
		beq	1$
		move.l	a6,-(sp)
		move.l	_PlaySidBase,a6
		jsr	_LVOStopSong(a6)
		moveq	#1,d0
		jsr	_LVOStartSong(a6)
		move.l	(sp)+,a6
1$		rts


**
** SID-Status in Datenstruktur schreiben
**

_GetSIDDump	move.l	4(sp),a1
		lea	Registers,a0

		moveq	#24,d0		;freq_lo_1 bis mode_vol
1$		move.b	(a0)+,(a1)+
		dbra	d0,1$

		st	(a1)+		;pot_x
		st	(a1)+		;pot_y
		clr.b	(a1)+		;osc_3
		clr.b	(a1)		;env_3
		rts


**
** Aus einem SID-Register lesen
** d0.w: Registernummer ($00-$1f)
** Rückgabe: d0.b: Byte
**
** Darf das obere Wort von d0 und d1 nicht verändern!
**

ReadFrom6581	cmp.b	#$19,d0
		beq	ReadFF
		cmp.b	#$1a,d0
		beq	ReadFF
		cmp.b	#$1b,d0
		beq	ReadRnd
		cmp.b	#$1c,d0
		beq	ReadRnd
		moveq	#0,d0
		rts

ReadFF		move.b	#-1,d0		;A/D-Wandler
		rts

ReadRnd		jsr	Random
		moveq	#0,d1		;MSW löschen
		rts


**
** In ein SID-Register schreiben
** d0.w: Registernummer ($00-$1f)
** d1.b: Byte
**
** Darf das obere Wort von d0 und d1 nicht verändern!
**

WriteTo6581	move.b	d1,(Registers,d0.w)
		cmp.w	#SIDTYPE_CARD,CurrentSIDType
		beq	WriteCard
		cmp.w	#SIDTYPE_A64,CurrentSIDType
		beq	WriteA64
		cmp.w	#SIDTYPE_PSID,CurrentSIDType
		beq	WritePSID
		rts

WriteCard	lea	$a00001,a0	;SID-Karte
		move.b	d1,(a0,d0.w*2)
		rts

WriteA64	tst.l	SIDHandle
		beq	1$
		move.l	a6,-(sp)
		move.l	_SID6581Base,a6
		move.l	SIDHandle,a1	;d0: Regnum, d1: Byte
		jsr	SID_WriteReg(a6)
		move.l	(sp)+,a6
		moveq	#0,d0
		moveq	#0,d1
1$		rts

WritePSID	tst.b	PSIDOpened
		beq	1$
		move.l	a6,-(sp)
		move.l	_PlaySidBase,a6
		move.l	$15a(a6),a0	;Code im C64-RAM der playsid.library ändern
		move.b	d0,$1004(a0)
		move.b	d1,$1002(a0)
		moveq	#1,d0		;Aufruf der Play-Routine erzwingen
		jsr	_LVOForwardSong(a6)
		move.l	(sp)+,a6
		moveq	#0,d0
		moveq	#0,d1
1$		rts


**
** SID-Register (nur als Backup für SAM und Snapshots)
**

Registers	ds.b	32


**
** Konstanten
**

IntName		dc.b	"Frodo SID VBI",0


**
** Initialisierte Daten
**

		SECTION	"DATA",DATA

VBInterrupt	dc.l	0,0
		dc.b	NT_INTERRUPT,0
		dc.l	IntName
SIDHandle	dc.l	0	;Handle für die 6581sid.library und Flag, daß sie verfügbar ist
		dc.l	IntProc

SIDType		dc.w	0	;Prefs: Art der SID-Emulation

PSIDHeader	dc.b	"PSID"	;Dummy-Modul-Header für playsid.library
		dc.w	2
		dc.w	PSIDHeaderEnd-PSIDHeader
		dc.w	$1000	;Load
		dc.w	$1000	;Init
		dc.w	$1001	;Play
		dc.w	1
		dc.w	1
		dc.l	0
		ds.b	HEADERINFO_SIZE
		ds.b	HEADERINFO_SIZE
		ds.b	HEADERINFO_SIZE
		dc.w	0
		dc.l	0
PSIDHeaderEnd
					;Init:
		dc.b	$60		; rts

					;Play:
		dc.b	$a9,0		; lda #xx
		dc.b	$8d,0,$d4	; sta $d4xx
		dc.b	$60		; rts
PSIDEnd


**
** Nicht initialisierte Daten
**

		SECTION	"BSS",BSS

CurrentSIDType	ds.w	1	;Augenblicklicher SID-Typ

SIDInited	ds.b	1	;Flag: InitSID wurde aufgerufen
PSIDOpened	ds.b	1	;Flag: playsid.library ist initialisiert

		END
