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

weitersagen ...
Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedIn

CBM prg StudioDie BITMAP-Modes des VIC-II

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…

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:

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 noch 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.

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:

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 $00 der Bildschirm mit $01 gefüllt wird. Ersetzt dazu das txa einfach mit lda #$01 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 16 Farben des C64.
Die 16 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

und startet dann das Programm…

HiRes_08Wie 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. mal 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 Bildschrimspeicher 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 den 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.

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 das 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:

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.


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!) im Java-Emulator ansehen oder auch 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 an einem Koala-Bild.)

Ihr habt auch gesehen, wie ‚verquast‚ die BITMAPs organisiert sind und 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! (5 Bewertungen | Ø 5,00 von 5 | 100,00%)

Loading...


 

<<< zurück | weiter >>>

 

weitersagen ...
Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedIn

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