Dem VIC-II seinen Speicher zuweisen
Der C64 bietet prinzipiell zwei unterschiedliche Grafikmodi. Direkt nach dem Einschalten befindet er sich im Textmodus, außerdem gibt es noch einen Bitmap-Modus. Wo der VIC-II die jeweils benötigten Daten findet, könnt ihr sehr flexibel einstellen. So habt ihr die Möglichkeit den Speicher des C64 nach euren Vorstellungen zu nutzen.
Wer sich jetzt fragt, warum man den Speicherbereich für den VIC-II verschieben sollte, dem empfehle ich einen Blick auf Landung 009. Ganz am Ende des Textes seht ihr, was für Probleme entstehen können, wenn man sich keine Gedanken macht, wo was im Speicher liegt.
Sichtbaren Speicherbereich festlegen
Wie ihr evtl. schon wisst (ich habe es ja schon hier und da mal erwähnt), kann der VIC-II nur 16KB auf einmal „sehen“. Den Beginn, des für ihn sichtbaren 16KB-Bereichs, könnt ihr selbst festlegen. Dazu werden die insg. 64KB des C64 in vier Blöcke zu je 16KB geteilt. Diese 16KB-Blöcke nennt man auch (Video-)Bank. Die Bank legt ihr über die Bits 0 & 1 des Registers 0 $dd00 vom CIA-2 fest.
Bits | Bank | Speicherbereich %00 | 3 | $c000-$ffff (KEIN ROM-Zeichensatz verfügbar) %01 | 2 | $8000-$bfff %10 | 1 | $4000-$7fff (KEIN ROM-Zeichensatz verfügbar) %11 | 0 | $0000-$3fff (Standard nach dem Einschalten)
Wie ihr seht, stimmt die Bank-Nr. nicht mit dem Wert der Bits überein, die man setzen muss, um den entsprechenden Speicherbereich zu wählen. Nach dem Einschalten ist automatisch die Bank 0 aktiv. In Bank 1 und 3 steht euch der ROM-Zeichensatz übrigens nicht zur Verfügung (dazu später mehr)!
Wer jetzt denkt, Bank-2 und 3 seien kaum zu gebrauchen, da dort ja größtenteils das ROM liegt, der irrt. Wir können dort durchaus unsere Daten für den VIC-II ablegen. Zunächstmal lassen sich die ROMs bekanntlich deaktivieren, aber auch bei aktiven ROMs kann der VIC-II den Speicher ansprechen, da der VIC nichts vom ROM weiß. Einzige Ausnahme ist das Zeichen-ROM, wie ihr gleich seht. Ihr müsst nur bedenken, dass die CPU beim Zugriff auf einen RAM-Bereich, der von einem ROM überdeckt wird, das RAM zwar beschreiben kann, beim Lesen aber aufs ROM zugreift und dass wir den E/A-Bereich natürlich benötigen, da dort die VIC-Register liegen. Der VIC-II selbst verwendet nur das RAM, um an seine Daten zu kommen, einzige Ausnahme ist ein kleiner Bereich für den Zeichsatzspeicher, in den Bänken 0 und 2, das seht ihr in der Grafik noch genauer.
Auch der RAM-Bereich unter der Ein- / Ausgabe ist nicht verloren. Ihr könnt dort Daten für den VIC ablegen, die sich nicht oder sehr selten ändern, z. B. der Zeichensatz. Deaktiviert dazu über $01 den E/A-Bereich, kopiert euren Zeichensatz dorthin, aktiviert die Ein- / Ausgabe wieder und weist dem VIC schließlich die entsprechenden Bänke zu. Genaueres findet ihr in den Beiträgen zu Level 03 – Die Fahrt zur Basis des Spieletutorials L.O.V.E..
16KB-Bank wählen
Um die Bank zu wählen, müssen wir den Datenport-A des CIA-2 verwenden. Diesem sollten wir zur Sicherheit mitteilen, dass wir etwas ins Register schreiben möchten, das machen wir über Register 2 $dd02 des CIA-2.
;*** Startadresse *=$0801 ;** BASIC-Zeile: 2018 SYS 2062 !word main-2, 2018 !byte $9e !text " 2062" !byte $00,$00,$00 main ;*** 16KB-Bank wählen lda #%00000011 ;Datenrichtung für Bit 0 & 1 des Port-A sta $dd02 ;zum Schreiben freigeben lda #%00000011 ;Bank-0 sta $dd00 ;auswählen rts ;zurück zum BASIC
Mit diesem kleinen Programm könnt ihr testen, was beim Umschalten der Speicherbank passiert. Startet ihr obiges Programm, passiert erstmal nichts. Das liegt daran, dass dort die Bank 0 aktiviert wird, die ja auch direkt nach dem Einschalten aktiv ist. Versucht mal die anderen Bänke. Euch fällt dann auf, dass bei Bank 1 und 3 keine Textzeichen auf dem Bildschirm erscheinen. Die Anzeige hängt allerdings vom jeweiligen System und / oder Emulator und der jeweiligen Konfiguration ab. Hier können wir schon sehen, was oben bereits erwähnt wurde, dass der ROM-Zeichensatz in Bank 1 & 3 nicht verfügbar ist.
Lage des Bildschirmspeichers festlegen
Wir können als nächstes bestimmen, wo sich, innerhalb der sichtbaren 16KB, der Textbildschimspeicher befinden soll. Dazu werden die sichtbaren 16KB in 16 Blöcke zu je 1KB unterteilt. Diesen Bereich können wir diesmal über die Bits 7-4 im VIC-II Register 24 $d018 bestimmen. Die Position lässt sich ganz einfach über den Wert der Bits 7-4 * 1KB $0400 berechnen, wenn man diese einzeln betrachtet.
Bits | Nr. | Position (Beginn der 16KB-Bank nicht vergessen) %0000 | 0 | $0000 %0001 | 1 | $0400 (Standard nach dem Einschalten) %0010 | 2 | $0800 %0011 | 3 | $0c00 %0100 | 4 | $1000 %0101 | 5 | $1400 %0110 | 6 | $1800 %0111 | 7 | $1c00 %1000 | 8 | $2000 %1001 | 9 | $2400 %1010 | 10 | $2800 %1011 | 11 | $2c00 %1100 | 12 | $3000 %1101 | 13 | $3400 %1110 | 14 | $3800 %1111 | 15 | $3c00
Damit ihr wisst, auf welchen Speicherbereich ihr zugreifen müsst, um etwas auf den Bildschirm zu bringen, müsst ihr zur hier gewählten Position natürlich noch den Beginn der von euch gewählten 16KB-Bank addieren. Nach dem Einschalten erhalten wir so die uns bekannte Adresse $0400 (Bank 0: ab $0000 + $0400 für die Position des Bildschirmspeichers = $0400).
Falls ihr euch jetzt fragt, was mit dem Farb-RAM ist, das lässt sich nicht verschieben! Ihr findet es immer ab $d800!
Denkt auch daran, dass sich die Sprite-Pointer immer in den letzten acht Bytes am Ende des Kilobytes, in dem der Bildschimspeicher liegt, befinden. Sprites werden übrigens ausfühlich in Sprites (ASM) behandelt.
;*** Startadresse *=$0801 ;** BASIC-Zeile: 2018 SYS 2062 !word main-2, 2018 !byte $9e !text " 2062" !byte $00,$00,$00 main ;*** 16KB-Bank wählen lda #%00000011 ;Datenrichtung für Bit 0 & 1 des Port-A sta $dd02 ;zum Schreiben freigeben lda #%00000011 ;Bank-0 sta $dd00 ;auswählen ;*** Beginn des BS-Speichers wählen lda $d018 ;VIC-II Register 24 in den Akku holen and #%00001111 ;Über Bits 7-4 ora #%00010000 ;den Beginn des sta $d018 ;Bildschirmspeichers festlegen rts ;zurück zum BASIC
Um den Bildschirmspeicher zu positionieren, holen wir zunächst den Inhalt des Registers 24 $d018 in den Akku. Blenden dann das obere Nibble per AND aus und setzen es danach durch ORA auf den von uns gewünschten Wert. Hier haben wir wieder den Standardwert vorliegen, so dass nach einem Start anscheinend nichts passiert. Ändert den Wert beim ORA mal auf Null und startet das Programm. Dann habt ihr den Anfang des BS-Speichers in die Zero-Page verschoben und ihr seht, dass sich von Geisterhand einige Zeichen ändern. Daran erkennt ihr, dass ihr beim Positionieren aufpassen müsst. Überdeckt der Bildschirmspeicher wichtige Speicherbereiche und ihr gebt etwas auf dem BS aus, dann wird der C64 früher oder später abstürzen.
Zeichspeicher positionieren
Die Möglichkeiten Speicherbereiche festzulegen, reißen einfach nicht ab 😉 . Ihr könnt dem VIC-II auch vorgeben, wo er die Zeichen für die Textausgabe findet. Dabei handelt es sich um 8*8 Punkte große Grafiken, die das Aussehen der Zeichen festlegen. Wer sich jetzt fragt, warum man diesen Speicher festlegen kann, schließlich stehen die Zeichen ja im Char-ROM: Man kann seine eigenen Zeichsätze entwerfen! Diese neuen Zeichen müssen dann natürlich im RAM liegen, also müssen wir dem VIC mitteilen, wo er sie findet. Da ein kompletter Zeichensatz 256-Symbole umfasst, benötigen wir also 1 Byte * 8 Zeilen * 256 Zeichen = 2KB. Die sichtbaren 16KB werden für die Wahl des Beginns des Zeichensatzes in 8 Blöcke zu je 2KB unterteilt. Auch dieser Speicherbereich lässt sich über das Register 24 $d018 des VIC-II festlegen. Dazu dienen die Bits 3-1.
Bits | Nr. | Speicherbereich (Startadresse der 16KB-Bank beachten) %000 | 0 | $0000-$07ff %001 | 1 | $0800-$0fff %010 | 2 | $1000-$17ff (Standard: ROM-Image Bank 0 & 2) %011 | 3 | $1800-$1fff (ROM-Image Bank 0 & 2) %100 | 4 | $2000-$27ff %101 | 5 | $2800-$2fff %110 | 6 | $3000-$37ff %111 | 7 | $3800-$3fff
Auch hier müsst ihr wieder beachten, dass der Beginn der 16KB-Bank zum Beginn des Zeichensatzes addiert werden muss, um die tatsächliche Adresse zu ermitteln. Nr. 2 und 3 stellen bei Benutzung von Bank 0 und 2 einen Sonderfall dar. Schaut ihr bei diesen Bänken in die Speicherstellen $1000-$1fff bei Bank 0 bzw. $9000-$9fff bei 2, so werdet ihr dort keine Zeichenmuster entdecken. Schreibt ihr etwas in diese Adressen, dann ändert sich der Zeichensatz auch nicht! Das liegt daran, dass der VIC-II bei diesen Werten in Bank 0 & 2 direkt auf das Char-ROM zugreift. Der Speicher steht euch dann ganz fürs Programm (Befehle und Daten) zur Verfügung, die CPU kann problemlos auf diese Adressen zugreifen.
Ihr könnt in diesen Bänken aber keinen eigenen Zeichensatz ablegen! Die CPU kann die Daten zwar dort ablegen, aber der VIC-II sieht sie nicht!
;*** Startadresse *=$0801 ;** BASIC-Zeile: 2018 SYS 2062 !word main-2, 2018 !byte $9e !text " 2062" !byte $00,$00,$00 main ;*** 16KB-Bank wählen lda #%00000011 ;Datenrichtung für Bit 0 & 1 des Port-A sta $dd02 ;zum Schreiben freigeben lda #%00000011 ;Bank-0 sta $dd00 ;auswählen ;*** Beginn des BS-Speichers wählen lda $d018 ;VIC-II Register 24 in den Akku holen and #%00001111 ;Über Bits 7-4 ora #%00010000 ;den Beginn des sta $d018 ;Bildschirmspeichers festlegen ;*** Start des Zeichensatzes festlegen lda $d018 ;VIC-II Register 24 in den Akku holen and #%11110001 ;Über Bits 3-1 ora #%00000100 ;den Beginn des sta $d018 ;Zeichensatzes festlegen rts ;zurück zum BASIC
Auch bei der Wahl des Zeichsatzspeichers, setzen wir zunächst per AND die Bits (hier 3 bis 1) auf Null und schreiben danach per ORA den Standardwert ins Register 24 $d018. Ein Start zeigt wieder keine Änderung, sobald ihr aber etwas Anderes eintrag, werden sich die Zeichen auf dem Bildschirm, je nach gewähltem Speicherbereich, in wirre Zeichen ändern. Benutzt ihr wieder die Zero-Page, dann bewegen sich auch einige Pixel der Zeichen.
Speicher für Bitmap-Grafik bestimmen
Auch für HiRes-/Bitmap-Grafik kann der benötigte Speicherbereich von uns bestimmt werden. Diese Grafiken benötigen sehr viel Speicher. Sie haben eine max. Auflösung von 320*200 Pixel = 64000 Bildschirmpunkte. Teilt man diese durch acht, um die Byte-Anzahl zu errechnen, dann kommen wir auf 8000 Bytes. Daher werden die sichtbaren 16KB in nur zwei 8KB-Bereiche unterteilt. Dieser Speicher wird über Bit-3 im Register 24 $d018 bestimmt. Achtet darauf, dass Bit-3 auch für die Position des Zeichenspeichers benötigt wird. Sobald ihr den HiRes-/Bitmap-Modus aktiviert, steuert Bit-3 aber die Position der Grafik.
BIT | Nr. | Speicherbereich (wieder 16KB-Bank beachten) %0 | 0 | $0000-$1fff %1 | 1 | $2000-$3fff
Wie gehabt, muss wieder der Beginn der 16KB-Bank berücksichtigt werden, um die tatsächliche Adresse zu berechnen. Um dies jetzt zu testen, müssen wir natürlich den Bitmap-Modus aktivieren.
;*** 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 #%00000000 ;den Beginn des sta $d018 ;Bitmapspeichers festlegen rts ;zurück zum BASIC
Sobald wir mit dem VIC-II Register 17 $d011 den Bitmap-Modus aktiviert haben, wird über Bit-3 im Register 24 $d018 die Position des Bitmap-Speichers bestimmt.
Übersicht der Speicherblöcke innerhalb einer 16KB-Bank
Abschließend nochmal eine kleine Übersicht, wo ihr etwas, innerhalb einer, für den VIC sichtbaren, 16KB-Bank, ablegen könnt.
Ich brauche wohl nicht extra zu erwähnen, dass ihr darauf achten solltet, dass sich gleichzeitig benötigte Speicherbereiche nicht überscheiden. Wählt ihr also z. B. CHAR 0 und BS 0, wird es übel enden. 😉
Wie wir oben gelernt haben, ist bei Bank-0 auch die Zero-Page eine schlechte Wahl! Auch die folgenden Pages 1-3 können dann durchaus problematisch sein. Überlegt euch also gut, wo ihr was ablegen wollt.
Eine kleine Ergänzung noch in Richtung Sprites. Der Speicherbereich $1000-$1FFF und $9000-$9FFFF kann auch nicht für Sprite-Daten verwendet werden. Bin am Anfang auch erst darauf reingefallen, weil ich schlau sein wollte und Sprite Daten in diesen Bereichen schrieb, damit mein prg File nicht so groß wird und bekam statt der Sprites nur “Unsinn” angezeigt.
Beim Googeln bin ich dann darüber gestoßen, dass der VIC mit dem Sprite Pointer diese Bereiche im RAM nicht sehen kann und stattdessen auf das CHAR ROM (Bitmap Font) zeigt.
ah danke für die tolle Erklärung! Ich habe jetzt sowohl bei $D000 als auch bei $D800 einen Zeichensatz liegen und es klappt!:-) Ich hatte ursprünglich versucht, bei ausgeblendetem E/A-Bereich die Zeichensätze direkt an die Zieladresse zu laden, was (nachvollziehbarerweise) nicht geht. Wenn ich sie zuerst an eine andere Adresse lade und dann im Speicher kopiere, gibt es kein Problem.
>>Der Zeichensatz müsste ja entweder bei $D000 oder bei $D800 liegen, um überhaupt vom VIC addressiert werden zu können.
Was ich damit meinte, war, dass man bspw. nicht einen 64-Zeichen-Zeichensatz nach $D400 (SID) legen kann, um das vermeintliche Überschreiben des Farbspeichers zu umgehen.
Viele Grüße!
Carsten
Hallo Jörn,
Du schreibst “Auch der RAM-Bereich unter der Ein- / Ausgabe ist nicht verloren. Ihr könnt dort Daten für den VIC ablegen, die sich nicht oder sehr selten ändern, z. B. der Zeichensatz. “.
Das will mir nicht in den Kopf: Der Zeichensatz müsste ja entweder bei $D000 oder bei $D800 liegen, um überhaupt vom VIC addressiert werden zu können. Bei $D000 liegen doch aber schon die VIC-Register und bei $D800 der Farbspeicher, also beides braucht der VIC selbst! Da kann ich doch dann keinen Zeichensatz ablegen, oder?
Viele Grüße!
Carsten
wie heißt es doch so schön: „Klingt komisch, ist aber so.“ 😉
Du musst dir vor Augen führen, was der E/A-Bereich wirklich macht. Wenn du z. B. die Hintergrundfarbe über
$D021
setzt, dann steht der Wert nicht im RAM an der Adresse$D021
! Der E/A-Bereich mappt die Adressen auf die Register / Speicherzellen der Custom-Chips (VIC, SID, CIA usw.). Die Hintergrundfarbe steht also direkt im VIC-II, daher braucht bzw. kennt der VIC selbst die Speicherstelle$D021
auch nicht. Das Farb-RAM verhält sich ähnlich. Es ist ein eigener RAM-Baustein auf dem C64-Board, der direkt mit dem VIC verbunden ist. Schreibst du also etwas nach$D800
, dann landet es (bei aktiven E/A-Bereich) im eigenen Farb-RAM. Da dieses RAM lediglich 4-Datenleitungen besitzt, lassen sich dort nur die Werte 0 bis 15 ablegen (also die 16 möglichen Farben des C64). Wenn man so will, hat der C64 also eigentlich 64,5KB. Holt der VIC nun seine Daten (z. B. Sprites oder den Zeichensatz), dann macht er dies immer aus dem normalen Arbeitsspeicher (RAM). Daher kann man, wie oben erwähnt, unter dem Kernal und E/A-Bereich auch entsprechende Daten für den VIC ablegen.Vielleicht solltest du dies einmal selbst auszuprobieren:
$D000
Gruß,
Jörn
EDIT: Habe noch etwas vergessen.
Warum? Der Zeichensatz kann auch noch weiter hinten liegen, sogar
$F800
ist möglich.