Magic Disk 64

home to index to text: MD9405-KURSE-IRQ-KURS_7.txt
             Interrupt-Kurs             
     "Die Hardware ausgetrickst..."     
                (Teil 7)                

Nachdem wir uns im letzten Kursteil ausgiebig um das IRQ-Timing gekümmert hatten, wollen wir in dieser Ausgabe nun eine Anwendung besprechen, bei der vor allem das Glätten von Interrupts eine große Bedeutung einnimmt: es geht um die Sideborderroutinen.
1) DAS PRINZIP Im dritten Kursteil hatten wir ja schon gelernt, wie man den oberen und unteren Bildschirmrand abschaltet. Wir hatten dem VIC hierzu zunächst mitgeteilt, daß er einen 25- Zeilen hohen Bildschirm darstellen soll. Zwei Rasterzeilen bevor er jedoch den Rand desselben erreichte schalteten wir ihn auf 24- Zeilen-Darstellung um, weswegen er glaubte, schon zwei Rasterzeilen vorher mit dem Zeichnen des Randes begonnen zu haben.
Da dies aber nicht der Fall war, und der VIC seine Arbeit normal fortführte," vergaß" er sozusagen, den unteren und den oberen Bildschirmrand zu zeichnen, was uns ermöglichte, in diesen Bereichen Sprites darzustellen. Derselbe Trick funktioniert nun auch mit dem linken und rechten Bildschirmrand - zumindest vom Prinzip her. Bit 3 in VIC-Register $ D016 steuert die Breite des sichtbaren Bildschirms. Ist dieses Bit gesetzt, so zeichnet der VIC 320 sichtbare Pixel in der Vertikalen, was einer Darstellung von 40 Zeichen pro Zeile entspricht.
Löschen wir dieses Bit, so stellt er nur 302 Pixel, bzw.38 Zeichen pro Zeile dar. Dieses Bit wird vor allem zum vertikalen Scrollen verwendet, da man bei einem 38- Spalten-Bildschirm neu hereinlaufende Bildschirmzeichen setzen kann, ohne daß sie vom Betrachter gesehen werden.
Durch das rechtzeitige Setzen und Lö- schen dieses Bits kann man nun auch den linken und rechten Bildschirmrand abschalten. Hierbei liegt die Betonung besonders auf dem Wort " rechtzeitig" . Da der Rasterstrahl in der vertikalen nämlich derart schnell ist, daß der Prozessor kaum nachkommt, müssen wir den Zeitpunkt der Umschaltung sehr genau abpassen, damit unser Effekt funktioniert.
Bei der 38- Zeichen-Darstellung beginnt der linke Bildschirmrand nur 8 Pixel später und endet nur 8 Pixel früher als sonst. Da aber genau dann, wenn er Endet, umgeschaltet werden muß, und der Rasterstrahl zum Zeichnen von 8 Pixeln gerade mal 1 .2 Taktzyklen benötigt, haben wir eben nur genau diese Zeit zur Verfügung, um Bit 3 in Register $ D016 zu löschen. Da ein " STA $ D016" aber drei Taktzyklen verbraucht, muß der Prozessor diesen Befehl also schon zwei Zyklen vor dem Bildschirmrand erreichen und beginnen abzuarbeiten. Der eigentliche Schreibzugriff findet dann im dritten Taktzyklus, genau zwischen 38- und 40- Zeichen-Rand statt. Zur Verdeutlichung sollten Sie sich einmal das Programmbeispiel " SIDEBORDER.0" anschauen. Wie alle unsere Beispiele ist es absolut ( also mit ",8,1") zu laden und mit SYS4096 zu starten. Sie verlassen die Routine mit einem Druck auf den Feuerknopf eines Joysticks in Port2 . Mit Joystickbewegungen nach oben und unten können Sie die Anzahl der zu öffnenden Zeilen variieren.
Wir möchten Ihnen nun die Kernroutine des Beispiels zeigen. Die Erklärung der Interruptinitialisierung werden wir uns an dieser Stelle sparen, da wir sie ja schon oft genug besprochen haben. Im Beispiel selbst haben wir alle Interruptquellen, außer den Interrupts vom Rasterstrahl gesperrt. Desweiteren wurde das Betriebssystems-ROM abgeschaltet, und die Adresse auf unsere Routine gleich in den Vektor bei $ FFFF/$ FFFE geschrieben, so daß der Prozessor ohne Zeitverzögerung zu unserer Routine springt. Selbige " glättet" den Interrupt zunächst nach der im letzten Kursteil beschriebenen Art und Weise. Direkt nach der Glättung folgt nun diese Routine:

     ...                                
     LDX #00     ;Zeilenzähler löschen  
     CLC                                
LOOP NOP         ;7 NOPs Verzögerung    
     NOP         ;  bis Zeilenende      
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     LDA #00     ;EINEN Taktzykl. vor   
     STA $D016   ;Beg. d.rechten Randes 
     LDA #08     ;Bit 3 löschen und     
     STA $D016   ;gleich wieder setzen  
     NOP         ;13 weitere NOPs zum   
     NOP         ; Verzögern von 26     
     NOP         ; Taktzyklen           
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     BIT $EA     ;3 Zyklen verzögern    
     INX         ;Linienzähler+1        
     CPX $02     ;Mit Anz.zu öffnender  
     BCC LOOP    ;Zeilen vgl. u. weiter 

. . .
Diese Abfolge von Befehlen beinhaltet nun haargenau das benötigte Timing, damit der Sideborder-Effekt auch sichtbar wird. Sie sehen hier zunächst 7 NOPs, mit denen wir vom Ende der Rasterglättung bis zum Beginn des sichbaren rechten Bildschirmrandes verzögern. Hieraufhin wird Bit 3 von Register $ D016 gelöscht, um auf 38- Zeilen-Darstellung umzuschalten, und gleich darauf wieder gesetzt, damit das Umschalten in der nächsten Rasterzeile noch genauso effektiv ist. Anschließend wird mit den 13 NOPs und dem BIT-Befehl um insgesamt 29 Taktzyklen verzögert, und geprüft, ob das X-Register, das als Zähler der schon geöffneten Rasterzeilen fungiert, den Wert der zu öffnenden Zeilen enthält, der in Speicherzelle $02 abgelegt ist.
Wenn nicht, so wird wieder zum Anfang der Schleife verzweigt und somit eine weitere Rasterzeile geöffnet.
2) EINE VERFEINERUNG Wenn Sie das Beispiel einmal gestartet und angesehen haben, so werden Sie bemerken, daß wir lediglich im oberen, sonst mit dem Rahmen verdeckten Bereich des Bildschirms den seitlichen Rand geöffnet haben. Das funktioniert natürlich nur, wenn wir den VIC zuvor mit dem normalen Bordereffekt überlistet haben, so daß er den oberen und unteren Bildschirmrand ebenfalls wegfallen lässt.
Desweiteren kann der Rand dort nur bis zur letzten Zeile, des ( unsichtbaren) oberen Bildschirmrandes geöffnet werden, was einen besonderen Grund hat: Wie wir mittlerweile ja wissen, liest der VIC zum Beginn einer jeden Charakterzeile ( also jede achte Rasterzeile), die 40 in dieser Zeile darzustellenden Zeichen ein, und blockiert während dieser Zeit den Prozessor für eine Zeitspanne von 42 Taktzyklen. Dadurch gerät natürlich unser ganzes Timing durcheinander, weshalb der Effekt an diesen Stellen nicht mehr zu sehen ist ( obwohl immer noch dieselbe Schleife läuft!) . Um nun innerhalb des normalen Bildschirms, in dem Zeichen dargestellt werden können, den Rand zu öffnen, verkompliziert sich das Timing natürlich erheblich, da wir jede achte Rasterzeile 42 Taktzyklen weniger zur Verfügung haben. Die einfachste Methode, dieses Problem zu umgehen, ist die Kombination der Sideborderroutine mit einer FLD-Routine. Dadurch schieben wir ja den Beginn der nächsten Charakterzeile vor dem VIC her, so daß er keine Daten liest und uns somit nicht bei der Arbeit stört. Diesen Weg geht unser zweites Programmbeispiel mit dem Namen " SIDEBOR-DER.1" . Hier das Listing der entscheidenden Schleife. Auch hier wurde der Interrupt natürlich zuvor geglättet:

     ...                                
LOOP LDA $D012  ;FLD-Routine            
     ADC #$02                           
     AND #$07                           
     ORA #$18                           
     STA $D011                          
     LDA #$00   ;wieder 1 Zykl. vor Beg.
     STA $D016  ;d. rechten Randes Bit 3
     LDA #$08   ;von $D016 löschen und  
     STA $D016  ;gleich wieder setzen   
     NOP        ;13 weitere NOPs zum    
     NOP        ; Verzögern von 26      
     NOP        ; Taktzyklen            
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     BIT $EA    ;3 Zyklen verzögern     
     INX        ;Linienzähler+1         
     CPX $02    ;Mit Anz. zu öffnender  
     BCC LOOP   ;Zeilen vgl. u. weiter  

. . .
Im Prinzip hat sich hier nicht viel geändert. Nur daß die 7 NOPs am Anfang der Routine den fünf Befehlen des FLD-Effektes gewichen sind. Diese brauchen ebenso 14 Taktzyklen, so daß unsere Routine vom Timing her immer noch genauso funktioniert, wobei wir den Bildschirmrand jedoch innerhalb des normalen Bildbereichs abgeschaltet haben!

3) SPRITES IM SIDEBORDER                

Wie bei den Bildschirmrändern oben und unten kann man natürlich auch in den seitlichen, abgeschalteten Rändern Sprites darstellen. Dies gestaltet sich jedoch ebenfalls schwieriger als sonst.
Nämlich so wie der VIC den Prozessor in jeder Charakterzeile anhält, um ungestört die Charakterdaten zu lesen, so hält er ihn auch an, wenn er Spritedaten zu lesen hat. Hierbei prüft der VIC zunächst einmal, ob ein Sprite an der aktuellen Rasterposition überhaupt sichtbar ist. Wenn ja, so liest er die drei Datenbytes des Sprites, die in dieser Rasterzeile anzuzeigen sind. Hierzu benötigt er ungefähr 2 .4 Taktzyklen, die uns von der Prozessorzeit natürlich wieder abgehen! Somit muß die Verzögerung innerhalb unserer Schleife verkürzt werden. Da wir aber keine Bruchteile von Taktzyklen verzögern können, wird die ganze Sache umso haariger! ! ! In der Regel hilft hier nur die alte " Trialand- Error"- Methode, um das perfekte Timing genau auszuloten. Zum Einschalten von sieben Sprites müssen wir nach dem Löschen und Setzen des Anzeigebits von Register $ D016 nur noch 12 Takte, anstelle von ursprünglich 29, warten, bis der Rasterstrahl die nächste Zeile erreicht. Es gehen uns also 17 Zyklen verloren! Programmbeispiel " SIDEBORDER.2" benutzt anstelle der 13 NOPs und dem BIT-Befehl nur noch 6 NOPs. Sehen Sie sich dieses Beispiel ruhig einmal mit einem Disassembler oder Speichermonitor an. Wenn Sie durch Herunterdrücken des Joysticks den geöffneten Bereich vergrößern, so werden Sie merken, daß der Sideborder- Effekt nur in den Rasterzeilen funktioniert, in denen die Sprites sichtbar sind. Das gilt auch dann, wenn Sie die Sprites in Y-Richtung expandieren ( vor dem Aufruf des Beispiels PO-KE53271,255 eingeben) . Hier funktioniert der Effekt dann doppelt soviele Zeilen lang, da der VIC bei Expansion jede Zeile eines Sprites einfach doppelt liest( auf zwei Rasterzeilen verteilt) . Möchten Sie den Rand weiter nach unten öffnen, so ist nur noch ein komisches Linien- Wirr-Warr zu sehen, da in diesen Zeilen keine Sprites angezeigt werden, und somit wieder eine Verzögerung von 29 Taktzyklen von Nöten wäre! Wohlgemerkt funktioniert dieser Effekt nur, wenn auch wirklich in jeder Rasterzeile sieben Sprites darzustellen sind. Schalten Sie weniger oder mehr Sprites ein, so ist der Sidebordereffekt zunichte gemacht. Das gleiche passiert, wenn Sie nicht alle sieben Sprites Yexpandieren.
Hier Endet der Effekt nach 21 Zeilen, nämlich dann wenn ein nichtexpandiertes Sprite zu Ende gezeichnet ist, und Sie sehen im Border nur die Hälfte der expandierten Sprites!
4) HINWEISE ZUM OPTIMALEN TIMING Möchten Sie nun selbst die Verzögerung ausloten, die zur Darstellung einer bestimmten Anzahl Sprites benötigt wird, so beachten Sie folgende Regeln:

* Geben Sie allen Sprites, die in Bereichen angezeigt werden, in denen der Sideborder abgeschaltet ist, ein und dieselbe Y-Koordinate, so daß sie horizontal in einer Linie liegen. Dadurch werden Ungleichheiten innerhalb der Rasterzeilen vermieden, so daß immer gleich viele Spritedaten gelesen werden. Gleiches gilt für die Y-Expandierung. Es sollten entweder alle oder keines der, in einem solchen Bereich sichtbaren, Sprites expandiert sein ( es sei denn Sie möchten nur die Hälfte eines expandierten Sprites sehen) .

* Achten Sie darauf, daß andere Sprites nicht in den Bereich ohne Sideborder hineinragen, da auch sie das Timing durcheinanderbringen.

* Beim Ausloten der Verzögerung sind NOPund BIT-Befehle am Besten zum schrittweisen Timing-Test geeignet.
Möchten Sie eine ungerade Anzahl Zy- klen verzögern, so benutzen Sie einen Zeropage-BIT- Befehl ( so wie der oben benutzte " BIT $ EA"), da er drei Zyklen verbraucht. Bei einer geraden Anzahl Zyklen reicht eine entsprechende Anzahl NOPs, die jeweils nur 2 Zyklen benötigen. So brauchen Sie zur Verzögerung von z. B.7 Zyklen 2 NOPs und einen BIT-Befehl. Bei 8 Zyklen sind 4 NOPs ohne BIT-Befehl ausreichend, usw.

* Schätzen Sie in etwa ab, wieviele Taktzyklen Sie bei einer bestimmten Anzahl Sprites etwa benötigen, um zu verzögern ( Formel:29- AnzSprites*2 .4) und testen Sie ob dieser Wert funktioniert. Wenn nicht ändern Sie in 1- Zyklus-Schritten nach oben oder unten.

* Natürlich wird der Prozessor immer nur eine gerade Anzahl Zyklen angehalten.
Der Richtwert von 2 .4 Zyklen pro Sprite ist lediglich zur Orientierung gedacht. Da der Prozessor immer nur zu einem Taktsignal eine Operation beginnen kann, sollte die Verzögerung also immer zu finden sein.

* Zum Testen, wann Register $ D016 geändert wird, sollten Sie die Zugriffe auf dieses Register zu Testzwecken mit Zugriffen auf Register $ D021 ersetzen.
Sie erzielen dadurch eine Farbänderung des Hintergrunds, und zwar genau zu dem Zeitpunkt, zu dem normalerweise auch Register $ D016 verändert wird.
Sehen Sie einen schwarzen Strich am Bildschirmrand, der länger als 8 Pixel ist, so ist der Zugriff zu früh. Sehen Sie gar keinen Strich, so war er zu spät. Wenn Sie die richtige Einstellung gefunden haben können Sie $ D021 wieder mit $ D016 ersetzen, und der Sideborder-Effekt sollte funktioieren.
5) NOCH TRICKREICHERE PROGRAMMIERUNG Zum Abschluß möchte ich Ihnen noch ein viertes Programmbeispiel erläutern, nämlich eine Sideborderroutine, die acht Sprites im Border darstellt. Das verwunderliche an ihr ist die Funktionsweise.
Denn obwohl die Sideborderschleife genauso viele Taktzyklen verbraucht wie die Version für sieben Sprites, kann dennoch ein Sprite mehr dargestellt werden. Ermöglicht wird dies durch eine trickreichere Variante des Löschen und Setzen des Bildschirmbreite-Bits aus Register $ D016 . Wir initialisieren dieses Register vor der eigentlichen Sideborderroutine nämlich mit dem Wert $ C8, was dem Standardwert dieses Registers entspricht. In der eigentlichen Routine wird dann Bit 3 gelöscht, indem wir das ganze Register mittels " DEC $ D016" um eins herunterzählen ( auf $ C7- bin.
%11000111- Bit 3 gelöscht) . Ein direkt folgendes " INC $ D016" setzt das Bit dann wieder. Hier das Listing der Kernroutine des Beispiels " SIDEBORDER.3" :

     ...                                
LOOP LDA $D012  ;FLD-Routine            
     ADC #$02                           
     AND #$07                           
     ORA #$18                           
     STA $D011                          
     DEC $D016  ;Bit 3 löschen und      
     INC $D016  ;gleich wieder setzen   
     NOP        ;Wie bei "SIDEBORDER.2" 
     NOP        ; stehen hier trotzdem  
     NOP        ; nur 6 NOPs!!!         
     NOP                                
     NOP                                
     NOP                                
     INX        ;Linienzähler+1         
     CPX $02    ;Mit Anz. zu öffnender  
     BCC LOOP   ;Zeilen vgl. u. weiter  

. . .
Der Grund, warum die Routine dennoch funktioniert ist, daß der Prozessor zur Abarbeitung des DEC-Befehls sechs Taktzyklen verbraucht, wobei der Schreibzugriff mit dem dekrementierten Wert durch den internen Aufbau des Befehls schon früher eintritt. Alles in Allem ist das Funktionieren dieser Methode umso wunderlicher, da die beiden Befehle DEC und INC, ebenso wie die bei- den LDAs und STAs vorher,12 Taktyklen benötigen. Wir tauchen hier also schon in die tiefere Bereiche der Assemblerprogrammierung ein, da es an der Funktionsweise des DEC-Befehls liegt, warum das Bit rechtzeitig gelöscht wird.
Beachten Sie daher bitte auch diese Variation der Sideborder-Routine für eigene Zwecke und benutzen Sie sie, wenn normales Ausloten nicht ausreicht.
6) EIN LETZTES BEISPIEL Als zusätzlichen Leckerbissen haben wir Ihnen noch ein weiteres Beispielprogramm auf dieser MD untergebracht:" SIDEBOR-DER.4" basiert auf dem selben Prinzip wie " SIDEBORDER.3", nur daß die Sprites zusätzlich Xexpandiert wurden und somit eine lückenlose Laufschrift durch den Bildschirmrand dargestellt werden kann.
Die Zeichendaten werden dabei direkt aus dem Zeichensatz-ROM geholt, und mit Hilfe des ROL-Befehls bitweise durch die Sprites rotiert. Wie so etwas funktio- niert wissen Sie bestimmt, weshalb ich es hier nicht noch eimal extra erläutere, zumal es ja eigentich nicht zu unserem Thema gehört.
Öbrigens: vielleicht ist Ihnen schon aufgefallen, daß beim Üffnen des Sideborders der rechte Bildschirmrand immer eine Rasterzeile höher geöffnet ist, als der Linke. Und das dieser umgekehrt eine Rasterzeile länger offen ist, als der Rechte. Das liegt daran, daß das Abschalten des Randes zwar am rechten Ende einer Rasterzeile geschieht, sich jedoch auf das linke Ende der folgenden Rasterzeile auswirkt!
In der nächsten Folge dieses Kurses werden wir Ihnen zeigen, wie man den Border OHNE wegdrücken des Bildschirms durch FLD abschaltet ( besonders haarige Timingprobleme) . Desweiteren bleiben wir dann beim Thema " Sprites", und wollen uns anschauen, wie man den VIC derart austrickst, daß er mehr als acht dieser kleinen Grafikstückchen auf den Bild- schirm zaubert. Bis dahin sollten Sie sich die in der heutigen Folge besprochenen Beispiele nocheinmal genauer anschauen, und ein wenig damit herumexperimentieren. Sie ahnen nicht wie vielseitig man mit dem abgeschalteten Border arbeiten kann. . .

                                 (ub/ih)

Valid HTML 4.0 Transitional Valid CSS!