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 befas- sen. 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" programmie- ren, der es uns ermöglicht, einen Scrolltext, so wie in Abspännen von Fil- men, von unten nach oben über den gesam- ten Bildschirm zu scrollen. Hierbei wer- den 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 Kennt- nis 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 darstel- len. Um nun mehrere Zeilen zu generie- ren, müssen wir in regelmäßigen Abstän- den einen Raster-IRQ erzeugen, der je- desmal die Y-Position, sowie die Spri- te-Pointer der acht Sprites neu setzt, um somit die nächste Scroller-Zeile dar- zustellen. Unsere Routine tut dies alle 24 Rasterzeilen, womit sich zwischen zwei Spritezeilen immer 3 Rasterzeilen Freiraum befinden. Um nun einen Scroll- effekt nach oben zu erzeugen, benutzen wir zusätzlich ein Zählregister, daß einmal pro Rasterdurchlauf um 1 ernie- drigt, und so von $17 bis $00 herunter- gezä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 Poin- terliste, freigewordenen Sprites werden dann die Buchstaben der neuen Zeile ein- kopiert, 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" unterschei- det 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 wer- de, 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 unte- ren Bildschirmrand befinden. $1500 Spritepointer-Liste, die auf die Sprites im Bereich von $2600- $3FFF zeigt. $1600 Der Scroll-Text im ASCII-Format. Die Initialisierungsroutine unseres Mo- vie-IRQs schaltet wie üblich das Be- triebssystem-ROM ab, und setzt im Hard- ware-IRQ-Zeiger bei $FFFE/$FFFF die Startadresse der MOVIEIRQ-Routine ($1200) ein. Zudem werden alle CIA- Interrupts gesperrt und die Spriteregi- ster 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. Deswei- teren wird die Farbe aller Sprites auf "weiß" geschaltet. Zusätzlich wird in einer eigenen Unterroutine der Speicher- bereich von $2600-$3FFF, 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 wer- den, 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 weite- ren Positionen resultieren dann aus dem jeweils letzten Positionswert plus dem Offset 24. Desweiteren erscheinen in diesem Pro- grammteil noch zwei Labels, mit den Na- men "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 wur- de, kann so der Basis-Y-Wert der ent- sprechenden Spritezeile (im ersten Durchlauf Zeile 0, Y-Pos 26) auf den Akkuinhalt aufaddiert werden. Der resul- tierende Wert entspricht nun der Y- Position aller Sprites dieser Zeile, die wir sogleich in die VIC-Register eintra- gen. 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 aufad- dieren von 1 auf den jeweils letzten Wert, was in unserer Routine durch die INX-Befehle durchgeführt wird. Die Zei- gertabelle enthählt nun die Werte von $98 bis $F8, als Zeiger auf die Sprites, die im Speicherbereich von $2600-$3FFF 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 angespro- chenen 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 Spritepoin- ter von $98-$9F gehen, womit ihre Spri- tes von im Bereich von $2600 bis $2800 untergebracht sind, aus dem oberen Bild- schirmrand, so muß die zweite Spritezei- le (Pointer von $A0-$A7) von nun an die erste, auf dem Bildschirm darzustellen- de, Zeile sein, wobei wir die gerade herausgescrollte Zeile als unterste Zei- le verwenden müssen. Nachdem ihr Textin- halt in die Sprites im Speicherbereich von $2600 bis $2800 eingetragen wurde, müssen nur noch die Spritezeiger zum richtigen Zeitpunkt auf diese Zeile um- geschaltet werden. Wir haben nun noch einen Weiteren Index, namens "SHOWLINE", der in Zeropageadresse $F8 untergebracht ist, und uns angibt, welche der Sprite- zeilen 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 pro- blemlos inkementiert werden, ohne dabei einen Zeilenüberlauf beachten zu müssen! Kommen wir jedoch wieder zurück zu unse- rer IRQ-Routine. Nachdem die Y- Positionen, sowie die Spritezeiger ge- setzt wurden müssen noch einige verwal- tungstechnische 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ß, mit- berü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" ausle- sen. Auf diesen Wert muß nun noch der momentane Scrolloffset aus "SOFTROLL" addiert werden, um die tatsächliche Ra- sterzeile zu erhalten. Der daraus resul- tierende Wert kann zunächst einmal in $D012 eingetragen werden. Sollte bei der Addition ein Öberlauf stattgefunden ha- ben, also ein Wert größer $FF herausge- kommen 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 untege- bracht 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 än- dern. 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 Spri- tezeilen 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 Spritezei- len dargestellt wurden. Ist dies nicht der Fall, so wird der IRQ durch Zurück- holen der Prozessorregister, gefolgt von einem RTI, beendet. Befinden wir uns allerdings schon im letzen der 13 IRQs, die pro Rasterdurchlauf auftreten sol- len, 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 Bild- schirm 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 Rasterdurch- lauf 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 darge- stellt werden. Durch Ändern des hier subtrahierten Wertes in 2 oder 3 können Sie übrigens auch die Scrollgeschwindig- keit 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 #$0d ; 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ür- lich alle 13 Spritezeilen wieder zurück- gesetzt 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 er- haltene 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 Lauf- schrift, die an dem Label "ESTEXT" abge- legt ist, schon zu Ende gescrollt wurde. Wenn ja, so wird in diesem Fall der Zei- ger "TPOINT", der auf das erste Zeichen der als nächstes darzustellenden Sprite- zeile zeigt, wieder mit der Startadra- dresse des Textes ("ESTEXT") initiali- siert: 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.)