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 anschauen, und dabei lernen, daß es auch
Sprites gibt, die mehr als 21 Rasterzeilen hoch sind. . .
1) DAS PRINZIP Wie immer kommen wir zu Beginn zum Funktionsprinzip des Rastereffektes dieses
Kursteils: Wie Sie vielleicht wissen, so
ist die Größe eines Sprites prinzipiell
auf 24 x21 Pixel begrenzt. Diese Größe
ist starr und eigentlich nicht veränderbar. 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 $ D01 D die Xund
Y-Expansion eines Sprites einschalten.
In diesem Fall kann sowohl die Xals
auch die Y-Ausdehnung verdoppelt werden, so daß wir ein Sprite mit einer maximalen Größe von 48 x42 Pixeln erhalten, in
dem die normalen 24 x21 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.
Drehund Angelpunkt dieses Effektes
wird das eben schon angesprochene Register 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 expandiert. Der VIC vergleicht nun jede Rasterzeilennummer 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 Schaltlogik 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 relevanten 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 Schreibzugriff 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 Spritedaten für die erste Rasterzeile.
Gleichzeitig wird bei Erreichen der
nächsten Rasterzeile der 1- Bit-Zähler um
eins erniedrigt. Tritt dabei ein Unterlauf auf, so reinitialisiert die Schaltlogik den Zähler wieder mit dem Inhalt
des Latch-Registers und erhöht die Quelladresse für die Speitedaten um 3 Bytes, so daß Sie anschließend dem VIC die Daten der nächsten Spritezeile zukommenlässt. Tritt kein Unterlauf auf, weil
der Zähler auf 1 stand und beim Herunterzählen auf 0 sprang, so bleibt die
Quelladresse der Spritedaten unverändert, so daß der VIC auch in dieser Rasterzeile dieselben Spritedaten liest, die er auch eine Rasterzeile vorher schon darstellte. Ist die Spriteexpansion 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 Rasterzeile eine neue Adresse an den Datenbus
angelegt, womit der VIC das Sprite in
der Vertikalen doppelt so hoch darstellt.
Wie nun eingangs schon erwähnt so werden
sowohl Latch als auch Zähler neu initialisiert, 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 Sprites bewirken ( indem jede Spritezeile 3-,4- oder 5- mal hintereinander dargestellt
wird) ! Wir haben sogar die Möglichkeit
jede Spritezeile beliebig, und voneinander 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 Rasterzeile 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 Sprites, um die Daten der nächsten Spritezeile in den VIC zu holen!
2) PROGRAMMBEISPIELE 1 UND 2
Um einen Eindruck von den Möglichkeiten
zu bekommen, die uns dieser Effekt bietet, sollten Sie sich einmal die Beispielprogramme " 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" gestartet. Die ersten beiden Beispiele stellen
ein Sprite dar, das normalerweise nur
eine diagonale Line von der linken oberen Ecke zur rechten unteren Ecke des
Sprites enthält. Im ersten Beispiel haben wir lediglich einige dieser Zeilen
mehrfach dargestellt. Das zweite Beispiel enthält eine Streckungstabelle, mit der wir jede Rasterzeile in Folge
1-,2-,3-,4-,5-, und 6- Mal darstellen, womit die Linie in etwa die Rundungen einer Sinuskurve bekommt!
Wollen wir uns nun einmal den Programmcode 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 identisch mit unseren anderen IRQ-Beispielen
ist. Wir legen hier den Raster-IRQ auf
Rasterzeile $82 fest und schalten Betriebssystem- 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 altbekannter Trick zum Glätten des IRQs aufgefü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 Routinenteile 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 Sprite- Stretch, FLDund Sideborderroutinen.
Hierzu schreiben wir erst einmal die
Anzahl der Rasterzeilen, in denen diese
drei Effekte aktiv sein sollen als Vergleichszähler in Adresse $02 und löschen
das X-Register, das als Index auf die
FLDund 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 öffneten. Da wir später jedoch wieder Rasterzeile $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 synchronisieren zwischen Programm und Ra- sterstrahl notwendig ist, durcheinanderbrächte und wir dadurch andere Befehle zum Verzögern einfügen müssten, und
zum Anderen um die Flexibilität der Routine dadurch nicht einzuschränken. Auf
diese Weise wird es z. B. für Sie sehr
einfach, die Routine mit einer Topund
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 Routine verlagern. Dies ist übrigens eine
saubere Möglichkeit timingkritische
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 befindet 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 gelesen und angezeigt, bei $01 wird immer
wieder die zuletzt dargestellte Spritezeile auf den Bildschirm gebracht. Folgen mehrere $01- Werte aufeinander, so
wird eine einzige Spritezeile so oft
wiederholt, wie $01- Werte in der Tabelle
stehen. Um die nächste Spritezeile darzustellen 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 verschieden oft. Hiernach wird die letzte
Spritezeile so oft wiederholt, bis das
Ende unseres, durch FLDund 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 Register $ D011 übertragen. Diese Befehlsfolge ist für den FLD-Effekt notwendig, mit dem wir den Beginn der nächsten Charakterzeile vor dem Rasterstrahl herschieben. Die Tabelle enthält immer wieder die Werte $19,$1 A,$1 B,$1 C,$1 D,$1 E,$1 F,$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
Runterund wieder Hochschalten zwischen
38- und 40- Spalten-Darstellung.
Durch die nun folgenden 9 NOP-Befehle
verzögern wir solange, bis der Rasterstrahl 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 diese Zeile gefüttert wurde. Jetzt schalten
wir die Y-Expansion wiederum ganz ab, um
das Register für den nächsten Wert vorzubereiten. 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 haben), 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önnen 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 gewohnt beenden.
Damit hätten wir auch schon den Kern unserer Routine besprochen. Experimentieren Sie doch ein wenig mit der Verzerrung, indem Sie die Tabelle " AD017" ab Adresse $1600 mit Hilfe eines Speichermoditors abändern. Sie werden sehen
welch lustige Deformierungen dabei entstehen 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)