Interrupt-Kurs - Teil 4
"Die Hardware ausgetrickst..."
Herzlich Willkommen zum vierten Teil
unseres IRQ-Kurses. In dieser Ausgabe
möchten wir uns mit sehr zeitkritischen
Rastereffekten beschäftigen und kurz
zeigen, wie man den oberen und unteren
Bildschirmrand mit Hilfe von Rasterin-
terrupts verschwinden lassen kann.
1) DIE TOP- UND BOTTOM-BORDER-ROUTINE
Wie Sie ja sicherlich wissen, ist der
Bildschirm des C64 auf einen sichbaren
Bereich von 320x200 Pixeln, oder auch
40x25 Textzeichen beschränkt. Der Rest
des Bildschirms ist von dem meist hell-
blauen Bildschirmrahmen verdeckt und
kann in der Regel nicht genutzt werden.
Werfen Sie jetzt jedoch einen Blick auf
diese Zeilen, so werden Sie feststellen,
daß das Magic-Disk-Hauptprogramm genau
25 Textzeilen, die maximale vertikale
Anzahl also, darstellt und trotzdem am
oberen Bildschirmrand die Seitennummer,
sowie am unteren Bildschirmrand das MD-
Logo zu sehen sind. Ganz offensichtlich
haben wir es geschafft, den Bildschirm
auf wundersame Weise zu vergrößern!
Tatsächlich schaltet das MD-Haupt-
programm den oberen und unteren Bild-
schirmrand schlichtweg ab, so daß wir
auch hier noch etwas darstellen können
und auf diese Weise mehr Informationen
auf einer Bildschirmseite abzulesen
sind. Dies ist wieder einmal nichts an-
deres als ein Rastertrick. Noch dazu
einer der simpelsten die es gibt. Wie
einfach er zu programmieren ist soll
folgendes kleines Rasterprogramm ver-
deutlichen. Sie finden es auf dieser MD
unter dem Namen "BORDERDEMO" und müssen
es wie immer mit ",8,1" laden und mit-
tels "SYS4096" starten:
INIT: SEI ;IRQ sperren
LDA #$7F ;Timer-IRQ
STA $DC0D ; abschalten
LDA $DC0D ;ICR löschen
LDA #$F8 ;Rasterzeile $F8 als
STA $D012 ; Interrupt-Auslöser
LDA $D011 ; festlegen (incl. dem
AND #$7F ; Löschen des HI-Bits)
STA $D011
LDA #$01 ;Raster als IRQ-
STA $D01A ; Quelle wählen
LDX #<(IRQ) ;IRQ-Vektoren auf
LDY #>(IRQ) ; eigene Routine
STX $0314 ; verbiegen
STY $0315 ;
LDA #$00 ;Letzte VIC-Adr. auf
STA $3FFF ; 0 setzen
LDA #$0E ;Rahmen- u. Hinter-
STA $D020 ; grundfarben auf
LDA #$06 ; hellblau/blau
STA $D021 ; setzen
LDY #$3F ;Sprite-Block 13
LDA #$FF ; ($0340) mit $FF
LOOP2 STA $0340,Y ; füllen
DEY
BPL LOOP2
LDA #$01 ;Sprite 0
STA $D015 ; einschalten
STA $D027 ; Farbe="Weiß"
LDA #$0D ;Spritezeiger auf
STA $07F8 ; Block 13 setzen
LDA #$64 ;X- und Y-Pos.
STA $D000 ; auf 100/100
STA $D001 ; setzen
CLI ;IRQs freigeben
RTS ;ENDE
IRQ LDA $D011 ;Bildschirm
AND #$F7 ; auf 24 Zeilen
STA $D011 ; Umschalten
DEC $D019 ;VIC-ICR löschen
LDX #$C0 ;Verzögerungs-
LOOP1 INX ; schleife
BNE LOOP1
LDA $D011 ;Bildschirm
ORA #$08 ; auf 25 Zeilen
STA $D011 ; zurückschalten
INC $D001 ;Sprite bewegen
END JMP $EA31 ;Weiter zum SYS-IRQ
Wie Sie sehen, besteht die eigentliche
IRQ-Routine, die den Border abschaltet
nur aus einer handvoll Befehlen. Die
Initialisierung der Routine sollte Ihnen
noch aus dem letzten Kursteil bekannt
sein. Wir sperren hier zunächst alle
IRQs und verhindern, daß CIA-A ebenfalls
IRQs auslöst, damit unser Rasterinter-
rupt nicht gestört wird. Als nächstes
wird Rasterzeile $F8 als Interruptaus-
löser festgelegt, was auch einen ganz
bestimmten Grund hat, wie wir weiter
unten sehen werden. Nun sagen wir noch
dem VIC, daß er Rasterinterrupts aus-
lösen soll, und verbiegen den IRQ-Vektor
bei $0314/$0315 auf unsere eigene Rou-
tine namens "IRQ". Die nun folgenden
Zeilen dienen lediglich "kosmetischen"
Zwecken. Wir setzen hier Rahmen- und
Hintergrundfarben auf die Standardwerte
und schalten Sprite 0 ein, das von
unserer Interruptroutine in der Verti-
kalen pro IRQ um einen Pixel weiterbe-
wegt werden soll. Zudem wird der Sprite-
block, der dieses Sprite darstellt, mit
$FF gefüllt, damit wir ein schönes Qua-
drat auf dem Bildschirm sehen und keinen
Datenmüll. Nach Freigabe der IRQs
mittels "CLI" wird dann wieder aus dem
Programm zurückgekehrt. Von nun an
arbeitet unsere kleine, aber feine
Raster-IRQ-Routine. Damit Sie sie
verstehen, müssen wir nun ein wenig in
die Funktionsweise des VIC einsteigen:
Normalerweise zeigt uns der Videochip
des C64, wie oben schon erwähnt, ein 25
Text-, bzw. 200 Grafikzeilen hohes Bild.
Nun können wir die Bildhöhe mit Hilfe
von Bit 3 in Register 17 des VICs auf 24
Textzeilen reduzieren. Setzen wir es auf
"1", so werden 25 Textzeilen darge-
stellt, setzen wir es auf "0", so sehen
wir lediglich 24 Textzeilen. Im letzte-
ren Fall werden dann jeweils vier
Grafikzeilen des oberen und unteren
Bildschirmrandes vom Bildschirmrahmen
überdeckt. Diese Einschränkung ist vor
allem bei der Programmierung eines
vertikalen Soft-Scrollers von Bedeutung.
Effektiv zeichnet der VIC nun also den
oberen Bildschirmrand vier Rasterzeilen
länger und den unteren vier Rasterzei-
len früher. Um nun den Rahmen zu
zeichnen kennt die Schaltlogik des VIC
zwei Rasterzeilen, die er besonders
behandeln muß. Erreicht er nämlich
Rasterzeile $F7, ab der der Bildschirm
endet, wenn die 24 Textzeilen-Darstel-
lung aktiv ist, so prüft er, ob Bit 3
von Register 17 gelöscht ist. Wenn ja,
so beginnt er den Rand zu zeichnen, wenn
nein, so fährt er normal fort. Erreicht
er dann Rasterzeile $FB, die das Ende
eines 25-zeiligen Bildschirms darstellt,
wird nochmals geprüft, ob das obige Bit
auf 0 ist. Wenn ja, so weiß der VIC, daß
er mit dem Zeichnen des Rahmens schon
begonnen hat. Wenn nein, so beginnt er
erst jetzt damit. Mit unserem Interrupt
tricksen wir den armen Siliziumchip nun
aus. Unsere Routine wird immer in Ras-
terzeile $F8 angesprungen, also genau
dann, wenn der VIC die 24-Zeilen-Prüfung
schon vorgenommen hat. Da die Darstel-
lung auf 25 Zeilen war, hat er noch
keinen Rand gezeichnet. Unsere Inter-
ruptroutine schaltet nun aber auf 24
Zeilen um und gaukelt dem VIC auf
diese Weise vor, er hätte schon mit dem
Zeichnen des Randes begonnen, weshalb er
nicht nocheinmal beginnen muß, und somit
ohne zu zeichnen weitermacht. Dadurch
erscheinen unterer und oberer Bild-
schimrand in der Hintergrundfarbe, und
es ist kein Rahmen mehr sichtbar. In
diesen Bereichen kann man nun zwar
keinen Text oder Grafik darstellen,
jedoch sind Sprites, die sich hier
befinden durchaus sichtbar! Sie werden
normalerweise nämlich einfach vom Rahmen
überdeckt, sind aber dennoch vorhanden.
Da der Rahmen nun aber weg ist, sieht
man auch die Sprites, wie das sich
bewegende Sprite 0 unserer Interrupt-
routine beweist!
Wichtig an unserer Routine ist nun noch,
daß wir vor Erreichen des oberen Bild-
randes die Darstellung nocheinmal auf 25
Zeilen zurückschalten, damit der Trick
beim nächsten Rasterdurchlauf nocheinmal
klappt. Hierbei darf natürlich frühes-
tens dann umgeschaltet werden, wenn der
Rasterstrahl an der zweiten Prüf-Posi-
tion, Rasterzeile $FB, schon vorbei ist.
Dies wird durch die kleine Verzögerungs-
schleife bewirkt, die genau 4 Rasterzei-
len wartet, bevor mit dem anschließenden
ORA-Befehl Bit 3 in Register 17 des VIC
wieder gesetzt wird. Am Ende unseres
Interrupts bewegen wir das Sprite noch
um eine Y-Position weiter und verzweigen
zum Betriebssystem-IRQ, damit die
Systemaufgaben trotz abgeschalteter CIA
dennoch ausgeführt werden. Die inter-
ruptauslösende Rasterzeile muß nicht
nochmal neu eingestellt werden, da wir
diesmal nur eine Rasterzeile haben, die
jedesmal wenn sie erreicht wird einen
Interrupt auslöst.
Wollen wir nun noch klären, warum wir
bei der Initialisierung eine 0 in
Adresse $3FFF geschrieben haben. Wie Sie
vielleicht wissen, kann der VIC Spei-
cherbereiche von lediglich 16KB adres-
sieren, aus denen er sich seine Daten
holt. Im Normalfall ist das der Bereich
von $0000-$3FFF. Die letzte Speicher-
zelle seines Adressbereichs hat nun eine
besondere Funktion. Der Bit-Wert, der in
ihr steht, wird nämlich in allen Spalten
der Zeilen des nun nicht mehr überdek-
kten Bildschirmrandes dargestellt - und
zwar immer in schwarzer Farbe. Durch das
Setzen dieser Zelle auf 0 ist hier also
gar nichts sichtbar. Schreiben wir
jedoch bei aktivierter Borderroutine
mittels "POKE16383,X" andere Werte
hinein, so werden je nach Wert mehr oder
weniger dicke, vertikale Linien in
diesem Bereich sichtbar. Durch Setzen
aller Bits mit Hilfe des Wertes 255 (mit
Rahmenfarbe=schwarz), können wir sogar
einen scheinbar vorhandenen Bildschirm-
rand simulieren!
Vielleicht fällt Ihnen nun auch noch ein
interessanter Nebeneffekt auf: nachdem
wir die oberen und unteren Bildschirm-
grenzen abgeschaltet haben, gibt es
Spritepositionen, an denen das Sprite
zweimal zu sehen ist. Nämlich sowohl im
oberen, als auch im unteren Teil des
Bildschirms. Das liegt daran, daß das
PAL-Signal, welches der VIC erzeugt
313 Rasterzeilen kennt, wir aber die
Y-Position eines Sprites nur mit 256
verschiedenen Werten angeben können.
Dadurch stellt der VIC das Sprite an den
Y-Positionen zwischen 0 und 30 sowohl am
unteren, als auch am oberen Rand dar.
Bei eingeschalteten Rändern fiel dieser
Nebeneffekt nie auf, da diese Sprite-
positionen normalerweise im unsichtbaren
Bereich des Bildschirms liegen, wo sie
vom Bildschirmrahmen überdeckt werden.
Bleibt noch zu erwähnen, daß wir mit ei-
nem ähnlichen Trick auch die seitlichen
Ränder des Bildschirms verschwinden las-
sen können, nur ist das hier viel
schwie- riger, da es auf ein sehr
genaues Timing ankommt. Wie man damit
umgeht müssen wir jetzt erst noch
lernen, jedoch werde ich in den nächsten
Kursteilen auf dieses Problem nocheinmal
zu sprechen kommen.
2) EINZEILEN-RASTER-EFFEKTE
Kommen wir nun zu dem oben schon erwähn-
ten Timing-Problem. Vielleicht haben Sie
nach Studieren des letzten Kursteils
einmal versucht einzeilige Farbraster-
effekte zu programmieren. Das heißt also
daß Sie gerade eine Zeile lang, die
Bildschirmfarben wechseln, und sie dann
wieder auf die Normalfarbe schalten.
Hierzu wären dann zwei Raster-Interrupts
notwendig, die genau aufeinander zu
folgen haben (z.B. in Zeile $50 und
$51). Wenn Sie verschucht haben ein
solches Rasterprogramm zu schreiben, so
werden Sie bestimmt eine Menge Probleme
dabei gehabt haben, da die Farben nie
genau eine Rasterzeile lang den
gewünschten Wert enthielten, sondern
mindestens eineinhalb Zeilen lang
sichtbar waren. Dieses Problem hat
mehrere Ursachen, die hauptsächlich
durch die extrem schnelle Gesschwin-
digkeit des Rasterstahls entstehen.
Selbiger bewegt sich nämlich in genau 63
Taktzyklen einmal von links nach rechts.
Da innerhalb von 63 Taktzyklen nicht
allzu viele Instruktionen vom Prozessor
ausgeführt werden können, kann jeder
Befehl zuviel eine zeitliche Verzögerung
verursachen, die eine Farbänderung um
mehrere Pixel nach rechts verschiebt, so
daß die Farbe nicht am Anfang der Zeile,
sondern erst in ihrer Mitte sichtbar
wird. Da ein IRQ nun aber verhältnis-
mäßig viel Rechenzeit benötigt, bis er
abgearbeitet ist, tritt ein Raster-IRQ
in der nächsten Zei- le meist zu früh
auf, nämlich noch bevor der erste IRQ
beendet wurde! Dadurch gerät das
Programm natürlich vollkommen aus dem
Takt und kann ggf. sogar abstürzen!
Noch dazu muß ein weiterer, hardware-
mäßiger Umstand beachtet werden: hat man
normale Textdarstellung auf dem Bild-
schirm eingeschaltet, so muß der VIC
nämlich jedes mal zu Beginn einer
Charakterzeile die 40 Zeichen aus dem
Video-RAM lesen, die er in den folgenden
acht Rasterzeilen darzustellen hat, und
sie entsprechend in ein Videosignal um-
wandeln. Um diesen Vorgang durchzuführen
hält er den Prozessor für eine Zeit von
genau 42 Taktzyklen an, damit er einen
ungestörten Speicherzugriff machen kann.
Eine Charakterzeile ist übrigens eine
der 25 Textzeilen. Da der Bildschirm in
der Regel bei Rasterzeile $32 beginnt,
und jede achte Rasterzeile ein solcher
Zugriff durchgeführt werden muß, sind
all diese Zeilen besonders schwierig
über einen Raster-IRQ programmierbar, da
erst nach dem VIC-Zugriff ein Raster-IRQ
bearbeitet werden kann, der jedoch durch
den Zugriff schon viel zu spät eintritt,
da die Zeile in der Zwischenzeit ja
schon zu zwei Dritteln aufgebaut wurde.
Hier muß man sich eines speziellen
Tricks behelfen. Um selbigen besser
erläutern zu können, wollen wir uns das
folgende Beispielprogramm einmal etwas
näher anschauen:
;**************************************
INIT SEI ;IRQs sperren
LDA #$7F ;CIA-A-IRQs
STA $DC0D ; unterbinden
LDA $DC0D
LDA #$82 ;Rasterzeile $82 als
STA $D012 ; Interruptauslöser
LDA $D011 ; festlegen (incl.
AND #$7F ; Löschen des
STA $D011 ; HI-Bits
LDA #$01 ;Rasterstrahl ist
STA $D01A ; IRQ-Auslöser
LDX #<(IRQ) ;IRQ-Vektor auf eigene
LDY #>(IRQ) ; Routine verbiegen
STX $0314
STY $0315
CLI ;IRQs freigeben
VERZ RTS ;ENDE
;**************************************
IRQ DEC $D019 ;VIC-IRQ freigeben
JSR VERZ ;Verzögern...
JSR VERZ
NOP
LDY #$00 ;Farb-Index init.
LOOP1 LDX #$08 ;Char-Index init.
LOOP2 LDA $1100,Y ;Farbe holen
STA $D020 ; und im Rahmen-und
STA $D021 ; Hintergrund setzen
INY ;Farb-Index+1
DEX ;Char-Index-1
BEQ LOOP1 ;Wenn Char=0 verzweig.
LDA VERZ ;Sonst verzögern...
JSR VERZ
JSR VERZ
JSR VERZ
CPY #$48 ;Farb-Index am Ende?
BCC LOOP2 ;Nein also weiter
LDA #$0E ;Sonst Rahmen/Hinter-
STA $D020 ; grund auf
LDA #$06 ; Standardfarben
STA $D021 ; zurücksetzen
JMP $EA31 ;IRQ mit SYS-IRQ beend
Besten laden Sie das Programm einmal und
starten es mit "SYS4096". Sie sehen nun
einen schönen Rasterfarbeneffekt auf dem
Bildschirm, wo wir ab Rasterzeile $83 in
jeder Zeile die Rahmen- und Hintergrund-
farbe ändern. Ich habe hierbei Farbab-
stufungen benutzt, die schöne Balken-
effekte erzeugen. Die entsprechende
Farbtabelle liegt ab Adresse $1100 im
Speicher und kann natürlich von Ihnen
auch verändert werden. Kommen wir nun
zur Programmbeschreibung. Die Initiali-
sierungsroutine sollte Ihnen keine Pro-
bleme bereiten. Wir schalten wie immer
die CIA ab, sagen dem VIC, daß er einen
Raster-IRQ generieren soll, legen eine
Rasterzeile (hier Zeile $82) als IRQ-
Auslöser fest, und verbiegen die IRQ-
Vektoren auf unser eigenes Programm.
Etwas seltsam mag Ihnen nun die eigent-
liche IRQ-Routine vorkommen. Nachdem wir
mit dem DEC-Befehl dem VIC bestätigt
haben, daß der IRQ bei uns angekommen
ist, folgen nun drei, scheinbar
sinnlose, Befehle. Wir springen nämlich
das Unterprogramm "VERZ" an, das
lediglich aus einem RTS-Befehl besteht,
und somit direkt zu unserem Programm
zurückverzweigt. Zusätzlich dazu folgt
noch ein NOP-Befehl der ebensowenig tut,
wie die beiden JSRs zuvor. Der Sinn
dieser Instruktionen liegt lediglich in
einer Zeitverzögerung, mit der wir
abwarten, bis der Rasterstrahl am Ende
der Zeile $82 angelangt ist. Wir hätten
hier auch jeden anderen Befehl verwenden
können, jedoch ist es mit JSRs am ein-
fachsten zu verzögern, da ein solcher
Befehl, ebenso wie der folgende RTS-
Befehl, jeweils 6 Taktzyklen verbraucht.
Durch einen JSR-Befehl vergehen also
genau 12 Taktzyklen, bis der nächste
Befehl abgearbeitet wird. Da ein NOP-
Befehl, obwohl er nichts macht, zwei
Taktzyklen zur Bearbeitung benötigt, und
wir zwei JSRs verwenden, verzögern wir
also um insgesamt 26 Taktzyklen. Genau
diese Verzögerung ist dem DEC-Befehl
zuvor und den folgenden LDX- und LDY-
Befehlen notwendig, um soviel zu
verzögern, daß sich der Rasterstrahl bis
ans Ende der Rasterzeile bewegt hat.
Hinzu kommt daß wir die 42 Taktzyklen
hinzurechnen müssen, die der VIC den
Prozessor sowieso schon gestoppt hat, da
Rasterzeile $82 eine der schon oben
angesprochenen Charakterzeilen darstellt
($82-$32=$50/8=10 - ohne Rest!).
Laden Sie nun bitte Teil 2 des Kurses!