Magic Disk 64

home to index to html: MD9409-KURSE-IRQ-KURS_11.2.html
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 schwe-
re Aufgabe, da der VIC uns diesbezüglich
ein wenig entgegenkommt. Ändern wir näm-
lich die X-Position eines Sprites inner-
halb einer Rasterzeile, so hat dies eine
sofortige eine Wirkung auf die Spritepo-
sition: 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  ge-
zeichnet wird geändert, so hat das keine
direkte  Auswirkung  auf  die restlichen
Spritezeilen. Der VIC zeichnet das Spri-
te stur zu Ende, bevor er die Y-Position
nocheinmal  prüft.  Das gibt uns die Mö-
glichkeit, die Y-Position schon im  Vor-
raus  zu  ändern, nämlich irgendwann in-
nerhalb der  21  Rasterzeilen,  die  das
Sprite  hoch  ist.  Setzen  wir die neue
Y-Position nun also genau  auf  die  Ra-
sterzeile  nach  der letzen Spritezeile,
so kümmert sich der VIC darum erst  ein-
mal 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 wor-
den, 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 Pro-
grammbeispiel zuvor gesehen  haben,  ist
das  eigentlich eine recht einfache Auf-
gabe. Da wir zum Abschalten  der  Ränder
links und rechts jedoch ein hypergenaues
Timing  benötigen, wäre es recht aufwen-
dig für die Rasterzeilen  $FA,  $28  und
$32  eigene  IRQ-Routinen  zu schreiben,
ohne dabei das Timing für  die  Sidebor-
derabschaltung  damit  durcheinander  zu
bringen. Aus diesem Grund haben wir  uns
einen Trick ausgedacht, mit dem wir bei-
de 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 Regi-
sters  $D011.  Warum  sollten  wir  also
nicht aus zwei eins machen, und generell
in  jeder Rasterzeile einen Wert in die-
ses Register schreiben?  Das  hilft  uns
zum  Einen  das  Verzögern  des  Raster-
strahls bis zum  Ende  der  Rasterzeile,
und  hat  zudem den angenehmen "Nebenef-
fekt" 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  vo-
renthalten.  Die Init-Routine werden wir
uns diesmal sparen, da sie nahezu ident-
lisch  mit  der des letzten Programmbei-
spiels 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 Rou-
tine  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  unbe-
kannte  Routine.  Der  hier  aufgezeigte
Auszug dient lediglich dem  Glätten  des
IRQs  und sollte Ihnen, wenn auch leicht
modifiziert, schon aus anderen  Beispie-
len  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  ei-
nen entscheidenden Vorteil: durch einfa-
ches 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,  je-
doch  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 Charakter-
zeilen abzuschalten. Da im  Prinzip  nur
drei verschiedene Werte in dieser Tabel-
le 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  Ta-
bellenwerte  übersprungen  und somit auf
den "richtigen"  ersten  Tabelleneintrag
zugegriffen  wird. Wichtig ist nur noch,
daß die Tabelle unbedingt innerhalb  ei-
nes  256-Byte-Blocks  steht und an einer
Adresse  mit  Low-Byte-Wert  0  beginnt.
Durch die Y-indizierte 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
$11FF,Y"  auf eine Speicherzelle zu, und
enthält in diesem  Fall  das  Y-Register
einen  Wert größer 1, so tritt ein Öber-
lauf   bei    der    Adressierung    auf
($11FF+1=$1200  -  High-Byte  muß  hoch-
gezä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 durcheinan-
derbringen,   weswegen  die  Tabelle  ab
Adresse $1500  abgelegt  wurde  und  mit
Ihren 156 Einträgen keinen solchen Öber-
lauf erzeugt. Die Tabelle selbst enthält
nun, jeweils blockweise, die Werte  $10,
$00  und  $18,  die an den richtigen Ra-
sterpositionen untergebracht wurden,  so
daß  die Ränder und Charakterzeilen beim
Schreiben des entsprechenden Wertes  ab-
geschaltet  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 Ra-
sterzeilen  die  seitlichen  Ränder  ab-
schalten  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 14x 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ück-  
   sta $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 fol-
genden 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 Spri-
tes) 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 des-
selben Registers wieder  auf  40  Zeilen
zurückgehen.  Damit wäre der Rand geöff-
net worden. Als nächstes wird  der  Wert
für  $D011 aus der besagten Tabelle aus-
gelesen und in dieses Register eingetra-
gen,   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 ei-
gentlichen Adresse, sondern 5 Bytes wei-
ter. 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  JSR-
und  RTS-Befehl  draufgehen.  Beide ver-
brauchen  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 ein-
fach  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 die-
ser  Zeile kein neuer Wert in $D011 ein-
getragen 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 ge-
raden Zeilennummern die alte Befehlsfol-
ge  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 einge-
tragen, 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än-
der, 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 Bild-
schirmrand wieder zum oberen Bildschirm-
rand  zu  wandern  hat, in der wir eben-
falls 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 ken-
nenlernen, wie man diese kleinen Grafik-
winzlinge hardwaremässig dehnen und ver-
biegen 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!