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)