Dieser Grafikchip war ein Überbleibsel aus dem CBM900-Projekt. Er wurde modifiziert im C128 eingesetzt, um
den 80-Zeichen-Schirm realisieren zu können.
Die Entwicklungsgeschichte des Chips ist recht abenteuerlich. Frühe Modelle hatten eine Fehlerrate von drei
Fehlern pro Sekunde statt alle drei Jahre. Das Blockverschiebefeature konnte nur maximal 256 Bytes
verschieben (3,5 Bildschirmzeilen...) und selbst dann vergaß es gelegentlich ein paar Zeichen. Um das zu
umgehen, musste man den Verschiebebefehl ZWEIMAL geben. Dummerweise vergaß man, dies dem armen Kerl zu sagen,
der CP/M für den C128 entwickelt hatte. In der Nacht vor der Präsentation auf der CES bekam er es dann mit
und setzte sich hin, um alle Schreibzugriffe auf die entsprechenden Register anzupassen. Da er die zum
neuassemblieren benötigten Gerätschaften nicht dabei hatte, musste dies direkt mit einem Diskettenmonitor
geschehen. Ach ja, bei CP/M sind die Bytes auf den Disketten rückwärts in ebenfalls rückwärts orientierten
Sektoren abgespeichert. Außerdem konnte er die Zahl der Instruktionen weder erhöhen noch verringern, sondern
bloß vorhandene Instruktionen auswechseln. Dazu kam das Sektorprüfsummenberechnen von Hand. All das mit einem
Diskettenmonitor...
Da der Chip keinerlei Interrupts abgibt, wenn er mit einer Aufgabe fertig ist, musste er permanent abgefragt
werden (der Running Gag bei Commodore wurde daraufhin, man braucht ja keine Klingel im Telefon, man kann ja
jederzeit abheben um zu schauen ob einer dran ist). Dazu kamen noch Synchronisationsprobleme, da der Chip mit
16 MHz getaktet wurde, der Rest des Systems aber mit 14,31818 MHz, dadurch wurden Speicherzugriffe ein reines
Glücksspiel. Die Fertigungsausbeute des Chips lag etwa bei 0.001 % (kein Witz!!!), für jeden Chip musste das
Netzteil anders eingestellt werden und das laden der Fonts dauerte bei jedem Einschalten etwa 10 Sekunden,
vorausgesetzt, es funktionierte überhaupt. Die meisten dieser Probleme wurden erst kurz vor der Präsentation
des C128 "behoben", das meiste waren "dirty hacks", um überhaupt etwas zeigen zu können.
Eigenschaften des VDC
Im Gegensatz zu beispielsweise dem VIC hatte der VDC seinen eigenen Grafikspeicher, der beim alten C128 16
KByte, beim neueren C128 DCR 64 KByte groß war. Dieser Speicher war nicht direkt für die CPU des Rechners
ansprechbar, und die Register des Chips waren auch nicht wie die der anderen im I/O-Bereich des Rechners zu
finden. Um den VDC zu steuern, wurde auf eine etwas seltsame Methode zurückgegriffen. Der Chip war nur mit
zwei Speicherstellen im Adressbereich der CPU vertreten, eine davon repräsentierte die Registernummer, die
andere den Registerwert. Wenn man nun etwas in ein Register schreiben wollte, dann schrieb man zuerst die
Registernummer in die eine Speicherzelle, und dann den gewünschten Wert in die andere. Das lesen
funktionierte analog, indem man erst die Registernummer angab, und den Wert dann aus dem zweiten
Speicherbereich las. Dies machte direkte Zugriffe auf den Bildschirmspeicher zwar nicht unmöglich (es gab
spezielle VDC-Register zum Ansprechen des Speichers) aber äußerst umständlich.
Dafür bekam man einen CGA-ähnlichen Grafikchip, der Texte in 16 Farben in einer 80x25 Matrix anzeigen konnte
(mittels eines Interlace-Tricks waren 80x50 Zeichen möglich, aber leider nur mit dem typischen geflimmere)
und Grafik in 640 x 200 Bildpunkten und mit Programmiertricks sogar noch viel höherer Auflösung auf den
Schirm brachte.
Darstellungsmodi
Art |
Auflösung |
Farben |
|
Text |
80 x 25 |
1 aus 16 |
|
Grafik |
640 x 200 |
1 aus 16 |
|
Register
Register 0 / $0: Anzahl der Zeichen zwischen Synchronimpulsen |
76543210 |
RW |
Anzahl der Zeichen zwischen Synchronimpulsen |
|
|
Register 1 / $1: Anzahl der Zeichen pro Zeile |
76543210 |
RW |
Anzahl der Zeichen pro Zeile |
|
|
Register 2 / $2: Linker Bildschirmrand |
76543210 |
RW |
Linker Bildschirmrand |
|
|
Register 3 / $3: Breite des horizontalen und vertikalen Synchronimpulses |
7654.... |
RW |
Breite des vertikalen Synchronimpulses |
|
....3210 |
RW |
Breite des horizontalen Synchronimpulses |
|
|
Register 4 / $4: Wie Register 0, aber für Anzahl der Zeilen. |
76543210 |
RW |
Gesamtzahl Zeichenzeilen minus 1 |
|
|
Register 5 / $5: Feineinstellung zu Register 4 |
765..... |
RW |
Ungenutzt |
|
...43210 |
RW |
Feinabgleich für Register 4 |
|
|
Register 6 / $6: Anzahl der Textzeilen |
76543210 |
RW |
Anzahl der Textzeilen |
|
|
Register 7 / $7: Oberer Bildschirmrand |
76543210 |
RW |
Beginn des Oberen Bildschirmrandes |
|
|
Register 8 / $8: Anzeigeform |
765432.. |
RW |
Ungenutzt |
|
......10 |
RW |
Anzeigeform: |
00 und 10 = Kein Zeilensprung, 01 = Zeilensprung, 11 = Zeilensprung doppelte Dichte |
|
Register 9 / $9: Zeichenlänge |
765..... |
RW |
Ungenutzt |
|
...43210 |
RW |
Anzahl der der Bildschirmzeilen pro Zeichenzeile minus 1 |
|
|
Register 10 / $a: Cursor-Darstellung |
7....... |
RW |
Ungenutzt |
|
.65..... |
RW |
Cursor-Modus: |
00 = Cursor blinkt nicht, 01 = kein Cursor sichtbar, 10 = Cursor blinkt mit 1/16 der Bildwiederholfrequenz, 11 = Cursor blinkt mit 1/32 der Bildwiederholfrequenz |
...43210 |
RW |
Cursor-Startzeile |
|
|
Register 11 / $b: Cursorende |
765..... |
RW |
Ungenutzt |
|
...43210 |
RW |
Cursor-Endzeile |
|
|
Register 12 / $c: Start des Bildschirmspeichers |
76543210 |
RW |
Startadresse des Bildschirmspeichers (High Byte) |
|
|
Register 13 / $d: Start des Bildschirmspeichers |
76543210 |
RW |
Startadresse des Bildschirmspeichers (Low Byte) |
|
|
Register 14 / $e: Cursorposition im Video RAM |
76543210 |
RW |
Cursor-Position im Bildschirmspeicher (High Byte) |
|
|
Register 15 / $f: Cursorposition im Video RAM |
76543210 |
RW |
Cursor-Position im Bildschirmspeicher (Low Byte) |
|
|
Register 16 / $10: Lightpen-Position |
76543210 |
RW |
Vertikale Light-Pen Position in Zeichen-Zeilen plus 1 |
|
|
Register 17 / $11: Lightpen-Position |
76543210 |
RW |
Horizontale Light-Pen Position in Zeichen-Zeilen plus 8 |
|
|
Register 18 / $12: Adresse im VDC-RAM |
76543210 |
RW |
Speicheradresse für direkten Bildschirmspeicherzugriff (High Byte) |
|
|
Register 19 / $13: Adresse im VDC-RAM |
76543210 |
RW |
Speicheradresse für direkten Bildschirmspeicherzugriff (Low Byte) |
|
|
Register 20 / $14: Startadresse des Attribut-RAM |
76543210 |
RW |
Startadresse des Attributspeichers (High Byte) |
|
|
Register 21 / $15: Startadresse des Attribut-RAM |
76543210 |
RW |
Startadresse des Attributspeichers (Low Byte) |
|
|
Register 22 / $16: Zeichenformat |
7654.... |
RW |
Zeichenbreite in Pixel minus 1 (Mit Zeichenzwischenraum) |
|
....3210 |
RW |
Zeichenbreite in Pixel minus 1 (Ohne Zeichenzwischenraum) |
|
|
Register 23 / $17: Zeichenformat |
765..... |
RW |
Ungenutzt |
|
...43210 |
RW |
Anzahl der Bildschirmzeilen pro Zeichenzeile minus 1 ohne vertikalen Zeilenabstand |
|
|
Register 24 / $18: Zeichenbreite und Flags |
7....... |
RW |
Block-Copy: |
0 = Block wird geschrieben nach einem Schreibzugriff auf Register 30, 1 = Block wird kopiert nach einem Schreibzugriff in Register 30 |
.6...... |
RW |
Darstellung: |
0 = Normaldarstellung, 1 = Inversdarstellung |
..5..... |
RW |
Blinkfrequenz der Buchstaben: |
0 = Blinkfrequenz ist 1/16 der Bildwiederholfrequenz, 1 = Blinkfrequenz ist 1/32 der Bildwiederholfrequenz |
...43210 |
RW |
Anzahl der Bildschirmzeilen, die der Bildschirm nach oben geschoben wird |
|
|
Register 25 / $19: Diverse Steuerzwecke |
7....... |
RW |
Betriebsart: |
0 = Textdarstellung, 1 = Grafikdarstellung |
.6...... |
RW |
Attribute: |
0 = Aus, 1 = An |
..5..... |
RW |
Semigrafik Betriebsart: |
0 = Der Horizontale Zwischenraum zwischen zwei Zeichen wird in der Hintergrundfarbe dargestellt, 1 = Der horizontale Zwischenraum zweier Zeichen wird in der gleichen Farbe dargestellt, wie das letzte normal dargestellte Zeichen |
...4.... |
RW |
Pixelgröße: |
0 = 1 DOT-Takt, 1 = 2 DOT-Takte |
....3210 |
RW |
Anzahl der Pixel, um die der Bildschirm nach links geschoben wird |
|
|
Register 26 / $1a: Farbregister |
7654.... |
RW |
Vordergrundfarbe wenn Bit 6 von Register 25 = 0 |
|
....3210 |
RW |
Hintergrund und Rahmenfarbe |
|
|
Register 27 / $1b: Adressen-Inkrement pro Zeile |
76543210 |
RW |
Wert, der zum Bildschirm- und Attributzeiger nach jeder Zeile aufaddiert wird, um die nachste Zeile zu erhalten |
|
|
Register 28 / $1c: Startadresse des Zeichensatzes |
765..... |
RW |
Adresse des Zeichengenerators (Bits 13 - 15) |
|
...4.... |
RW |
DRAM-Typ: |
0 = 4416, 1 = 4164 |
|
Register 29 / $1d: Position der Unterstreichung |
...43210 |
RW |
Bildschirmzeilenzähler für Untersteichen |
|
|
Register 30 / $1e: Zählregister |
76543210 |
RW |
Anzahl der Block-Write oder Block-Copy-Zyklen. Eine 0 entspricht 256 Zyklen. Das verändern dieses Registers startet den Block-Zyklus |
|
|
Register 31 / $1f: Datenregister |
76543210 |
RW |
Daten für Block-Write und normales Write. Beim Lesen des Registers wird die durch Register 18/19 bestimmte Bildschirmposition übergeben |
|
|
Register 32 / $20: Startadresse des Blocks |
76543210 |
RW |
Block-Copy Quelladresse (High-Byte). Die Zieladresse wird in Register 18/19 erwartet |
|
|
Register 33 / $21: Startadresse des Blocks |
76543210 |
RW |
Block-Copy Quelladresse (Low-Byte) |
|
|
Register 34 / $22: technische Werte |
76543210 |
RW |
Anzahl der Zeichen vom Beginn der dargestellten Zeile bis zur positiven Flanke des Display-Enable-Pins |
|
|
Register 35 / $23: technische Werte |
76543210 |
RW |
Anzahl der Zeichen vom Beginn der dargestellten Zeile bis zur negativen Flanke des Display-Enable-Pins |
|
|
Register 36 / $24: technische Werte |
7654.... |
RW |
Ungenutzt |
|
....3210 |
RW |
Anzahl der Refresh-Zyklen pro Bildschirmzeile |
|
|