Magic Disk 64

home to index to text: MD9409-KURSE-IRQ-KURS_11.2.txt

Gleichzeitig müssen wir berücksichtigen, daß alle 21 Rasterzeilen die Y-Position der Sprites um diesen Betrag hochgezählt werden muß, damit sie auch untereinander auf dem Bildschirm erscheinen. Dies ist glücklicherweise eine nicht allzu schwere Aufgabe, da der VIC uns diesbezüglich ein wenig entgegenkommt. Ändern wir nämlich die X-Position eines Sprites innerhalb einer Rasterzeile, so hat dies eine sofortige eine Wirkung auf die Spriteposition: das Sprite erscheint ab dieser Rasterzeile an der neuen X-Position. Mit der Y-Position verhält es sich anders:
wird sie noch während das Sprite gezeichnet wird geändert, so hat das keine direkte Auswirkung auf die restlichen Spritezeilen. Der VIC zeichnet das Sprite stur zu Ende, bevor er die Y-Position nocheinmal prüft. Das gibt uns die Möglichkeit, die Y-Position schon im Vorraus zu ändern, nämlich irgendwann innerhalb der 21 Rasterzeilen, die das Sprite hoch ist. Setzen wir die neue Y-Position nun also genau auf die Rasterzeile nach der letzen Spritezeile, so kümmert sich der VIC darum erst einmal gar nicht. Er zeichnet zunächst sein aktuelles Sprite zu Ende, und wirft dann erst einen Blick in das Y-Positions- Register des Sprites. Da er dort dann eine Position vorfindet, die er noch nicht überlaufen hat, glaubt er, das Sprite wäre noch nicht gezeichnet worden, woraufhin er unverzüglich mit dem Zeichnen wiederanfängt - und schon wäre dasselbe Sprite zweimal untereinander auf dem Bildschirm zu sehen! Dadurch können wir uns in der Rasterroutine mit der Neupositionierung Zeit lassen, und selbige über zwei Rasterzeilen verteilt durchführen ( innerhalb einer Zeile wäre auch gar nicht genug Zeit dafür) .
Um die Bildschirmränder unsichtbar zu machen muß sich ein Teil unserer IRQ-Routine um das Abschalten des oberen und unteren Bildschirmrandes, sowie der Cha- rakterzeilen kümmern. Wie Sie im Programmbeispiel zuvor gesehen haben, ist das eigentlich eine recht einfache Aufgabe. Da wir zum Abschalten der Ränder links und rechts jedoch ein hypergenaues Timing benötigen, wäre es recht aufwendig für die Rasterzeilen $ FA,$28 und $32 eigene IRQ-Routinen zu schreiben, ohne dabei das Timing für die Sideborderabschaltung damit durcheinander zu bringen. Aus diesem Grund haben wir uns einen Trick ausgedacht, mit dem wir beide Aufgaben quasi " in einem Aufwasch" bewältigen: Wie Sie zuvor vielleicht schon bemerkt haben, hatten alle IRQs unserer Beispielroutine eins gemeinsam:
Sie erzielten den gewünschten Effekt durch irgendeine Manipulation des Registers $ D011 . Warum sollten wir also nicht aus zwei eins machen, und generell in jeder Rasterzeile einen Wert in dieses Register schreiben? Das hilft uns zum Einen das Verzögern des Rasterstrahls bis zum Ende der Rasterzeile, und hat zudem den angenehmen " Nebeneffekt" die horizontalen Bildschirmränder, sowie die Charakterzeilendarstellung quasi " automatisch" abzuschalten.
Nach all dieser trockenen Theorie möchte ich Ihnen den Source-Code zu unserer ESCOS-Routine nun nicht mehr länger vorenthalten. Die Init-Routine werden wir uns diesmal sparen, da sie nahezu identlisch mit der des letzten Programmbeispiels ist. Wichtig für uns ist, daß Sie den VIC darauf vorbereitet, einen IRQ in Rasterzeile 0 auszulösen, bei dessen Auftreten der Prozessor in die Routine " SIDEBORD" zu springen hat. Selbige Routine befindet sich an Adresse $1200 und sieht folgendermaßen aus:
SIDEBORD:

   pha        ;Prozessorregs. retten    
   txa                                  
   pha                                  
   tya                                  
   pha                                  
   dec $d019  ;VIC-ICR löschen          
   inc $d012  ;nächste Rasterz. neuer   
   lda #<irq2 ; IRQ-Auslöser, mit Sprung
   sta $fffe  ; auf "IRQ2"              
   cli        ;IRQs erlauben            
ch:nop        ;13 NOPs während der der  
   ...        ;IRQ irgendwann auftritt  
   nop                                  
   jmp ch                               
IRQ2:                                   
   lda #$ff      ;Alle Sprites, sowie   
   sta $d015     ; X-Expansion          
   sta $d01d     ; einschalten          
   clc                                  
   lda $00FB      ;"SOFTROLL" lesen     
   adc #$02       ;Die Y-Position       
   sta $d001      ; der Sprites         
   sta $d003      ; befindet sich       
   sta $d005      ; 2 Rasterzeilen      
   sta $d007      ; nach RasterIRQ      
   sta $d009      ; und muß demnach     
   sta $d00b      ; gesetzt werden.     
   sta $d00d                            
   sta $d00f                            
   lda $d012     ;den letzen Zyklus     
   cmp $d012     ;korrigieren           
   bne onecycle                         

ONECYCLE:

   pla           ;Vom 2. IRQ erzeugte   
   pla           ;Rückspr.adr, u. CPU-  
   pla           ;Status v.Stapel entf. 
   lda #<sidebord;IRQ-Ptr. wieder auf   
   sta $fffe     ; "SIDEBORD" zurück    
   dec $d019     ;VIC-ICR löschen       
   lda $FB       ;Index für $D011-      
   lsr           ; Tabelle vorbereiten  
   tay                                  

Soweit also keine uns besonders unbekannte Routine. Der hier aufgezeigte Auszug dient lediglich dem Glätten des IRQs und sollte Ihnen, wenn auch leicht modifiziert, schon aus anderen Beispielen bekannt sein. Einzig zu erwähnender Punkt wäre die Adresse $ FB. In dieser Zeropageadresse steht die Nummer der Rasterzeile, in der der IRQ aufgerufen werden soll. Obwohl das bei uns zwar immer der Wert 0 ist ( womit also immer 0 in diesem Register steht), hat dies einen entscheidenden Vorteil: durch einfaches Hochzählen dieses Registers um 1, nach jedem Rasterdurchlauf, wird der Raster-IRQ jeweils eine Zeile später erst auftreten, womit wir einen ganz simplen Scrolleffekt erzielen. Zu sehen ist dies auch im Beispiel " ESCOS2", das absolut identisch zu " ESCOS1" ist, jedoch mit der oben genannten Änderung.
Gleichzeitig hat die Adresse $ FB noch eine zweite Aufgabe: sie wird zusätzlich als Index auf eine Liste " mißbraucht", in der die Werte stehen, die in $ D011 eingetragen werden müssen, um oberen und unteren Bildschirmrand, sowie Charakterzeilen abzuschalten. Da im Prinzip nur drei verschiedene Werte in dieser Tabelle stehen, und zudem die Änderungen der Werte nicht hundertprozentig genau in einer speziellen Rasterzeile auftreten müssen, sondern ruhig auch einmal eine Rasterzeile früher oder später, haben wir die Tabelle nur halb solang gemacht und tragen nur jede zweite Rasterzeile einen Wert von ihr in $ D011 ein. Dies hat den zusätzlichen Vorteil, daß sie nicht länger als 256 Byte wird und somit keine High-Byte- Adressen berücksichtigt werden müssen. Aus diesem Grund wird also auch der Index-Wert, der ins Y-Register geladen wird, durch einen " LSR"- Befehl halbiert, damit bei einer Verschiebung automatsich die ersten Tabellenwerte übersprungen und somit auf den " richtigen" ersten Tabelleneintrag zugegriffen wird. Wichtig ist nur noch, daß die Tabelle unbedingt innerhalb eines 256- Byte-Blocks steht und an einer Adresse mit Low-Byte- Wert 0 beginnt.
Durch die Yindizierte Adressierung, mit der wir auf sie zugreifen, könnte es im anderen Fall zu Timing-Problemen kommen.
Greifen wir nämlich mit dem Befehl " LDA $11 FF, Y" auf eine Speicherzelle zu, und enthält in diesem Fall das Y-Register einen Wert größer 1, so tritt ein Öberlauf bei der Adressierung auf ($11 FF+1=$1200- High-Byte muß hochgezählt werden!) . Ein solcher Befehl benötigt dann nicht mehr 4 Taktzyklen, sondern einen Taktzyklus mehr, in dem das High-Byte korrigiert wird! Dadurch würden wir also unser Timing durcheinanderbringen, weswegen die Tabelle ab Adresse $1500 abgelegt wurde und mit Ihren 156 Einträgen keinen solchen Öberlauf erzeugt. Die Tabelle selbst enthält nun, jeweils blockweise, die Werte $10,$00 und $18, die an den richtigen Rasterpositionen untergebracht wurden, so daß die Ränder und Charakterzeilen beim Schreiben des entsprechenden Wertes abgeschaltet werden. Sie können ja einmal mit Hilfe eines Speichermonitors einen Blick hineinwerfen.
Wir werden uns nun um den Rest unserer Routine kümmern, in der wir in 294 Rasterzeilen die seitlichen Ränder abschalten und zudem jede 21 . Rasterzeile die Sprites neu positionieren, so daß insgesamt 14 Zeilen zu je 8 Sprites auf dem Bildschirm erscheinen. Dies ist die Fortsetzung der SIDEBORD-IRQ- Routine:
nop ; Diese Befehlsfolge wird jsr open21 ; insgesamt 14 x aufgerufen

   ...       ; um je 21 Zeilen zu öffnen
   ...                                  
   nop       ;Sprite Line 14            
   jsr open21                           
   lda #$00  ;Sprites aus               
   sta $d015                            

lda #$ c8 ;40- Spalten-Modus zurücksta $ d016 ; setzen lda $ FB ; Zeile aus $ FB für nächsten sta $ d012 ; Raster-IRQ setzen

   pla       ;Prozessorregs. zurückholen
   tay       ; und IRQ beenden0         
   pla                                  
   tax                                  
   pla                                  
   rti                                  

Wie Sie sehen besteht der Kern unserer Routine aus den 14 Mal aufeinander folgenden Befehlen:" NOP" und " JSR OPEN21" .
Der NOP-Befehl dient dabei lediglich dem Verzögern. Die Unterroutine " OPEN21" ist nun dafür zuständig, in den folgenden 21 Rasterzeilen ( genau die Höhe eines Sprites) den Rand zu öffnen, sowie die neuen Y-Spritepositionen zu setzen. Sie sieht folgendermaßen aus:

;line 00                                
OPEN21:                                 

nop ; Verzögern bis rechter

   nop         ; Rand erreicht          
   dec $d016   ;38 Spalten              
   inc $d016   ;40 Spalten              
   lda $1500,y ;$D011-Wert aus Tabelle  
   sta $d011   ; lesen und eintragen    
   iny         ;Tabellen-Index+1        
   jsr cycles+5;24 Zyklen verzögern     
;line 01                                
   dec $d016   ;38 Spalten              
   inc $d016   ;40 Spalten              
   jsr cycles  ;34 Zyklen verzögern     

. . . ; dito für 2-15 Wie Sie sehen können, verzögern wir zunächst mit zwei NOPs bis zum Ende des sichtbaren Bildschirmfensters, wo wir durch Herunterzählen von Register $ D016 die 38- Spalten-Darstellung einschalten, und gleich darauf durch Hochzählen desselben Registers wieder auf 40 Zeilen zurückgehen. Damit wäre der Rand geöffnet worden. Als nächstes wird der Wert für $ D011 aus der besagten Tabelle aus- gelesen und in dieses Register eingetragen, sowie der Index-Zähler im Y-Register für die Öbernächste Zeile um 1 erhöht. Danach wird die Routine " CYCLES" aufgerufen, jedoch nicht an ihrer eigentlichen Adresse, sondern 5 Bytes weiter. Diese Routine besteht aus insgesamt 11 NOPs, die lediglich die Zeit verzögern sollen, bis die nächste Rand-Abschaltung fällig wird. Da ein NOP 2 Taktzyklen verbraucht, verzögert sie 22 Taktyklen. Hier muß man zusätzlich noch die Zeit hinzurechnen, die für den JSRund RTS-Befehl draufgehen. Beide verbrauchen je 6 Taktzyklen, womit die " CYCLES"- Routine insgesamt 34 Zyklen in Anspruch nimmt. Durch den Offset von 5 Bytes, den wir beim ersten Einsprung in die Routine machen," übergehen" wir einfach die ersten 5 NOPs, womit nur 24 Zyklen verbraucht werden. Nach Rückkehr aus dieser Routine befindet sich der Rasterstrahl nun genau an der Position, an der wir den Rand für die neue Raster- zeile öffnen müssen, was sogleich auch durch die INC-DEC- Befehlsfolge getan wird. Anschließend wird wieder verzögert, wobei wir diesmal die " CYCLES"- Routine komplett durchlaufen, da in dieser Zeile kein neuer Wert in $ D011 eingetragen wird, und wir somit 10 Zyklen mehr zu verzögern haben!
Dieser Programm-Auszug wiederholt sich nun insgesamt noch 7 Mal, bis wir in die 16 . Spritezeile gelangt sind. Hier nun beginnen wir mit dem Neupositionieren der Sprites:

;line 16                                
   dec $d016   ;Altbekannte Methode     
   inc $d016   ; um Zeile 16 zu öffnen  
   lda $1500,y ; u. $D011 neu zu setzen 
   sta $d011                            
   iny                                  
   jsr cycles+5                         
;line 17                                
   dec $d016   ;38 Spalten              
   inc $d016   ;40 Spalten              
   clc         ;Neue Y-Pos. für nächste 
   lda $d001   ; Spr.-Reihe= Alte Y-Pos.
   adc #$15    ; plus 21                
   sta $d001   ;Und für Sprites 0-3     
   sta $d003   ; eintragen              
   sta $d005                            
   sta $d007                            
   nop         ;Nur 10 Zyklen verzögern 
   nop                                  
   nop                                  
   nop                                  
   nop                                  
;line 18                                
   dec $d016   ;Altbekannte Methode zum 
   inc $d016   ; öffnen der 17. Zeile...
   lda $1500,y                          
   sta $d011                            
   iny                                  
   jsr cycles+5                         
;line 19                                
   dec $d016  ;38 Spalten               
   inc $d016  ;40 Spalten               
   clc        ;Neue Y-Pos. für nächste  
   lda $d009  ; Sprite-Reihe berechnen  
   adc #$15                             
   sta $d009  ;und für Sprites 4-7      
   sta $d00b  ; setzen                  
   sta $d00d                            
   sta $d00f                            
   nop        ;Nur 10 Zyklen verzögern  
   nop                                  
   nop                                  
   nop                                  
   nop                                  
;line 20                                
   dec $d016  ;38 Spalten               
   inc $d016  ;40 Spalten               
   lda $1500,y;Neuen Wert für $D011 aus 
   sta $d011  ; Tab. holen u. eintragen 
   iny        ;Tab-Index+1              
   nop        ;6 Zyklen verzögern       
   nop                                  
   nop                                  
   rts        ;Und fertig!              

Wie Sie sehen, benutzen wir für die geraden Zeilennummern die alte Befehlsfolge zum Üffnen des Randes und Setzen des neuen $ D011- Wertes. In den Spritezeilen 17 und 19 jedoch wird nach dem Üffnen des Randes die Y-Position um 21 erhöht und in je vier Y-Sprite- Register eingetragen, womit also die Y-Positionen der nächsten Spritereihe festgelegt wären.
In Spritezeile 20 wird nun ein letztes Mal der $ D011- Wert neu gesetzt und nach einer kurzen Verzögerung wieder zum Hauptprogramm zurückgekehrt, von wo die Routine für alle 14 Spritereihen nochmal aufgerufen wird, und womit unser ESCOS-Effekt fertig programmiert ist!
4) ABSCHLIESSENDE HINWEISE Wie schon erwähnt, ist der Prozessor zur Anzeige dieser insgesamt 114 Sprites, sowie dem Abschalten der Bildschirmränder, fast den gesamten Rasterdurchlauf lang voll beschäftigt. Viel Rechenzeit bleibt uns nicht mehr übrig, um weitere Effekte zu programmieren. Dennoch sind am Ende des Rasterdurchlaufs noch 16 Rasterzeilen zur freien Verfügung, und man muß noch die Zeit hinzurechnen, in der der Rasterstrahl vom unteren Bildschirmrand wieder zum oberen Bildschirmrand zu wandern hat, in der wir ebenfalls noch mit dem Prozessor arbeiten können. Diese Zeit wurde im Beispiel " ESCOS2" benutzt, um die Spritereihen zusätzlich noch zu scrollen. Jedoch ist auch noch ein Moviescroller problemlos machbar. Dieses Thema werden wir jedoch erst nächsten Monat ansprechen, und uns auch weiterhin mit den Sprites befassen.
Sie werden dabei eine Möglichkeit kennenlernen, wie man diese kleinen Grafikwinzlinge hardwaremässig dehnen und verbiegen kann. Wie immer gibt es dabei einen Trick, den VIC zu überreden das Unmögliche möglich zu machen. . .
( ih/ ub)

Valid HTML 4.0 Transitional Valid CSS!