Interruptkurs (Teil6 - 2.Hälfte)
Kommen wir nun zur ersten FLD-Routine.
In ihr wird der Interrupt geglättet, was
eine besonders trickreiche Angelegenheit
ist. Sehen Sie sich hierzu einmal den
Sourcecode an:
;*** FLD-Routine mit Glättung ($1100) FLD1 pha ;Akku, X- u. Y-Reg. txa ; retten pha tya pha dec $d019 ;neue IRQs erlauben inc $d012 ;nächte Raster.=Ausl. lda #<FLD2 ;Lo-Byte von FLD-IRQ2- sta $fffe ;Routine setzen cli ;IRQs erlauben WIRQ nop ;13 NOPs nop ; (innerhalb dieser nop ; Schleife wird der nop ; Interrupt ausge- nop ; löst werden!!) nop nop nop nop nop nop nop nop jmp QIRQ FLD2 pla ;Programmzähler und pla ; Statusreg. gleich pla ; wieder v. Stack holen nop ;19 NOPs zum Verzögern nop ; bis zum tatsächlichen nop ; Charakterzeilen- nop ; anfang nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop lda $d012 ;Den letzten Zyklus cmp $d012 ; korrigieren bne cycle cycle ... ;eigentlicher IRQ
Hier werden Ihnen einige Dinge etwas
merkwürdig vorkommen ( vor allem die vielen NOPs) . Beginnen wir von Anfang an:
In der Borderroutine hatten wir die Rasterzeile festgelegt, in der die FLD1- Routine angesprungen werden soll. Dies
war Zeile 61($3 D), die genau zwei Rasterzeilen vor der eigentlichen IRQ- Rasterzeile liegt. In diesen zwei Zeilen, die wir den IRQ früher ausgelöst
haben, werden wir ihn jetzt glätten. Wie
in jedem Interrupt retten wir zunächst
die Prozessorregister. Daran anschließend wird das Low-Byte der Routine
" FLD2" in das Low-Byte des IRQ-Vektors
geschrieben, und die nächste Rasterzeile
( durch den INC-Befehl) als nächster Interruptauslöser festgelegt. Beachten Sie
hierbei, daß die diese Routine dasselbe
High-Byte in der Adresse haben muß, wie
die erste FLD-Routine. Das kann man dadurch erzielen, daß " FLD1" an einer
Adresse mit 0- Lowbyte ablegt wird und
sofort danach die Routine " FLD2" folgt
( im Beispiel ist FLD1 an Adresse $1100) .
Um einen neuen Interrupt zu ermöglichen, müssen noch das ICR des VIC und das Interrupt- Flag des Prozessors gelöscht
werden ( beachten Sie, daß letzteres vom
Prozessor automatisch bei Auftreten des
IRQs gesetzt wurde) .
Es folgt nun der eigentliche " Glät- tungsteil" . Hierzu lassen wir den Prozessor ständig durch eine Endlos-Schleife mit NOP-Befehlen laufen. Dadurch wird sichergestellt, daß der Raster- IRQ der nächsten Rasterzeile in
jedem Fall während der Ausführung eines
NOP-Befehls auftritt. Da dieser Befehl
nur 2 Taktzyklen verbraucht, kann die
Verzögerung des Interupts nur 0 oder 1 Taktzyklen lang sein. Diesen einen Zyklus zu korrigieren ist nun die Aufgabe
des zweiten FLD-IRQs. Nachdem er angesprungen wurde holen wir gleich wieder
die, vom Prozessor automatisch gerettete, Programmzähleradresse und das Statusregister vom Stapel, da sie nur zum
ersten FLD-Interrupt gehören, in den
nicht mehr zurückgekehrt werden soll.
Danach folgen 19 NOP-Befehle, die nur
der Verzögerung dienen, um das Ende der
Rasterzeile zu erreichen. Die letzten
drei Befehle sind die Trickreichsten!
Sie korrigieren den einen möglichen
Verzögerungs-Zyklus. Zum besseren Verständnis sind sie hier nochmal aufgelistet:
lda $d012 ;Den letzten Zyklus cmp $d012 ; korrigieren bne cycle cycle ... ;eigentlicher IRQ
Obwohl diese Folge recht unsinnig erscheint, hat sie es ganz schön in sich:
Wir laden hier zunächst den Akku mit dem
Inhalt von Register $ D012, das die Nummer der aktuell bearbeiteten Rasterzeile
beinhaltet, und vergleichen ihn sofort
wieder mit diesem Register. Danach wird
mit Hilfe des BNE-Befehls auf die Folgeadresse verzweigt, was noch unsinniger
erscheint.
Der LDA-Befehl befindet sich nun durch
die NOP-Verzögerung genau an der Kippe
zur nächsten Rasterzeile, nämlich einen
Taktzyklus bevor diese Zeile beginnt.
Sollte nun der FLD2- IRQ ohne den einen
Taktzyklus Zeitverzögerung ausgeführt worden sein, so enthält der Akku z. B.
den Wert 100 . Der CMP-Befehl ist dann
ein Vergleich mit dem Wert 101, da der
Rasterstahl nach dem LDA-Befehl schon in
die nächste Zeile gesprungen ist. Dadurch sind die beiden Werte ungleich, womit das Zero-Flag gelöscht ist, und
der Branch tatsächlich ausgeführt wird.
Beachten Sie nun, daß ein Branch-Befehl
bei zutreffender Bedingung durch den
Sprung einen Taktzyklus mehr Zeit verbraucht, als bei nicht zutreffender Bedingung (3 Zyklen!) . War der FLD2- IRQ
allerdings mit dem einem Taktzyklus
Verzögerung aufgetreten, so wird der
LDA-Befehl genau dann ausgeführt, wenn
Register $ D012 schon die Nummer der
nächsten Rasterzeile enthält, womit der
Akku den Wert 101 beinhaltet. Durch den
Vergleich mit dem Register, das dann
immer noch den Wert 101 enthält, wird
das Zero-Flag gesetzt, da die beiden
Werte identisch sind. Dadurch trifft die
Bedingung des BNE-Befehls nicht zu, und er verbraucht nur 2 Taktzyklen! Dies
gewährleistet, daß in beiden Fällen immer die gleiche Zyklenzahl verbraucht
wird! War der FLD2- IRQ ohne Verzögerung, so verbracht die Routine einen Zyklus
mehr, als wenn er mit einem Zyklus
Verspätung auftrat!
Hiermit hätten wir den IRQ also geglättet und können die eigentliche FLDund
Farbsetz-Routine ausführen. Beachten Sie
für Folgebeispiele, daß wir in Zukunft
auf diese Weise die IRQs immer glätten
werden müssen, um saubere Ergebnisse zu
erzielen. Hierzu wird immer wieder diese
Routine verwandt, wobei das eigentliche
IRQ-Programm dann nach dem Branch-Befehl
eingesetzt wird. Gleichmäßiger kann man
Raster-IRQ nun wirklich nicht mehr
ausführen!
Nach dem Glätten folgt die eigentliche
Interruptroutine, und zwar direkt nach
dem Label " Cycle" . Sie setzt Rasterzeile
$ F8 als Interruptauslöser fest und ver- biegt den IRQ-Vektor wieder auf die Borderroutine, womit der Kreislauf von
Neuem beginnt. Gleichzeitig setzt Sie
die Darstellung auf 25 Zeilen zurück, damit der Bordereffekt auch funktioniert. Anschließend wird der FLD-Effekt
durchgeführt, indem der Zeilenanfang vor
dem Rasterstrahl hergeschoben wird. Währenddessen werden die Vorderund Hintergrundfarbe nach zwei Farbtabellen bei
$1200 und $1300 verändert. Der Rastersplit wird durch ausreichende Verzögerung bis zur Mitte einer Rastezeile erzeugt. Zum Schluß des IRQs wird noch bis
zum Ende der Rasterzeile verzögert, und
die Standardfarben zurückgesetzt, bevor
die ursprünglichen Inhalte der Prozessorregister, wie sie vor dem Auftreten
des FLD1- IRQs vorhanden waren, zurückgeholt werden und der IRQ beendet wird:
Cycle dec $ d019 ; VIC-ICR löschen lda #$18 ;25- Zeilen-Darst. einsta $ d011 ; schlaten ( Bordereff.)
lda #$f8 ;Rasterz. $F8 ist näch- sta $d012 ; ster IRQ-Auslöser ldx #<Bord ;IRQ-Vektor ldy #>Bord ; auf stx $fffe ; Border-Routine sty $ffff ; verbiegen nop ;Verzögern lda $02 ;FLD-Zähler laden beq FDEnd ; Wenn 0, kein FLD! ldx #$00 ;Zählreg.f.Farben init. clc FDLop lda $d012 ;FLD-Sequenz (Zeilen- adc #$02 ; anfang vor Raster- and #$07 ; strahl herschieben ora #$18 sta $d011 lda $1200,x;Farbe links holen sta $d020 ; und setzen sta $d021 nop ;Verzögern bis Mitte nop nop nop nop nop nop lda $1300,x;Farbe rechts holen sta $d020 ; und setzen sta $d021 bit $ea ;Verzögern inx ;Farb-Zähler+1 cpx $02 ;Mit FLD-Zähler vgl. bcc FDLop ;ungl., also weiter FDEnd nop ;Verz. bis Zeilenende nop nop nop nop lda #$0e ;Vorder-/Hintergrund- sta $d020 ; farben auf lda #$06 ; hellblau u. dunkel- sta $d021 ; blau setzen pla ;Akku, X- und Y-Reg. tay ; zurückholen pla tax pla rti ;IRQ beenden
Das war es dann wieder für diesen Monat.
Im nächsten Kursteil werden wir eine
weitere Anwendung besprechen, die eine
IRQ-Glättung benötigt: die Sideborderroutinen zum Abschalten des linken und
rechten Bildschirmrands, nämlich.
(ub)