128er
Grafik

Doppelte Grafikauflösung für C 128

Ein kleines Maschinenprogramm macht dem 80-Zeichen-Videocontroller des C 128 Beine und bringt eine Grafikauflösung von 640 x 200 Punkten.

Wie Sie als stolzer C 128-Besitzer vielleicht wissen, besitzt Ihr Computer zwei Arten der Zeichendarstellung. Die üblichen 40 Zeichen pro Zeile und den 80-Zeichen-Bildschirm.

Schalten Sie auf 80 Zeichen um, so übernimmt ein anderer Video-Chip die Arbeit des uns vom C 64 her bekannten VIC II und zaubert ein 80-Zeichen-Feature auf den Bildschirm. Sehen können Sie dabei allerdings nur etwas, wenn Sie auch einen Monitor an der RGB-Buchse des C 128 angeschlossen haben, denn nur dort sind 80 Zeichen pro Zeile möglich. Der 8563-Videocontroller sorgt in diesem Modus für ein anständiges Bild.

Doch er kann noch mehr. Neben Buchstaben und Zahlen ausgeben ist er fähig, Punktgrafik zu erzeugen, und das in doppelter Auflösung, also statt mit den bekannten 320 mal 200 Punkten nun 640 mal 200 Punkte. Sie haben richtig gelesen. Das sind insgesamt 128000 Bildpunkte, die einzeln ansprechbar sind. Prima, werden Sie sagen, der C 128 hat ja die vielen tollen Grafikbefehle … Doch halt ! Die Freude ist ein wenig verfrüht. Die doppelte Auflösung des 8563 wird nämlich unverständlicherweise von diesen Basic-Befehlen nicht ausgenutzt. Die ganzen fantastischen Grafikbefehle des C 128 sprechen nur die vom C 64 bekannte 320 x 200 Punkte-Grafik an. Warum das so ist, das weiß nur Commodore allein.

Auch der Versuch, die Punkte einfach in den Grafikspeicher des 8563 zu POKE wird fehlschlagen, denn dieser Grafikspeicher ist vom Prozessor aus nicht ansprechbar. Der Videocontroller 8563 steht nämlich im Genuß eines eigenen Zeichenspeichers von 16 KByte, der nicht im normalen Adreßbereich liegt und nur ihm selbst zugänglich ist.

Doch ganz so eigenständig ist der VDC 8563 nun auch nicht. Es muß selbstverständlich ein Informationsaustausch zwischen Videoprozessor und dem übrigen Computer stattfinden können. Den gibt es natürlich auch.

Die Verbindung besteht allerdings nur aus 2 Byte im Input/Output-Bereich mit den Adressen $D600 und $D601. Durch sie hindurch drängt sich der gesamte Informationsverkehr von VDC 8563 und Computer.

Denn der VDC 8563 muß viel wissen, wenn er ein ordentliches Bild erzeugen will. Da er nur seinen »Privatspeicher« von 16 KByte kennt, kann er auf den Zeichengenerator nicht direkt zugreifen. Damit er trotzdem die Zeichen erzeugen kann, wird er beim Anschalten des Computers mit den nötigen Bytes aus dem Zeichengenerator über den Engpaß $D600/$D601 gefüttert. Dies geschieht übrigens auch, wenn Sie mit der ASCII/DIN-Taste auf den anderen Zeichensatz umschalten. Die erhaltenen Zeichen legt er in seinem RAM ab, damit er nun darauf zugreifen kann.

Die Aufteilung seines Speichers sieht dann folgendermaßen aus:

Diese Konfiguration gilt für den Textmodus des 8563. Doch wehe, man setzt im Register 25 das Bit für den Grafikmodus, dann läßt der VDC 8563 Zeichen Zeichen sein und bearbeitet seine 16 KByte im »Bitmapping«-Modus. Das heißt für jedes gesetzte Bit läßt er einen Punkt auf dem Monitor leuchten, für jedes ungesetzte Bit eben nicht. Sein gesamter Speicher ist nun Grafikspeicher.

16 KByte RAM mal 8 Bit ergibt nach sorgfältigem Rechnen genau 128000 Bit, was in unserem Falle 128000 ansteuerbare Bildpunkte bedeutet. Doch wie, werden Sie fragen, kann man durch nur 2 Byte ($D600/$D601) die Register des VDC 8563 oder gar seinen Speicher manipulieren? Die Antwort ist ganz einfach: Sie lautet indirekte Adressierung.

In das erste Verbindungsbyte ($D600) schreibt man die Nummer des Registers, das man ansprechen will (der VDC 8563 hat deren 31). Danach liest man den Wert des angesteuerten Registers über das zweite Byte ($D601), oder man schreibt den gewünschten Wert hinein. Eine einfache Sache.

Wie aber kann nun der 16-KByte-Speicher des Videocontrollers manipuliert werden?

Der 8563 besitzt mehrere Register, von denen einige Informationen über die Speicheraufteilung der 16 KByte geben (Bild 1).

Bild 1. Die Register des VDC 8563

So gibt es Register, die beispielsweise die Startadresse des 80-Zeichen-Speichers enthalten. In den Registern 18 und 19 ist nun eine aktuelle Adresse des Videospeichers abgelegt, dessen Wert gerade bearbeitet werden soll. Und Register 31 hält den Inhalt dieser Adresse bereit.

Man muß also nach obengenanntem Schema die Register 18 und 19 (HI/LO) mit der Adresse des Videospeichers belegen, die man ansprechen will, und kann dann den Inhalt dieser Adresse über das Register 31 auslesen oder sie mit dem gewünschten Wert beschreiben.

Um Ihnen den Aufwand zu ersparen, ein eigenes Programm schreiben zu müssen, das die Grafik des 8563 ausnutzt, haben wir ein Assemblerlisting (Listing 1) dazu abgedruckt. Es stammt aus dem Commodore 128-Handbuch von Peter Rosenbeck (Markt & Technik-Verlag). Sie können dieses Programm direkt mit dem in den C 128 integrierten Maschinensprache-Monitor eingeben.

Es übernimmt die Arbeit der gerade erwähnten Prozedur der indirekten Adressierung des VDC 8563 und bietet auch eine Routine zum Punkte setzen und löschen. Ein unentbehrliches Werkzeug für die Arbeit mit der 640 mal 200 Punkte-Grafik auf dem 80-Zeichenbildschirm.

Die einzelnen Routinen können über den SYS-Befehl angesprochen werden, wie es das kleine Basic-Beispielprogramm (Listing 2) zeigt. Die X- und Y-Koordinaten werden einfach mit dem SYS-Befehl übergeben (Zeile 160). Das Beispielprogramm erzeugt eine Sinuskurve auf dem Monitor.

Wer Spaß daran hat, kann sich weitere Routinen (zum Beispiel zum Linien ziehen) dazuschreiben und somit die Grafikfähigkeiten des VDC 8563 voll ausnutzen.

(M.Thomas/ev)
1400 4c 1e 14 jmp $141e ;Grafikmodus anschalten
1403 4c 26 14 jmp $1426 ;Grafikmodus einschalten
1406 4c 87 14 jmp $1487 ;Grafikbildschirm löschen
1409 4c 62 ff jmp $ff62 ;Zeichensatz neu laden
140c 4c 81 ff jmp $ff81 ;Editor initialisieren
140f 4c a3 14 jmp $14a3 ;Punkt setzen
1412 4c 9d 14 jmp $149d ;Punkt löschen
1415 4c ?? ?? jmp $???? ;frei für Erweiterungen
1418 4c ?? ?? jmp $???? ;frei für Erweiterungen
141b 4c ?? ?? jmp $???? ;frei für Erweiterungen


;Grafikmodus anschalten


141e a9 80    lda #$80  ;Bit 7 setzen
1420 a2 19    ldx #$19  ;Register 25
1422 20 cc cd jsr $cdcc ;Register 25 mit
                        $80 besetzen
1425 60       rts


;Grafikmodus ausschalten


1426 a9 40    lda #$40  ;Bit 6 setzen
1428 a2 19    ldx #$19  ;Register 25
142a 20 cc cd jsr $cdcc ;Register 25 mit
                        $40 besetzen
142d 60       rts


;aktuelle Adresse setzen (in X und Y)


142e a9 12    lda #$12  ;Register 18
1430 8d 00 d6 sta $d600 ;ansteuern
1433 8e 01 d6 stx $d601 ;HI von Adresse nach Reg. 18
1436 20 45 14 jsr $1445 ;Warten auf Statusbit
1439 a9 13    lda #$13  ;Register 19
143b 8d 00 d6 sta $d600 ;ansteuern
143e 8c 01 d6 sty $d601 ;LO von Adresse
                        nach Reg. 19
1441 20 45 14 jsr $1445 ;Warten auf Statusbit
1444 60       rts


;Warten bis Statusbit gesetzt

1445 2c 00 d6 bit $d600 ;Bit 7 (Status) gesetzt
1448 10 fb    bpl $1445 ;nein, dann warte
144a 60       rts


;Warten bis Statusbit gelöscht


144b 2c 00 d6 bit $d600 ;Bit 7 (Status) gelöscht
144e 30       bmi $144b ;nein, dann warten
1450 60       rts


;Wortzähler Null setzen


1451 a9 1e    lda #$1e  ;Register 30
1453 8d 00 d6 sta $d600 ;ansteuern
1456 20 45 14 jsr $1445 ;Warten auf Statusbit
1459 a9 00    lda #$00  ;Rewgister 30
145b 8d 01 d6 sta $d601 ;Null setzen
145e 60       rts


;Datenbyte Null setzen


145f a9 1f    lda #$1f  ;Register 31
1461 8d 00 d6 sta $d600 ;ansteuern
1464 20 45 14 jsr $1445 ;Warten auf Statusbit
1467 a9 00    lda #0    ;Register 31
1469 8d 01 d6 sta $d601 ;Null setzen
146c 60       rts


;Datenbyte nach A holen


146d a9 1f    lda #$1f  ;Register 31
146f 8d 00 d6 sta $d600 ;ansteuern
1472 20 45 14 jsr $1445 ;Warten auf Statusbit
1475 ad 01 d6 lda $d601 ;Byte nach A holen
1478 60       rts


;Datenbyte (in A) in aktuelle Adresse
;ablegen


1479 48       pha       ;A retten
147a a9 1f    lda #$1f  ;Register 31
147c 8d 00 d6 sta $d600 ;ansteuern
147f 20 45 14 jsr $1445 ;testen auf Statusbit
1482 68       pla       ;A wieder holen
1483 8d 01 d6 sta $d601 ;in aktuelle Adr. speichern
1486 60       rts


;Bildschirm löschen


1487 a2 00    ldx #$00  ;HI Adresse von Bildschirm in X
1489 a0 00    ldy #$00  ;LO Adresse in Y
148b 20 2e 14 jsr $142e ;aktuelle Adresse setzen
148e 20 51 14 jsr $1451 ;Wortzähler Null setzen
1491 20 5f 14 jsr $145f ;Datenbyte Null setzen
1494 c8       iny       ;nächste Adresse
1495 d0 f4    bne $148b
1497 e8       inx
1498 e0 40    cpx #$40  ;letzte Adresse von Bildschirm?
149a d0 ef    bne $148b ;nein, dann nächste Adresse
149c 60       rts


;Punkt löschen


149d 48       pha       ;A retten
149e a9 00    lda #$00  ;Flag für Löschen
14a0 4c a6 14 jmp $14a6 ;Punkt löschen


;Punkt setzen


14a3 48       pha       ;A retten
14a4 a9 ff    lda #$ff  ;Flag für Setzen
14a6 85 c3    sta $c3   ;zwischenspeichern
14a8 68       pla       ;A wieder holen
14a9 20 db 14 jsr $14db ;Adressberechnung
                        (X-Koord. in A,X
                        ; Y-Koord. in Y)
14ac b0 ee    bcs $149c ;Angaben außerhalb
                        des Bereichs
14ae 85 c4    sta $c4   ;Bitmaske (in A)
                        zwischenspeichern
14b0 a4 c1    ldy $c1   ;LO Adresse nach Y
14b2 a6 c2    ldx $c2   ;HI Adresse nach X
14b4 20 2e 14 jsr $142e ;aktuelle Adresse
                        (in X/Y) setzen
14b7 20 6d 14 jsr $146d ;Datenbyte aus aktueller
                        Adr. nach A holen
14ba 48       pha       ;retten
14bb a5 c3    lda $c3   ;Flag für setzen/löschen
                        nach Adr. f0 06
14bd f0 06    beq $14c5 ;Punkt löschen, dann
                        zu Löschen
14bf 68       pla       ;Datenbyte wieder holen
14c0 05 c4    ora $c4   ;mit Bitmaske verknüpfen
                        (Bit setzen)
14c2 4c ce 14 jmp $14ce ;zum Abspeichern
14c5 68       pla       ;Datenbyte wieder holen
14c6 85 c3    sta $c3   ;zwischenspeichern
14c8 a5 c4    lda $c4   ;Bitmaske nach A
14ca 49 ff    eor #$ff  ;alle Bits umdrehen
14cc 25 c3    and $c3   ;mit Datenbyte verknüpfen
                        (löschen)
14ce 48       pha       ;neues Datenbyte retten
14cf a4 c1    ldy $c1   ;LO Adresse nach Y
14d1 a6 c2    ldx $c2   ;HI Adressenach X
14d3 20 2e 14 jsr $142e ;aktuelle Adresse setzen
14d6 68       pla       ;neues Datenbyte wieder holen
14d7 20 79 14 jsr $1479 ;Datenbyte in aktueller
                        Adr. ablegen

14da 60       rts


;Adresse berechnen (X-Koord. in A/X; Y-Koord. in Y)
;Adresse nach $C1/$C2; Bitmaske nach A


14db c9 03    cmp #$03  ;A größer gleich 3?
14dd b0 55    bcs $1534 ;wenn ja, dann ord. zu groß
14df c9 02    cmp #$02  ;A kleiner 2?
14e1 d0 04    bne $14e7 ;wenn ja, dann X-Koord. ok
14e3 e0 80    cpx #$80  ;LO von X-Koord. zu groß?
14e5 10 4d    bpl $1534 ;wenn ja, keine
                        Adreßberechnung
14e7 c0 c8    cpy #$c8  ;Y-Koord. zu groß?
14e9 b0 49    bcs $1534 ;wenn ja, dann keine
                        Adreßberechnung
14eb 48       pha       ;HI X-Koord. retten
14ec 8a       txa       ;LO X-Koord.
14ed 48       pha       ;retten
14ee 98       tya
14ef 29 0f    and #$0f
14f1 aa       tax
14f2 bd 40 15 lda $1540,x;LO-Wert aus Tabelle 1
14f5 85 c1    sta $c1   ;ablegen
14f7 bd 50 15 lda $1550,x;HI-Wert aus Tabelle 2
14fa 85 c2    sta $c2   ;ablegen
14fc 98       tya
14fd 29 f0    and #$f0
14ff 4a       lsr
1500 4a       lsr
1501 4a       lsr
1502 4a       lsr
1503 aa       tax
1504 bd 60 15 lda $1560,x;HI-Wert aus Tabelle 3
1507 18       clc
1508 65 c2    adc $c2   ;dazuzählen
150a 85 c2    sta $c2
150c 68       pla
150d aa       tax
150e 68       pla
150f a8       tay
1510 b9 70 15 lda $1570,y;LO-Wert aus Tabelle 4
1513 18       clc
1514 65 c1    adc $c1   ;dazuzählen
1516 85 c1    sta $c1
1518 90 02    bcc $151c
151a e6 c2    inc $c2
151c 8a       txa
151d 29 f8    and #$f8
151f 4a       lsr
1520 4a       lsr
1521 4a       lsr
1522 18       clc
1523 65 c1    adc $c1
1525 85 c1    sta $c1
1527 90 02    bcc $152b
1529 e6 c2    inc $c2
152b 8a       txa
152c 29 07    and #$07
152e aa       tax
152f bd 73 15 lda $1573,x;Bitmaske aus Tabelle 5
1532 18       clc       ;holen
1533 60       rts
1534 38       sec
1535 60       rts


;Tabellen für Adreßberechnung


Tabelle 1

.: 1540 00 50 a0 f0 40 90 e0 30
.: 1548 80 d0 20 70 c0 10 60 b0

Tabelle 2

.: 1550 00 00 00 00 01 01 01 02
.: 1558 02 02 03 03 03 04 04 04

Tabelle 3

.: 1560 00 05 0a 0f 14 19 1e 23
.: 1568 28 2d 32 37 3c 41 46 00

Tabelle 4

.: 1570 00 20 40

Tabelle 5 (Bitmasken)

.: 1573 80 40 20 10 08 04 02 01
Listing 1. Assemblerprogramm zur Nutzung der verborgenen Grafikfähigkeiten des C 128
100 bank 15
110 sys dec("1400") : rem *** grafik einschalten
120 sys dec("1406") : rem *** bildschirm loeschen
130 for x = 0 to 639
140 y=sin(x/100)    : rem *** sinuswert berechnen
150 y=99*y+100      : rem *** wert an bildschirmausgabe anpassen
160 sys dec("140f"), int(x/256), x and 255, y
170 next x
180 sys dec("1403") : rem *** text einschalten
190 sys dec("1409") : rem *** zeichensatz neu laden, editor initialisieren
Listing 2. Eine Demonstration der hochauflösenden 80-Zeichen-Grafik
PDF Diesen Artikel als PDF herunterladen
Mastodon Diesen Artikel auf Mastodon teilen
← Vorheriger ArtikelNächster Artikel →