Magic Disk 64

home to index to text: MD9402-KURSE-IRQ-KURS_4.1.txt

Interrupt-Kurs - Teil 4" Die Hardware ausgetrickst. . ." Herzlich Willkommen zum vierten Teil unseres IRQ-Kurses. In dieser Ausgabe möchten wir uns mit sehr zeitkritischen Rastereffekten beschäftigen und kurz zeigen, wie man den oberen und unteren Bildschirmrand mit Hilfe von Rasterinterrupts verschwinden lassen kann.
1) DIE TOP-UND BOTTOM-BORDER- ROUTINE Wie Sie ja sicherlich wissen, ist der Bildschirm des C64 auf einen sichbaren Bereich von 320 x200 Pixeln, oder auch 40 x25 Textzeichen beschränkt. Der Rest des Bildschirms ist von dem meist hellblauen Bildschirmrahmen verdeckt und kann in der Regel nicht genutzt werden.
Werfen Sie jetzt jedoch einen Blick auf diese Zeilen, so werden Sie feststellen, daß das Magic-Disk- Hauptprogramm genau 25 Textzeilen, die maximale vertikale Anzahl also, darstellt und trotzdem am oberen Bildschirmrand die Seitennummer, sowie am unteren Bildschirmrand das MD-Logo zu sehen sind. Ganz offensichtlich haben wir es geschafft, den Bildschirm auf wundersame Weise zu vergrößern!
Tatsächlich schaltet das MD-Haupt- programm den oberen und unteren Bildschirmrand schlichtweg ab, so daß wir auch hier noch etwas darstellen können und auf diese Weise mehr Informationen auf einer Bildschirmseite abzulesen sind. Dies ist wieder einmal nichts anderes als ein Rastertrick. Noch dazu einer der simpelsten die es gibt. Wie einfach er zu programmieren ist soll folgendes kleines Rasterprogramm verdeutlichen. Sie finden es auf dieser MD unter dem Namen " BORDERDEMO" und müssen es wie immer mit ",8,1" laden und mittels " SYS4096" starten:

INIT: SEI         ;IRQ sperren          
      LDA #$7F    ;Timer-IRQ            
      STA $DC0D   ; abschalten          

LDA $ DC0 D ; ICR löschen

      LDA #$F8    ;Rasterzeile $F8 als  
      STA $D012   ; Interrupt-Auslöser  
      LDA $D011   ; festlegen (incl. dem
      AND #$7F    ; Löschen des HI-Bits)
      STA $D011                         
      LDA #$01    ;Raster als IRQ-      
      STA $D01A   ; Quelle wählen       
      LDX #<(IRQ) ;IRQ-Vektoren auf     
      LDY #>(IRQ) ; eigene Routine      
      STX $0314   ; verbiegen           
      STY $0315   ;                     
      LDA #$00    ;Letzte VIC-Adr. auf  
      STA $3FFF   ; 0 setzen            
      LDA #$0E    ;Rahmen- u. Hinter-   
      STA $D020   ; grundfarben auf     
      LDA #$06    ; hellblau/blau       
      STA $D021   ; setzen              
      LDY #$3F    ;Sprite-Block 13      
      LDA #$FF    ; ($0340) mit $FF     
LOOP2 STA $0340,Y ; füllen              
      DEY                               
      BPL LOOP2                         
      LDA #$01    ;Sprite 0             
      STA $D015   ; einschalten         
      STA $D027   ; Farbe="Weiß"        
      LDA #$0D    ;Spritezeiger auf     
      STA $07F8   ; Block 13 setzen     
      LDA #$64    ;X- und Y-Pos.        
      STA $D000   ; auf 100/100         
      STA $D001   ; setzen              
      CLI         ;IRQs freigeben       
      RTS         ;ENDE                 
IRQ   LDA $D011   ;Bildschirm           
      AND #$F7    ; auf 24 Zeilen       
      STA $D011   ; Umschalten          
      DEC $D019   ;VIC-ICR löschen      
      LDX #$C0    ;Verzögerungs-        
LOOP1 INX         ; schleife            
      BNE LOOP1                         
      LDA $D011   ;Bildschirm           
      ORA #$08    ; auf 25 Zeilen       
      STA $D011   ; zurückschalten      

INC $ D001 ; Sprite bewegen END JMP $ EA31 ; Weiter zum SYS-IRQ Wie Sie sehen, besteht die eigentliche IRQ-Routine, die den Border abschaltet nur aus einer handvoll Befehlen. Die Initialisierung der Routine sollte Ihnen noch aus dem letzten Kursteil bekannt sein. Wir sperren hier zunächst alle IRQs und verhindern, daß CIA-A ebenfalls IRQs auslöst, damit unser Rasterinterrupt nicht gestört wird. Als nächstes wird Rasterzeile $ F8 als Interruptauslöser festgelegt, was auch einen ganz bestimmten Grund hat, wie wir weiter unten sehen werden. Nun sagen wir noch dem VIC, daß er Rasterinterrupts auslösen soll, und verbiegen den IRQ-Vektor bei $0314/$0315 auf unsere eigene Routine namens " IRQ" . Die nun folgenden Zeilen dienen lediglich " kosmetischen" Zwecken. Wir setzen hier Rahmenund Hintergrundfarben auf die Standardwerte und schalten Sprite 0 ein, das von unserer Interruptroutine in der Vertikalen pro IRQ um einen Pixel weiterbewegt werden soll. Zudem wird der Spriteblock, der dieses Sprite darstellt, mit $ FF gefüllt, damit wir ein schönes Quadrat auf dem Bildschirm sehen und keinen Datenmüll. Nach Freigabe der IRQs mittels " CLI" wird dann wieder aus dem Programm zurückgekehrt. Von nun an arbeitet unsere kleine, aber feine Raster-IRQ- Routine. Damit Sie sie verstehen, müssen wir nun ein wenig in die Funktionsweise des VIC einsteigen:
Normalerweise zeigt uns der Videochip des C64, wie oben schon erwähnt, ein 25 Text-, bzw.200 Grafikzeilen hohes Bild.
Nun können wir die Bildhöhe mit Hilfe von Bit 3 in Register 17 des VICs auf 24 Textzeilen reduzieren. Setzen wir es auf"1", so werden 25 Textzeilen dargestellt, setzen wir es auf "0", so sehen wir lediglich 24 Textzeilen. Im letzteren Fall werden dann jeweils vier Grafikzeilen des oberen und unteren Bildschirmrandes vom Bildschirmrahmen überdeckt. Diese Einschränkung ist vor allem bei der Programmierung eines vertikalen Soft-Scrollers von Bedeutung.
Effektiv zeichnet der VIC nun also den oberen Bildschirmrand vier Rasterzeilen länger und den unteren vier Rasterzeilen früher. Um nun den Rahmen zu zeichnen kennt die Schaltlogik des VIC zwei Rasterzeilen, die er besonders behandeln muß. Erreicht er nämlich Rasterzeile $ F7, ab der der Bildschirm endet, wenn die 24 Textzeilen-Darstel- lung aktiv ist, so prüft er, ob Bit 3 von Register 17 gelöscht ist. Wenn ja, so beginnt er den Rand zu zeichnen, wenn nein, so fährt er normal fort. Erreicht er dann Rasterzeile $ FB, die das Ende eines 25- zeiligen Bildschirms darstellt, wird nochmals geprüft, ob das obige Bit auf 0 ist. Wenn ja, so weiß der VIC, daß er mit dem Zeichnen des Rahmens schon begonnen hat. Wenn nein, so beginnt er erst jetzt damit. Mit unserem Interrupt tricksen wir den armen Siliziumchip nun aus. Unsere Routine wird immer in Rasterzeile $ F8 angesprungen, also genau dann, wenn der VIC die 24- Zeilen-Prüfung schon vorgenommen hat. Da die Darstellung auf 25 Zeilen war, hat er noch keinen Rand gezeichnet. Unsere Interruptroutine schaltet nun aber auf 24 Zeilen um und gaukelt dem VIC auf diese Weise vor, er hätte schon mit dem Zeichnen des Randes begonnen, weshalb er nicht nocheinmal beginnen muß, und somit ohne zu zeichnen weitermacht. Dadurch erscheinen unterer und oberer Bildschimrand in der Hintergrundfarbe, und es ist kein Rahmen mehr sichtbar. In diesen Bereichen kann man nun zwar keinen Text oder Grafik darstellen, jedoch sind Sprites, die sich hier befinden durchaus sichtbar! Sie werden normalerweise nämlich einfach vom Rahmen überdeckt, sind aber dennoch vorhanden.
Da der Rahmen nun aber weg ist, sieht man auch die Sprites, wie das sich bewegende Sprite 0 unserer Interruptroutine beweist!
Wichtig an unserer Routine ist nun noch, daß wir vor Erreichen des oberen Bildrandes die Darstellung nocheinmal auf 25 Zeilen zurückschalten, damit der Trick beim nächsten Rasterdurchlauf nocheinmal klappt. Hierbei darf natürlich frühestens dann umgeschaltet werden, wenn der Rasterstrahl an der zweiten Prüf-Posi- tion, Rasterzeile $ FB, schon vorbei ist.
Dies wird durch die kleine Verzögerungsschleife bewirkt, die genau 4 Rasterzeilen wartet, bevor mit dem anschließenden ORA-Befehl Bit 3 in Register 17 des VIC wieder gesetzt wird. Am Ende unseres Interrupts bewegen wir das Sprite noch um eine Y-Position weiter und verzweigen zum Betriebssystem-IRQ, damit die Systemaufgaben trotz abgeschalteter CIA dennoch ausgeführt werden. Die interruptauslösende Rasterzeile muß nicht nochmal neu eingestellt werden, da wir diesmal nur eine Rasterzeile haben, die jedesmal wenn sie erreicht wird einen Interrupt auslöst.
Wollen wir nun noch klären, warum wir bei der Initialisierung eine 0 in Adresse $3 FFF geschrieben haben. Wie Sie vielleicht wissen, kann der VIC Speicherbereiche von lediglich 16 KB adressieren, aus denen er sich seine Daten holt. Im Normalfall ist das der Bereich von $0000-$3 FFF. Die letzte Speicherzelle seines Adressbereichs hat nun eine besondere Funktion. Der Bit-Wert, der in ihr steht, wird nämlich in allen Spalten der Zeilen des nun nicht mehr überdekkten Bildschirmrandes dargestellt - und zwar immer in schwarzer Farbe. Durch das Setzen dieser Zelle auf 0 ist hier also gar nichts sichtbar. Schreiben wir jedoch bei aktivierter Borderroutine mittels " POKE16383, X" andere Werte hinein, so werden je nach Wert mehr oder weniger dicke, vertikale Linien in diesem Bereich sichtbar. Durch Setzen aller Bits mit Hilfe des Wertes 255( mit Rahmenfarbe= schwarz), können wir sogar einen scheinbar vorhandenen Bildschirmrand simulieren!
Vielleicht fällt Ihnen nun auch noch ein interessanter Nebeneffekt auf: nachdem wir die oberen und unteren Bildschirmgrenzen abgeschaltet haben, gibt es Spritepositionen, an denen das Sprite zweimal zu sehen ist. Nämlich sowohl im oberen, als auch im unteren Teil des Bildschirms. Das liegt daran, daß das PAL-Signal, welches der VIC erzeugt 313 Rasterzeilen kennt, wir aber die Y-Position eines Sprites nur mit 256 verschiedenen Werten angeben können.
Dadurch stellt der VIC das Sprite an den Y-Positionen zwischen 0 und 30 sowohl am unteren, als auch am oberen Rand dar.
Bei eingeschalteten Rändern fiel dieser Nebeneffekt nie auf, da diese Spritepositionen normalerweise im unsichtbaren Bereich des Bildschirms liegen, wo sie vom Bildschirmrahmen überdeckt werden.
Bleibt noch zu erwähnen, daß wir mit einem ähnlichen Trick auch die seitlichen Ränder des Bildschirms verschwinden lassen können, nur ist das hier viel schwieriger, da es auf ein sehr genaues Timing ankommt. Wie man damit umgeht müssen wir jetzt erst noch lernen, jedoch werde ich in den nächsten Kursteilen auf dieses Problem nocheinmal zu sprechen kommen.
2) EINZEILEN-RASTER- EFFEKTE Kommen wir nun zu dem oben schon erwähnten Timing-Problem. Vielleicht haben Sie nach Studieren des letzten Kursteils einmal versucht einzeilige Farbraster- effekte zu programmieren. Das heißt also daß Sie gerade eine Zeile lang, die Bildschirmfarben wechseln, und sie dann wieder auf die Normalfarbe schalten.
Hierzu wären dann zwei Raster-Interrupts notwendig, die genau aufeinander zu folgen haben ( z. B. in Zeile $50 und $51) . Wenn Sie verschucht haben ein solches Rasterprogramm zu schreiben, so werden Sie bestimmt eine Menge Probleme dabei gehabt haben, da die Farben nie genau eine Rasterzeile lang den gewünschten Wert enthielten, sondern mindestens eineinhalb Zeilen lang sichtbar waren. Dieses Problem hat mehrere Ursachen, die hauptsächlich durch die extrem schnelle Gesschwindigkeit des Rasterstahls entstehen.
Selbiger bewegt sich nämlich in genau 63 Taktzyklen einmal von links nach rechts.
Da innerhalb von 63 Taktzyklen nicht allzu viele Instruktionen vom Prozessor ausgeführt werden können, kann jeder Befehl zuviel eine zeitliche Verzögerung verursachen, die eine Farbänderung um mehrere Pixel nach rechts verschiebt, so daß die Farbe nicht am Anfang der Zeile, sondern erst in ihrer Mitte sichtbar wird. Da ein IRQ nun aber verhältnismäßig viel Rechenzeit benötigt, bis er abgearbeitet ist, tritt ein Raster-IRQ in der nächsten Zeile meist zu früh auf, nämlich noch bevor der erste IRQ beendet wurde! Dadurch gerät das Programm natürlich vollkommen aus dem Takt und kann ggf. sogar abstürzen!
Noch dazu muß ein weiterer, hardwaremäßiger Umstand beachtet werden: hat man normale Textdarstellung auf dem Bildschirm eingeschaltet, so muß der VIC nämlich jedes mal zu Beginn einer Charakterzeile die 40 Zeichen aus dem Video-RAM lesen, die er in den folgenden acht Rasterzeilen darzustellen hat, und sie entsprechend in ein Videosignal umwandeln. Um diesen Vorgang durchzuführen hält er den Prozessor für eine Zeit von genau 42 Taktzyklen an, damit er einen ungestörten Speicherzugriff machen kann.
Eine Charakterzeile ist übrigens eine der 25 Textzeilen. Da der Bildschirm in der Regel bei Rasterzeile $32 beginnt, und jede achte Rasterzeile ein solcher Zugriff durchgeführt werden muß, sind all diese Zeilen besonders schwierig über einen Raster-IRQ programmierbar, da erst nach dem VIC-Zugriff ein Raster-IRQ bearbeitet werden kann, der jedoch durch den Zugriff schon viel zu spät eintritt, da die Zeile in der Zwischenzeit ja schon zu zwei Dritteln aufgebaut wurde.
Hier muß man sich eines speziellen Tricks behelfen. Um selbigen besser erläutern zu können, wollen wir uns das folgende Beispielprogramm einmal etwas näher anschauen:

;************************************** 
INIT  SEI         ;IRQs sperren         
      LDA #$7F    ;CIA-A-IRQs           
      STA $DC0D   ; unterbinden         

LDA $ DC0 D

      LDA #$82    ;Rasterzeile $82 als  
      STA $D012   ; Interruptauslöser   
      LDA $D011   ; festlegen (incl.    
      AND #$7F    ; Löschen des         
      STA $D011   ; HI-Bits             
      LDA #$01    ;Rasterstrahl ist     
      STA $D01A   ; IRQ-Auslöser        
      LDX #<(IRQ) ;IRQ-Vektor auf eigene
      LDY #>(IRQ) ; Routine verbiegen   
      STX $0314                         
      STY $0315                         
      CLI         ;IRQs freigeben       
VERZ  RTS         ;ENDE                 
;************************************** 
IRQ   DEC $D019   ;VIC-IRQ freigeben    
      JSR VERZ    ;Verzögern...         
      JSR VERZ                          
      NOP                               
      LDY #$00    ;Farb-Index init.     
LOOP1 LDX #$08    ;Char-Index init.     
LOOP2 LDA $1100,Y ;Farbe holen          
      STA $D020   ; und im Rahmen-und   
      STA $D021   ; Hintergrund setzen  
      INY         ;Farb-Index+1         
      DEX         ;Char-Index-1         
      BEQ LOOP1   ;Wenn Char=0 verzweig.
      LDA VERZ    ;Sonst verzögern...   
      JSR VERZ                          
      JSR VERZ                          
      JSR VERZ                          

CPY #$48 ; Farb-Index am Ende?
BCC LOOP2 ; Nein also weiter

      LDA #$0E    ;Sonst Rahmen/Hinter- 
      STA $D020   ; grund auf           
      LDA #$06    ; Standardfarben      
      STA $D021   ; zurücksetzen        
      JMP $EA31   ;IRQ mit SYS-IRQ beend

Besten laden Sie das Programm einmal und starten es mit " SYS4096" . Sie sehen nun einen schönen Rasterfarbeneffekt auf dem Bildschirm, wo wir ab Rasterzeile $83 in jeder Zeile die Rahmenund Hintergrundfarbe ändern. Ich habe hierbei Farbabstufungen benutzt, die schöne Balkeneffekte erzeugen. Die entsprechende Farbtabelle liegt ab Adresse $1100 im Speicher und kann natürlich von Ihnen auch verändert werden. Kommen wir nun zur Programmbeschreibung. Die Initialisierungsroutine sollte Ihnen keine Probleme bereiten. Wir schalten wie immer die CIA ab, sagen dem VIC, daß er einen Raster-IRQ generieren soll, legen eine Rasterzeile ( hier Zeile $82) als IRQ-Auslöser fest, und verbiegen die IRQ-Vektoren auf unser eigenes Programm.
Etwas seltsam mag Ihnen nun die eigentliche IRQ-Routine vorkommen. Nachdem wir mit dem DEC-Befehl dem VIC bestätigt haben, daß der IRQ bei uns angekommen ist, folgen nun drei, scheinbar sinnlose, Befehle. Wir springen nämlich das Unterprogramm " VERZ" an, das lediglich aus einem RTS-Befehl besteht, und somit direkt zu unserem Programm zurückverzweigt. Zusätzlich dazu folgt noch ein NOP-Befehl der ebensowenig tut, wie die beiden JSRs zuvor. Der Sinn dieser Instruktionen liegt lediglich in einer Zeitverzögerung, mit der wir abwarten, bis der Rasterstrahl am Ende der Zeile $82 angelangt ist. Wir hätten hier auch jeden anderen Befehl verwenden können, jedoch ist es mit JSRs am einfachsten zu verzögern, da ein solcher Befehl, ebenso wie der folgende RTS-Befehl, jeweils 6 Taktzyklen verbraucht.
Durch einen JSR-Befehl vergehen also genau 12 Taktzyklen, bis der nächste Befehl abgearbeitet wird. Da ein NOP-Befehl, obwohl er nichts macht, zwei Taktzyklen zur Bearbeitung benötigt, und wir zwei JSRs verwenden, verzögern wir also um insgesamt 26 Taktzyklen. Genau diese Verzögerung ist dem DEC-Befehl zuvor und den folgenden LDXund LDY- Befehlen notwendig, um soviel zu verzögern, daß sich der Rasterstrahl bis ans Ende der Rasterzeile bewegt hat.
Hinzu kommt daß wir die 42 Taktzyklen hinzurechnen müssen, die der VIC den Prozessor sowieso schon gestoppt hat, da Rasterzeile $82 eine der schon oben angesprochenen Charakterzeilen darstellt ($82-$32=$50/8=10- ohne Rest!) .
Laden Sie nun bitte Teil 2 des Kurses!

Valid HTML 4.0 Transitional Valid CSS!