Im Herzen des JOYCE

Teil 6: Blickkontakt - der Bildschirm

Auch wenn der Monochrom-Bildschirm des JOYCE nicht mehr so ganz dem neuesten Stand der Technik entspricht, steht er doch bei Vergleichen mit anderen Monitoren dieser Klasse gar nicht so schlecht da. Betrachtet man jedoch die softwareabhängigen Fähigkeiten - zum Beispiel die Frage, wie (un-)kompliziert es ist, Informationen auf dem Bildschirm darzustellen -, so stößt man sehr schnell auf Grenzen, die ohne detaillierte Kenntnisse der Hardware nicht überwunden werden können. Die PCW-Rechner sind eben "nur" als bessere elektronische Schreibmaschinen konzipiert worden, und erst der Wunsch des Anwenders nach mehr Funktionen läßt sie zu Computer-Ehren kommen.

Um diesem Wunsch entgegenzukommen, wollen wir uns im folgenden Beitrag etwas näher mit den Systemroutinen des Bildschirmtreibers befassen, eines Programm-Moduls, das im BIOS die Ausgabe von Informationen auf den Bildschirm steuert.
Wie schon in der vorigen Folge erwähnt, benötigt man zum Verständnis der vom Bildschirmtreiber durchgeführten Prozesse einige Kenntnisse über die Hardware des Rechners, insbesondere über Aufbau und Organisation der Speicherbänke. Da dieses Thema jedoch schon mehrfach in früheren Veröffentlichungen dieser Zeitschrift behandelt wurde, sei hier nur das Wichtigste kurz wiederholt:
Eine Speicherbank, also der Bereich, der vom Prozessor direkt adressiert werden kann (= 64 kByte), besteht bei den PCW-Rechnern aus vier Speicherblöcken zu je 16 kByte. Diese vier Blöcke werden vom BIOS des CP/M-Systemprogramms so konfiguriert, daß jeweils folgende Blöcke zusammengeschaltet sind:
Bank#0: Block 0, 1, 3, 7
Bank#1: Block 4, 5, 6, 7
Bank#2: Block 0, 8, 3, 7
Bank#3: Block 0, 9, 3, 7
...
Weitere Speicherbänke entstehen, indem ein entsprechender Speicherblock anstelle von Block #1 in die Speicherbank 0 eingeblendet wird. Der Block #7, die sogenannte Common Memory, ist in allen Bänken vorhanden. Sie dient der Bankumschaltung und dem Parametertransfer von einer Bank in die andere.
Eine Ausnahme in dieser Bankkonfiguration bildet das "Screen-Environment". Es besteht aus den Speicherblöcken 0, 1, 2 und 7. Neben dem größten Teil des Bildschirmspeichers enthält es einige Komponenten, die sehr wichtig für die Ausgabe von Informationen an den Bildschirm sind:
1. Das Roller RAM; es enthält Adreß-Vektoren, über die der Bildschirmspeicher adressiert werden kann.
2. Das Character Matrix RAM; es enthält die Matrizen für die Buchstaben und Zeichen, die auf dem Bildschirm dargestellt werden können.
Die Adreßlage dieser Komponenten zeigt die Abbildung 1, Aufbau und Organisation von Roller RAM und Character Matrix RAM können Sie der Tabelle "Aufbau von Character Matrix RAM..." entnehmen. Für die Koordinaten (Pixel beziehungsweise Zeichenposition) gilt, daß die Position 0,0 die linke obere Ecke des Bildschirms bezeichnet.
Die Aufteilung des "Screen-Environment" im JOYCE
Die Aufteilung des "Screen-Environment" im JOYCE
 
Character Matrix RAM:
Startadresse: $B800
byte 0:Zeichen 0(8 Bytes)
byte 8:Zeichen 1(8 Bytes)
byte 16:Zeichen 2(8 Bytes)
...
...
byte 2040:Zeichen 255(8 Bytes)
Jeder Zeicheneintrag hat folgendes Format:
byte 0:Pixelreihe 0(8 Bits)
byte 1:Pixelreihe 1(8 Bits)
...
...
byte 7:Pixelreihe 7(8 Bits)
Aufbau von Charakter Matrix RAM und Roller Ram
Roller RAM:
Startadresse: $B600
bytes 0, 1:(Adresse der Pixelzeile #0)/2
bytes 2,3:(Adresse der Pixelzeile #1)/2
bytes 4,5:(Adresse der Pixelzeile #2)/2
...
...
bytes 510,511(Adresse der Pixelreihe #255)/2
Jede Pixelreihe hat folgendes Format:
byte 0:Pixelspalten 0..7
byte 1:Pixelspalten 8..15
byte 2:Pixelspalten 16..23
...
...
byte 712:Pixelspalten 712..719

Den Bildschirm treiben

Da für den Zugriff auf den Bildschirmspeicher jeweils das "Screen-Environment" eingeschaltet werden muß, bietet der Bildschirmtreiber natürlich auch eine Routine an, mit der dies durchgeführt werden kann:
SCR CALL BC $1740
blendet den Speicherblock #2 in den Adreßbereich des Prozessors ein und ruft die im BC-Register übergebene Adresse auf. Nach Rückkehr aus der aufgerufenen Routine wird die ursprüngliche Speicherkonfiguration wiederhergestellt und die Kontrolle an das aufrufende Programm zurückgegeben. Für die Übergabe von Parametern stehen außer A und BC alle Register zur Verfügung.
Falls Sie den Roller RAM manipulieren möchten (oder müssen), sollten Sie sicherstellen, daß der Bildschirmspeicher während dieser Zeit nicht von der Hardware (dem Gate-Array) ausgelesen wird. Dies läßt sich verhindern, indem die Routine
SCR WAIT FLYBACK $1653
aufgerufen wird. Diese Routine wartet so lange, bis der Elektronenstrahl der Bildröhre dunkelgetastet und wieder in die Startposition gebracht wird, nachdem der Bildschirm "gezeichnet" wurde (Flyback). Während dieser Zeit findet dann kein Speicherzugriff durch das Gate-Array statt.
Für die Initialisierung des Bildschirms läßt sich folgende Funktion einsetzen:
SCR INITIALIZE $14E0
löscht den Bildschirm, initialisiert Farben und Roller RAM. Falls in Ihrem Bildschirmspeicher einmal alles durcheinandergeraten sein sollte, schafft diese Routine wieder Ordnung.
SCR STL SWAP $1525
eine Routine, speziell für die Aktivierung der Druckerstatuszeile. Diese Routine sichert den Hintergrund der unteren Bildschirmzeile (Zeile 31), indem die Vektoren des Roller RAM auf einen Puffer umgeleitet werden, in den der Druckertreiber seine Statusinformationen schreiben kann. Wird SCR STL SWAP ein zweites Mal aufgerufen, so werden die ursprünglichen Vektoren des Roller RAM wieder eingetragen und zeigen dann auf die Zeile 31 im Bildschirmspeicher.
Zum Löschen des Bildschirms beziehungsweise bestimmter Bildschirmbereiche lassen sich die folgenden Routinen aufrufen:
SCR CLR BOX $1549
löscht ein "Fenster" des Bildschirms, das durch die Koordinaten obere Zeile/linke Spalte und untere Zeile/rechte Spalte definiert wird.
SCR CLR LINE $156C
löscht eine Zeile oder einen Teil einer Zeile. Durch Übergabe der Startspalte (linke Spalte) und der Zeilenlänge lassen sich beliebig große Abschnitte einer Zeile löschen.
SCR CLR CHAR $1573
löscht ein einzelnes Zeichen, dessen Position über das DE-Register übergeben wird.
Alle CLR-Routinen löschen die definierten Bereiche, indem die entsprechend Bytes im Bildschirmspeicher auf $00 gesetzt werden.
Die Ausgabe von Zeichen auf dem Bildschirm erfolgt normalerweise durch
SCR WRITE $15AA
Diese Routine überträgt eine Zeichenmatrix aus dem Character Matrix RAM in den Bildschirmspeicher. Die Art der Übertragung wird mit
SCR SET ATTRIBUTE $15C4
festgelegt, denn die Ausgabe-Attribute des Bildschirms werden durch unterschiedliche Übertragungsfunktionen erzeugt. Für das Attribut "INVERS" wird zum Beispiel jedes Byte der Zeichenmatrix bei der Übertragung vom Character Matrix RAM in den Screen RAM invertiert. Für das Attribut "UNTERSTREICHEN" wird das letzte Byte der Zeichenmatrix auf $00 oder $FF gesetzt. SCR SET ATTRIBUTE ändert dabei, je nach gewünschtem Attribut, die Aufrufadresse der entsprechenden Übertragungsroutine.
Für die Erzeugung des Cursorblocks wird eine andere Funktion aufgerufen:
SCR CHAR INVERT $160A
invertiert das Zeichen an der im DE-Register übergebenen Position.
Für die Bewegung des Cursors wird SCR CHAR INVERT gleich zweimal benötigt:
Zuerst muß die alte Position "normalisiert" werden, und dann muß an der neuen Stelle der Cursor wieder gezeichnetwerden.
Auch das "Rollen" des Bildschirms nach oben oder nach unten läßt sich vom Anwenderprogramm aufrufen:
SCR ROLL UP $1620
rollt den Bildschirminhalt oder einen Ausschnitt davon um eine Zeile nach oben.
SCR ROLL DOWN $1653
rollt den Bildschirm oder einen Ausschnitt davon um eine Zeile nach unten. Bei beiden Routinen können sowohl der gesamte Bildschirm (32 Zeilen) als auch kleinere Zeilenbereiche "gerollt" werden. Die Größe des Bereichs wird durch die obere und untere Zeile definiert. Allerdings lassen sich nur ganze Zeilen verschieben, was bedeutet, der jeweils verschobene Bereich reicht vom linken bis zum rechten Rand des Bildschirms. Nach dem Rollen der Zeilen wird oben beziehungsweise unten (je nach Roll-Richtung) eine Leerzeile eingefügt.
Eine besondere Spezialität bietet der Bildschirmtreiber für das Auslesen des Bildschirmspeichers. Wer bisher mühsam über Zeilen-Offset und Roller-RAM-Vektoren die Adressen für den Zugriff auf den Bildschirmspeicher errechnen mußte, kann nun die Routine
SCR READ $174F
aufrufen. Sie liest eine Zeichenmatrix aus dem Bildschirmspeicher und überträgt sie in einen Puffer, der über HL adressiert wird. Die Position der Zeichenmatrix wird über das DE-Register als Zeilen-/Spalten-Koordinate übergeben.
Dennoch kann es sein, daß man um eine separate Berechnung dieser Adressen nicht herumkommt. Gerade bei Grafikroutinen, die direkt in den Bildschirmspeicher schreiben, wird es oft erforderlich, über das Roller RAM die Adresse zu berechnen. Aber auch hierfür stellt der Bildschirmtreiber eine Routine bereit:
SCR CHAR POSITION $169C
gibt die Adresse des oberen Bytes der im DE-Register definierten Zeichenposition im Bildschirmspeicher zurück. Beim Aufruf muß jedoch beachtet werden, daß diese Routine nicht automatisch das "Screen-Environment", also den Speicherblock #2, einblendet. Daher sollte diese Routine über SCR CALL BC (siehe oben) aufgerufen werden.

Im Reich der Farben

Die Farbenvielfalt bleibt den PCW-Rechnern zwar auch in der Zukunft verschlossen, denn beim JOYCE läßt sich in Sachen "Farbe" nur ein Änderung von Schwarz auf Weiß (beziehungsweise Grün) erreichen - aber dennoch sind im Bildschirmtreiber Routinen vorhanden, die anderes vermuten lassen.
Für die Berechnung der Farbe, die für die Anzeige benutzt wird, gibt es eine Farbtabelle mit jeweils zwei Einträgen für drei Farben. Dabei wird zwischen Ink #1, Ink #2 und der Border-Farbe (= Randfarbe, Ink #0) unterschieden.
Für jede "Ink" sind zwei Farbwerte vorhanden (1. Farbe / 2. Farbe), die abwechselnd miteinander verknüpft werden, um daraus ein Steuerkommando für die Umschaltung zu errechnen. Diese Berechnung erfolgt innerhalb einer Interrupt-Routine, so daß sich ein Anwenderprogramm darum nicht zu kümmern braucht. Allerdings habe ich bei meinen Experimenten festgestellt, daß all dies nicht notwendig ist, denn die Routinen scheinen nicht unbedingt nur für die Hardware des JOYCE gedacht zu sein.
Schaltet man den Interrupt nämlich ab, so läßt sich die Umschaltung der Farben direkt über die Port-Adresse $F7 durchführen. Aber, für eigene Experimente, hier die Routinen für die Farbumschaltung beim JOYCE:
SCR SET INK $16ED
legt die Werte für die Farben 0 & 1 (Ink #1 beziehungsweise Ink #2) fest. Die übergebenen Farbwerte werden in die Farbtabelle eingetragen und zur Berechnung des Umschaltkommandos benutzt.
SCR SET BORDER $16F2
Mit diesem Aufruf werden die Farbwerte für die Border-Farbe (Ink #0) festgelegt und in die Farbtabelle eingetragen. Ist Bit 6 im Farbwert gesetzt, so bedeckt die Border-Farbe den gesamten Bildschirm, kann also dazu benutzt werden, den Bildschirm dunkel- beziehungsweise hellzuschalten.
SCR SET FLASHING $1736
legt die Zeitwerte für den Wechsel zwischen Farbe 1 und Farbe 2 fest. Das bedeutet, der im HL-Register übergebene Wert bestimmt die Zeit, in der ein Farbwert für die Berechnung des Farb-Steuerkommandos zuständig ist. Hier läßt sich ein Blinken des Bildschirms erreichen, wenn die Zeitwerte entsprechend verändert und die Farbwerte unterschiedlich gesetzt werden. Die Zeitbasis für die übergebenen Werte ist 1/50 Sek.
Ein Flashing-Wert von "0" schaltet den Interrupt ab und erlaubt das direkte Umschalten der Farben über den Port $F7. Die dazu benötigten Kommandos sind in der Tabelle "Kommandos für die..." aufgeführt.
Kompletten Bildschirm dunkelschalten:$00
Hintergrund schwarz/Schriftfarbe hell:$40
Kompletten Bildschirm hellschalten:$80
Hintergrund hell/Schriftfarbe Schwarz:$C0
Nach dem Abschalten des Farbwechsel-Interrupts kann die Umschaltung folgendermaßen durchgeführt werden:
InAssembler:LD A,080H;Steuerkommando -> A
 OUT (0F7H),A;und ausgeben
In BASIC:OUT &HF7,&H80 
In Turbo Pascal 3.0:PORT[$F7]:=$80; 
Kommandos für die Umschaltung der Bildschirmfarben
Wenn Sie genug mit "Farbe" und "Flashing" experimentiert haben und wieder alles auf "normal" setzen möchten, so sollte die Routine
Farbtabellen initialisieren $16CE
aufgerufen werden. Damit wird dann alles wieder auf Default-Werte gesetzt und der Interrupt wieder eingeschaltet.
So, damit können wir das Thema Bildschirmtreiber abschließen. Die Tabelle "Die Systemroutinen..." faßt noch einmal alle hier erläuterten Routinen zusammen, die Listings zeigen anhand von Beispielen, wie Sie die neu gewonnenen Erkenntnisse über die Systemroutinen in den verschiedenen Programmiersprachen anwenden können. Da die Listings ausführlich kommentiert sind, kann an dieser Stelle auf weitere Erklärungen verzichtet werden.
Die System-Routinen des Bildschirmtreibers

Hinweis:
1. Alle im Beitrag aufgeführten und verwendeten Adressen beziehen sich auf die BlOS-Version 1.4 (CP/M-Systemdatei J14GCPM3.EMS).
2. In Ermangelung offizieller Dokumentation wurden die Bezeichnungen (Namen) der beschriebenen Routinen, in Anlehnung an die ROM-Routinen der CPC-Computer, vom Autor "erfunden".
3. Da sämtliche hier erläuterten Routinen in der Systemspeicherbank (Bank #0) liegen, muß vor dem Aufruf die Speicherbank umgeschaltet werden. Dazu läßt sich die BIOS-Funktion #30 (USERF) verwenden, die mit
CALL 0FC5AH
DEFW routine

aufgerufen werden kann. "routine" bezeichnet dabei die Adresse der gewünschten Systemroutine in der Speicherbank #0.
Alle Standardregister werden bei der Umschaltung der Speicherbänke gesichert, so daß sie für die Übergabe von Parametern verwendet werden können.

(Norbert Finke/rs)

Die Listings zu diesem Teil:
[5. Teil] [Inhalt]

Eingescanned von Werner Cirsovius
März 2003
© DMV Verlag