IRQ-KURS "Die Hardware ausgetrickst..." (Teil 10)
Herzlich Willkommen zum zehnten Teil
unseres Raster-IRQ- Kurses. In dieser
Ausgabe wollen wir uns weiterhin mit der
trickreichen Spriteprogrammierung befassen. Im letzten Kursteil hatten Sie ja
schon gelernt, wie man mit Hilfe eines
Sprite-Multiplexers mehr als 8 Sprites
auf den Bildschirm zaubert. Mit einem
ähnlichen Trick werden wir heute einen
sogenannten " Movie-Scroller" programmieren, der es uns ermöglicht, einen
Scrolltext, so wie in Abspännen von Filmen, von unten nach oben über den gesamten Bildschirm zu scrollen. Hierbei werden wir wieder durch den Multiplex-Effekt insgesamt 104( !) Sprites aus dem
VIC locken!
1) DAS PRINZIP DES MOVIESCROLLERS Das Funktionsprinzip des Movie-Scrollers
ist recht einfach und sollte nach Kenntnis der Multiplex-Routinen kein Problem
für Sie sein: Jede Zeile unseres Movie-Scrollers soll aus 8 Sprites aufgebaut
sein, in denen wir einen Text darstellen. Um nun mehrere Zeilen zu generieren, müssen wir in regelmäßigen Abständen einen Raster-IRQ erzeugen, der jedesmal die Y-Position, sowie die Sprite- Pointer der acht Sprites neu setzt, um somit die nächste Scroller-Zeile darzustellen. Unsere Routine tut dies alle
24 Rasterzeilen, womit sich zwischen
zwei Spritezeilen immer 3 Rasterzeilen
Freiraum befinden. Um nun einen Scrolleffekt nach oben zu erzeugen, benutzen
wir zusätzlich ein Zählregister, daß
einmal pro Rasterdurchlauf um 1 erniedrigt, und so von $17 bis $00 heruntergezählt wird. Es dient als zusätzlicher
Offset auf die Rasterzeilen, in denen
ein IRQ ausgelöst werden muß. Ist dieses Zählregister auf 0 heruntergezählt, so
wird es wieder auf $17 zurückgesetzt und
die Spritepointer für jede einzelne
Spritezeile werden alle um je eine Zeile
höher kopiert. In die, am Ende der Pointerliste, freigewordenen Sprites werden
dann die Buchstaben der neuen Zeile einkopiert, die sich dann von unten wieder
in den Scroller einreihen können.
2) DER PROGRAMMABLAUF Um den Movie-Scroll- Effekt besser zu
erläutern haben wir natürlich wieder
einige Beispielprogramme für Sie parat.
Sie heißen " MOVIE.1" und " MOVIE.2" und
befinden sich ebenfalls auf dieser MD.
Wie immer müssen beide Beispiele mit
",8,1" geladen und durch ein " SYS4096" gestartet werden." MOVIE.2" unterscheidet sich von " MOVIE.1" nur darin, daß
zusätzlich zum Scroller auch der obere
und untere Bildschirand geöffnet wurden.
Dies ist nur durch einen ganz besonderen Trick möglich, den wir später noch erläutern werden. Ich möchte Ihnen nun
zunächst eine Speicheraufteilung der
beiden Routinen geben, damit Sie sich
die einzelnen Unterroutinen, die ich
nicht alle in diesem Kurs erläutern werde, mit Hilfe eines Disassemblers einmal
selbst anschauen können:
Adr. Funktion
$0800 Zeichensatz $1000 IRQ-Init, incl. aller Sprite-Initialiserungen $1100 Movie-IRQ- Routine. Dies ist die
eigentliche IRQ-Routine, die alle
24 Rasterzeilen die Sprites neu
setzt.
$1200 BORDERNMI ( nur in " MOVIE.2"), zum
Üffnen des oberen und unteren
Bildschirmrandes.
$1300 MOVESPR-Routine. Sie bewegt die
13 Spritezeilen pro Rasterdurlauf
um eine Rasterzeile nach oben.
$1400 MAKETEXT-Routine. Diese Routine
schreibt den Text in die acht
Sprites, die sich gerade am unteren Bildschirmrand befinden.
$1500 Spritepointer-Liste, die auf die
Sprites im Bereich von $2600-$3 FFF zeigt.
$1600 Der Scroll-Text im ASCII-Format.
Die Initialisierungsroutine unseres Movie- IRQs schaltet wie üblich das Betriebssystem- ROM ab, und setzt im Hardware- IRQ-Zeiger bei $ FFFE/$ FFFF die
Startadresse der MOVIEIRQ-Routine
($1200) ein. Zudem werden alle CIA-Interrupts gesperrt und die Spriteregister des VICs initialisiert. Hierbei
tragen wir lediglich alle X-Positionen
der Sprites ein, die bei $58 beginnen, und in 24- Pixel-Schritten pro Sprite
erhöht werden. Natürlich muß auch das
X-Position- High-Bit des achten Sprites, daß sich ganz rechts auf dem Bildschirm befindet, auf 1 gesetzt werden. Desweiteren wird die Farbe aller Sprites auf
" weiß" geschaltet. Zusätzlich wird in
einer eigenen Unterroutine der Speicherbereich von $2600-$3 FFF, in dem die 104 Sprites unterbringen, gelöscht. Zuletzt
legt die Init-Routine Rasterzeile $17 als ersten IRQ-Auslöser fest und erlaubt
dem VIC IRQs zu erzeugen.
Die MOVIEIRQ-Routine stellt nun den Kern
unseres Movie-Scrollers dar. Es handelt
sich hierbei um die IRQ-Routine, die
alle 24 Rasterzeilen die Y-Positionen
der Sprites ändert. Sie wird zum ersten
Mal an Rasterzeile $17 angesprungen und
setzt dann die folgenden IRQ-Rasterzeilen von selbst. Hier nun der
kommentierte Sourcecode:
MOVIEIRQ:
pha ;Prozessorregs. retten txa pha tya pha lda #$ff ;Alle Sprites sta $d015 ; einschalten inc $d020 ;Rahmenfarbe erhöhen
Nach Einsprung in die IRQ-Routine werden, nach dem obligatorischen Retten
aller Prozessorregister, zunächst alle
Sprites eingeschaltet. Anschließend
erhöhen wir die Rahmenfarbe um 1, damit
Sie sehen können, wie lange es dauert, bis alle Sprites neu gesetzt wurden. Nun
beginnt der eigentliche Hauptteil der
Routine:
clc ; C-Bit für Add. löschen ldy counter ; Rasterzähler als Y-Index
lda softroll; Akt. Scrolloffs. holen. .
adc ypos, y ; . . Y-Wert addieren. .
sta $ d001 ; . . und selbigen in alle sta $ d003 ; acht Y-Positionen sta $ d005 ; der Sprites eintragen
sta $ d007
sta $d009 sta $d00b sta $d00d sta $d00f
Wir setzen hier die Y-Positionen der
Sprites. Hierbei hilft uns eine Tabelle
namens " YPOS", in der alle Basis-Y- Positionen der insgesamt 13 Spritezeilen
untergebracht sind. Die Y-Positionen der
Sprites in der ersten Zeile sind dabei
auf den Wert 26 festgelegt. Alle weiteren Positionen resultieren dann aus dem
jeweils letzten Positionswert plus dem
Offset 24 .
Desweiteren erscheinen in diesem Programmteil noch zwei Labels, mit den Namen " SOFTROLL" und " COUNTER" . Sie stehen
für die Zeropageadressen $ F6 und $ F7, in
denen wir Zwischenwerte unterbringen.
" SOFTROLL"($ F6) ist der oben schon
erwähnte Rasterzeilenzähler, der von $17 auf 0 heruntergezählt wird. In " COUNTER" ist vermerkt, wie oft die IRQ-Routine während des aktuellen Rasterdurchlaufs
schon aufgerufen wurde. Dies dient
gleichzeitig als Zähler dafür, welche
Spritezeile wir momentan zu bearbeiten
haben. Beim ersten Aufruf enthält " COUN-TER" den Wert 0 . Auf diese Weise können
wir ihn als Index auf die YPOS-Tabelle
verwenden. Nachdem der Akku nun also mit
den Scrolloffset " SOFTROLL" geladen wurde, kann so der Basis-Y- Wert der entsprechenden Spritezeile ( im ersten
Durchlauf Zeile 0, Y-Pos 26) auf den
Akkuinhalt aufaddiert werden. Der resultierende Wert entspricht nun der Y-Position aller Sprites dieser Zeile, die
wir sogleich in die VIC-Register eintragen.
Nun müssen noch die Spritepointer der
neuen Spritezeile neu gesetzt werden, da
diese ja einen anderen Text enthält als
die vorherige:
ldy isline ; Zgr.- Index in Y holen
ldx pointer,y;Zgr.-Basiswert aus Tab. stx $07f8 ;für Sprite0 setzen.. inx ; ..um 1 erhöhen und stx $07f9 ; für Sprite1 setzen inx ;Ebenso für Sprites2-7 stx $07fa inx stx $07fb inx stx $07fc inx stx $07fd inx stx $07fe inx stx $07ff
Auch hier verwenden wir ein Label um auf
eine Zeropageadresse zuzugreifen." ISLI-NE" steht für Adresse $ F9, die uns als
Zwischenspeicher für einen Index auf die
Spritezeigerliste dient. In letzterer
sind nun alle Spritepointerwerte für das
jeweils 0 . Sprite einer jeden Zeile un- tergebracht. Die Zeiger für die Sprites
von 1 bis 7 resultieren aus dem aufaddieren von 1 auf den jeweils letzten
Wert, was in unserer Routine durch die
INX-Befehle durchgeführt wird. Die Zeigertabelle enthählt nun die Werte von
$98 bis $ F8, als Zeiger auf die Sprites, die im Speicherbereich von $2600-$3 FFF
liegen, jeweils in Achterschritten. Hier
eine Auflistung der kompletten Tabelle:
pointer .byte $98,$a0,$a8,$b0 .byte $b8,$c0,$c8,$d0 .byte $d8,$e0,$e8,$f0,$f8 .byte $98,$a0,$a8,$b0 .byte $b8,$c0,$c8,$d0 .byte $d8,$e0,$e8,$f0,$f8
Wie Sie sehen, liegen hier die angesprochenen Pointer-Werte zweimal vor. Das
ist notwendig, um das Umschalten der
Spritezeilen, wenn diese aus dem oberen
Bildschirmrand herausgescrollt werden,
zu vereinfachen. Verschwindet nämlich
die erste Spritezeile, deren Spritepointer von $98-$9 F gehen, womit ihre Sprites von im Bereich von $2600 bis $2800 untergebracht sind, aus dem oberen Bildschirmrand, so muß die zweite Spritezeile ( Pointer von $ A0-$ A7) von nun an die
erste, auf dem Bildschirm darzustellende, Zeile sein, wobei wir die gerade
herausgescrollte Zeile als unterste Zeile verwenden müssen. Nachdem ihr Textinhalt in die Sprites im Speicherbereich
von $2600 bis $2800 eingetragen wurde, müssen nur noch die Spritezeiger zum
richtigen Zeitpunkt auf diese Zeile umgeschaltet werden. Wir haben nun noch
einen Weiteren Index, namens " SHOWLINE", der in Zeropageadresse $ F8 untergebracht
ist, und uns angibt, welche der Spritezeilen als Erstes auf dem Bildschirm
dargestellt werden muß. Zu Beginn eines
neuen Rasterdurchlaufs wird " ISLINE" mit
diesem Index initialisert. Dadurch, daß
unsere Tabelle nun nach dem Wert $ F8 ein zweites Mal von vorne beginnt, kann " IS-LINE" während des Programmablaufs problemlos inkementiert werden, ohne dabei
einen Zeilenüberlauf beachten zu müssen!
Kommen wir jedoch wieder zurück zu unserer IRQ-Routine. Nachdem die Y-Positionen, sowie die Spritezeiger gesetzt wurden müssen noch einige verwaltungstechnische Aufgaben durchgeführt
werden:
clc ; C-Bit für Add. löschen ldy counter ; Spr-Zeilen- Index holen lda ad012+1, y; LO-Byte f. nächst. IRQ adc softroll ; Scroll-Offs. add.
sta $ d012 ; u. als nächst. IRQ setzen
ror ; C-Bit in Akku rotieren and #$80 ; und isolieren ora ad011+1, y; HI-Bit f. nächst. IRQ ora $ d011 ; sowie $ D011 einodern sta $ d011 ; und setzen dec $ d019 ; VIC-IRQs freigeben dec $ d020 ; Rahmenfarbe zurücksetz.
In diesem Teil der Routine wird nun der
folgende Raster-IRQ vorbereitet. Hierzu
muß zunächst die Rasterzeile ermittelt
werden, in der dieser auftreten soll.
Dafür existieren zwei weitere Tabellen, die die Basis-Rasterstrahlpositionen für
die 13 Interrupts enthalten. Hierbei
wird es wieder etwas kompliziert, da
nämlich auch IRQs an Strahlpositionen
größer als $ FF ausgelöst werden müssen, und wir deshalb das High-Bit der auslösenden Rasterstrahlposition, das in Bit
7 von $ D011 eingetragen werden muß, mitberücksichtigen müssen. Auch hierfür
müssen wir sehr trickreich vorgehen:
Zunächst einmal ermitteln wir das Low-Byte der nächsten Rasterposition, indem
wir es, mit " COUNTER" als Index im Y-Register, aus der Tabelle " AD012" auslesen. Auf diesen Wert muß nun noch der
momentane Scrolloffset aus " SOFTROLL" addiert werden, um die tatsächliche Rasterzeile zu erhalten. Der daraus resul- tierende Wert kann zunächst einmal in
$ D012 eingetragen werden. Sollte bei der
Addition ein Öberlauf stattgefunden haben, also ein Wert größer $ FF herausgekommen sein, so wurde dies im Carry-Flag
vermerkt. Selbiges rotieren wir mit dem
ROR-Befehl in den Akku hinein, und zwar
an Bitposition 7, wo auch das High-Bit
der IRQ-Rasterstrahls in $ D011 untegebracht wird. Nachdem nun dieses High-Bit
durch ein " AND $80" isoliert wurde, odern wir aus der Tabelle " AD011" das
High-Bit der Standard-Rasterposition
( ohne Offset) in den Akku ein. Danach
müssen natürlich auch noch alle weiteren
Bits von $ D011 in den Akku eingeknüpft
werden, damit wir beim folgenden
Schreibzugriff keine Einstellungen ändern.
Nun muß nur noch das ICR des VIC
gelöscht werden, damit der nächste IRQ
auch auftreten kann. Gleichzeitig wird
die Rahmenfarbe wieder heruntergezählt
( dadurch entstehen die grauen Rechen- zeit-Anzeigen im Bildschirmrahmen) .
Nun noch der letzte Teil der IRQ-Routine, der prüft, ob schon alle Spritezeilen aufgebaut wurden:
inc isline ;Akt. Zgr.-Zähler. +1 inc counter ;Y-Pos-Zähler +1 lda counter ;Y-Pos-Zähler holen cmp #$0c ; und mit 14 vgl. bne endirq ;Wenn ungl., dann weiter jsr movespr lda #$00 sta $d015 mt:jsr maketext
ENDIRQ:
pla tay pla tax pla rti
Hier werden jetzt die Zähler und Indizes
für den nächsten IRQ voreingestellt, sowie geprüft, ob schon alle Spritezeilen dargestellt wurden. Ist dies nicht
der Fall, so wird der IRQ durch Zurückholen der Prozessorregister, gefolgt von
einem RTI, beendet. Befinden wir uns
allerdings schon im letzen der 13 IRQs, die pro Rasterdurchlauf auftreten sollen, so fällt der Vergleich von " COUN-TER" mit dem Wert 14 negativ aus, womit
die Routine " MOVESPR" angesprungen wird.
Sie sorgt für das korrekten Herabzählen
des Scrolloffsets, und erkennt, wenn die
oberste Spritezeile gerade aus dem Bildschirm gescrollt wurde:
MOVESPR:
lda #$00 ;IRQ-Zähler init. sta counter sec ;C-Bit für Subtr. setzen lda softroll;Scroll-Offs. holen sbc #$01 ;Und 1 subtrahieren
sta softroll; neuen Scroll-Offs. abl.
bpl rollon ; Wenn >0, dann weiter
Wie Sie sehen, wird hier zunächst der
IRQ-Zähler für den nächsten Rasterdurchlauf auf 0 zurückgesetzt. Anschließend
subtrahieren wir den Wert 1 vom Scroll-Offset, wodurch die Sprites im nächsten
Durchlauf eine Y-Position höher dargestellt werden. Durch Ändern des hier
subtrahierten Wertes in 2 oder 3 können
Sie übrigens auch die Scrollgeschwindigkeit erhöhen. Gab es bei der Subtraktion
keinen Unterlauf, so wird zum Label
" ROLLON"( s. u.) verzweigt. Im anderen
Fall wurde durch den Scroll soeben eine
ganze Spritezeile aus dem Bildschirm
gescrollt, weswegen wir die Zeile unten, mit einem neuen Text belegt, wieder
einfügen müssen. Zusätzlich müssen die
Spritepointer in anderer Reihenfolge
ausgelesen werden, was wir durch das
Hochzählen von " SHOWLINE" bewirken.
Diese Aufgaben werden in folgendem Pro- grammteil ausgeführt:
inc showline ;1 . Spr-Zeilen- Ind. erh.
sec ; C-Bit f. Subtr. setzen lda showline ; Showline holen sbc #$0 d ; und 13 subtr.
bmi noloop ; Bei Unterlauf weiter sta showline ; Sonst Wert abl.
Da unsere Pointertabelle zwar doppelt, aber nicht ewig lang ist, muß sie natürlich alle 13 Spritezeilen wieder zurückgesetzt werden, was durch den SBC-Befehl
geschieht. Erzeugte die Subtraktion ein
negatives Ergebnis, so sind wir noch in
einer Zeile kleiner als 13, und der erhaltene Wert wird ignoriert. Im andern
Fall haben wir soeben die Mitte der
Pointerliste erreicht, ab der ja die
selben Werte stehen wie am Anfang, und
wir können " SHOWLINE" wieder auf den
erhaltenen Wert ( immer 0) zurücksetzen.
Den nun folgenden Routinenteil, der ab
dem Label " NOLOOP" beginnt, möchte ich Ihnen nur der Vollständigkeit halber
hier auflisten. Er prüft, ob die Laufschrift, die an dem Label " ESTEXT" abgelegt ist, schon zu Ende gescrollt wurde.
Wenn ja, so wird in diesem Fall der Zeiger " TPOINT", der auf das erste Zeichen
der als nächstes darzustellenden Spritezeile zeigt, wieder mit der Startadradresse des Textes (" ESTEXT") initialisiert:
NOLOOP:
lda tpoint cmp #<estext+($34*$18) bne continue lda tpoint+1 cmp #<estext+($34*$18) bne continue lda #$00 sta showline lda #<estext sta tpoint+0 lda #>estext sta tpoint+1
( Anm. d. Red. : Bitte wählen Sie nun den zweiten Teil des IRQ-Kurses aus dem Textmenu aus.)