Magic Disk 64

home to index to text: MD9404-KURSE-IRQ-KURS_6.2.txt
    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)

Valid HTML 4.0 Transitional Valid CSS!