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)