Erstellt: 30. März 2014 (zuletzt geändert: 1. November 2021)

VIC-II: Grafikmodes – BITMAP (Hi-Res)

Die BITMAP-Modes des VIC-II

C64 Studio & AMCE

Nach der Behandlung der Textmodes im letzten Beitrag VIC-II: Grafikmodes – Text, werfen wir nun mal einen Blick auf die Bitmap-Eigenschaften des C64. Es geht hier erstmal um die beiden standard Modi: Hi-Res und Multi-Color.

Wat is ne BITMAP? Da stelle mer uns janz dumm. Und da sage mer so…

Eine Bitmap erlaubt es uns, direkt jeden einzelnen Pixel der Anzeige anzusprechen. Wir können also jeden Punkt der 320*200 Pixel, die der C64 uns zur Verfügung stellt, direkt setzen oder löschen. Diese maximale Auflösung wird Hi-Res genannt und die schauen wir uns jetzt als Erstes an, dort werden wir uns auch die genaue Funktionsweise erarbeiten.

Hi-Res

Im Hi-Res-Modus haben wir Zugriff auf die höhste Auflösung des C64. Wie wir aus der Werbung und den Technischendaten wissen, beträgt diese 320*200 Punkte. Wenn wir nun den Speicherbedarf berechnen, dann kommen wir zunächst auf fast 8KB (320*200/8 = 8000 Byte) für die einzelnen Bildschirmpunkte. Da dies für C64-Verhältnisse schon eine Menge Platz ist, sollten wir uns als erstes um die Speicherbelegung kümmern.

Wie man die Speicheraufteilung vornimmt und den Hi-Res-Modus aktiviert, wurde bereits in VIC-II: Speicherbereiche festlegen erklärt, dieser Text wird nun vorausgesetzt!

Wir beginnen nun damit, unseren Speicherbereich festzulegen und den Bitmap-Modus (Hi-Res) zu aktivieren. Wir sind mal ganz mutig und verwenden weiterhin die VIC-Bank-0 (dies ist bekanntlich der Standard nach dem Einschalten). Unsere 8KB große Bitmap legen wir in die zweite Hälfte der 16KB-Bank, also in den Speicherbereich $2000 – $3fff. Die restlichen Einstellungen lassen wir unangetastet.

Hier nun das erste Progrämmchen…

;*** 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       = 2                             ;Nr. (0 - 7) des 2KB-Blocks für den Zeichensatz         | Standard: 2
VICBITMAPBBLOCKNO       = 1                             ;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           = VICBASEADR+VICCHARSETBLOCKNO*2048   ;Adresse des Zeichensatzes                        | Standard: $1000 ($d000)
VICSCREENRAM            = VICBASEADR+VICSCREENBLOCKNO*1024    ;Adresse des Bildschirmspeichers
VICBITMAPADR            = VICBASEADR+VICBITMAPBBLOCKNO * 8192 ;Adresse der BITMAP

;*** Startadresse 
*=$0801
;** BASIC-Zeile: 2018 SYS 2062
 !word main-2, 2018 
 !byte $9e
 !text " 2062"
 !byte $00,$00,$00

!zone main
main
 ;*** Bitmap-Modus aktivieren
 lda $d011                          ;VIC-II Register 17 in den Akku
 ora #%00100000                     ;Bitmap-Modus
 sta $d011                          ;aktivieren

 ;*** Start des Bitmapspeichers festlegen
 lda $d018                          ;VIC-II Register 24 in den Akku holen
 and #%11110111                     ;Mit Bit-3
 ora #VICBITMAPBBLOCKNO*8           ;den Beginn des
 sta $d018                          ;Bitmapspeichers festlegen
 rts                                ;zurück zum BASIC

Wie unter VIC-II: Die Register erwähnt, wird der Bitmap-Modus mit Bit-5 im Register 17 $d011 aktiviert, das machen wir auch direkt bei main. Danach legen wir dann natürlich noch den Bitmap-Speicher fest. Am Schluß geht es zurück zum BASIC.

Starten wir nun das Programm einmal.

Nicht gerade hochauflösend!?!
Nicht gerade hochauflösend!?!

Die Anzeige ist irgendwie enttäuschend. Das sieht doch eher nach einem Textmodus aus, oder? Eure Anzeige könnte übrigens ein anderes Muster aufweisen, es hängt davon ab, was im von uns gewählten Speicherbereich steht. Der Standard bei VICE sieht vor, dass der Speicher abwechselnd mit $00 und $ff gefüllt wird, es werden immer 64 Bytes am Stück mit den erwähnt Werten gefüllt.

Löschen wir zunächst mal den Bildschirm. Dazu müssen wir diesmal 8KB auf $00 setzen. Fügt eine neue Konstante ZP_HELPADR1 = $fb ;Hilfsadresse auf der Zero-Page zum Programm hinzu und vor dem rts rufen wir nun die Löschfunktion auf jsr clrHiResBitmap ;BITMAP löschen.
Diese Routine sieht dann so aus:

!zone clrHiResBitmap
;*** Die 8KB der Hi-Res BITMAP löschen
clrHiResBitmap
 lda #<VICBITMAPADR                 ;auf die Zero-Page
 sta ZP_HELPADR1
 lda #>VICBITMAPADR
 sta ZP_HELPADR1+1

 ldx #32                            ;Schleifenzähler 32 Pages (32 * 256 = 8192 = 8KB)
 ldy #0                             ;Schleifenzähler für 256 BYTES je Page
 tya                                ;Akku auf 0 setzen
.loop
 sta (ZP_HELPADR1),Y                ;Akku 'ausgeben'
 dey                                ;Y verringern
 bne .loop                          ;solange größer 0 nochmal -> .loop
 inc ZP_HELPADR1+1                  ;Adresse auf der Zeropage um eine Page erhöhen
 dex                                ;Pagezähler verringern
 bne .loop                          ;solange größer 0 nochmal -> .loop
 rts                                ;zurück zum Aufrufer

Als erstes legen wir die Startadresse der Bitmap auf der Zero-Page ab. Das X-Register dient als Schleifenzähler für die Pages (wie ihr wisst, hat eine Page 256 Bytes). Diese 256-Bytes zählen wir mit dem Y-Register. Dann legen wir eine 0 im Akku ab und schreiben diese 0 in die 8192 Bytes, die wir für die Bitmap benötigen, um diese zu löschen. Wie ihr oben gesehen habt, sind es eigentlich nur 8000 Bytes, aber aus Bequemlichkeit löschen wir immer eine komplette Page. Wurden alle Bytes auf 0 gesetzt, dann wird die Funktion wieder verlassen.

Schon besser, aber es wurde nicht alles gelöscht!
Schon besser, aber es wurde nicht alles gelöscht!

Ein weiterer Start führt zur obigen Anzeige. Das sieht schon besser aus, aber der Bildschirm ist noch nicht ganz sauber. Legen wir doch mal den Text des Bildschirmspeichers darüber.

Der Bildschirmspeicher hat auch noch eine Auswirkung.
Der Bildschirmspeicher hat auch noch eine Auswirkung.

OK, nun ist es offensichtlich, wir erkennen, dass auch der normale Bildschirmspeicher bei Bitmaps benötigt wird. Löschen wir diesen also auch noch. Bauen wir daher eine einfache Löschfunktion für den Bildschirmspeicher (der liegt hier ja an der Standardadresse $0400) und rufen diese Routine direkt hinter dem Label clrHiResBitmap auf.

!zone clrHiResColor
;*** Bildschirmspeicher löschen
clrHiResColor
 ldx #0                             ;Schleifenzähler 256 BYTES
 txa                                ;0 in den Akku
.loop
 sta VICSCREENRAM,X                 ;1. Page des Bildschirmspeichers
 sta VICSCREENRAM+256,X             ;2. Page
 sta VICSCREENRAM+512,X             ;3. Page
 sta VICSCREENRAM+768,X             ;4. Page
 dex                                ;Schleifenzähler verringern
 bne .loop                          ;solange nicht 0 nochmal -> .loop
 rts                                ;zurück zum Aufrufer

Außerdem habe ich das rts am Ende von main noch gegen eine Endlosschleife jmp * ;Endlosschleife getauscht, damit der Bildschirm wirklich sauber ist.

Ein leerer Hi-Res-Bildschirm.
Ein leerer Hi-Res-Bildschirm.

Da der Bildschirmspeicher auch zur Hi-Res-BITMAP zu gehören scheint, beträgt der Speicherbedarf also nicht 8000, sondern sogar 9000 Bytes! Ihr könnt euch (an Hand der obigen Bilder) bestimmt schon denken, wozu der Bildschirmspeicher dient, oder? Ändert doch mal die Löschfunktion clrHiResColor geringfügig und verschiebt das txa hinter das Label .loop.

Der Bildschirmspeicher ist für die Farben zuständig.
Der Bildschirmspeicher ist für die Farben zuständig.

Wie ihr seht, dient der Bildschirmspeicher dazu die Farben zu beeinflussen. Es wird im Bitmap-Modus also nicht das Farb-RAM ab $d800, sondern der Bildschirmspeicher verwendet! Da wir alle Pixel gelöscht haben, kann es sich hier also nur um die Hintergrundfarbe handeln. Dies ist in der Tat so! Wir können hier für jeden 8*8-Pixel-Block separat die Hintergrundfarbe bestimmen. Diese Erkenntnis ist jetzt nicht unerheblich! Auch im Bitmap-Modus ist die gesamte Organisation sehr am Zeichen- / Textmodus angeleht! Wer möchte kann es im Bild oben nachzählen oder einfach ausrechnen: 320 Pixel / 8 = 40; 200 Pixel / 8 = 25. Das Ergebnis 40 * 25 sollte euch bekannt sein, das ist exakt die Auflösung im Textmodus.

Ändert die Löschroutine clrHiResColor bitte wieder so, dass der Bildschirm komplett gelöscht wird (also schwarz ist). Wir wollen nun einige Pixel setzen, dazu ändern wir main direkt vor der Endlosschleife jmp * etwas:

 ldx #7                             ;Schleifenzähler
 lda #$ff                           ;Pixelmuster
.loop
 sta VICBITMAPADR,X                 ;Pixel ausgeben
 lsr                                ;Muster im Akku ändern
 dex                                ;Schleifenzähler verringern
 bpl .loop                          ;solange positiv nochmal -> .loop

 jmp *                              ;Endlosschleife

Bevor wir das Programm starten, was für eine Ausgabe erwartet ihr?

Wir geben über den Akku immer 8-Bit (also 8-Pixel) aus. Der Akku-Inhalt wird bei jedem Durchlauf nach rechts verschoben. Wer schon für andere Systeme programmiert hat, würde jetzt sicher acht unterschiedlich lange Linien nebeneinander erwarten, oder nicht?

Startet nun das Programm und ihr seht…nichts!

Ändert die Löschroutine clrHiResColor jetzt so, dass statt 0 der Bildschirm mit 1 gefüllt wird. Ersetzt dazu das txa einfach mit lda #1 und startet das Programm danach nochmal.

Hintergrundfarbe beim Löschen auf weiß setzen.
Hintergrundfarbe beim Löschen auf weiß setzen.

Ahhh, da ist ja etwas zu sehen. Wir erkennen nun, dass wir tatsächlich etwas in die Bitmap geschrieben haben. Allerdings war es durch die Verwendung von schwarz auf schwarz nicht wirklich gut zu erkennen 😉 . Wie können wir nun die Pixelfarbe bestimmen? Wir wissen ja, dass der C64 nur 16 Farben beherrscht

Die Farben des C64.
Die Farben des C64.

und das der Bildschirmspeicher beim Bitmap-Modus für die Hintergrundfarbe zuständig ist. Mit einem Nibble lassen sich aber bereits die 16 möglichen Farben bestimmen, daher wird es euch nicht überraschen, dass das obere Nibble eines Bytes im Bildschirmspeicher für die Pixelfarbe und das untere (wie eben gesehen) für die Hintergrundfarbe zuständig ist. Überprüfen wir dies durch eine kleine Programmänderung.

Ergänzt main vor der Endlosschleife noch um die folgenden beiden Zeilen

 lda #$27                           ;Pixel = rot / Hintergrund = gelb
 sta VICSCREENRAM                   ;Farbe über Bildschirmspeicher bestimmen

und startet dann das Programm…
Wie ihr seht, haben wir für den ersten 8*8 Pixel-Block die Hintergrundfarbe auf gelb und die Pixelfarbe auf rot gesetzt. Ihr könnt also für jeden dieser Blöcke jeweils eine eigene Pixel- und Hintergrundfarbe festlegen.

Außerdem stellen wir fest, dass wir nicht (wie weiter oben erwartet) die Linien nebeneinander, sondern übereinander erhalten (sie bilden das Dreieck, das wir auf dem BS sehen). Dies ist der bereits erwähnten Nähe zum Textmodus geschuldet.

Ihr könnt euch den Bitmap-Modus so vorstellen, als wäre er ein großer, frei änderbarer Zeichsatzspeicher. Nur dass dieser 1000 Zeichen umfasst und sich jedes Zeichen an einer festen Position befindet. Werft zum besseren Verständnis ggf. nochmal einen Blick auf VIC-II: Eigener Zeichensatz und schaut euch die folgende Grafik / Tabelle an.

Positionen der BYTES.
Positionen der Bytes.

In der obigen Grafik seht ihr, wo sich welches Byte der Hi-Res-Bitmap befindet und erkennt außerdem welche Adressen die Farbinformationen beinhalten (natürlich beziehen sich die Farb-Adressen ab $0400 nur auf unser Beispiel, der Bildschirmspeicher kann ja auch verschoben werden). Da ein Byte bekanntlich 8-Bits hat, könnt ihr euch so vielleicht besser vorstellen, dass die Bytes 0 bis 7 das erste 8*8-Pixel große „Zeichen“ bilden, die Bytes 8 bis 15 das zweite und die Bytes 312 bis 319 das 40. „Zeichen“, usw.

Wir können also nicht direkt einen Pixel durch die Angabe der X- und Y-Position setzen bzw. löschen, sondern müssen dazu einige Berechnungen anstellen. Wir müssen die Position des entsprechenden 8*8-Blocks, die Zeile (sprich das Byte) im jeweiligen Block berechnen und dort den gewünschen Pixel durch Bit-Manipulation ändern. Möchten wir jetzt das Pixel an X=314 & Y=197 setzen, dann sollten wir erstmal die gedachte Textzeile ermitteln, in der sich das benötigte Byte befindet. Also 197 / 8 = 24 Rest 5, wir landen also in der 25. „Textzeile“, wie es der Zufall will, finden wir diese oben in der Grafik 😉 . Der Rest von 5 bestimmt dann innerhalb dieser Textzeile die Byte-Zeile. Wir befinden uns also in der Reihe, die mit BYTE-7685 & BYTE-7693 beginnt (die Zählung beginnt wieder mit 0!). Jetzt müssen wir noch die X-Position (hier 314) umrechnen. Teilen wir nun 314 / 8 = 39 Rest 2; wir landen also im 40. Zeichen (oben in der Grafik BYTE-7997) und müssen dort das 3. Bit (Zählung beginnt bei 0!) von links (also eigentlich Bit-5) setzen. Erstellen wir nun auf Basis unserer Beobachtungen eine neue Funktion, mit der wir gezielt einzelne Pixel setzen können. Ein Problem sollten wir vorher noch klären: Wie wollen wir die X-Position an die Funktion übergeben? Schließlich kann X Werte von 0 bis 319 annehmen und diese passen schwerlich in ein Byte. Ich verwende hier einfach das Carry-Flag als höhstes Bit für die X-Position.

!zone setPixel
;*** X-Position = Carry + X-Register
;*** Y-Position = Y-Register
setPixel
 lda #<VICBITMAPADR                 ;Bitmap-Adresse auf die Zero-Page
 sta ZP_HELPADR1
 lda #>VICBITMAPADR
 sta ZP_HELPADR1+1
 bcc .skip                          ;falls Carry gelöscht weiter -> .skip
 inc ZP_HELPADR1+1                  ;sonst MSB+1
 clc                                ;und C wieder löschen (für ADC)
.skip
 txa                                ;X in den Akku
 pha                                ;und für später merken
 and #%11111000                     ;'Pixel'-Position ausblenden
 adc ZP_HELPADR1                    ;Anzahl der Bytes für X-Position addieren
 sta ZP_HELPADR1                    ;und speichern
 bcc .skip1                         ;falls C gelöscht ist weiter -> .skip1
 inc ZP_HELPADR1+1                  ;sonst MSB wegen gesetztem Carry erhöhen
.skip1
 tya                                ;Y in den Akku
 pha                                ;und merken
 lsr                                ;jetzt durch 8 teilen
 lsr
 lsr
 beq .skip2
 tay
.loop
 clc                                ;Für jede 'Textzeile' 320 Bytes addieren
 lda ZP_HELPADR1
 adc #64
 sta ZP_HELPADR1
 lda ZP_HELPADR1+1
 adc #1
 sta ZP_HELPADR1+1
 dey
 bne .loop
.skip2
 pla
 and #%00000111                     ;Bytes für die 'Ziel-Textzeile'
 tay                                ;ins Y-Register (für Y-nach-indizierte-Adr.)
 pla                                ;jetzt noch die BIT-Position (X) berechnen
 and #%00000111
 tax
 lda #$00
 sec
.loop1
 ror                                ;Zielpixel 'einrotieren'
 dex
 bpl .loop1                         ;wiederhole, solange X positiv
 ora (ZP_HELPADR1),Y                ;mit bisherigen Wert ODER-Verknüpfen
 sta (ZP_HELPADR1),Y                ;und speichern
 rts                                ;zurück zum Aufrufer

Die Routine legt zu Beginn die Startadresse des Bitmap-Speichers auf der Zero-Page ab. Dann wird geprüft, ob das Carry-Flag (X-Pos. größer 255) gesetzt ist. Falls dies der Fall sein sollte wird das MSB auf der Zero-Page direkt um eins erhöht und C für die gleich folgende Addition gelöscht. Anschließend kopieren wir den Inhalt des X-Registers in den Akku, merken uns den Wert auf dem Stack und blenden die unteren drei Bits aus, diese bestimmen später die exakte Pixelposition. Den Rest, der im Akku verblieben ist, addieren wir nun zur Adresse auf der Zero-Page. Damit haben wir die X-Position für unser Ziel-Byte berechnet. Nun muss noch die Y-Position addiert werden, dies geschieht ab .skip1. Dort kopieren wir den Inhalt des Y-Registers in den Akku und merken uns diesen wieder auf dem Stack. Nun teilen wir durch 8; bleibt nichts übrig geht es bei .skip2 weiter, sonst wird der Akku zurück ins Y-Register kopiert, dann blenden wir die unteren drei Bits aus und in einer Schleife wird für jede Zeile 320 zur Adresse auf der Zero-Page addiert. Zum Schluß brauchen wir noch die Reste. Der nicht durch 8 teilebare Rest der Y-Position landet direkt im Y-Register und wird gleich für die Y-nach-indizierte-Adressierung benötigt. Der nicht durch 8 teilbare Rest der X-Position wird verwendet, um durch Rotation & Verschieben das Bit für das Ziel-Byte an die richtige Stelle zu bringen. Da wir ein einzelnes Bit verändert, ist es wichtig, den aktuellen Inhalt des Ziel-Byte zu erhalten. Also verknüpfen wir unser Bit im Akku mit dem Byte an der berechneten Adresse (zzgl. dem Y-Register) per ora und speichern es abschließend an der berechneten Position.

Um dies nun zu testen, solltet ihr bei clrHiResColor die Farbe zum Löschen auf #$10 (weiße Pixel auf schwarzem Hintergrund) setzen. Dann ändert main noch etwas:

main
 ;*** Bitmap-Modus aktivieren
 lda $d011                          ;VIC-II Register 17 in den Akku
 ora #%00100000                     ;Bitmap-Modus
 sta $d011                          ;aktivieren

 ;*** Start des Bitmapspeichers festlegen
 lda $d018                          ;VIC-II Register 24 in den Akku holen
 and #%11110111                     ;Mit Bit-3
 ora #VICBITMAPBBLOCKNO*8           ;den Beginn des
 sta $d018                          ;Bitmapspeichers festlegen
 
 jsr clrHiResBitmap

 clc
 ldx #0
 ldy #0
 jsr setPixel
 sec
 ldx #63
 ldy #0
 jsr setPixel
 sec
 ldx #63
 ldy #199
 jsr setPixel
 clc
 ldx #0
 ldy #199
 jsr setPixel
 clc
 ldx #160
 ldy #100
 jsr setPixel
 
 jmp *                              ;Endlosschleife

Wenn ihr das Programm jetzt ausführt, dann sollten euch weiße Punkte in den vier Ecken und in der Mitte des Bildschirms auffallen. (Ist blöd zu erkennen, ich hätte den Rahmen evtl. auch auf schwarz setzen sollen, aber dann hätte man nicht gesehen, dass die Punkte tatsächlich in den Ecken sitzen.)

Einzelne Punkte mit SETPIXEL setzen.
Einzelne Punkte mit SETPIXEL setzen.

Wer möchte kann ja gerne mal den kompletten Bildschirm per setPixel füllen, dass dies sehr langsam ist, sollte bei einem Blick auf die Funktion klar sein. Sie dient mal wieder einzig zur Anschauung. In der Praxis ist es wohl wenig sinnvoll so vorzugehen.

;*** 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       = 2                             ;Nr. (0 - 7) des 2KB-Blocks für den Zeichensatz         | Standard: 2
VICBITMAPBBLOCKNO       = 1                             ;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           = VICBASEADR+VICCHARSETBLOCKNO*2048   ;Adresse des Zeichensatzes                        | Standard: $1000 ($d000)
VICSCREENRAM            = VICBASEADR+VICSCREENBLOCKNO*1024    ;Adresse des Bildschirmspeichers
VICBITMAPADR            = VICBASEADR+VICBITMAPBBLOCKNO * 8192 ;Adresse der BITMAP

ZP_HELPADR1             = $fb                           ;Hilfsadresse auf der Zero-Page

;*** Startadresse 
*=$0801
;** BASIC-Zeile: 2018 SYS 2062
 !word main-2, 2018 
 !byte $9e
 !text " 2062"
 !byte $00,$00,$00

main
 ;*** Bitmap-Modus aktivieren
 lda $d011                          ;VIC-II Register 17 in den Akku
 ora #%00100000                     ;Bitmap-Modus
 sta $d011                          ;aktivieren

 ;*** Start des Bitmapspeichers festlegen
 lda $d018                          ;VIC-II Register 24 in den Akku holen
 and #%11110111                     ;Mit Bit-3
 ora #VICBITMAPBBLOCKNO*8           ;den Beginn des
 sta $d018                          ;Bitmapspeichers festlegen
 
 jsr clrHiResBitmap

 clc
 ldx #0
 ldy #0
 jsr setPixel
 sec
 ldx #63
 ldy #0
 jsr setPixel
 sec
 ldx #63
 ldy #199
 jsr setPixel
 clc
 ldx #0
 ldy #199
 jsr setPixel
 clc
 ldx #160
 ldy #100
 jsr setPixel
 
 jmp *                              ;Endlosschleife



!zone setPixel
;*** X-Position = Carry + X-Register
;*** Y-Position = Y-Register
setPixel
 lda #<VICBITMAPADR                 ;Bitmap-Adresse auf die Zero-Page
 sta ZP_HELPADR1
 lda #>VICBITMAPADR
 sta ZP_HELPADR1+1
 bcc .skip                          ;falls Carry gelöscht weiter -> .skip
 inc ZP_HELPADR1+1                  ;sonst MSB+1
 clc                                ;und C wieder löschen (für ADC)
.skip
 txa                                ;X in den Akku
 pha                                ;und für später merken
 and #%11111000                     ;'Pixel'-Position ausblenden
 adc ZP_HELPADR1                    ;Anzahl der Bytes für X-Position addieren
 sta ZP_HELPADR1                    ;und speichern
 bcc .skip1                         ;falls C gelöscht ist weiter -> .skip1
 inc ZP_HELPADR1+1                  ;sonst MSB wegen gesetztem Carry erhöhen
.skip1
 tya                                ;Y in den Akku
 pha                                ;und merken
 lsr                                ;jetzt durch 8 teilen
 lsr
 lsr
 beq .skip2
 tay
.loop
 clc                                ;Für jede 'Textzeile' 320 Bytes addieren
 lda ZP_HELPADR1
 adc #64
 sta ZP_HELPADR1
 lda ZP_HELPADR1+1
 adc #1
 sta ZP_HELPADR1+1
 dey
 bne .loop
.skip2
 pla
 and #%00000111                     ;Bytes für die 'Ziel-Textzeile'
 tay                                ;ins Y-Register (für Y-nach-indizierte-Adr.)
 pla                                ;jetzt noch die BIT-Position (X) berechnen
 and #%00000111
 tax
 lda #$00
 sec
.loop1
 ror                                ;Zielpixel 'einrotieren'
 dex
 bpl .loop1                         ;wiederhole, solange X positiv
 ora (ZP_HELPADR1),Y                ;mit bisherigen Wert ODER-Verknüpfen
 sta (ZP_HELPADR1),Y                ;und speichern
 rts                                ;zurück zum Aufrufer



!zone clrHiResBitmap
;*** Die 8KB der Hi-Res BITMAP löschen
clrHiResBitmap
 jsr clrHiResColor
 lda #<VICBITMAPADR                 ;auf die Zero-Page
 sta ZP_HELPADR1
 lda #>VICBITMAPADR
 sta ZP_HELPADR1+1

 ldx #32                            ;Schleifenzähler 32 Pages (32 * 256 = 8192 = 8KB)
 ldy #0                             ;Schleifenzähler für 256 BYTES je Page
 tya                                ;Akku auf 0 setzen
.loop
 sta (ZP_HELPADR1),Y                ;Akku 'ausgeben'
 dey                                ;Y verringern
 bne .loop                          ;solange größer 0 nochmal -> .loop
 inc ZP_HELPADR1+1                  ;Adresse auf der Zeropage um eine Page erhöhen
 dex                                ;Pagezähler verringern
 bne .loop                          ;solange größer 0 nochmal -> .loop
 rts                                ;zurück zum Aufrufer



!zone clrHiResColor
;*** Bildschirmspeicher löschen
clrHiResColor
 ldx #0                             ;Schleifenzähler 256 BYTES
 lda #$10
.loop
 sta VICSCREENRAM,X                 ;1. Page des Bildschirmspeichers
 sta VICSCREENRAM+256,X             ;2. Page
 sta VICSCREENRAM+512,X             ;3. Page
 sta VICSCREENRAM+768,X             ;4. Page
 dex                                ;Schleifenzähler verringern
 bne .loop                          ;solange nicht 0 nochmal -> .loop
 rts                                ;zurück zum Aufrufer

Euch steht nun die Welt der Hi-Res-Bitmaps offen. Ihr könnt die Pixel- und Hintergrundfarbe bestimmen und gezielt einzelne Bildpunkte setzen. Als abschließendes Beispiel könnt ihr euch eine Doodle-Hi-Res-Grafik (die stammt nicht von mir!) als Programm herunterladen.

Die Grafik ist direkt ins Programm eingebunden und wurde von mir so gelegt, dass die Bitmap gleich passend an unserer Startadresse liegt, nur die Farben müssen noch in den Bildschirmspeicher kopiert werden.

Im nächsten Beitrag zeige ich das Vorgehen detaillierter mit einem Koala-Bild.

Ihr habt auch gesehen, wie verquast die Bitmaps organisiert sind. Nun versteht ihr wohl etwas besser, warum es wenige Spiele gibt, die wirklich im Bitmap-Modus laufen. Unmöglich ist es natürlich nicht, neben 3D-Spielen, gibt es aber nur sehr wenige Titel in dieser Richtung. Ein schönes Beispiel ist The Trapdoor.

Das Spiel läuft im BITMAP-Modus.
Das Spiel läuft komplett (nur die Menüs fallen aus dem Rahmen) im Hi-Res-BITMAP-Modus.

Nach dem Hi-Res-Modus, schauen wir uns demnächst natürlich noch den Multi-Color-Bitmap-Modus an. Da werfen wir auch einen Blick auf das Koala-Format und zeigen eine hübsche Grafik an.


Schrott!!Naja...Geht so...Ganz gut...SUPER! (9 Bewertungen | Ø 5,00 von 5 | 100,00%)

Loading...


ZurückWeiter

Ein Gedanke zu „VIC-II: Grafikmodes – BITMAP (Hi-Res)“

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Protected by WP Anti Spam