
; Listing27e2.s - CIA TOD "rainbow" by ross - modified by Rock'n Roll

	Section	TOD_RAINBOW,CODE

;	Include	"DaWorkBench.s"	; entferne das; vor dem Speichern mit "WO"

*****************************************************************************
	include	"/Sources/startup2.s"	; speichern interrupt, dma etc.	
*****************************************************************************


; Mit DMASET entscheiden wir, welche DMA-Kanäle geöffnet und welche geschlossen werden sollen

			;5432109876543210
DMASET	equ	%1000000000000000	; kein DMA aktivieren

WaitDisk	equ	30				; 50-150 zur Rettung (je nach Fall)

Start:
    lea		$dff000,a6			; Custom-Chip-Base
    lea     $bfd000,a5			; CIAB - Base
    lea     6(a6),a4			; $00dff006 - VHPOSR
    move.w  #$7fff,$9a(a6)		; $00dff09a - INTENA - all off
	lea     Irq6b(pc),a0		; Adresse eigene Interrupt-Routine
    move.l  a0,($78).w			; nach Lvl 6 Int 

    moveq	#0,d0				; 
    move.w  #$7fff,$9c(a6)		; $00dff09c - INTREQ - all off
.wv        
	andi.w	#$20,$1e(a6)		; $00dff01e - INTREQR - VERTB
    beq.b   .wv					; 
    move.w  #$0200,$96(a6)		; $00dff096 - DMACON - only Enable all DMA below (no dma!)
    move.w  #$8000,$2a(a6)		; $00dff02a - VPOSW - LOF-Bit set (to get 313 lines)
   
    move.b  #$7f,$d00(a5)		; $00bfdd00 - ICR - CIAB interrupt control register
								; (MSB is 0, means clear any bit whose value is 1 in the rest of the byte)
   
								; hier wird der Alarm gesetzt
    move.b  #$80,$f00(a5)		; $00bfdf00 - CRB - CIAB Control register B, 1-writing to TOD registers sets Alarm
    move.b  d0,$a00(a5)			; $00bfda00 - todhi	 - Horizontal sync event counter bits 23-16
    move.b  d0,$900(a5)			; $00bfd900 - todmid - Horizontal sync event counter bits 15-8
    move.b  #255,$800(a5)		; $00bfd800 - todlo  - Horizontal sync event counter bits 7-0
    
								; hier wird die Clockzeit gesetzt
	move.b  d0,$f00(a5)			; $00bfdf00 - CIAB Control register B, 0 - writing to TOD registers sets TOD clock
    move.b  d0,$a00(a5)			; $00bfda00 - todhi  - Horizontal sync event counter bits 23-16 ; schreiben nach todhi stoppt die Uhr
    move.b  d0,$900(a5)			; $00bfd900 - todmid - Horizontal sync event counter bits 15-8
    move.b  #229,$800(a5)		; $00bfd800 - todlo  - Horizontal sync event counter bits 7-0
								; Die Uhr wird erst wieder gestartet nach einem Schreibvorgang in das LSB-Ereignisregister.
	
	move.b  $d00(a5),d0			; $00bfdd00 - ICR - CIAB interrupt control register (auslesen setzt alles auf Null zurück)

    move.b  #$84,$d00(a5)		; $00bfdd00 - ICR - CIAB interrupt control register (setze Flag)
    move.w  #$e000,$9a(a6)		; $00dff09a - INTENA SET/INTEN/EXTER
	
	move #$1a,d7				; start y position
	moveq #1,d6					; y add
	move #51,d5					; 53 nach oben, 51 nach unten
	
Mouse:
	btst	#6,$bfe001
	bne.s	Mouse

	move.w  #$6000,$9a(a6)		; $00dff09a - INTENA CLEAR/INTEN/EXTER
	move.b  #$04,$d00(a5)		; $00bfdd00 - ICR - CIAB interrupt control register (lösche Flag)

	rts

*****************************************************************************
*	INTERRUPT-ROUTINE $78 (Level 6) - Extern
*****************************************************************************

Irq6:
	move.w  #254,d1				; aktuelle Nachladezeit für Clocktime: 255-254=1 Zeile 
    lea     Colors(pc),a0		; Colors-Base nach a0
    move.w  (a0),d0				; aktueller Zählerwert auslesen (ist Offsetwert und Farbwert)   
    addq.w  #2,d0				; Zählerwert wird immer um zwei erhöht, steht somit bei beginn auf 0, dann 2 usw.
    cmpi.w  #8*14*2-2,d0		; mit "Ende" vergleichen ob 112 Farbwerte = $de
    bne.b   .0					; solange ungleich überspringen
	moveq   #52,d1				; ansonsten Nachladezeit für Clocktime auf 52 setzen für stabiles Raster
    moveq   #0,d0				; Zählerwert auf Null zurück
.0        
	move.b  d1,$800(a5)			; $00bfd800 - Clockzeit - Zählerwert nach todlo - Horizontal sync event counter bits 7-0 
								; Die Uhr wird erst wieder gestartet nach einem Schreibvorgang in das LSB-Ereignisregister.
    move.w  d0,(a0)+			; aktuellen Zählerwert zurückschreiben und aufs nächste Wort zeigen 
    adda.w  d0,a0				; Adresse auf aktuellen Farbwert erhöhen
    move.b  (a4),d0				; $00dff006 - Inhalt von VHPOSR nach d0 nur die vertikale Position
.wl        
	cmp.b   (a4),d0				; $00dff006 - VHPOSR auf nächste Zeile warten
    beq.b   .wl					; 
    move.b  $d00(a5),d0			; $00bfdd00 - ICR - CIAB interrupt flags register (auslesen setzt alles auf Null zurück)
    move.w  #$2000,$9c(a6)		; $00dff09c - INTREQ - Interrupt löschen
    move.w  (a0),$180(a6)		; $00dff180 - Farbe setzen
    rte


*****************************************************************************
*	INTERRUPT-ROUTINE $78 (Level 6) - Extern - mit Bouncing
*****************************************************************************

Irq6b:
	move.w  #254,d1				; aktuelle Nachladezeit für Clocktime: 255-254=1 Zeile 
    lea     Colors(pc),a0		; Colors-Base nach a0
    move.w  (a0),d0				; aktueller Zählerwert auslesen (ist Offsetwert und Farbwert)   
    addq.w  #2,d0				; Zählerwert wird immer um zwei erhöht, steht somit bei Beginn auf 0, dann 2 usw.
    cmpi.w  #8*14*2-2,d0		; mit "Ende" vergleichen ob 112 Farbwerte = $de
    bne.b   NoBounce			; solange ungleich überspringen
;-----------
	add.b d6,d7
	cmp.b #201,d7				; bottom check - sind wir unten?
	bne Ok1						; 				
	neg d6						; change direction	1 wird -1
	addq #2,d5
Ok1:
	cmp.b #$1a,d7				; sind wir oben?
	bne Ok2						; 
	neg d6						; change direction	-1 wird 1
	subq #2,d5
Ok2:	
	move d5,d1					; ansonsten Nachladezeit für Clocktime auf 51/53 setzen
;-----------
    moveq   #0,d0				; Zählerwert auf Null zurück
NoBounce:        
	move.b  d1,$800(a5)			; $00bfd800 - Clockzeit - Zählerwert nach todlo - Horizontal sync event counter bits 7-0 
								; Die Uhr wird erst wieder gestartet nach einem Schreibvorgang in das LSB-Ereignisregister.
    move.w  d0,(a0)+			; aktuellen Zählerwert zurückschreiben und aufs nächste Wort zeigen 
    adda.w  d0,a0				; Adresse auf aktuellen Farbwert erhöhen
    move.b  (a4),d0				; $00dff006 - Inhalt von VHPOSR nach d0 nur die vertikale Position
.wl2        
	cmp.b   (a4),d0				; $00dff006 - VHPOSR auf nächste Zeile warten
    beq.b   .wl2					; 
    move.b  $d00(a5),d0			; $00bfdd00 - ICR - CIAB interrupt flags register (auslesen setzt alles auf Null zurück)
    move.w  #$2000,$9c(a6)		; $00dff09c - INTREQ - Interrupt löschen
    move.w  (a0),$180(a6)		; $00dff180 - Farbe setzen
    rte


Colors:
    dc.w          -2,$0110,$0220,$0330,$0440,$0550,$0660,$0770
    dc.w        $0880,$0990,$0aa0,$0bb0,$0cc0,$0dd0,$0ee0,$0ff0
    dc.w        $0ff0,$0fe0,$0fd0,$0fc0,$0fb0,$0fa0,$0f90,$0f80
    dc.w        $0f70,$0f60,$0f50,$0f40,$0f30,$0f20,$0f10,$0f00
    dc.w        $0f00,$0f01,$0f02,$0f03,$0f04,$0f05,$0f06,$0f07
    dc.w        $0f08,$0f09,$0f0a,$0f0b,$0f0c,$0f0d,$0f0e,$0f0f
    dc.w        $0f0f,$0e0f,$0d0f,$0c0f,$0b0f,$0a0f,$090f,$080f
    dc.w        $070f,$060f,$050f,$040f,$030f,$020f,$010f,$000f
    dc.w        $000f,$001f,$002f,$003f,$004f,$005f,$006f,$007f
    dc.w        $008f,$009f,$00af,$00bf,$00cf,$00df,$00ef,$00ff
    dc.w        $00ff,$00fe,$00fd,$00fc,$00fb,$00fa,$00f9,$00f8
    dc.w        $00f7,$00f6,$00f5,$00f4,$00f3,$00f2,$00f1,$00f0
    dc.w        $00f0,$00e0,$00d0,$00c0,$00b0,$00a0,$0090,$0080
    dc.w        $0070,$0060,$0050,$0040,$0030,$0020,$0010,0
			
	end


Programmbeschreibung:

Das Programm ist sehr optimiert.
Zu Beginn werden Custom-Chip-Base, CIAB - Base und VHPOSR in Adressregister
geladen. Alle Interrupts werden deaktiviert und die eigene Interrupt-Routine
wird an Adresse LVL-6 gelegt. Anschließend werden alle Interrupt-Requests 
zurückgesetzt.
In einer Warteschleife wird nun durch ständige Abfrage des VERTB-Flags in 
INTREQR auf den nächsten Frame gewartet. Wenn ein neuer Frame startet werden
alle DMA-Kanäle über das Bit DMA Enable ausgeschaltet. Nur die CPU und CIA 
sind nun noch am Laufen. Das setzen von VPOSW - LOF-Bit bewirkt, dass man
313 Zeilen erhält.

Im CIAB Interrupt Control Register wird alles ausgeschaltet. Nun erfolgt das 
Setzen der Zeit und des Alarms mit dem anschließenden Setzen des Flags im CIA-
Interrupt Register und des EXTERN Interrupts in INTENA.

Beschreibung zu TOD:

Der Amiga hat 2 CIA-Chips 8520 und diese sind nur geringüfig anders als die 
CIA-Chips 6526. Ein Unterschied betrifft die TOD (Time of Day) Timer. Während
im 6526 hier die Zeit BCD-codiert in den Registern steht und das Eingangssignal
vom 50Hz/60Hz Stromnetz abgeleitet ist, werden beim 8520 Hsync oder 
Vsync-Signale gezählt.
CIA-A : 50/60 Hz event counter (VSync or line tick)
CIA-B : Horizontal sync event

Das Hsync-Signal tritt während der horizontalen Austastlücke und das Vsync-
Signal während der vertikalen Austastlücke auf. Der CIA 8520 TOD zählt binar
und der Wertebereich ist 24 Bit, d.h. von 0 bis $FFFFFF (16.777.215)

Jedes Bild hat 313 horizontale Synchronimpulse (Hsync) und das ist die 
Zeitbasis also der "Eingangstakt" (oder TOD-Clock) des TOD. Der Name TOD ist
eigentlich irreführend und nur historisch übernommen vom CIA 6526.

Da ein Frame 313 Zeilen hat treten also pro Frame 313 Hsync Ereignisse auf und
diese werden im TOD gezählt. 

Der TOD selber ist aber einerseits ein Zählregister und zwar ein Vorwärtszähler
im Unterschied zu Timer A oder Timer B und ein Alarmregister. Nun kann ein 
"Alarm" zu einem bestimmten Zeitpunkt in der Zukunft programmiert werden, der 
dann bei Erreichendes Zählerwertes (Zeitpunkt) einen Alaram auslöst und einen
Interrupt erzeugt.

Die Alarm Register befinden sich an denselben Adressen wie die entsprechenden
TOD-Register. Der Zugriff auf den Alarm wird durch ein Steuerregister-Bit 
geregelt und zwar über das Steuerregister CRB Bit 7.

CRB Bit7 Alarm - 1 writing to TOD registers sets Alarm
			   - 0 writing to TOD registers sets Clock 

Das Lesen von TOD ergibt immer die TOD clock, also die Zeit. Der Alarm ist dabei
schreibgeschützt, d.h. bei jedem Lesen von TOD wird die Zeit unabhängig vom
Zustand des ALARM-Zugriffsbits gelesen.

Für das ordnungsgemäße Setzen und Lesen von TOD, also bei der Einstellung der 
Zeit muss eine bestimmte Reihenfolge eingehalten werden. Durch einen 
Schreibvorgang in das Register todhi wird TOD gestoppt. Die Uhr wird erst wieder 
gestartet nach einem Schreibvorgang in das LSB-Ereignisregister todlo. Dadurch
wird sichergestellt, dass TOO immer zum gewünschten Zeitpunkt startet.

Lesevorgang:
Da ein Übertrag von einer Stufe zur nächsten jederzeit in Bezug auf eine 
Leseoperation auftreten kann, ist eine Latching-Funktion enthalten, um alle
TOO-Informationen während einer Lesesequenz konstant zu halten. 

Wenn nur ein Register gelesen werden soll, gibt es kein Übertragsproblem, und
das Register kann "on the fly" gelesen werden, vorausgesetzt, dass auf jedes 
Lesen des MSB-Ereignisses ein Lesen des LSB-Ereignisses folgt, um die 
Verriegelung zu deaktivieren.

Es bietet sich daher an in folgender Reihenfolge die Register zu beschreiben:
Erst die Alarmzeit und dann die "Uhrzeit".

								; hier wird der Alarm gesetzt
    move.b  #$80,($f00,a5)		; $00bfdf00 - CRB - CIAB Control register B, 1-writing to TOD registers sets Alarm
    move.b  d0,($a00,a5)		; $00bfda00 - todhi - Horizontal sync event counter bits 23-16
    move.b  d0,($900,a5)		; $00bfd900 - todmid - Horizontal sync event counter bits 15-8
    move.b  #255,($800,a5)		; $00bfd800 - todlo - Horizontal sync event counter bits 7-0
    
								; hier wird die Clockzeit gesetzt
	move.b  d0,($f00,a5)		; $00bfdf00 - CRB - CIAB Control register B, 0 - writing to TOD registers sets TOD clock
    move.b  d0,($a00,a5)		; $00bfda00 - todhi - Horizontal sync event counter bits 23-16 ; schreiben nach todhi stoppt die Uhr
    move.b  d0,($900,a5)		; $00bfd900 - todmid - Horizontal sync event counter bits 15-8
    move.b  #229,($800,a5)		; $00bfd800 - todlo - Horizontal sync event counter bits 7-0 
								; Die Uhr wird erst wieder gestartet nach einem Schreibvorgang in das LSB-Ereignisregister.

zum Programm:

Bei der Initialisierung wird nun also eine Alarmzeit und eine aktuelle Zeit
eingestellt. 313 horizontale Sync-Ereignisse treten pro Frame auf. Die Zahl
313 passt aber nicht in ein Byte, sondern maximal 255. Und wichtig ist nur
der Abstand zwischen der aktuellen Zeit und dem Alarm. D.h. die maximale
Alarmzeit wäre begrenzt auf den Wert 255, wenn wir uns auf ein Byte 
beschränken wollen. Dies können wir, da wir eine Rasterbar über 112 Zeilen
erstellen wollen. Bei maximal 313 Zeilen und einer Rasterbar über 112 Zeilen
muss der späteste Start in Zeile 313-112=201 erfolgen, was als Zählwerte in 
ein Byte passt.

Wir stellen also die Uhrzeit und die Alarmzeit so ein, dass nach maximal
255 Zeilen ein Timer-Interrupt auftritt. Wenn also der Alarmwert auf 255
eingestellt wird und die erste Uhrzeit auf 155, erfolgt der Interrupt und
somit der Start der Routine in Zeile 100. 255-155=100.

Soll die Rasterbar ganz oben mit der ersten sichtbaren Zeile $1a (26) anfangen,
wäre der Clockwert 255-26=229 und wenn die Rasterbar mit der letzten Zeile 313 
aufhören soll, wäre der Clockwert: 313-112=201.

Es gibt nun einen Anfangswert vor dem ersten Interruptaufruf und zwei 
verschiedene Nachladewerte in jedem Interruptaufruf für den nächsten Interrupt.

Der Interrupt tritt pro Frame 112 Mal auf und die Interruptroutine wird somit 
auch 112 Mal abgearbeitet. Die Ausführungzeit der Routine ist dabei kürzer, als 
eine Rasterzeile und pro Interrupt wird für eine Zeile der Farbwert geändert, 
der dann bis zum nächsten Interrupt konstant bleibt. Beim nächsten Interrupt 
sind wir eine Zeile tiefer, bzw. nach 112 Farbwerten wieder am Anfang. 
Somit tritt nun bei jedem Hsync-Ereignis, also bei allen 112 folgenden Zeilen
am Ende ein Interrupt auf, der dann mit Beginn der neuen Zeile den neuen 
Farbwert einstellt. Die Änderung, also das Setzen des neuen Farbwertes erfolgt
snychronisiert mit dem Erreichen der neuen Zeile.
Vorher werden nur die Interrupt-Anfoderungen quittiert, also zurückgesetzt.

Bei der Initialisierung wird der Alarmwert auf 255 und den Clockwert auf 54
gesetzt. Dadurch startet die Rasterbar in Zeile 201. 255-54=201. 
Die Rasterbar endet in Zeile 313: 201+112=313

Das Programm befindet sich danach in einer Endlosschleife und wird nur durch 
den Interrupt unterbrochen. 
Jetzt soll der Interrupt für 112 Farbwerte immer eine Zeile tiefer erfolgen:
Dabei wird der Clockwert auf 254 gesetzt und somit mit dem nächsten Hsync-
Ereignis der nächste Interrupt ausgelöst. 255-254=1
Nach 112 Farbwerten muss die Clockzeit so eingestellt werden, dass der Start
der Rasterbar stabil ist. 255-201=54

Durch Tests wure der genaue Wert gefunden: 
- stabile Rasterbar = 52
- von 53 bis 255 beweget sich der Balken nach oben
- von 51 bis 0 nach unten

Die Farbtabelle hat 14 Zeilen * 8 dc.w's. D.h. 14*8*2=224 Bytewerte, oder
112 Farbwerte. Der erste Wert ist ein Zählerwert und gleichzeitig ein 
Farbwert. Der letzte Wert ist 0, dies steht für die Farbe schwarz und ist
keine Endemarke. Also, der erste Wert des Feldes ein Zählerwert der laufend
von 0 bis 222 erhöht wird. Dieser Zählerwert dient als Offsetwert auf die 
Farbwerte. adda.w  d0,a0.
Das cmpi vergleicht auf den Zählwert 222, weil zuvor +2 hinzugefügt wurde.
Wenn 222 erreicht wurde, dann wird der Zählerwert d0 auf Null zurückgesetzt
und d1 auf den Wert 52, ansonsten wird der Teil übersprungen.

Erst beim Verlassen der Interrupt-Routine wird der Farbwert gesetzt.  
move.w  (a0),$180(a6)

Der Alarmwert bleibt konstant, aber die Clockzeit wird immer neu gesetzt. 