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 Rasterinterrupts 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 320 x200 Pixeln, oder auch
40 x25 Textzeichen beschränkt. Der Rest
des Bildschirms ist von dem meist hellblauen 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 Bildschirmrand 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 anderes als ein Rastertrick. Noch dazu
einer der simpelsten die es gibt. Wie
einfach er zu programmieren ist soll
folgendes kleines Rasterprogramm verdeutlichen. Sie finden es auf dieser MD
unter dem Namen " BORDERDEMO" und müssen
es wie immer mit ",8,1" laden und mittels " SYS4096" starten:
INIT: SEI ;IRQ sperren LDA #$7F ;Timer-IRQ STA $DC0D ; abschalten
LDA $ DC0 D ; 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 Rasterinterrupt nicht gestört wird. Als nächstes
wird Rasterzeile $ F8 als Interruptauslöser festgelegt, was auch einen ganz
bestimmten Grund hat, wie wir weiter
unten sehen werden. Nun sagen wir noch
dem VIC, daß er Rasterinterrupts auslösen soll, und verbiegen den IRQ-Vektor
bei $0314/$0315 auf unsere eigene Routine namens " IRQ" . Die nun folgenden Zeilen dienen lediglich " kosmetischen" Zwecken. Wir setzen hier Rahmenund
Hintergrundfarben auf die Standardwerte
und schalten Sprite 0 ein, das von
unserer Interruptroutine in der Vertikalen pro IRQ um einen Pixel weiterbewegt werden soll. Zudem wird der Spriteblock, der dieses Sprite darstellt, mit
$ FF gefüllt, damit wir ein schönes Quadrat 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 dargestellt, setzen wir es auf "0", so sehen
wir lediglich 24 Textzeilen. Im letzteren 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 Rasterzeilen 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 Rasterzeile $ F8 angesprungen, also genau
dann, wenn der VIC die 24- Zeilen-Prüfung
schon vorgenommen hat. Da die Darstellung auf 25 Zeilen war, hat er noch
keinen Rand gezeichnet. Unsere Interruptroutine 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 Bildschimrand 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 Interruptroutine beweist!
Wichtig an unserer Routine ist nun noch, daß wir vor Erreichen des oberen Bildrandes die Darstellung nocheinmal auf 25 Zeilen zurückschalten, damit der Trick
beim nächsten Rasterdurchlauf nocheinmal
klappt. Hierbei darf natürlich frühestens dann umgeschaltet werden, wenn der
Rasterstrahl an der zweiten Prüf-Posi- tion, Rasterzeile $ FB, schon vorbei ist.
Dies wird durch die kleine Verzögerungsschleife bewirkt, die genau 4 Rasterzeilen 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 interruptauslö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 $3 FFF geschrieben haben. Wie Sie
vielleicht wissen, kann der VIC Speicherbereiche von lediglich 16 KB adressieren, aus denen er sich seine Daten
holt. Im Normalfall ist das der Bereich
von $0000-$3 FFF. Die letzte Speicherzelle 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 überdekkten 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 Bildschirmrand simulieren!
Vielleicht fällt Ihnen nun auch noch ein
interessanter Nebeneffekt auf: nachdem
wir die oberen und unteren Bildschirmgrenzen 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 Spritepositionen normalerweise im unsichtbaren
Bereich des Bildschirms liegen, wo sie
vom Bildschirmrahmen überdeckt werden.
Bleibt noch zu erwähnen, daß wir mit einem ähnlichen Trick auch die seitlichen
Ränder des Bildschirms verschwinden lassen können, nur ist das hier viel
schwieriger, 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ähnten 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 Gesschwindigkeit 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ältnismäßig viel Rechenzeit benötigt, bis er
abgearbeitet ist, tritt ein Raster-IRQ
in der nächsten Zeile 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, hardwaremäßiger Umstand beachtet werden: hat man
normale Textdarstellung auf dem Bildschirm 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 umwandeln. 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 $ DC0 D
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 Rahmenund Hintergrundfarbe ändern. Ich habe hierbei Farbabstufungen benutzt, die schöne Balkeneffekte 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 Initialisierungsroutine sollte Ihnen keine Probleme 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 eigentliche 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 einfachsten 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 LDXund 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!