IRQ-KURS "Die Hardware ausgetrickst..." (Teil 13) ----------------------------------------
Herzlich Willkommen zum dreizehnten Teil unseres IRQ-Kurses. Auch diesen Monat soll es um das trickreiche manipulieren von Sprites gehen. Wir werden uns einige Routinen zum Dehnen von Sprites an- schauen, und dabei lernen, daß es auch Sprites gibt, die mehr als 21 Rasterzei- len hoch sind... 1) DAS PRINZIP Wie immer kommen wir zu Beginn zum Funk- tionsprinzip des Rastereffektes dieses Kursteils: Wie Sie vielleicht wissen, so ist die Größe eines Sprites prinzipiell auf 24x21 Pixel begrenzt. Diese Größe ist starr und eigentlich nicht veränder- bar. Es existieren jedoch 3 Sonderfälle, in denen das Sprite auch größer sein kann, nämlich dann, wenn wir mit Hilfe der Register $D017 und $D01D die X- und Y-Expansion eines Sprites einschalten. In diesem Fall kann sowohl die X- als auch die Y-Ausdehnung verdoppelt werden, so daß wir ein Sprite mit einer maxima- len Größe von 48x42 Pixeln erhalten, in dem die normalen 24x21 Pixel einfach doppelt dargestellt werden. Wie Sie also sehen ist der VIC rein theoretisch doch in der Lage größere Sprites auf den Bildschirm zu zaubern. Jedoch auch dies nur in höchst beschränkter Form, da wir nun ebenfalls an eine fixe Auflösung gebunden sind. Wir werden allerdings gleich ein Verfahren kennenlernen, mit dem es uns möglich sein wird, zumindest die Y-Auflösung eines Sprites variabel festzulegen. Dreh- und Angelpunkt dieses Effektes wird das eben schon angesprochene Regi- ster zur Y-Expansion der Sprites ($D017) sein. Wollen wir zunächst einmal klären, wie der VIC die Sprites überhaupt dar- stellt: Nehmen wir also an, daß wir ein Sprite auf dem Bildschirm darstellen möchten. Zunächst einmal nicht expan- diert. Der VIC vergleicht nun jede Ra- sterzeilennummer mit der Y-Position des Sprites. Sind beide Werte gleich, so hat er die Rasterzeile erreicht, in der die oberste Linie des Sprites zu sehen sein soll. Es wird nun eine interne Schaltlo- gik aktiviert, die dem VIC zu Beginn einer jeden Rasterzeile die Adresse der nächsten drei Datenbytes an den Datenbus legt und ihn somit jedesmal mit den in dieser Zeile für dieses Sprite relevan- ten Daten füttert. Die Schaltlogik verfügt nun für jedes Sprite über ein 1-Bit-Zähl-, sowie ein 1-Bit-Latch- Register, die beide bei einem Schreibzu- griff auf das Register $D017, mit dem Zustand des Bits zum zugehörigen Sprite neu initialisiert werden. Das Latch- Register dient dabei als "Merkhilfe" für den Zustand der Y-Expansion des Sprites. Enthält es den Bitwert 1, so soll das Sprite in Y-Richtung verdoppelt werden, enthält es den Wert 0, so soll es normal dargestellt werden. Ab der Rasterzeile, ab der das Sprite nun gezeichnet werden soll legt die Schaltlogik nun zunächst die aktuelle Spritedatenadresse an den Bus und füttert den VIC so mit den Spri- tedaten für die erste Rasterzeile. Gleichzeitig wird bei Erreichen der nächsten Rasterzeile der 1-Bit-Zähler um eins erniedrigt. Tritt dabei ein Unter- lauf auf, so reinitialisiert die Schalt- logik den Zähler wieder mit dem Inhalt des Latch-Registers und erhöht die Quel- ladresse für die Speitedaten um 3 Bytes, so daß Sie anschließend dem VIC die Da- ten der nächsten Spritezeile zukom- menlässt. Tritt kein Unterlauf auf, weil der Zähler auf 1 stand und beim Herun- terzählen auf 0 sprang, so bleibt die Quelladresse der Spritedaten unverän- dert, so daß der VIC auch in dieser Ra- sterzeile dieselben Spritedaten liest, die er auch eine Rasterzeile vorher schon darstellte. Ist die Spriteexpan- sion nun abgeschaltet, so wurden Latch und Zähler zu Beginn mit dem Wert 0 gefüttert. In dem Fall läuft der Zähler in jeder Rasterzeile unter und somit bekommt der VIC in jeder Rasterzeile neue Spritedaten zugewiesen. Ist die Y-Expansion eingeschaltet, so enthalten Zähler und Latch den Wert 1 und es wird fortlaufend nur in jeder zweiten Raster- zeile eine neue Adresse an den Datenbus angelegt, womit der VIC das Sprite in der Vertikalen doppelt so hoch dar- stellt. Wie nun eingangs schon erwähnt so werden sowohl Latch als auch Zähler neu initia- lisiert, sobald ein Schreibzugriff auf Register $D017 erfolgt. Dadurch haben wir also auch direkten Einfluß auf den Y-Expansions-Zähler. Was sollte uns nun also davon abhalten, diesen Zähler in JEDER Rasterzeile durch Setzen des Y- Expansionsbits des gewünschten Sprites wieder auf 1 zurückzusetzen, so daß die Schaltlogik nie einen Unterlauf erzeugen kann, und somit immer wieder dieselben Spritedaten angezeigt werden? Und ganz genau so können wir ein Sprite länger strecken als es eigentlich ist und z.B. 3-, 4-, oder 5-fache Expansion des Spri- tes bewirken (indem jede Spritezeile 3-, 4- oder 5-mal hintereinander dargestellt wird)! Wir haben sogar die Möglichkeit jede Spritezeile beliebig, und voneinan- der unabhängig oft, zu wiederholen, so daß man z.B. auch eine Sinuswelle über das Sprite laufen lassen kann! Hierzu muß lediglich exakt zu Beginn einer Ra- sterzeile entweder das Expansionsbit gesetzt werden, wenn die Rasterzeile dieselben Spritedaten enthalten soll wie die letzte Rasterzeile, oder wir Löschen das Expansionsbit des gewünschten Spri- tes, um die Daten der nächsten Sprite- zeile in den VIC zu holen!
2) PROGRAMMBEISPIELE 1 UND 2
Um einen Eindruck von den Möglichkeiten zu bekommen, die uns dieser Effekt bie- tet, sollten Sie sich einmal die Bei- spielprogramme "STRETCHER.1" bis "STRET- CHER.4" auf dieser MD anschauen. Sie werden alle wie immer mit LOAD"Name",8,1 geladen und durch ein "SYS4096" gestar- tet. Die ersten beiden Beispiele stellen ein Sprite dar, das normalerweise nur eine diagonale Line von der linken obe- ren Ecke zur rechten unteren Ecke des Sprites enthält. Im ersten Beispiel ha- ben wir lediglich einige dieser Zeilen mehrfach dargestellt. Das zweite Bei- spiel enthält eine Streckungstabelle, mit der wir jede Rasterzeile in Folge 1-, 2-, 3-, 4-, 5-, und 6-Mal darstel- len, womit die Linie in etwa die Rundun- gen einer Sinuskurve bekommt! Wollen wir uns nun einmal den Programm- code anschauen, den wir zur Erzeugung der Verzerrung in Beispiel "STRETCHER.1" verwenden. Die Initialisierung und den Beginn der IRQ-Routine möchte ich wie immer aussparen, da beides absolut iden- tisch mit unseren anderen IRQ-Beispielen ist. Wir legen hier den Raster-IRQ auf Rasterzeile $82 fest und schalten Be- triebssystem-ROM ab, um direkt über den IRQ-Vektor bei $FFFE/$FFFF zu springen. Die IRQ-Routine selbst beginnt nun ab Adresse $1100, wo zunächst unser altbe- kannter Trick zum Glätten des IRQs auf- geführt ist. Der für uns wesentliche Teil beginnt wie immer ab dem Label "ONECYCLE", ab den der IRQ geglättet wurde, und die für uns relevanten Routi- nenteile stehen. Zusätzlich sei erwähnt, daß wir gleichzeitig, um Timingprobleme zu vermeiden, eine FLD-Routine benutzen um die Charakterzeilen wegzudrücken und gleichzeitig den linken und rechten Bildschirmrand öffnen, damit wir in spä- teren Beispielen auch Sprites in diesen Bereichen darstellen und sehen können. Hier nun jedoch zunächst der Source- Code: onecycle:
lda #$18 ;1. Wert für FLD sta $d011 ; in $D011 schreiben lda #$f8 ;Nächsten IRQ bei Raster- sta $d012 ; zeile $f8 auslösen dec $d019 ;VIC-ICR löschen lda #21*5 ;Zähler f. FLD init. (21*5= sta $02 ; 5-fache Spritehöhe) nop ;Verzögern.. ldx #$00 ;Tabellenindex init.
fldloop:
lda ad017,x;Wert aus Stretch-Tab lesen sta $d017 ; und in Y-Exp. eintragen lda ad011,x;Wert aus FLD-Tab lesen sta $d011 ; und in $D011 eintragen dec $d016 ;38 Spalten (Rand inc $d016 ;40 Spalten öffnen) nop ;Bis zum Anfang der nop ; nächsten Rasterzeile nop ; verzögern... nop nop nop nop nop nop lda #$00 ;Y-Exp. auf 0 sta $d017 ; zurücksetzen inx ;Tab-Index+1 cpx $02 ;Mit FLD-Zähler vgl. bcc fldloop;Kleiner, also weiter lda #$82 ;Nächster IRQ bei Rasterz. sta $d012 ; $82 ldx #$00 ;IRQ-Vektoren ldy #$11 ; auf eigene stx $fffe ; Routine sty $ffff ; umstellen lda #$0e ;Farben auf hellblau/ sta $d020 ; schwarz setzen lda #$00 sta $d021 pla ;Prozessorregs. zurück- tay ; holen pla tax pla rti ;IRQ beenden
Ab dem Label ONECYCLE setzen wir zunächst einige Basiswerte für die Spri- te-Stretch, FLD- und Sideborderroutinen. Hierzu schreiben wir erst einmal die Anzahl der Rasterzeilen, in denen diese drei Effekte aktiv sein sollen als Ver- gleichszähler in Adresse $02 und löschen das X-Register, das als Index auf die FLD- und Sprite-Stretch-Tabellen dienen soll (dazu später mehr). Das Setzen des nächsten IRQ-Auslösers auf Rasterzeile $F8 ist lediglich ein Relikt aus älteren IRQ-Routinen, in denen wir den unteren und oberen Rand des Bildschirms öffne- ten. Da wir später jedoch wieder Raster- zeile $82 als IRQ-Auslöser festlegen, ist diese Befehlsfolge eigentlich unnö- tig. Dennoch haben wir sie beibehalten, da das Entfernen der beiden Befehle zum Einen das Timing, das zum exakten syn- chronisieren zwischen Programm und Ra- sterstrahl notwendig ist, durcheinan- derbrächte und wir dadurch andere Befeh- le zum Verzögern einfügen müssten, und zum Anderen um die Flexibilität der Rou- tine dadurch nicht einzuschränken. Auf diese Weise wird es z.B. für Sie sehr einfach, die Routine mit einer Top- und Bottom-Border-Funktion "nachzurüsten", indem Sie lediglich die IRQ-Vektoren weiter unten auf eine solche Routine verbiegen und das Festlegen des nächsten IRQs bei Rasterzeile $82 auf diese Rou- tine verlagern. Dies ist übrigens eine saubere Möglichkeit timing-kritische IRQ-Routinen zu schreiben, und sie zusätzlich zu anderen Raster-Effekten erweiterbar zu halten. Da wir sowieso die meiste Zeit verzögern müssen tun uns die beiden Befehle auch nicht weiter weh. Es folgt nun der eigentliche Kern der IRQ-Routine; eine Schleife namens "FLD- LOOP". Hier lesen wir zunächst einmal einen Wert aus der Tabelle "AD017" aus und tragen ihn in das Y-Expansions- Register ein. Die besagte Tabelle befin- det sich im Code ab Adresse $1600 und enthält die Werte, die nötig sind, um das Sprite wie gewünscht zu dehnen. Da wir uns in den Beispielen 1 und 2 nur auf ein Sprite beschränken (Sprite 0 nämlich), enthält die Tabelle natürlich nur $00- und $01-Werte. Bei $00 wird ganz normal die nächste Spritezeile ge- lesen und angezeigt, bei $01 wird immer wieder die zuletzt dargestellte Sprite- zeile auf den Bildschirm gebracht. Fol- gen mehrere $01-Werte aufeinander, so wird eine einzige Spritezeile so oft wiederholt, wie $01-Werte in der Tabelle stehen. Um die nächste Spritezeile dar- zustellen muß jetzt mindestens einmal ein $00-Wert folgen. Diese Zeile kann nun ebenfalls beliebig oft wiederholt werden, usw. Zur besseren Öbersicht hier ein Auszug aus der Tabelle mit Ihren Werten für das Beispiel "STRETCHER.1": ad017:
.byte $01,$01,$01,$00,$01,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00 .byte $01,$01,$01,$00,$01,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00 .byte $01,$01,$01,$01,$01,$01,$01,$01 .byte $01,$01,$01,$01,$01,$01,$01,$01 .byte $01,$01,$01,$01,$01,$01,$01,$01 .byte $01,$01,$01,$01,$01,$01,$01,$01 .byte $01,$01,$01,$01,$01,$01,$01,$01 .byte $01,$01,$01,$01,$01,$01,$01,$01 .byte $01,$01,$01,$01,$01,$01,$01,$01 .byte $01,$01,$01,$01,$01,$01,$01,$01 .byte $01,$01,$01,$01,$01,$01,$01,$01 .byte $01,$01,$01,$01,$00,$00,$00,$00
Wie Sie sehen verzerren wir hier zunächst die ersten Spritezeilen ver- schieden oft. Hiernach wird die letzte Spritezeile so oft wiederholt, bis das Ende unseres, durch FLD- und Sideborder behandelten, Bildschirmbereichs erreicht wurde. Kommen wir jedoch wieder zu unserer FLD- LOOP zurück. Nach dem Setzen des Y- Expansions-Registers wird abermals ein Tabellenwert gelesen und diesmal in Re- gister $D011 übertragen. Diese Befehls- folge ist für den FLD-Effekt notwendig, mit dem wir den Beginn der nächsten Cha- rakterzeile vor dem Rasterstrahl her- schieben. Die Tabelle enthält immer wie- der die Werte $19, $1A, $1B, $1C, $1D, $1E, $1F, $18, usw., womit wir in jeder Rasterzeile die Horizontalverschiebung um eine Rasterzeile versetzt vor dem Rasterstrahl herdrücken. Als Nächstes folgt das Üffnen des linken und rechten Bildschirmrandes durch die altbekannte Befehlsfolge zum schnellen Runter- und wieder Hochschalten zwischen 38- und 40-Spalten-Darstellung. Durch die nun folgenden 9 NOP-Befehle verzögern wir solange, bis der Raster- strahl eine Position erreicht hat, zu der die VIC-Schaltlogik schon die gewünschte Spritezeilenadresse an den Datenbus angelegt hat, und somit der VIC mit den gewünschten Spritedaten für die- se Zeile gefüttert wurde. Jetzt schalten wir die Y-Expansion wiederum ganz ab, um das Register für den nächsten Wert vor- zubereiten. Gleichzeitig stellen wir damit sicher, daß der Rest des Sprites, der ggf. über unseren, vom Raster-IRQ behandelten, Bereich hinausragt (z.B. wenn wir das Sprite zu lang gedehnt ha- ben), in normaler Darstellung auf den Bildschirm gelangt. Es wird nun nur noch der Index-Zähler im X-Register für den nächsten Schleifendurchlauf um 1 erhöht und mit der Anzahl der Raster-Effekt- Zeilen in Register $02 verglichen. Ist er noch kleiner als dieser Wert, so kön- nen wir die Schleife wiederholen, um die nächste Rasterzeile zu bearbeiten. Im anderen Fall sind wir am Ende angelangt, wo der nächste Interrupt vorbereitet wird, bevor wir die IRQ-Routine wie ge- wohnt beenden. Damit hätten wir auch schon den Kern unserer Routine besprochen. Experimen- tieren Sie doch ein wenig mit der Ver- zerrung, indem Sie die Tabelle "AD017" ab Adresse $1600 mit Hilfe eines Spei- chermoditors abändern. Sie werden sehen welch lustige Deformierungen dabei ent- stehen können! Beachten Sie dabei, daß Sie die Form des Sprites im Speicher niemals ändern, sonder daß die Änderung hardwaremäßig eintritt! (Anm.d.Red.: Bitte wählen Sie nun den 2. Teil des IRQ-Kurs aus dem Textmenu)