Die Textmodi des VIC-II
Wie euch bestimmt bekannt ist, bietet der C64 grundsätzlich zwei unterschiedliche Grafikmodi: Text und Bitmap. Diese beiden bieten dann wiederum verschiedene Untermodi und es gibt noch eine ganze Reihe „neu erfundener“ BITMAP-Modes z. B. FLI oder MCI.
Da wir gleich die Farben verwenden, hier die Liste mit den 16 Farben des C64:
Textmodus
Dieser Modus wird wohl am häufigsten verwendet. Fast alle Spiele verwenden ihn und auch bei Demos läßt sich eine Menge damit anstellen. Da der Standardzeichensatz auf Dauer zu langweilig ist, verwendet man meistens einen eigenen Zeichensatz, dieses Thema wird in VIC-II: Eigener Zeichensatz behandelt.
Der Textmodus kann wiederum auf drei verschiedene Arten verwendet werden, die wir uns nun einmal kurz ansehen. Da wir gleich einige VIC-II-Register benötigen, werft evtl. noch mal einen Blick auf VIC-II: Die Register.
Standardzeichen
Diesen Modus kennt ihr bereits zur Genüge, er ist direkt beim Start des C64 aktiv. Ihr könnt einfarbige Zeichen darstellen (natürlich kann jedes Zeichen seine eigene Farbe bekommen, aber halt nur eine zur Zeit). Zwei Musterzeichensätze befinden sich bereits im ROM, einer mit ausschließlich Großbuchstaben, dafür mehr grafischen Symbolen, ein zweiter mit Groß- und Kleinbuchstaben, dafür eben weniger Symbole.
Ein Zeichen ist 8 Pixel breit und 8 Zeilen hoch. Ein Bit bestimmt somit, ob der jeweilige Pixel des Zeichens sichtbar ist oder nicht.
Ist ein Bit gesetzt, dann erscheint an der Stelle ein Pixel in der Zeichenfarbe aus dem Color-RAM $d800, bei einer Null für das Bit ist das Zeichen an der Stelle durchsichtig und die Hintergrundfarbe aus $d021 ist zu sehen.
SCREENRAMADR = $0400 ;*** Startadresse *=$0801 ;** BASIC-Zeile: 2018 SYS 2062 !word main-2, 2018 !byte $9e !text " 2062" !byte $00,$00,$00 main lda #$00 ;schwarz für sta $d021 ;Hintergrund lda #$0e ;Hellblau sta $286 ;als Zeichen- sta $d020 ;und Rahmenfarbe lda #$93 ;Bildschirm über jsr $ffd2 ;Kernalfunktion löschen jsr printtext ;Text ausgeben rts ;zurück zum BASIC printtext ldx #$00 ;Nächstes Zeichen in X (0 = erstes) loop lda outtext,x ;Zeichen in den Akku beq exit ;0 = Textende -> exit sta SCREENRAMADR,x ;Zeichen ausgeben inx ;Position für nächstes Zeichen erhöhen jmp loop ;nochmal exit rts ;zurück zum Aufrufer outtext !scr "aaa ... dies ist ein 'normaler' text!" !byte $00 ;Textende
Sorry, für dieses Beispiel!
Hier löschen wir den Bildschirm und geben dann, wie schon so oft, einfach einen Text aus. Damit wir gleich beim nächsten Schritt das Ergebnis besser verfolgen können, wird außerdem noch die Hintergrundfarbe auf schwarz gesetzt, sowie Rahmen und Zeichenfarbe auf den Standard hellblau. Da dieser Modus ein altbekannter ist, sollte dieses Beispiel reichen, damit haben wir ja schon bis zum Abwicken rumgespielt.
Multicolor-Modus (mehrfarbige Zeichen)
Im Multicolor-Modus könnt ihr für ein Zeichen bis zu drei Farben verwenden. Dafür nimmt die Auflösung der Zeichen in der Breite (also X-Richtung), wie bei den MC-Sprites, ab. Ein Zeichen belegt zwar immer noch 8 Pixel auf dem Bildschirm, wir können in der Breite allerdings nur vier setzen, da immer zwei Bits für die Farbauswahl benötigt werden. Es bleibt aber weiterhin bei 8 Zeilen in der Höhe.
Schaltet ihr auf den Multicolor-Textmodus um, dann sieht das A im Speicher natürlich noch wie eben aus. Aber da jetzt immer zwei Bits für eine Farbinformation genommen werden, erscheint es auf dem Bildschirm unfreiwillig bunt und recht grob. Bei den Farben ist zu beachten, dass, ähnlich wie bei den MC-Sprites, je Zeichen eine Farbe freiwählbar ist, die beiden anderen gelten für alle Zeichen zusammen. Die vierte Farbe %00 ist natürlich wieder transparent, damit man auch den Hintergrund durchscheinen sieht. In der folgenden Liste könnt ihr nachlesen, welche Bit-Kombination, für welches Farbregister steht.
%00 = $d021 (Hintergrundfarbe) %01 = $d022 %10 = $d023 %11 = Farbe laut Farb-RAM (ab $d800)
Im Multicolor-Modus werden also die VIC-II-Register 34 und 35 $d022 & $d023, die eigentlich für den gleich folgenden Extended Background Color Mode vorgesehen sind, verwendet, um die zusätzliche Zeichenfarbe zu bestimmen. Ändern wir unser Testprogramm etwas ab und schauen uns das Ergebnis einmal an. Ersetzt den obigen main-Block durch diesen:
main lda #$00 ;schwarz für sta $d021 ;Hintergrund lda #$0e ;Hellblau sta $286 ;als Zeichen- sta $d020 ;und Rahmenfarbe lda #$05 ;grün sta $d022 ;für %01 im Zeichen lda #$02 ;rot sta $d023 ;für %10 im Zeichen lda $d016 ora #%00010000 ;Multicolor-Textmodus aktivieren sta $d016 lda #$93 ;Bildschirm über jsr $ffd2 ;Kernalfunktion löschen jsr printtext ;Text ausgeben rts ;zurück zum BASIC
Wir setzen den Hintergrund wieder auf schwarz, außerdem füllen wir $d022 & $d023 mit grün und rot, wie oben in der Grafik. Dann wird über Bit-4 im VIC-Register 22 $d016 der Multicolor-Textmodus aktiviert, der Bildschirm gelöscht und nochmal der selbe Text ausgegeben.
Die Texte sind noch annähernd zu erkennen, aber zoomen wir trotzdem mal etwas ran…
Hier erkennt ihr, dass das A wirklich, wie oben in der Übersichtsgrafik schon zu erkennen war, dargestellt wird, nur unsere Hintergrundfarbe ist hier schwarz.
Den Grund für meine Farbwahl (Rahmen hellblau / Hintergrund schwarz) könnt ihr auch in der Vergrößerung finden. Schaut euch mal die blaue Farbe des Zeichens an…diese sollte doch eigentlich der Rahmenfarbe entsprechen, oder? Wir haben die Zeichenfarbe schließlich auf hellblau gesetzt (vergleicht es ggf. noch mal mit der „normalen“ Ausgabe). Hier kommt eine zusätzliche Steuerungsmöglichkeit für den Multicolormodus zum Tragen. Man möchte ja nicht unbedingt alle Zeichen als Multicolor ausgeben, zum Beispiel reine Texte oder Punkteanzeigen sehen einfarbig (aber hochauflösend) einfach besser aus. Genau dies wird durch die jeweilige Zeichenfarbe im Color-RAM ab $d800 möglich. Mit der Zeichenfarbe wird gesteuert, ob ein Zeichen einfarbig oder mehrfarbig dargestellt werden soll. Dabei sorgen die Farben $00 – $07 für eine einfarbige und $08 – $0f für eine mehrfarbige Darstellung. Da Hellblau den Wert $0e hat, erscheinen alle unsere Zeichen als Multicolor.
Fügt vor das rts, am Ende von main, einmal die beiden folgenden Zeilen ein
lda #$01 ;weiß als Zeichenfarbe sta $d801 ;für das zweite „A“
und startet das Programm.
In der Vergrößerung könnt ihr besser erkennen, dass das A in weiß $01 nun wieder hochauflösend ist.
Also wird mit dem höchsten Bit (denkt daran, dass es nur 4 Bits sind!) des Farbwertes im Color-RAM gesteuert, ob das Zeichen ein- oder mehrfarbig ist. Somit haben wir auch die Erklärung, warum das Blau in den Multicolor As nicht hell- sondern dunkelblau ist. $0e = %1110 = 15 aktiviert den Mehrfarbenmodus, damit bleiben nur die unteren drei Bits für die Farbe %110 = $06 = 6 und dies entspricht der Farbe dunkelblau. Im Umkehrschluß bedeutet dies, dass wir im Multicolormodus, als Zeichenfarbe, nur die Farben 0 bis 7 verwenden können.
In Kombination mit VIC-II: Eigener Zeichensatz, könnt ihn nun also eure Spiele viel hübscher gestalten. Beim mehrfarbigen Zeichensatz müsst ihr nur darauf achten, dass auch die richtige Farbe im Color-RAM landet und schon wird es viel bunter.
Wie ein schicker Multicolor-Zeichensatz aussehen kann, habe ich zwar schon mal gezeigt, aber weil es so schön ist und als Motivation für euch, hier nochmal das Wizball-Beispiel.
Kleines Intermezzo
Um einen Multi-Color-Zeichensatz mal in Aktion zu sehen, habe ich das Uridium-Beispiel (s. VIC-II: Eigener Zeichensatz, der Beitrag wird jetzt vorausgesetzt!) in ein kleines Programm umgesetzt.
Das Programm verwendet den Zeichensatz und die Leveldaten des ersten Levels von Uridium. Der Level wird angezeigt und die ersten 256 Spalten scrollen Ping-Pong-mäßig hin und her. Es gibt allerdings kein Softscrolling, sondern es wird immer um ein ganzes Zeichen gescrollt. Um den Unterschied zwischen Multi-Color / normalem Modus, sowie einem eigenen und dem ROM Zeichensatz zu zeigen könnt ihr über die Funktionstasten diese Werte jeder Zeit umschalten.
F1 – Multi-Color
F3 – Standard-Textmodus
F5 – Uridium-Zeichensatz
F7 – ROM-Zeichensatz
RETURN – Pause / weiter
;*** VIC-II Speicher-Konstanten VICBANKNO = 0 ;Nr. (0 - 3) der 16KB Bank | Standard: 0 VICSCREENBLOCKNO = 1 ;Nr. (0 -15) des 1KB-Blocks für den Textbildschirm | Standard: 1 VICCHARSETBLOCKNO = charset/2048 ;Nr. (0 - 7) des 2KB-Blocks für den Zeichensatz | Standard: 2 VICBITMAPBBLOCKNO = 0 ;Nr. (0 - 1) des 8KB-Blocks für die BITMAP-Grafik | Standard: 0 VICBASEADR = VICBANKNO*16384 ;Startadresse der gewählten VIC-Bank | Standard: $0000 VICCHARSETADR = VICCHARSETBLOCKNO*2048+VICBASEADR ;Adresse des Zeichensatzes | Standard: $1000 ($d000) VICSCREENRAM = VICSCREENBLOCKNO*1024+VICBASEADR ;Adresse des Bildschirmspeichers COLORRAM = $d800 ;hier liegt das Color-RAM ZP_HELPADR1 = $fb ;Hilfsadressen auf der Zero-Page ZP_HELPADR2 = $fd ;*** Startadresse *=$0801 ;** BASIC-Zeile: 2018 SYS 2062 !word main-2, 2018 !byte $9e !text " 2062" !byte $00,$00,$00 !zone main main jsr setUridiumCharset lda $d016 ora #%00010000 ;Multicolor-Textmodus aktivieren sta $d016 lda #$00 ;schwarz für die sta $d020 ;Rahmenfarbe lda #$01 ;weiß für die sta $d021 ;Hintergrundfarbe lda #$0f ;hellgrau sta $d022 ;für %01 im Zeichen lda #11 ;dunkelgrau sta $d023 ;für %10 im Zeichen jsr clrScreen ;BS löschen und Color-RAM init lda #$00 ;zu Beginn keine Pause! sta $03 .loop jsr waitForVsync ;Ausgabe synchronisieren lda $cb ;aktuell gedrückte Taste cmp #$40 ;prüfen, ob eine Taste betätigt wurde beq .skip ;falls nicht weiter bei -> @skip cmp $02 beq .skip cmp #$04 ;F1? bne .skip1 jsr setUridiumCharset ;Uridium Zeichensatz jmp .skip .skip1 cmp #$05 ;F3? bne .skip2 jsr setRomCharset ;ROM-Zeichensatz jmp .skip .skip2 cmp #$06 ;F5? bne .skip3 lda $d016 ora #%00010000 ;Multicolor-Textmodus aktivieren sta $d016 jmp .skip .skip3 cmp #$03 ;F7? bne .skip4 lda $d016 and #%11101111 ;Multicolor-Textmodus abschalten sta $d016 jmp .skip .skip4 cmp #$01 ;RETURN? bne .skip lda $03 eor #%11111111 ;Pause / Weiter sta $03 .skip lda $cb ;letzte Taste merken sta $02 lda $03 ;Pause? cmp #$ff beq .loop ;falls ja direkt wieder zum -> @loop jsr drawMap ;kompletten BS ausgeben incOrDec inc drawMapOffset+1 ;BS nach links / rechts bewegen bne .loop ;wiederholen, wenn NICHT 0 lda incOrDec ;sonst Befehl bei @incordec umschalten eor #%00100000 ;aus INC wird DEC und umgekehrt sta incOrDec ;Richtungsänderung speichern jmp incOrDec ;bei @incordec gleich wieder für Bewegung sorgen waitForVsync lda #250 ;Zeile gewünschte Zeile in den Akku cmp $d012 ;Akku gegen aktuelle Rasterzeile prüfen beq *-3 ;warten bis unterschiedlich cmp $d012 bne *-3 ;warten bis Zeile erreicht wurde rts ;zurück zum Aufrufer !zone clrScreen clrScreen ldx #$00 ;256 Durchläufe .loop lda #" " ;Leerzeichen sta VICSCREENRAM,X ;1. Page des BS-Speichers sta VICSCREENRAM+256,X ;2. Page des BS-Speichers sta VICSCREENRAM+512,X ;3. Page des BS-Speichers sta VICSCREENRAM+768,X ;4. Page des BS-Speichers lda #08 ;schwarz für Multicolor!!! sta COLORRAM,X ;1. Page des Color-RAMs sta COLORRAM+256,X ;2. Page des Color-RAMs sta COLORRAM+512,X ;3. Page des Color-RAMs sta COLORRAM+768,X ;4. Page des Color-RAMs dex ;Schleifenzähler verringern bne .loop ;wiederholen, bis 0 erreicht wird rts ;zurück zum Aufrufer setRomCharset ;*** Start des Zeichensatzes festlegen lda #VICSCREENBLOCKNO*16+4 sta $d018 ;Adresse für Bildschirm und Zeichensatz festlegen rts setUridiumCharset ;*** Start des Zeichensatzes festlegen lda #VICSCREENBLOCKNO*16+VICCHARSETBLOCKNO*2 sta $d018 ;Adresse für Bildschirm und Zeichensatz festlegen rts !zone drawMap drawMap lda #<level ;Startadresse des Level auf die Zero-Page sta ZP_HELPADR1 lda #>level sta ZP_HELPADR1+1 lda ZP_HELPADR1 clc drawMapOffset ;hier simulieren wir von Außen des Scrolling adc #0 ;dieser Wert hinter ADC wird verändert! sta ZP_HELPADR1 ;Offset auf die Hilfsadresse addieren lda ZP_HELPADR1+1 adc #0 ;evtl. Überlauf aufs MSB sta ZP_HELPADR1+1 lda #<VICSCREENRAM ;Startadresse des BS-Speichers auf die Zero-Page sta ZP_HELPADR2 lda #>VICSCREENRAM sta ZP_HELPADR2+1 ldx #17 ;Level-Höhe = 17 Zeilen .loop ldy #39 ;BS-Breite = 40 Zeichen .loop1 lda (ZP_HELPADR1),Y ;Zeichen aus den Leveldaten holen sta (ZP_HELPADR2),Y ;und auf dem BS ausgeben dey ;Schleife für Zeile verringern bpl .loop1 ;solange positiv, nochmal inc ZP_HELPADR1+1 ;Der Level ist 512 BYTEs breit, daher inc ZP_HELPADR1+1 ;MSB der ZP-Leveladresse um zwei erhöhen lda ZP_HELPADR2 ;ZP-Adresse für den Bildschirm um 40 Zeichen clc ;erhöhen adc #40 sta ZP_HELPADR2 lda ZP_HELPADR2+1 adc #0 ;ggf. Übertrag beachten sta ZP_HELPADR2+1 dex ;Schleife für Level-Höhe verringern bne .loop ;solange größer 0, nochmal rts ;zurück zum Aufrufer !align 255,0 level !bin "map.bin" !align 2047,0 charset !bin "chars.bin"
Das Listing ist eigentlich gut kommentiert, daher möchte ich mich auf einige Anmerkungen beschränken: In main wird alles vorbereitet, als erstes werden die Farben und Adressen gesetzt. Der Zeichensatz muss dieses Mal nicht kopiert werden, da wir ihn durch entsprechende Platzierung direkt verwenden. Dazu wird er, wie auch die Leveldaten, direkt ins Programm gebunden. Wichtig ist, dass der Zeichensatz hinter den Leveldaten folgt, damit vermeiden wir automatisch den problematischen Bereich bei $1000! In main befindet sich auch die Hauptschleife. Diese bewegt das Raumschiff und fragt die Tasten ab, um eine konstante Geschwindigkeit zu erhalten, wird alles über waitForVsync getimed. Für das Scrolling wird einfach regelmäßig drawMap aufgerufen, diese zeichnet jedes Mal den gesamten Bildschirm, abhängig von einem vorgegebenen Offset. Dieser Offset wird von main direkt beim adc aktualisiert und bewirkt das Scrolling.
Ich denke es ist nun am Besten, wenn ihr euch das Programm vornehmt und etwas damit rumspielt. Ihr könnt euch das komplette Projekt inkl. der benötigten .bin-Dateien herunterladen.
Uridium Beispiel
Extended Background Color Mode (Erweiterter Hintergrundfarbmodus)
Abschließend werfen wir noch einen Blick auf den Extended Background Color Mode. Die deutsche Bezeichnung „Erweiterter Hintergrundfarbmodus“ verrät eigentlich schon, was es damit auf sich hat. Wie ihr wisst, haben alle Zeichen ein und dieselbe Hintergrundfarbe (die aus $d021), die immer dann sichtbar wird, wenn ein Pixel des Zeichens den Wert 0 hat. Mit dem Extended Background Color Mode habt ihr nun die Möglichkeit bis zu vier unterschiedliche Hintergrundfarben für die Zeichen zu verwenden! Aktiviert wird der Modus über Bit-6 im VIC-II Register 17 $d011. Die vier Hintergrundfarben legen wir in den Registern 33 bis 36 $d021 – $d024 ab.
Schalten wir den Extended Background Color Modus einfach mal an und schauen was passiert. Ersetzt dazu den bisherigen main-Block durch den folgenden:
main lda #$00 ;schwarz für sta $D021 ;Hintergrund lda #$0E ;Hellblau sta $286 ;als Zeichen- sta $D020 ;und Rahmenfarbe lda #$05 ;grün sta $D022 ;für %01 im Zeichen lda #$02 ;rot sta $D023 ;für %10 im Zeichen lda $D011 ora #%01000000 ;Multicolor-Textmodus aktivieren sta $D011 lda #$93 ;Bildschirm über jsr $FFD2 ;Kernalfunktion löschen jsr printtext ;Text ausgeben rts ;zurück zum BASIC
Wir setzen hier wieder unsere Farben, aktivieren dann den gewünschten Modus durchs Setzen von Bit-6 in $d011 und werfen einen Blick auf die Ausgabe.
Wie ihr seht, zeigt sich kein Unterschied zum normalen Textmodus aus dem ersten Beispiel. Wirklich kein Unterschied? Schaut noch mal genau hin… Richtig, der Cursor ist nun rot. Unser Text sieht nur deshalb, wie bisher aus, da wir nur Zeichen ausgeben, die einen Screencode unter 64 haben. Im Extended Background Color Mode stehen uns nämlich nur die ersten 64 Zeichen zur Verfügung, da die oberen zwei Bits des Zeichencodes, für die Auswahl der Hintergrundfarbe verwendet werden. Um dies nun zu überprüfen, fügt die nächsten neun Zeilen bei main, direkt vor dem rts, ein.
lda #$0f ;Hellgrau als sta $d024 ;dritte Hintergrundfarbe lda #"A" ;"A" in den Akku ora #%01000000 ;Bit-6 für Hintergrundfarbe-1 setzen sta $0401 ;Zeichenausgaben eor #%11000000 ;Bit-7 für Hintergrundfarbe-2 setzen sta $0402 ora #%01000000 ;Bit-7 und 6 für Hintergrundfarbe-3 setzen sta $0403
Startet ihr das Programm jetzt, dann seht ihr vier hellblaue As mit unterschiedlichen Hintergrundfarben.
Auch dies noch mal zur Verdeutlichung etwas größer…
Natürlich könnt ihr auch die Farbe der einzelnen Zeichen weiterhin verändern…
Das wars eigentlich schon zum Extended Background Color Mode, ihr könnt hiermit z. B. Textstellen hervorheben, um den Anwender darauf aufmerksam zu machen. Durch die Limitierung auf 64 Zeichen ist man allerdings nicht so flexibel, was die Darstellungsmöglichkeiten angeht.
Wer jetzt auf die Idee kommt, den Multicolor und Extended Background Color Mode gleichzeitig zu nutzen, der wird leider enttäuscht! Der VIC-II mag dies überhaupt nicht und zeigt nur einen schwarzen Bildschirm, probiert es ruhig selbst mal.
Jetzt seid ihr über die Textmodi des C64 informiert und könnt diese für eure Projekte nutzten. Der Textmodus ist, wie bereits erwähnt, mit am wichtigsten für Spiele und Demos, aber natürlich möchte man hin und wieder auch mal eine schicke Grafik anzeigen. Darum dreht sich der kommende Beitrag.