Also: Könntet ihr mir bitte erklären bzw. ne Möglichkeit geben wo ich des am besten nachlesen kann, um was es sich bei Double IRQs genau handelt?
Double IRQs sind ein bewährtes Mittel um Prozessor und Rasterstrahl exakt zu synchronisieren. Benötigt wird das für viele Demoeffekte, etwa um den Sideborder zu öffnen. Da musst du $d016 exakt zu dem Zeitpunkt beschreiben wenn der Rasterstrahl gerade das Zeichen unmittelbar vor dem rechten Border darstellt. Wenn dein Schreibzugriff auf $d016 auch nur einen Taktzyklus früher oder später stattfindet klappt's nicht.
Das ganze funktioniert so:
Wenn ein Interrupt auftritt dann arbeitet der Prozessor den aktuellen Befehl des Hauptprogramms noch zu Ende ab und springt dann erst zur Interruptroutine. Dadurch wird der IRQ halt mit einigen wenigen Zyklen Verzögerung ausgeführt (0-6 Zyklen, um genau zu sein), wobei man in der Regel nicht weiss wieviele Zyklen das sind. Beim
Double IRQ fängt man diese unbekannte Verzögerung in zwei Schritten auf:
1) Als erstes wird gleich zu Beginn der Interruptroutine ein zweiter Rasterinterrupt für die unmittelbar folgende Rasterzeile gesetzt. Danach werden eine Menge NOPs ausgeführt. Der Sinn dahinter ist, das der neu gesetzte Rasterinterrupt den gerade laufen ersten Interrupt unterbricht (das kann man problemlos machen, solange man keinen Müll auf dem Stack zurücklässt), und zwar während dieser gerade ein NOP ausführt. Da ein NOP nur zwei Taktzyklen benötigt kann man sicher sein das der zweite IRQ mit einer Verzögerung (die durch die Zuende-Abarbeitung des aktuellen Befehls zustande kommt) von null oder einem Zyklus ausgeführt wird.
2) Die zweite IRQ-Routine wartet nun eine exakt bestimmte Anzahl von Taktzyklen ab, bis der Rasterstrahl fast am Ende der Rasterzeile angekommen ist. Dann wird die Sequenz
lda $d012 cmp $d012 beq *+2 ausgeführt. Falls der zweite IRQ zu Beginn 0 Zyklen Verzögerung hatte dann ist der Rasterstrahl während des CMP gerade eben noch in der gleichen Rasterzeile wie beim LDA. Somit haben LDA und CMP die gleichen Werte und der BEQ verzweigt (dazu braucht er drei Zyklen). Falls der zweite IRQ andererseits zu Beginn eine Verzögerung von einem Zyklus hatte dann ist der Rasterstrahl während des CMP gerade in die nächste Zeile gesprungen. LDA und CMP haben dann unterschiedliche Werte, der BEQ verzweigt nicht und verbraucht deshalb nur zwei Zyklen. In beiden Fällen ist der Prozessor nun mit dem Rasterstrahl synchron.
Der Code dazu (ungetestet, habe ihn gerade aus der Codebase herauskopiert)
|
Source code
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
irq1
sta reseta1 ;Preserve A,X and Y
stx resetx1 ;Registers
sty resety1 ;VIA self modifying
;code
;(Faster than the
;STACK is!)
lda #<irq2 ;Set IRQ Vector
ldx #>irq2 ;to point to the
;next part of the
sta $fffe ;Stable IRQ
stx $ffff ;ON NEXT LINE!
inc $d012
asl $d019 ;Ack RASTER IRQ
tsx ;We want the IRQ
cli ;To return to our
nop ;endless loop
nop ;NOT THE END OF
nop ;THIS IRQ!
nop
nop ;Execute nop's
nop ;until next RASTER
nop ;IRQ Triggers
nop
nop ;2 cycles per
nop ;instruction so
nop ;we will be within
nop ;1 cycle of RASTER
nop ;Register change
nop
irq2
txs ;Restore STACK
;Pointer
ldx #$08 ;Wait exactly 1
dex ;lines worth of
bne *-1 ;cycles for compare
bit $ea ;Minus compare
lda $d012
cmp $d012
beq start ;If no waste 1 more
;cycle
start [hier beginnt dann die eigentliche IRQ-Routine]
|
Und was für Probleme gibts eig. bei den sog. Badlines? Also, ich hab recherchiert und herausgefunden, dass bei den Badlines (alle 8 Rasterlines oder?) die Farbinformationen neu geholt werden und somit 40 Cycles "vergeudet" werden.
Genau. Zu Beginn einer jeden Textzeile holt sich der VIC die entsprechenden 40 Zeichen aus dem Videoram, und dazu benötigt er 40 Taktzyklen extra. Während dieser Zeit wird der Prozessor angehalten, er hat in der Rasterzeile also nur noch 13 Zyklen zur Verfügung. Immerhin merkt sich der VIC die 40 Zeichen solange bis die nächste Textzeile beginnt (in der Regel also 8 Rasterzeilen später), und dann gibt's die nächste
Badline.
Was kann ich "dagegen machen" bzw. dem begegnen bzw. wo bekomm ich damit überhaupt Probleme?
Probleme gibt es dann, wenn dein Programm in einer Rasterzeile mehr Taktzyklen benötigt als zur Verfügung stehen (z.B. weil du in der Zeile mehrere VIC-Register umschalten musst) oder wenn du einen Befehl exakt zu einem Zeitpunkt ausführen musst, an dem der Prozessor gerade angehalten wird. Letzteres kann zum Beispiel bei Rastersplits oder bei der Kommunikation mit externen Geräten der Fall sein.
Die wahrscheinlich meist genutzte Methode dem zu begegnen ist das Eintreten der nächsten Badline mittels FLD hinauszuzögern. Da sich der VIC dann natürlich auch keine neuen Daten aus dem Videoram holt muss man das, was der VIC dann anzeigt, halt entsprechend kaschieren (z.B. durch Umschalten des Zeichensatzes)
So, und jetzt ist glaube ich ein Zitat meinem Prof. angebracht: "Hat das jemand verstanden? Wenn nicht erkläre ich's nochmal."

By the way, in der Magic Disk gab's mal einen IRQ-Kurs, den kann ich dir wärmstens empfehlen.