IRQ-KURS "Die Hardware ausgetrickst..." (Teil 14)
Unser Kurs neigt sich langsam dem Ende
zu, und als wahre " Raster-Feinschmecker" haben wir uns das beste Stückchen Code
ganz für den Schluß aufgehoben. In diesem und den nächsten beiden ( letzten) Kursteilen werden wir uns mit Raster-Tricks beschäftigen, die es uns ermöglichen, den Bildschirm des C64 HARDWA-REMÄSSIG in alle Richtungen zu scrollen.
" Hardwaremässig" heißt, daß wir nicht
etwa die Softscroll-Register des VICs
beschreiben, und dann alle 8 Scollzeilen/- spalten den gesamten Bildschirm um
8 Pixel ( oder einen Charakter) weiterkopieren, sondern daß wir vielmehr den VIC
derart austricksen, daß er uns diese
Arbeit von alleine abnimmt, und zwar
ohne, daß wir auch nur einen einzigen
Taktzyklus zum Kopieren von Grafikdaten
verschwenden müssen! Die hierzu notwen- digen Routinen heißen " VSP", zum vertikalen Scrollen," HSP", zum horizontalen
Scrollen, sowie " AGSP", die eine Kombination Kombination aus den beiden ersteren dartsellt. Mit ihnen können wir den
gesamten Bildschirm ganz problemlos
( auch Hires-Grafiken!) in alle Richtungen verschieben. Im heutigen Kursteil
wollen wir mit der VSP-Routine beginnen:
1) FLD UND VSP - DIE ( UN) GLEICHEN BRÜDER
Sie werden sich jetzt sicher wundern, warum hier der Begriff " FLD", auftaucht, war das doch eine der " einfacheren" Routinen, die wir schon zu Anfang dieses
Kurses besprochen hatten. Doch wie ich
schon öfter erwähnte, ist die FLD-Routine meist der Schlüssel zu den kompexeren Rastertricks, und leistete uns
auch schon manchen guten Dienst um Timingprobleme extrem zu vereinfachen.
Diesmal jedoch dreht sich alles direkt
um unseren kleinen Helfershelfer, da die
VSP-Routine sehr stark mit ihm verwandt ist, wenn die Beiden nicht sogar identisch sind." VSP" steht für " Vertical
Screen Position", was der einfach Ausdruck für das ist, was die Routine bewirkt: durch sie wird es uns ermöglicht, den gesamten Bildschirm ohne jeglichen
Kopieraufwand vollkommen frei nach oben
oder unten zu scrollen. Und jetzt der
Clou an der ganzen Sache: FLD und VSP
unterscheiden sich lediglich durch einen
einzigen NOP-Befehl voneinander. Fügt
man Letzterern den Verzögerungs-NOPs
nach der IRQ-Glättung der FLD-Routine
hinzu, so erhält man eine voll funktionstüchtige VSP-Routine, die gleichzeitig noch einen FLD-Effekt miteingebaut hat. Damit Sie genau wissen, wovon
wir hier sprechen, sollten Sie sich auf
dieser MD einmal die Programmbeispiele
" FLD" und " VSP1" anschauen. Ersteres ist
die FLD-Routine, so wie wir sie zu Beginn dieses Kurses kennengelernt hatten.
Durch Bewegen des Joysticks nach oben
und unten können wir mit ihr den Bild- schirm nach unten " wegdrücken" . Das
Prinzip, das dabei verfolgt wurde war, daß die FLD-Routine den Beginn der nächsten Charakterzeile vor dem Rasterstrahl
herschob, indem sie ihn ständig mit
neuen vertikalen Verschiebeoffsets in
Register $ D011 fütterte. Da er deshalb
glaubte, sich noch nicht in der richtigen Rasterzeile zu befinden, in der er
die nächste Charakterzeile zu lesen hatte," vergaß" er solange sie zu zeichnen, bis wir ihm durch Beenden der FLD-Schleife die Möglichkeit dazu gaben, endlich die Rasterzeile zu erreichen, in
der er nun tatsächlich die versäumte
Charakterzeile lesen und anzeigen durfte. Egal, wieviele Rasterzeilen wir ihn
dadurch " vertrödeln" ließen, er begann
dann immer bei der Charakterzeile, die
er eigentlich als nächstes aufzubauen
gehabt hätte, so als wenn der FLD-Effekt
nie aufgetreten wäre. War das die erste
Charakterzeile des Bildschirms, so konnte man auch problemlos den Bildschirm erst in der Mitte oder am unteren Bildschirmrand beginnen lassen ( so arbeitet
übrigens auch der Effekt, mit dem Sie
die Seiten, die Sie gerade lesen umblättern) .
2) DAS PROGRAMMBEISPIEL VSP1 Wollen wir uns nun einmal die IRQ-Routine des Programmbeispiels " VSP1" anschauen. Im Prinzip nichts besonderes, da sie, wie schon erwähnt, fast identisch mit der FLD-Routine ist. Sie ist
ab Adresse $1100 zu finden und wird wie
die meisten unserer IRQ-Routinen von der
Border-IRQ- Routine, die wir immer zum
Abschalten des unteren und oberen Bildrandes benutzen initialisiert:
fld pha ;Prozessorregs. txa ; auf Stapel retten pha tya pha dec $d019 ;VIC-ICR löschen inc $d012 ;Glättungs-IRQ lda #<irq2 ; vorbereitem sta $fffe cli ;IRQs freigeben ch nop ;Insgesamt 13 NOPs ... ; zum Verzögern bis nop ; zum nächsten jmp ch ; Raster-IRQ irq2 pla ;Statusreg. u. pla ; IRQ-Adr. vom pla ; Stapel werfen dec $d019 ;VIC-ICR freigeben lda #$f8 ;Rasterpos. f. Bor- sta $d012 ; der IRQ festlegen ldx #<bord ;IRQ-Vektoren ldy #>bord ; auf Border-IRQ stx $fffe ; zurücksetzen (für sty $ffff ; nächst. Durchlauf) nop ;Verzögern bis nop ; zum Raster- nop ; zeilenende nop nop nop nop lda $d012 ;den letzen Cyclus cmp $d012 ;korrigieren bne onecycle onecycle lda #$18 ;25-Zeilen-Bildschirm sta $d011; einschalten nop ;Insgesamt 16 NOPs zum ... ;Verzögern für FLD
Es folgt nun der alles entscheindende
siebzehnte NOP-Befehl, der den VSP-Effekt auslöst:
nop ;NOP für VSP
Nun gehts weiter mit dem normalen FLD-Code. Entfernen Sie das obige NOP aus
dem Code, so erhalten Sie wieder eine
ganz normale FLD-Routine!
lda $02 ;Zeilenz. holen beq fldend ;Wenn 0 -> Ende ldx #$00 ;Zeiger init. fldloop lda rollvsp,x ;FLD-Offs. zum ora #$18 ;Verschieben d. sta $d011 ; Charakterz. lda colors1,x ;Blaue Raster sta $d020 ; im FLD-Be- sta $d021 ; reich darst. nop ;Bis Zeilen- nop ; mitte verzö- nop ; gern nop nop nop nop lda colors2,x ;Rote Raster sta $d020 ; Im FLD-Be- sta $d021 ; reich darst. nop ;Verzögern bis nop ; Zeilenende bit $ea inx ;Zeilenz.+1 cpx $02 ;Mit $02 vgl. bcc fldloop ;ungl.->weiter fldend nop ;Verz. bis nop ; Zeilenende nop nop nop lda #$0e ;Normale sta $d020 ; Bildschirm- lda #$06 ; farben ein- sta $d021 ; schalten pla ;Prozessorregs. tay ; zurückholen pla tax pla rti ; und ENDE
Die Tabelle " ROLLVSP" enthält Softscroll- Werte für Register $ D011( nur die
untersten 3 Bit sind genutzt), die die
vertikale Verschiebung immer um eine
Rasterzeile vor dem Rasterstrahl herschieben, so daß der FLD-Effekt entstehen kann.
Die Tabellen " Color1" und " Color2" geben
die Farben an, die die FLD-Routine im weggedrückten FLD-Bereich darstellt.
Dies ist nur als " Verschönerung" nebenbei gedacht.
3) DAS FUNKTIONPRINZIP VON VSP Intern kann man sich die Vorgehensweise
des VIC beim Aufbauen des Bildschirms
mit all seinen Rasterzeilen folgendermaßen vorstellen: Trifft unser armer Grafikchip auf eine Rasterzeile, in der
eigentlich die nächste Charakterzeile
erscheinen sollte, so bereitet er sich
auf den gleich folgenden Zugriff auf das
Video-RAM vor, und zählt schon einmal
einen internen Adresszeiger auf die
gewünschte Adresse um 40 Zeichen nach
oben, um die folgenden 40 Bytes rechtzeitig lesen zu können. Nun vergleicht
er die Rasterstrahlposition ständig mit
seiner Startposition für den Lesevorgang
und hält beim Erreichen von Selbiger den
Prozessor für 42 Taktzyklen an, um seinen Lesezugriff durchzuführen. Durch die
FLD-Routine wurde nun jedoch der Beginn der gesamten Charakterzeile ständig vor
dem VIC hergeschoben, weswegen er sie
auch schön brav erst später zeichnete.
Unsere VSP-Routine verfügt nun über einen einzigen NOP mehr, der hinter der
IRQ-Glättung eingefügt wurde. Das bedeutet, daß der FLD-Schreibzugriff auf Register $ D011, mit dem der vertikale Verschiebeoffset um eins erhöht wird, exakt
2 Taktzyklen später eintritt als sonst.
In genau diesen beiden Taktzyklen aber
hat der VIC schon die interne Adresse
auf das Video-RAM um 40 Zeichen erhöht
und wartet jetzt auf das Erreichen der
Startposition für den Lesevorgang. Da
der in unserem Programm folgende
Schreibzugriff auf $ D011 diese Position
für den VIC nun aber eine Rasterzeile
weiterschiebt, kann er diese Position
nie erreichen. Das Endergebnis, das wir
dadurch erhalten ist Folgendes:
* Der FLD-Effekt greift, und wir lassen
den VIC die nächste Charakterzeile
eine Rasterzeile später zeichnen.
* Der interne Adresszeiger des VIC auf
die Daten der nächsten Charakterzeile
wurde um 40 Zeichen ( also eine Charakterzeile) erhöht.
* Da der VIC beim Erreichen der nächsten
Rasterzeile wieder glaubt, er müsse
sich auf die Charakterzeile vorbereiten, zählt er den internen Adresszeiger um weitere 40 Bytes nach oben und
wartet auf die richtige Startposition
zum Lesen der nächsten 40 Bytes.
* Lassen wir ihn nun tatsächlich die
Charakterzeile lesen, indem wir die
FLD( VSP)- Routine beenden, so stellt er
zwar, wie beim normalen FLD-Effekt, wieder Charakterzeilen dar, jedoch
wurde durch das zweimalige Erhöhen des
Adresszeigers um 40 Bytes eine gesamte
Charakterzeile übersprungen! Hatten
wir den FLD-Effekt also z. B. vor Erreichen der ersten Charakterzeile einsetzen lassen, so zeichnet der VIC nun
nicht die erste, sondern die ZWEITE
Charakterzeile, da er sich ja 40 Bytes zu weit nach vorne bewegte! ! ! Die erste Charakterzeile wurde somit ÖBER-SPRUNGEN!
Im Klartext bedeutet das: für jede Rasterzeile, die wir die FLD( VSP)- Routine
länger laufen lassen, überspringt der
VIC eine Charakterzeile. Auf diese Weise
können wir also auch die Daten, die in
der Mitte des Video-RAMs liegen am Anfang des Bildschirms anzeigen lassen.
Der VIC liest nun aber pro Rasterdurchlauf immer 25 Charakterzeilen. Lassen
wir ihn also durch unseren VSP-Trick 40 Bytes zu spät auf die vermeintliche 1 .
Charakterzeile zugreifen ( es handelt
sich um die 1 . Charakterzeile die auf
dem Bildschirm zu sehen ist, jedoch mit
den Daten, die eigentlich erst in der
zweiten Charakterzeile des Bildschirms
erscheinen sollten), so hört für ihn der
Bildschirm auch 40 Bytes zu spät auf.
Das Ergebnis: in der 25 . Bildschirmzeile
liest und stellt der VIC die Daten des
sonst ungenutzten Bereichs des Video- RAMs im Adressbereich von $07 E8 bis
$07 FF dar ( wenn wir von der Stanard-Basisadresse des Video-RAMs bei $0400 ausgehen) . Dies sind 24 Zeichen, die
somit am Anfang der letzten Bildschirmzeile zu sehen sind. Hieraufhin folgen
16 weitere Zeichen, die sich der VIC
wieder aus der Anfangsadresse des Video-RAMs holt.
Zum besseren Verständnis sollten wir
vielleicht noch klären, wie der VIC auf
das Video-RAM zugreift. Selbiges ist
immer 1024 Bytes ( also exakt 1 KB) lang, obwohl der Bildschirm nur 1000 Bytes
groß ist und somit die letzten 24 Bytes
nie sichtbar werden. Wie Sie nun wissen, kann der VIC immer nur 16 KB der 64 KB des
C64 adresssieren, wobei die Lage dieses
Bereichs allerdings auch verschoben werden kann. Das heißt also, daß der VIC
insgesamt zwar Adressen in einem 64 KB-Raum (16 Adressbits sind hierzu notwendig) adressieren, aber gleichzeitig immer nur auf 16 KB für Grafikdaten zugrei- fen kann. Möchte man diesen 16 KB-Bereich
verschieben, so kann das mit den untersten zwei Bits von Adresse $ DD00 getan
werden. Im Normalfall stehen beide auf 0 und lassen den VIC deshalb im Bereich
von $0000 bis $3 FFF arbeiten. Diese
Adressen dienen als Basis für den VIC
die ihm von der NMI-CIA in die obersten
zwei Adressbits für seine RAM-Zugriffe
eingeblendet werden. Die Zugriffsadresse
ist also 16 Bit groß, wobei die beiden
Bits 0 und 1 aus $ DD00 immer in den
obersten zwei Bits (14 und 15) erscheinen. Die Bits 10-13 dieser Adresse bestimmen die Basisadresse des Video-RAMs.
Sie werden von den Bits 4-7 der VIC-Adresse $ D018 geliefert. Steht hier der
Bitcode $0001( so wie nach Einschalten
des Computers), so wird also zusammen
mit den Bits aus $ DD00 die Adresse $0400 angesprochen, die im Normalfall die Basisadresse für das Video-RAM ist. Die
restlichen, zur Adressierung benötigten
10 Bits kommen aus dem oben schon erwähnten, VICinternen Adresszähler. Er
ist exakt 10 Bits lang, womit maximal
1 KB-Bereiche adressiert werden können.
Dadurch erklärt sich auch, warum der VIC
durch austricksen mit der VSP-Routine, in der letzten Zeile die 24 sonst ungenutzten Zeichen des Video-RAMs darstellt: Da der 10- Bit-Zähler nun auf den
Offset $03 E8 zeigt, werden von dort auch
die Daten gelesen. Hierbei findet dann
aber nach dem 24 . Byte ein Öberlauf des
10- Bit-Zählers statt, womit selbiger
wieder auf 0 zurückspringt und wieder
den Anfang des Video-RAMs adressiert.
Dadurch erklärt sich auch, warum in der
letzten Bildschirmzeile nach den Zeichen
aus $07 E8-$07 FF wieder die Zeichen ab
Adresse $0400 erscheinen.
Bleibt noch zu erwähnen, daß dasselbe
für das Color-RAM bei $ D800 gilt. Dies
ist immer an einer Fixadresse, wobei die
untersten 10 Bit ebenfalls aus dem Charakterzeilen- Adresszähler kommen. Das
heißt also, daß die 24 sonst unsichtba- ren Zeichen ihre Farbe aus dem ebenfalls
sonst ungenutzten Color-RAM- Bereich von
$ DBE8-$ DBFF erhalten.
5) PROBLEME DIE BEI VSP ENTSTEHEN KÖNNEN
Die Nachteile, die durch die Charakterverschiebung entstehen, sollten auch
nicht unerwähnt bleiben:
Durch die Verschiebung der Video-RAM- Daten am Ende des Bildschirms um 24 Zeichen nach rechts, ist natürlich auch die
gesamte Grafik in zwei Teile zerlegt
worden, weswegen ein Scroller nicht ganz
so einfach durchzuführen ist. Um diesen
Fehler auszugleichen müssen wir einen
zweiten Video-RAM- Bereich verwalten, in
dem die gesamte Grafik um 24 Zeichen
versetzt abgelegt sein muß. In diesem
Video-RAM sind dann die ersten 24 Zeichen ungenutzt. Durch einen stinknormalen Rasterinterrupt können wir dann problemlos vor Beginn der Charakterzeile, an der der VIC-Adresszeiger überläuft, auf das zweite Video-RAM umschalten,
wobei dieses dann zwar an der überlaufenden Adresse ausgelesen wird, was jedoch durch unsere Verschiebung wieder
ausgeglichen wird.
Ein weiterer Nachteil, der bei VSP entsteht, liegt auf der Hand: Die letzten
acht Bytes des jetzt sichtbaren Video-RAMs sind die Adressen, in denen die
Spritepointer abgelegt werden. Das heißt
also, daß wir beim gleichzeitigen Anzeigen von Grafikzeichen in diesen acht
Bytes mit der Spriteanzeige Probleme
bekomen. Jedoch auch dies ist zu bewältigen. Entweder, indem man so geschickt
arbeitet, daß in diesen Bereichen auf
dem Bildschirm nur Zeichen in der Hintergrundfarbe zu sehen sind ( bei schwarzem Hintergrund einfach das Color-RAM in
den Adressen von $ DBF0-$ DBFF ebenfalls
auf schwarz setzen) . Oder aber indem wir
in diesen Zeichen immer nur dann die
eigentlichen Video-RAM- Werte eintragen, wenn sie ausgerechnet vom VIC benötigt
werden ( also auch exakt vor dem Lesen der Öberlauf-Charakterzeile), und ansonsten die Spritepointer hineinschreiben. Da der VIC schon 42 Taktzyklen nach
Beginn der Rasterzeile, in der die Charakterzeile beginnen soll, die Video-RAM- Daten gelesen hat, können wir also
getrost wieder die Spritepointer zurückschreiben und haben so lediglich eine
einzige Rasterzeile, in der die Sprites
garnicht ( oder nur verstümmelt) dargestellt werden können.
Soll der gesamte Bildschirm einmal
durchgescrollt werden können, so muß
davon ausgegangen werden, daß maximal 25 Rasterzeilen am Beginn des Bildschirms
ungenutzt bleiben müssen, da in Ihnen
das Timing für den VSP-Effekt unterbracht werden muß. Da pro Rasterzeile
der interne Adresspointer um eine Charakterzeile weitergezählt wird, muß also
im Maximalfall in 25 Rasterzeilen der
VSP-Effekt eingesetzt werden. Will man
den Bildschirm nun konstant an eine bestimmten Position beginnen lassen, so muß nach der Anzahl der zu überspringenden Charakterzeilen wieder eine normale
FLD-Routine ( ohne das zusätzlich NOP) benutzt werden, um bis zur 25 . Rasterzeile nach Bildschirmanfang zu verzögern, OHNE daß gleich alle 25 Charakterzeilen übersprungen werden.
6) WEITERE PROGRAMMBEISPIELE Ausser der oben schon erwähnten " VSP1"- Routine finden Sie auf dieser MD auch
noch zwei weitere Beispielprogramme, mit
den Namen " VSP2" und " VSP3"( wie alle
unserer Beispiele werden auch sie mit
" SYS4096" startet) . In Ersterem haben
wir zusätzlich zum VSP-Effekt die unteren drei Bits von $ D011 zum Softscrollen des Bildschirms verwendet, so
daß der Bildschirm weich nach oben und
unten geschoben werden kann. Die Routine
" VSP3" scrollt das gesamte Video-RAM
ununterbrochen durch, wodurch quasi ein
unendlicher Horizontalscroller durchgeführt werden kann. Die 25 unbenutzten Rasterzeilen am Bildschirmanfang, die
für das VSP-Timing benötigt werden, sind
mit Sprites hinterlegt, in denen man
z. B. in einem Spiel auch ein Scoreboard
unterbringen kann.
In der nächsten Folge des IRQ-Kurses
werden wir uns den horizonalen Hardwarescroller " HSP" anschauen, bei dem das
VSP-Prinzip auf die Horizontale Bildschirmposition umgesetzt wurde.( ih/ ub)