*
* 6526SC.asm - Einzelzyklus-CIA-Emulation
*
* Copyright (C) 1994-1995 by Christian Bauer
*

*
* Anmerkungen:
* ------------
*
* Funktionsweise/Periodic:
*  - Für jeden Zyklus wird vom 6510-Task die Routine Periodic6526
*    aufgerufen, die die Timer herunterzählt, ggf. Interrupts auslöst
*    und einen Zyklus der CPU ausführt
*
* Timer/Latches:
*  - Die Bytefolge im Register-File ist gegenüber dem echten 6526 umgekehrt,
*    das wird aber bei den WriteTo6526- und ReadFrom6526-Routinen wieder
*    ausgeglichen
*
* TOD-Clocks:
*  - Die TODs werden synchron mit dem VBlank gezählt. Es wird also ein
*    50Hz-Eingangssignal simuliert.
*
* Zyklenzähler:
*  - Periodic6526 erhöht auch den einen Langwort-Zyklenzähler, der von der
*    6510- und 6569-Emulation benutzt wird
*
* Tastaturabfrage:
*  - Das Feld KeyMatrix enthält für jede Taste entsprechend der C64-
*    Tastaturmatrix ein Bit (0: Taste gedrückt, 1: Taste nicht gedrückt)
*  - Bei Lesezugriffen aus CIA-A, Port B werden entsprechend der aktiven
*    (ausgewählten) Zeilen der Tastatur die entsprechenden Bits aus der
*    Tastaturmatrix zusammengestellt
*  - Die Help-Taste löst einen NMI aus (Restore), F10 einen RESET
*
* Joystickabfrage:
*  - Die Joysticks werden im VBlank abgefragt, wenn auch die TODs
*    gezählt werden
*
* Lightpen:
*  - Bei jedem Schreibzugriff auf PRB/DDRB von CIA-A wird geprüft, ob die
*    Lightpen-Leitung (Bit 4) einen Übergang 1->0 macht. In diesem Fall
*    wird der VIC informiert
*
* Inkompatibilitäten:
*  - Die TOD-Clock sollte bei einem Lesezugriff nicht angehalten,
*    sondern gelatcht werden
*  - Das Starten eines Timers sollte drei Zyklen verzögert werden, das
*    Stoppen zwei Zyklen
*

		MACHINE	68020

		XREF	ShowPrefs	;Main.asm
		XREF	ResetC64

		XREF	_ciaaprb
		XREF	_ciaaddrb

		XREF	IntIsNMI	;6510SC.asm
		NREF	IntIsIRQ
		NREF	IntIsCIAIRQ
		NREF	FirstIRQCycle
		XREF	FirstNMICycle
		XREF	NMIState

		XREF	ChangedVA	;6569SC.asm
		XREF	TriggerLightpen
		XREF	CycleCounter

		XREF	IECIsOpen	;IEC.asm

		XDEF	Reset6526
		XDEF	_GetCIA1Dump
		XDEF	_GetCIA2Dump
		XDEF	ReadFrom6526A
		XDEF	ReadFrom6526B
		XDEF	WriteTo6526A
		XDEF	WriteTo6526B
		XDEF	Periodic6526
		XDEF	ChangedKeys
		XDEF	CountTODs
		XDEF	_KeyPressed

		XDEF	CIACycles	;Prefs
		XDEF	Joystick1On
		XDEF	Joystick2On
		XDEF	JoystickSwap
		XDEF	KeyboardYZ

		NEAR	a4,-2

		SECTION	"text",CODE

		FAR


**
** Definitionen
**

Cycles		= 1

; CIA-Register
PRA		= 0
PRB		= 1
DDRA		= 2
DDRB		= 3
TAHI		= 4	;Timer-Wert A
TALO		= 5	;Achtung: Umgekehrte Bytefolge!
TBHI		= 6	;Timer-Wert B
TBLO		= 7
TOD10THS	= 8
TODSEC		= 9
TODMIN		= 10
TODHR		= 11
SDR		= 12
ICR		= 13	;Interrupt-Data
CRA		= 14
CRB		= 15

; Zusätzliche Register
LTCHA		= 16	;Timer-Latch A
LTCHB		= 18	;Timer-Latch B
INTMASK		= 20	;Interrupt-Enable
TODHALT		= 21	;TOD zwecks Beschreiben/Auslesen gestoppt
ALM10THS	= 22	;Alarmzeit
ALMSEC		= 23
ALMMIN		= 24
ALMHR		= 25
TODDIV		= 26	;TOD-Frequenzteiler
TACNTPHI2	= 27	;Timer A läuft und zählt Phi2
TBCNTPHI2	= 28	;Timer B läuft und zählt Phi2
TBCNTTA		= 29	;Timer B läuft und zählt Unterläufe von Timer A
PREVLP		= 30	;Voriger Zustand der Lightpen-Leitung (nur CIA-A)


**
** CIAs zurücksetzen
**

		FAR

; CIA-A
Reset6526	lea	Registers1,a0
		clr.l	(a0)
		clr.l	4(a0)
		clr.l	8(a0)
		clr.l	12(a0)
		move.w	#-1,TAHI(a0)		;Timer auf -1
		move.w	#-1,TBHI(a0)

		move.w	#$0001,LTCHA(a0)	;Latches auf 1
		move.w	#$0001,LTCHB(a0)
		clr.b	INTMASK(a0)		;Interrupts abschalten
		clr.b	TODHALT(a0)		;TOD läuft
		clr.l	ALM10THS(a0)		;Alarmzeit auf 00:00:00.0
		clr.w	TACNTPHI2(a0)		;Beide Timer anhalten
		move.b	#$10,PREVLP(a0)		;Lightpen-Leitung ist High

		move.b	#$ff,Joystick1		;Joystick inaktiv
		move.b	#$ff,Joystick2
		move.b	#$ff,Joystick2Key

; CIA-B
		lea	Registers2,a0
		clr.l	(a0)
		clr.l	4(a0)
		clr.l	8(a0)
		clr.l	12(a0)
		move.w	#-1,TAHI(a0)		;Timer auf -1
		move.w	#-1,TBHI(a0)

		move.w	#$0001,LTCHA(a0)	;Latches auf 1
		move.w	#$0001,LTCHB(a0)
		clr.b	INTMASK(a0)		;Interrupts abschalten
		clr.b	TODHALT(a0)		;TOD läuft
		clr.l	ALM10THS(a0)		;Alarmzeit auf 00:00:00.0
		clr.w	TACNTPHI2(a0)		;Beide Timer anhalten

; Zyklenzähler zurücksetzen
		clr.l	CycleCounter

; Tastaturmatrix löschen
		moveq	#-1,d0
		move.l	d0,KeyMatrix
		move.l	d0,KeyMatrix+4

; VIC-Bank 0 einstellen
		moveq	#0,d0
		bra	ChangedVA


**
** CIA-Status in Datenstruktur schreiben
**

_GetCIA1Dump	lea	Registers1,a0
		bra	GetCIADump

_GetCIA2Dump	lea	Registers2,a0

GetCIADump	move.l	4(sp),a1
		move.l	(a0),(a1)+
		move.b	TALO(a0),(a1)+	;Wegen umgekehrter Bytefolge
		move.b	TAHI(a0),(a1)+
		move.b	TBLO(a0),(a1)+
		move.b	TBHI(a0),(a1)+
		move.l	TOD10THS(a0),(a1)+
		move.l	SDR(a0),(a1)+

		move.b	LTCHA+1(a0),(a1)+
		move.b	LTCHA(a0),(a1)+
		move.b	LTCHB+1(a0),(a1)+
		move.b	LTCHB(a0),(a1)+
		move.l	ALM10THS(a0),(a1)+
		move.b	INTMASK(a0),(a1)
		rts


**
** Tastaturbelegung geändert, Y und Z sortieren
**

ChangedKeys	tst.w	KeyboardYZ
		bne	1$
		move.l	#$00010004,KeyPatch1
		move.l	#$00030001,KeyPatch2
		rts
1$		move.l	#$00030001,KeyPatch1
		move.l	#$00010004,KeyPatch2
		rts


**
** In ein CIA-A-Register schreiben
** d0.w: Registernummer ($00-$0f)
** d1.b: Byte
**

		NEAR

WriteTo6526A	lea	Registers1,a0
		move.l	WriteTabA(pc,d0.w*4),a1
		jmp	(a1)

		CNOP	0,4
WriteTabA	dc.l	WrNormal
		dc.l	WrAPRB
		dc.l	WrNormal
		dc.l	WrADDRB
		dc.l	WrTALO
		dc.l	WrTAHI
		dc.l	WrTBLO
		dc.l	WrTBHI
		dc.l	WrTOD10THS
		dc.l	WrTODSEC
		dc.l	WrTODMIN
		dc.l	WrTODHR
		dc.l	WrNormal
		dc.l	WrAICR
		dc.l	WrCRA
		dc.l	WrCRB

WrNormal	move.b	d1,(a0,d0.w)
		rts

WrAPRB		move.b	d1,PRB(a0)
		bra	CheckLP

WrADDRB		move.b	d1,DDRB(a0)

CheckLP		move.b	DDRB(a0),d0	;Lightpen-Leitung
		not.b	d0
		or.b	PRB(a0),d0
		and.b	#$10,d0
		cmp.b	PREVLP(a0),d0	;Änderung?
		beq	1$
		move.b	d0,PREVLP(a0)	;Ja, negative Flanke?
		bne	1$
		bra	TriggerLightpen	;Ja, LP triggern
1$		rts

WrTALO		move.b	d1,LTCHA+1(a0)
		rts

WrTAHI		move.b	d1,LTCHA(a0)
		btst	#0,CRA(a0)		;Timer A gestoppt?
		bne	1$
		move.w	LTCHA(a0),TAHI(a0)	;Ja, Timer laden
1$		rts

WrTBLO		move.b	d1,LTCHB+1(a0)
		rts

WrTBHI		move.b	d1,LTCHB(a0)
		btst	#0,CRB(a0)		;Timer B gestoppt?
		bne	1$
		move.w	LTCHB(a0),TBHI(a0)	;Ja, Timer laden
1$		rts

WrTOD10THS	and.b	#$0f,d1
		clr.b	TODHALT(a0)	;TOD weiterlaufen lassen
		btst	#7,CRB(a0)	;Alarm-Zeit schreiben?
		bne	1$
		move.b	d1,TOD10THS(a0)
		rts
1$		move.b	d1,ALM10THS(a0)
		rts

WrTODSEC	and.b	#$7f,d1
		btst	#7,CRB(a0)	;Alarm-Zeit schreiben?
		bne	1$
		move.b	d1,TODSEC(a0)
		rts
1$		move.b	d1,ALMSEC(a0)
		rts

WrTODMIN	and.b	#$7f,d1
		btst	#7,CRB(a0)	;Alarm-Zeit schreiben?
		bne	1$
		move.b	d1,TODMIN(a0)
		rts
1$		move.b	d1,ALMMIN(a0)
		rts

WrTODHR		and.b	#$9f,d1
		st.b	TODHALT(a0)	;TOD anhalten
		btst	#7,CRB(a0)	;Alarm-Zeit schreiben?
		bne	1$
		move.b	d1,TODHR(a0)
		rts
1$		move.b	d1,ALMHR(a0)
		rts

WrAICR		bclr	#7,d1			;S/C-Bit löschen
		bne	1$			;War es gesetzt?
		not.b	d1			;Nein, Bits zum Löschen negieren
		and.b	d1,INTMASK(a0)		;Und Bits löschen
		bra	2$
1$		or.b	d1,INTMASK(a0)		;Bits setzen
2$
		move.b	ICR(a0),d0		;Anstehende Interrupts erlaubt?
		and.b	INTMASK(a0),d0
		and.b	#$1f,d0
		beq	3$
		or.b	#$80,ICR(a0)		;Ja, IRQ auslösen
		tst.w	IntIsIRQ
		bne	4$
		move.l	CycleCounter,FirstIRQCycle
4$		st.b	IntIsCIAIRQ
		rts
3$		and.b	#$7f,ICR(a0)		;Nein, IRQ zurücknehmen
		clr.b	IntIsCIAIRQ
		rts

WrCRA		bclr	#4,d1			;Force load?
		beq	1$
		move.w	LTCHA(a0),TAHI(a0)	;Ja, Timer laden
1$		move.b	d1,CRA(a0)

		and.b	#$21,d1			;Läuft der Timer und zählt er Phi2?
		cmp.b	#$01,d1
		seq.b	TACNTPHI2(a0)		;Ja, Flag setzen
		rts

WrCRB		bclr	#4,d1			;Force load?
		beq	1$
		move.w	LTCHB(a0),TBHI(a0)	;Ja, Timer laden
1$		move.b	d1,CRB(a0)

		and.b	#$61,d1			;Läuft der Timer und zählt er Phi2?
		cmp.b	#$01,d1
		seq.b	TBCNTPHI2(a0)		;Ja, Flag setzen
		cmp.b	#$41,d1			;Läuft er und zählt Unterläuft von Timer A?
		seq.b	TBCNTTA(a0)		;Ja, Flag setzen
		rts


**
** In ein CIA-B-Register schreiben
** d0.w: Registernummer ($00-$0f)
** d1.b: Byte
**

WriteTo6526B	lea	Registers2,a0
		move.l	WriteTabB(pc,d0.w*4),a1
		jmp	(a1)

		CNOP	0,4
WriteTabB	dc.l	WrBPRA
		dc.l	WrNormal
		dc.l	WrBDDRA
		dc.l	WrNormal
		dc.l	WrTALO
		dc.l	WrTAHI
		dc.l	WrTBLO
		dc.l	WrTBHI
		dc.l	WrTOD10THS
		dc.l	WrTODSEC
		dc.l	WrTODMIN
		dc.l	WrTODHR
		dc.l	WrNormal
		dc.l	WrBICR
		dc.l	WrCRA
		dc.l	WrCRB

WrBPRA		move.b	d1,PRA(a0)		;Floppy/VA
		tst.b	IECIsOpen		;Wenn IEC aktiv ist, Port setzen
		beq	WrBNewVA
		move.b	d1,_ciaaprb
		bra	WrBNewVA

WrBDDRA		move.b	d1,DDRA(a0)		;Floppy/VA
		tst.b	IECIsOpen		;Wenn IEC aktiv ist, DDR setzen
		beq	WrBNewVA
		move.b	d1,_ciaaddrb
WrBNewVA	move.b	DDRA(a0),d0		;VA extrahieren
		not.b	d0
		or.b	PRA(a0),d0
		not.b	d0
		and.b	#$03,d0
		bra	ChangedVA		;Und dem VIC mitteilen

WrBICR		bclr	#7,d1			;S/C-Bit löschen
		bne	1$			;War es gesetzt?
		not.b	d1			;Nein, Bits zum Löschen negieren
		and.b	d1,INTMASK(a0)		;Und Bits löschen
		bra	2$
1$		or.b	d1,INTMASK(a0)		;Bits setzen
2$
		move.b	ICR(a0),d0		;Anstehende Interrupts erlaubt?
		and.b	INTMASK(a0),d0
		and.b	#$1f,d0
		beq	3$
		or.b	#$80,ICR(a0)		;Ja, NMI auslösen
		tst.b	NMIState
		bne	4$
		move.l	CycleCounter,FirstNMICycle
		st.b	NMIState
		st.b	IntIsNMI
4$		rts
3$		and.b	#$7f,ICR(a0)		;Nein, NMI zurücknehmen
		clr.b	NMIState
		rts


**
** Aus einem CIA-A-Register lesen
** d0.w: Registernummer ($00-$0f)
** Rückgabe: d0.b: Byte
**

ReadFrom6526A	lea	Registers1,a0
		move.l	ReadTabA(pc,d0.w*4),a1
		jmp	(a1)

		CNOP	0,4
ReadTabA	dc.l	RdAPRA
		dc.l	RdAPRB
		dc.l	RdNormal
		dc.l	RdNormal
		dc.l	RdTALO
		dc.l	RdTAHI
		dc.l	RdTBLO
		dc.l	RdTBHI
		dc.l	RdTOD10THS
		dc.l	RdNormal
		dc.l	RdNormal
		dc.l	RdTODHR
		dc.l	RdNormal
		dc.l	RdAICR
		dc.l	RdNormal
		dc.l	RdNormal

RdNormal	move.b	(a0,d0.w),d0
		rts

RdAPRA		move.b	DDRA(a0),d0
		not.b	d0
		or.b	PRA(a0),d0
		and.b	Joystick2,d0
		rts

RdAPRB		lea	KeyMatrix,a1
		move.b	PRA(a0),d1	;Tastaturabfrage
		move.b	#$ff,d0		;Alle aktiven Reihen dazuANDen
		lsr.b	#1,d1
		bcs	1$
		and.b	(a1),d0
1$		lsr.b	#1,d1
		bcs	2$
		and.b	1(a1),d0
2$		lsr.b	#1,d1
		bcs	3$
		and.b	2(a1),d0
3$		lsr.b	#1,d1
		bcs	4$
		and.b	3(a1),d0
4$		lsr.b	#1,d1
		bcs	5$
		and.b	4(a1),d0
5$		lsr.b	#1,d1
		bcs	6$
		and.b	5(a1),d0
6$		lsr.b	#1,d1
		bcs	7$
		and.b	6(a1),d0
7$		lsr.b	#1,d1
		bcs	8$
		and.b	7(a1),d0

8$		and.b	Joystick1,d0
		move.b	DDRB(a0),d1
		not.b	d1
		or.b	PRB(a0),d1
		and.b	d1,d0
		rts

RdTALO		move.b	TALO(a0),d0		;Weil die Timer im Registerfile
		rts				;als big-endian gespeichert sind

RdTAHI		move.b	TAHI(a0),d0
		rts

RdTBLO		move.b	TBLO(a0),d0
		rts

RdTBHI		move.b	TBHI(a0),d0
		rts

RdTOD10THS	move.b	TOD10THS(a0),d0
		clr.b	TODHALT(a0)	;TOD weiterlaufen lassen
		rts

RdTODHR		st.b	TODHALT(a0)	;TOD anhalten
		move.b	TODHR(a0),d0
		rts

RdAICR		move.b	ICR(a0),d0	;ICR beim Lesen löschen
		clr.b	ICR(a0)
		clr.b	IntIsCIAIRQ	;IRQ zurücknehmen
		rts


**
** Aus einem CIA-B-Register lesen
** d0.w: Registernummer ($00-$0f)
** Rückgabe: d0.b: Byte
**

ReadFrom6526B	lea	Registers2,a0
		move.l	ReadTabB(pc,d0.w*4),a1
		jmp	(a1)

		CNOP	0,4
ReadTabB	dc.l	RdBPRA
		dc.l	RdBPRB
		dc.l	RdNormal
		dc.l	RdNormal
		dc.l	RdTALO
		dc.l	RdTAHI
		dc.l	RdTBLO
		dc.l	RdTBHI
		dc.l	RdTOD10THS
		dc.l	RdNormal
		dc.l	RdNormal
		dc.l	RdTODHR
		dc.l	RdNormal
		dc.l	RdBICR
		dc.l	RdNormal
		dc.l	RdNormal

RdBPRA		move.b	DDRA(a0),d0	;Floppy/VA
		not.b	d0
		or.b	PRA(a0),d0

		tst.b	IECIsOpen	;Wenn IEC aktiv ist, davon lesen
		beq	1$
		and.b	#$03,d0
		move.b	_ciaaprb,d1
		and.b	#$fc,d1
		or.b	d1,d0
1$		rts

RdBPRB		move.b	DDRB(a0),d0	;Userport
		not.b	d0		;Eingabebits immer 1
		or.b	PRB(a0),d0
		rts

RdBICR		move.b	ICR(a0),d0	;ICR beim Lesen löschen
		clr.b	ICR(a0)
		clr.b	NMIState	;NMI zurücknehmen
		rts


**
** Wird jede Rasterzeile einmal aufgerufen
**

*
* CIA-A
* d1: ICR
* d2: INTMASK
*

Periodic6526	lea	Registers1,a0
		move.b	ICR(a0),d1
		move.b	INTMASK(a0),d2

*
* Timer A
*

		tst.b	TACNTPHI2(a0)	;Wird Phi2 gezählt?
		beq	CiaATADone

		subq.w	#Cycles,TAHI(a0)	;Ja, herabzählen
		bcc	CiaATADone	;Unterlauf?

		or.b	#$01,d1		;Ja, IRQ-Bit setzen
		btst	#0,d2		;IRQ freigegeben?
		beq	CiaATANoIRQ
		or.b	#$80,d1		;Ja, IR-Bit setzen
		tst.w	IntIsIRQ	;IRQ schon ausgelöst?
		bne	1$
		move.l	CycleCounter,FirstIRQCycle ;Nein, Zyklus merken
1$		st.b	IntIsCIAIRQ	; und IRQ auslösen

CiaATANoIRQ	move.w	LTCHA(a0),TAHI(a0) ;Zähler neu laden
		btst	#3,CRA(a0)	;One-Shot?
		beq	1$
		and.b	#$fe,CRA(a0)	;Ja, Zähler stoppen
		clr.b	TACNTPHI2(a0)

1$		tst.b	TBCNTTA(a0)	;Läuft Timer B und zählt er
		beq	CiaATADone	; Unterläufe von Timer A?

		subq.w	#1,TBHI(a0)	;Ja, Timer B runterzählen
		bcs	CiaATBUnderflow	;Untergelaufen?
CiaATADone

*
* Timer B
*

		tst.b	TBCNTPHI2(a0)	;Wird Phi2 gezählt?
		beq	CiaATBDone

		subq.w	#Cycles,TBHI(a0)	;Ja, herabzählen
		bcc	CiaATBDone	;Unterlauf?

CiaATBUnderflow	or.b	#$02,d1		;Ja, IRQ-Bit setzen
		btst	#1,d2		;IRQ freigegeben?
		beq	CiaATBNoIRQ
		or.b	#$80,d1		;Ja, IR-Bit setzen
		tst.w	IntIsIRQ	;IRQ schon ausgelöst?
		bne	1$
		move.l	CycleCounter,FirstIRQCycle ;Nein, Zyklus merken
1$		st.b	IntIsCIAIRQ	; und IRQ auslösen

CiaATBNoIRQ	move.w	LTCHB(a0),TBHI(a0) ;Zähler neu laden
		btst	#3,CRB(a0)	;One-Shot?
		beq	CiaATBDone
		and.b	#$fe,CRB(a0)	;Ja, Zähler stoppen
		clr.b	TBCNTPHI2(a0)
		clr.b	TBCNTTA(a0)
CiaATBDone

*
* ICR zurückschreiben
*

		move.b	d1,ICR(a0)

*
* CIA-B
* d1: ICR
* d2: INTMASK
*

		lea	Registers2,a0
		move.b	ICR(a0),d1
		move.b	INTMASK(a0),d2

*
* Timer A
*

		tst.b	TACNTPHI2(a0)	;Wird Phi2 gezählt?
		beq	CiaBTADone

		subq.w	#Cycles,TAHI(a0) ;Ja, herabzählen
		bcc	CiaBTADone	;Unterlauf?

		or.b	#$01,d1		;Ja, IRQ-Bit setzen
		btst	#0,d2		;IRQ freigegeben?
		beq	CiaBTANoIRQ
		or.b	#$80,d1		;Ja, IR-Bit setzen
		tst.b	NMIState	;NMI schon ausgelöst?
		bne	1$
		move.l	CycleCounter,FirstNMICycle ;Nein, Zyklus merken
		st.b	NMIState	;und NMI auslösen
		st.b	IntIsNMI
1$

CiaBTANoIRQ	move.w	LTCHA(a0),TAHI(a0) ;Zähler neu laden
		btst	#3,CRA(a0)	;One-Shot?
		beq	1$
		and.b	#$fe,CRA(a0)	;Ja, Zähler stoppen
		clr.b	TACNTPHI2(a0)

1$		tst.b	TBCNTTA(a0)	;Läuft Timer B und zählt er
		beq	CiaBTADone	; Unterläufe von Timer A?

		subq.w	#1,TBHI(a0)	;Ja, Timer B runterzählen
		bcs	CiaBTBUnderflow	;Untergelaufen?
CiaBTADone

*
* Timer B
*

		tst.b	TBCNTPHI2(a0)	;Wird Phi2 gezählt?
		beq	CiaBTBDone

		subq.w	#Cycles,TBHI(a0)	;Ja, herabzählen
		bcc	CiaBTBDone	;Unterlauf?

CiaBTBUnderflow	or.b	#$02,d1		;Ja, IRQ-Bit setzen
		btst	#1,d2		;IRQ freigegeben?
		beq	CiaBTBNoIRQ
		or.b	#$80,d1		;Ja, IR-Bit setzen
		tst.b	NMIState	;NMI schon ausgelöst?
		bne	1$
		move.l	CycleCounter,FirstNMICycle
		st.b	NMIState	;und NMI auslösen
		st.b	IntIsNMI
1$

CiaBTBNoIRQ	move.w	LTCHB(a0),TBHI(a0) ;Zähler neu laden
		btst	#3,CRB(a0)	;One-Shot?
		beq	CiaBTBDone
		and.b	#$fe,CRB(a0)	;Ja, Zähler stoppen
		clr.b	TBCNTPHI2(a0)
		clr.b	TBCNTTA(a0)
CiaBTBDone

*
* ICR zurückschreiben
*

		move.b	d1,ICR(a0)

*
* 6510-Zyklus ausführen und Zyklenzähler erhöhen
*

		jsr	(a6)
		addq.l	#1,CycleCounter
		rts


**
** TODs zählen
**

*
* CIA-A
*

CountTODs	lea	Registers1,a0
		subq.b	#1,TODDIV(a0)	;Frequenzteiler herabzählen
		bpl	CiaATODNop

		btst	#7,CRA(a0)	;Untergelaufen,
		beq	CiaATOD60Hz	; je nach 50/60Hz-Flag neu laden
		move.b	#4,TODDIV(a0)
		bra	CiaATODLoaded
CiaATOD60Hz	move.b	#5,TODDIV(a0)

CiaATODLoaded	move	#0,ccr		;X löschen
		move.b	#1,d0		;1/10 Sekunden erhöhen
		move.b	TOD10THS(a0),d1
		abcd	d0,d1
		move.b	d1,TOD10THS(a0)
		cmp.b	#$10,d1		;Über 10?
		blo	CiaATODDone

		clr.b	TOD10THS(a0)	;Ja, 1/10 Sekunden auf Null setzen
		move	#0,ccr		;und Sekunden erhöhen
		move.b	#1,d0
		move.b	TODSEC(a0),d1
		abcd	d0,d1
		move.b	d1,TODSEC(a0)
		cmp.b	#$60,d1		;Über 60?
		blo	CiaATODDone

		clr.b	TODSEC(a0)	;Ja, Sekunden auf Null setzen
		move	#0,ccr		;und Minuten erhöhen
		move.b	#1,d0
		move.b	TODMIN(a0),d1
		abcd	d0,d1
		move.b	d1,TODMIN(a0)
		cmp.b	#$60,d1		;Über 60?
		blo	CiaATODDone

		clr.b	TODMIN(a0)	;Ja, Minuten auf Null setzen
		move	#0,ccr		;und Stunden erhöhen
		move.b	#1,d0
		move.b	TODHR(a0),d1
		and.b	#$1f,d1		;AM/PM ausmaskieren
		abcd	d0,d1
		and.b	#$80,TODHR(a0)	;Stunden schreiben, AM/PM lassen
		or.b	d1,TODHR(a0)

		cmp.b	#$12,d1		;Über 12?
		blo	CiaATODDone

		and.b	#$1f,TODHR(a0)	;Ja, Stunden auf Null setzen
		eor.b	#$80,TODHR(a0)	;und AM/PM umdrehen

CiaATODDone	move.l	TOD10THS(a0),d0	;Alarmzeit erreicht?
		cmp.l	ALM10THS(a0),d0
		bne	CiaATODNop
		move.b	ICR(a0),d0	;Ja, IRQ-Bit setzen
		or.b	#$04,d0
		btst	#2,INTMASK(a0)	;IRQ freigegeben?
		beq	CiaATODNoIRQ
		or.b	#$80,d0		;Ja, IR-Bit setzen
		tst.w	IntIsIRQ	;IRQ schon ausgelöst?
		bne	1$
		move.l	CycleCounter,FirstIRQCycle ;Nein, Zyklus merken
1$		st.b	IntIsCIAIRQ	;und IRQ auslösen
CiaATODNoIRQ	move.b	d0,ICR(a0)
CiaATODNop

*
* CIA-B
*

		lea	Registers2,a0
		subq.b	#1,TODDIV(a0)	;Frequenzteiler herabzählen
		bpl	CiaBTODNop

		btst	#7,CRA(a0)	;Untergelaufen,
		beq	CiaBTOD60Hz	; je nach 50/60Hz-Flag neu laden
		move.b	#4,TODDIV(a0)
		bra	CiaBTODLoaded
CiaBTOD60Hz	move.b	#5,TODDIV(a0)

CiaBTODLoaded	move	#0,ccr		;X löschen
		move.b	#1,d0		;1/10 Sekunden erhöhen
		move.b	TOD10THS(a0),d1
		abcd	d0,d1
		move.b	d1,TOD10THS(a0)
		cmp.b	#$10,d1		;Über 10?
		blo	CiaBTODDone

		clr.b	TOD10THS(a0)	;Ja, 1/10 Sekunden auf Null setzen
		move	#0,ccr		;und Sekunden erhöhen
		move.b	#1,d0
		move.b	TODSEC(a0),d1
		abcd	d0,d1
		move.b	d1,TODSEC(a0)
		cmp.b	#$60,d1		;Über 60?
		blo	CiaBTODDone

		clr.b	TODSEC(a0)	;Ja, Sekunden auf Null setzen
		move	#0,ccr		;und Minuten erhöhen
		move.b	#1,d0
		move.b	TODMIN(a0),d1
		abcd	d0,d1
		move.b	d1,TODMIN(a0)
		cmp.b	#$60,d1		;Über 60?
		blo	CiaBTODDone

		clr.b	TODMIN(a0)	;Ja, Minuten auf Null setzen
		move	#0,ccr		;und Stunden erhöhen
		move.b	#1,d0
		move.b	TODHR(a0),d1
		and.b	#$1f,d1		;AM/PM ausmaskieren
		abcd	d0,d1
		and.b	#$80,TODHR(a0)	;Stunden schreiben, AM/PM lassen
		or.b	d1,TODHR(a0)

		cmp.b	#$12,d1		;Über 12?
		blo	CiaBTODDone

		and.b	#$1f,TODHR(a0)	;Ja, Stunden auf Null setzen
		eor.b	#$80,TODHR(a0)	;und AM/PM umdrehen

CiaBTODDone	move.l	TOD10THS(a0),d0	;Alarmzeit erreicht?
		cmp.l	ALM10THS(a0),d0
		bne	CiaBTODNop
		move.b	ICR(a0),d0	;Ja, IRQ-Bit setzen
		or.b	#$04,d0
		btst	#2,INTMASK(a0)	;IRQ freigegeben?
		beq	CiaBTODNoIRQ
		or.b	#$80,d0		;Ja, IR-Bit setzen
		tst.b	NMIState	;NMI schon ausgelöst?
		bne	1$
		move.l	CycleCounter,FirstNMICycle ;Nein, Zyklus merken
		st.b	NMIState	;und NMI auslösen
		st.b	IntIsNMI
1$
CiaBTODNoIRQ	move.b	d0,ICR(a0)
CiaBTODNop

*
* Joystickabfrage
*

		lea	Registers1,a0

; Port 1
		move.b	#$ff,d2		;Vorgabe: Joystick inaktiv
		tst.w	Joystick1On(pc)
		beq	15$

		btst	#6,$bfe001	;Feuerknopf
		bne	11$
		bclr	#4,d2

11$		move.w	$dff00a,d0
		btst	#1,d0		;Rechts
		beq	12$
		bclr	#3,d2

12$		btst	#9,d0		;Links
		beq	13$
		bclr	#2,d2

13$		move.w	d0,d1
		add.w	d0,d0
		eor.w	d1,d0
		btst	#1,d0		;Runter
		beq	14$
		bclr	#1,d2

14$		btst	#9,d0		;Hoch
		beq	15$
		bclr	#0,d2

15$		move.b	d2,Joystick1

; Port 2
		move.b	Joystick2Key,d2	;Vorgabe: Zehnerblock-Emulation
		tst.w	Joystick2On(pc)
		beq	25$

		btst	#7,$bfe001	;Feuerknopf
		bne	21$
		bclr	#4,d2

21$		move.w	$dff00c,d0
		btst	#1,d0		;Rechts
		beq	22$
		bclr	#3,d2

22$		btst	#9,d0		;Links
		beq	23$
		bclr	#2,d2

23$		move.w	d0,d1
		add.w	d0,d0
		eor.w	d1,d0
		btst	#1,d0		;Runter
		beq	24$
		bclr	#1,d2

24$		btst	#9,d0		;Hoch
		beq	25$
		bclr	#0,d2

25$		move.b	d2,Joystick2

; Joysticks vertauschen?
		tst.w	JoystickSwap(pc)
		beq	30$
		move.b	Joystick1,d0
		move.b	Joystick2,Joystick1
		move.b	d0,Joystick2
30$		rts


**
** Taste wurde gedrückt
**

		FAR

_KeyPressed	move.l	4(sp),d0
		lea	KeyMatrix,a0

		bclr	#7,d0				;KeyUp/KeyDown
		bne	KeyUp

		cmp.b	#$40,d0
		bhs	KeyDownSpecial
		and.w	#$003f,d0			;$00..$3f

		cmp.b	#$0f,d0				;Joystick-Emulation
		beq	KeyDownJoyFire
		cmp.b	#$1d,d0
		beq	KeyDownJoyDL
		cmp.b	#$1e,d0
		beq	KeyDownJoyDown
		cmp.b	#$1f,d0
		beq	KeyDownJoyDR
		cmp.b	#$2d,d0
		beq	KeyDownJoyLeft
		cmp.b	#$2e,d0
		beq	KeyDownJoyFire
		cmp.b	#$2f,d0
		beq	KeyDownJoyRight
		cmp.b	#$3d,d0
		beq	KeyDownJoyUL
		cmp.b	#$3e,d0
		beq	KeyDownJoyUp
		cmp.b	#$3f,d0
		beq	KeyDownJoyUR

		movem.w	KeyTable(pc,d0.w*4),d0/d1
		bclr	d1,(a0,d0.w)
		rts

KeyUp		cmp.b	#$40,d0
		bhs	KeyUpSpecial
		and.w	#$003f,d0			;$00..$3f

		cmp.b	#$0f,d0				;Joystick-Emulation
		beq	KeyUpJoyFire
		cmp.b	#$1d,d0
		beq	KeyUpJoyDL
		cmp.b	#$1e,d0
		beq	KeyUpJoyDown
		cmp.b	#$1f,d0
		beq	KeyUpJoyDR
		cmp.b	#$2d,d0
		beq	KeyUpJoyLeft
		cmp.b	#$2e,d0
		beq	KeyUpJoyFire
		cmp.b	#$2f,d0
		beq	KeyUpJoyRight
		cmp.b	#$3d,d0
		beq	KeyUpJoyUL
		cmp.b	#$3e,d0
		beq	KeyUpJoyUp
		cmp.b	#$3f,d0
		beq	KeyUpJoyUR

		movem.w	KeyTable(pc,d0.w*4),d0/d1
		bset	d1,(a0,d0.w)
KeyNOP		rts

KeyDownSpecial	sub.b	#$40,d0
		cmp.b	#$20,d0
		bhs	KeyDownMod
		and.w	#$1f,d0				;$40..$5f
		jmp	([KeyDownSpecTab,pc,d0.w*4])

KeyDownMod	sub.b	#$20,d0
		cmp.b	#$08,d0
		bhs	KeyNOP
		and.w	#$07,d0				;$60..$67
		cmp.w	#$07,d0				;Amiga rechts ignorieren
		beq	1$
		movem.w	KeyModTable(pc,d0.w*4),d0/d1
		bclr	d1,(a0,d0.w)
1$		rts

KeyUpSpecial	sub.b	#$40,d0
		cmp.b	#$20,d0
		bhs	KeyUpMod
		and.w	#$1f,d0				;$40..$5f
		jmp	([KeyUpSpecTab,pc,d0.w*4])

KeyUpMod	sub.b	#$20,d0
		cmp.b	#$08,d0
		bhs	KeyNOP
		and.w	#$07,d0				;$60..$67
		cmp.w	#$07,d0				;Amiga rechts ignorieren
		beq	1$
		movem.w	KeyModTable(pc,d0.w*4),d0/d1
		bset	d1,(a0,d0.w)
1$		rts

KeySpaceD	bclr	#4,7(a0)
		rts
KeySpaceU	bset	#4,7(a0)
		rts
KeyBackD	bclr	#0,(a0)
		rts
KeyBackU	bset	#0,(a0)
		rts
KeyEnterD
KeyReturnD	bclr	#1,(a0)
		rts
KeyEnterU
KeyReturnU	bset	#1,(a0)
		rts
KeyEscD		bclr	#7,7(a0)
		rts
KeyEscU		bset	#7,7(a0)
		rts
KeyDeleteD	bclr	#3,6(a0)
		rts
KeyDeleteU	bset	#3,6(a0)
		rts
KeyUpD		bclr	#4,6(a0)
		bclr	#7,(a0)
		rts
KeyUpU		bset	#4,6(a0)
		bset	#7,(a0)
		rts
KeyDownD	bclr	#7,(a0)
		rts
KeyDownU	bset	#7,(a0)
		rts
KeyRightD	bclr	#2,(a0)
		rts
KeyRightU	bset	#2,(a0)
		rts
KeyLeftD	bclr	#4,6(a0)
		bclr	#2,(a0)
		rts
KeyLeftU	bset	#4,6(a0)
		bset	#2,(a0)
		rts
KeyF1D		bclr	#4,(a0)
		rts
KeyF1U		bset	#4,(a0)
		rts
KeyF3D		bclr	#5,(a0)
		rts
KeyF3U		bset	#5,(a0)
		rts
KeyF5D		bclr	#6,(a0)
		rts
KeyF5U		bset	#6,(a0)
		rts
KeyF7D		bclr	#3,(a0)
		rts
KeyF7U		bset	#3,(a0)
		rts
KeyF2D		bclr	#4,6(a0)
		bclr	#4,(a0)
		rts
KeyF2U		bset	#4,6(a0)
		bset	#4,(a0)
		rts
KeyF4D		bclr	#4,6(a0)
		bclr	#5,(a0)
		rts
KeyF4U		bset	#4,6(a0)
		bset	#5,(a0)
		rts
KeyF6D		bclr	#4,6(a0)
		bclr	#6,(a0)
		rts
KeyF6U		bset	#4,6(a0)
		bset	#6,(a0)
		rts
KeyF8D		bclr	#4,6(a0)
		bclr	#3,(a0)
		rts
KeyF8U		bset	#4,6(a0)
		bset	#3,(a0)
		rts
KeyF10D		jmp	ResetC64
KeyNKPlusD	bclr	#0,5(a0)	;+
		rts
KeyNKPlusU	bset	#0,5(a0)
		rts
KeyNKMinusD	bclr	#3,5(a0)	;-
		rts
KeyNKMinusU	bset	#3,5(a0)
		rts
KeyNKAsterD	tst.w	KeyboardYZ
		bne	1$
		bclr	#1,6(a0)	;*
		rts
1$		bclr	#5,6(a0)	;=
		rts
KeyNKAsterU	tst.w	KeyboardYZ
		bne	1$
		bset	#1,6(a0)
		rts
1$		bset	#5,6(a0)
		rts
KeyNKSlashD	tst.w	KeyboardYZ
		bne	1$
		bclr	#7,6(a0)	;/
		rts
1$		bclr	#6,6(a0)	;^
		rts
KeyNKSlashU	tst.w	KeyboardYZ
		bne	1$
		bset	#7,6(a0)
		rts
1$		bset	#6,6(a0)
		rts
KeyNKLeftParD	bclr	#4,6(a0)	;[
		bclr	#5,5(a0)
		rts
KeyNKLeftParU	bset	#4,6(a0)
		bset	#5,5(a0)
		rts
KeyNKRightParD	bclr	#4,6(a0)	;]
		bclr	#2,6(a0)
		rts
KeyNKRightParU	bset	#4,6(a0)
		bset	#2,6(a0)
		rts
KeyHelpD	tst.b	NMIState	;NMI schon ausgelöst?
		bne	1$
		move.l	CycleCounter,FirstNMICycle ;Nein, Zyklus merken
		st.b	IntIsNMI	; und NMI auslösen
1$		rts

; Joystick-Emulation
KeyDownJoyUp	bclr	#0,Joystick2Key
		rts
KeyDownJoyDown	bclr	#1,Joystick2Key
		rts
KeyDownJoyLeft	bclr	#2,Joystick2Key
		rts
KeyDownJoyRight	bclr	#3,Joystick2Key
		rts
KeyDownJoyUL	bclr	#0,Joystick2Key
		bclr	#2,Joystick2Key
		rts
KeyDownJoyUR	bclr	#0,Joystick2Key
		bclr	#3,Joystick2Key
		rts
KeyDownJoyDL	bclr	#1,Joystick2Key
		bclr	#2,Joystick2Key
		rts
KeyDownJoyDR	bclr	#1,Joystick2Key
		bclr	#3,Joystick2Key
		rts
KeyDownJoyFire	bclr	#4,Joystick2Key
		rts

KeyUpJoyUp	bset	#0,Joystick2Key
		rts
KeyUpJoyDown	bset	#1,Joystick2Key
		rts
KeyUpJoyLeft	bset	#2,Joystick2Key
		rts
KeyUpJoyRight	bset	#3,Joystick2Key
		rts
KeyUpJoyUL	bset	#0,Joystick2Key
		bset	#2,Joystick2Key
		rts
KeyUpJoyUR	bset	#0,Joystick2Key
		bset	#3,Joystick2Key
		rts
KeyUpJoyDL	bset	#1,Joystick2Key
		bset	#2,Joystick2Key
		rts
KeyUpJoyDR	bset	#1,Joystick2Key
		bset	#3,Joystick2Key
		rts
KeyUpJoyFire	bset	#4,Joystick2Key
		rts


**
** Datenbereich
**

		CNOP	0,4
Registers1	ds.b	32	;CIA-A-Register
Registers2	ds.b	32	;CIA-B-Register

; Prefs
CIACycles	dc.w	0	;Unbenutzt
Joystick1On	dc.w	0	;Joystick an Port 1 wird abgefragt
Joystick2On	dc.w	0	;Joystick an Port 2 wird abgefragt
JoystickSwap	dc.w	0	;Joysticks vertauschen
KeyboardYZ	dc.w	0	;Amerikanische Tastaturbelegung

Joystick1	dc.b	0	;Joystick 1 AND-Wert
Joystick2	dc.b	0	;Joystick 2 AND-Wert
Joystick2Key	dc.b	0	;Joystick 2 AND-Wert für Emulation über Zehnerblock

; Tastaturübersetzungstabelle:
; Für jeden Amiga-RawKey Spalte und Zeile in der KeyMatrix
		CNOP	0,4
KeyTable	dc.w	7,1	;` -> <-
		dc.w	7,0	;1
		dc.w	7,3	;2
		dc.w	1,0	;3
		dc.w	1,3	;4
		dc.w	2,0	;5
		dc.w	2,3	;6
		dc.w	3,0	;7
		dc.w	3,3	;8
		dc.w	4,0	;9
		dc.w	4,3	;0
		dc.w	5,0	;ß -> +
		dc.w	5,3	;´ -> -
		dc.w	6,0	;\ -> £
		dc.w	0,0
		dc.w	4,3	;NP 0

		dc.w	7,6	;Q
		dc.w	1,1	;W
		dc.w	1,6	;E
		dc.w	2,1	;R
		dc.w	2,6	;T
KeyPatch1	dc.w	1,4	;Y -> Z
		dc.w	3,6	;U
		dc.w	4,1	;I
		dc.w	4,6	;O
		dc.w	5,1	;P
		dc.w	5,6	;ü -> @
		dc.w	6,1	;+ -> *
		dc.w	0,0
		dc.w	7,0	;NP 1
		dc.w	7,3	;NP 2
		dc.w	1,0	;NP 3

		dc.w	1,2	;A
		dc.w	1,5	;S
		dc.w	2,2	;D
		dc.w	2,5	;F
		dc.w	3,2	;G
		dc.w	3,5	;H
		dc.w	4,2	;J
		dc.w	4,5	;K
		dc.w	5,2	;L
		dc.w	5,5	;ö -> :
		dc.w	6,2	;ä -> ;
		dc.w	6,5	;# -> =
		dc.w	0,0
		dc.w	1,3	;NP 4
		dc.w	2,0	;NP 5
		dc.w	2,3	;NP 6

		dc.w	6,6	;< -> ^
KeyPatch2	dc.w	3,1	;Z -> Y
		dc.w	2,7	;X
		dc.w	2,4	;C
		dc.w	3,7	;V
		dc.w	3,4	;B
		dc.w	4,7	;N
		dc.w	4,4	;M
		dc.w	5,7	;,
		dc.w	5,4	;.
		dc.w	6,7	;- -> /
		dc.w	0,0
		dc.w	5,4	;NP .
		dc.w	3,0	;NP 7
		dc.w	3,3	;NP 8
		dc.w	4,0	;NP 9

KeyDownSpecTab	dc.l	KeySpaceD
		dc.l	KeyBackD
		dc.l	KeyNOP
		dc.l	KeyEnterD
		dc.l	KeyReturnD
		dc.l	KeyEscD
		dc.l	KeyDeleteD
		dc.l	KeyNOP

		dc.l	KeyNOP
		dc.l	KeyNOP
		dc.l	KeyNKMinusD
		dc.l	KeyNOP
		dc.l	KeyUpD
		dc.l	KeyDownD
		dc.l	KeyRightD
		dc.l	KeyLeftD

		dc.l	KeyF1D
		dc.l	KeyF2D
		dc.l	KeyF3D
		dc.l	KeyF4D
		dc.l	KeyF5D
		dc.l	KeyF6D
		dc.l	KeyF7D
		dc.l	KeyF8D

		dc.l	KeyNOP
		dc.l	KeyF10D
		dc.l	KeyNKLeftParD
		dc.l	KeyNKRightParD
		dc.l	KeyNKSlashD
		dc.l	KeyNKAsterD
		dc.l	KeyNKPlusD
		dc.l	KeyHelpD

KeyUpSpecTab	dc.l	KeySpaceU
		dc.l	KeyBackU
		dc.l	KeyNOP
		dc.l	KeyEnterU
		dc.l	KeyReturnU
		dc.l	KeyEscU
		dc.l	KeyDeleteU
		dc.l	KeyNOP

		dc.l	KeyNOP
		dc.l	KeyNOP
		dc.l	KeyNKMinusU
		dc.l	KeyNOP
		dc.l	KeyUpU
		dc.l	KeyDownU
		dc.l	KeyRightU
		dc.l	KeyLeftU

		dc.l	KeyF1U
		dc.l	KeyF2U
		dc.l	KeyF3U
		dc.l	KeyF4U
		dc.l	KeyF5U
		dc.l	KeyF6U
		dc.l	KeyF7U
		dc.l	KeyF8U

		dc.l	KeyNOP
		dc.l	KeyNOP
		dc.l	KeyNKLeftParU
		dc.l	KeyNKRightParU
		dc.l	KeyNKSlashU
		dc.l	KeyNKAsterU
		dc.l	KeyNKPlusU
		dc.l	KeyNOP

KeyModTable	dc.w	1,7		;Shift left
		dc.w	6,4		;Shift right
		dc.w	1,7		;Caps lock -> Shift left
		dc.w	7,2		;Control
		dc.w	7,5		;Alt left -> C=
		dc.w	7,5		;Alt right -> C=
		dc.w	7,5		;Amiga left -> C=
		dc.w	0,0		;Amiga right

; Bit  7   6   5   4   3   2   1   0
; 0   CUD  F5  F3  F1  F7 CLR RET DEL
; 1   SHL  E   S   Z   4   A   W   3
; 2    X   T   F   C   6   D   R   5
; 3    V   U   H   B   8   G   Y   7
; 4    N   O   K   M   0   J   I   9
; 5    ,   @   :   .   -   L   P   +
; 6    /   ^   =  SHR HOM  ;   *   £
; 7   R/S  Q   C= SPC  2  CTL  <-  1

KeyMatrix	ds.b	8	;C64-Tastaturmatrix pro Taste ein Bit
				;0: Taste gedrückt

		END
