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)