Graf Ikmodus läßt bitten
Der Displaycontroller des Portfolio (HD61830 von Hitachi) verfügt neben dem wohlbekannten Textmodus auch über einen Grafikmodus, in dem sich alle 240x64 Pixel beliebig ein- oder ausschalten lassen. Zum Umschalten des Videomodus bedienen wir uns der Funktion 0 (ah=0) des BIOS-Interrupts 10h:
mov ax,6 ; Grafikmodus
int 10h
...
mov ax,0 ; Textmodus
int 10h
Die Unterfunktion 6 (al=6) aktiviert bei CGA-und VGA-Karten den Monochrom-Grafikmodus mit einer Auflösung von 640x200 Pixeln. Man kann sich vorstellen, daß der Bildschirm des Portfolio hiervon nur die linke obere Ecke darstellt. Manche andere Grafikmodi (z.B. al=0Ah) sind ebenfalls verwendbar, wobei aber beim Portfolio keine Unterschiede existieren. Zurück in den Textmodus gelangt man beispielsweise durch Aktivieren des Modus 0 ("BW40"). Auch hierzu gibt es gleichwertige Alternativen, wie z.B. den Modus 3 ("CO80").
Auf den Punkt gebracht
Wenn der Grafikmodus aktiviert wurde, stehen die Funktionen 0Ch und
0Dh von Int 10h zur Verfügung, um den Zustand einzelner Bildpunkte
zu setzen bzw. auszulesen. Die gewünschten Koordinaten müssen
sich in den Registern cx und dx befinden und beziehen sich auf die linke
obere Bildschirmecke als Ursprung. Das Register al enthält die "Farbe"
des Pixels, wobei (nur) der Wert 0 dem abgeschaltetem Zustand entspricht.
Werte ab 128 bewirken beim Pixel-Setzen übrigens eine Invertierung.
Das folgende Beispiel aktiviert zunächst das Pixel bei (x=100;y=50),
löscht es sofort wieder, und liest schließlich seine Farbe aus,
was zu al=0 führen muß:
mov cx,100 ; x = 0..239
mov dx,50 ; y = 0..63
mov ax,0C01h ; Pixel setzen
int 10h
dec ax
; wieder löschen
int 10h
; (ax=0C00h)
mov ah,0Dh ; Pixelfarbe
int 10h
; ermitteln
Ein photographisches Gedächtnis
Konventionelle PC-Grafikkarten besitzen traditionell einen Bildspeicher,
der dem Prozessor im Adressraum z.B. ab B800:0 (je nach Karte und Modus)
direkt zugänglich ist. Der LCD-Controller des Portfolio verfügt
dagegen über ein separates 2 KByte großes RAM ohne direkte Zugriffsmöglichkeit
durch den Prozessor. Um trotzdem die Kompatibilität mit Anwendungen
zu gewährleisten, die direkt in den Bildspeicher schreiben, bietet
der Portfolio bekanntlich in seinem System-Menü verschiedene Optionen
für den Bildaufbau. 4 KByte des installierten Hauptspeichers (beim
Standard-Pofo bleiben deshalb nur 124 KByte verfügbar) sind hierzu
16 mal hintereinander in den Speicherbereich zwischen B000:0 und C000:0
eingeblendet.
Je nach gewählter Bildaufbau-Option wird nun timergesteuert oder
bei jedem Tastendruck der dortige "Phantombildspeicher" ins LCD übertragen.
Die Größe von 4 KByte ergibt sich aus der Repräsentation
des Textbildschirms durch 80x25x2=4000 Bytes (Je Zeichen: ASCII-Code und
Attribut).
Der Textmodus einer herkömmlichen Grafikkarte wird auf diese Art
recht brauchbar emuliert. Zwar unterhält das BIOS auch im Grafikmodus
einen Bildspeicher bei B800:0, doch weicht dieser leider in seiner Organisation
vom PC-Standard ab, so daß PC-Programme, die den Videospeicher zur
Grafikdarstellung direkt ansprechen, nicht verwendbar sind. Da ein Byte
des Videospeichers 8 Pixel repräsentiert, benötigt beim Portfolio
jede Pixelzeile (240 Pixel) 30 Bytes. Alle 64 Pixelzeilen des Displays
werden lückenlos hintereinander abgelegt, so daß der gesamte
Grafikbildschirm in 1920 Bytes - der Länge einer PGF-Datei - untergebracht
wird. Dieses vom BIOS verwaltete Abbild des Grafikbildschirms bietet im
wesentlichen nur den Vorteil, daß der Zustand einzelner Pixel nicht
nur gesetzt, sondern auch ausgelesen werden kann. Der Versuch,
über entsprechende Befehle des LCD-Controllers an den eigentlichen
Bildspeicher heranzukommen, scheitert nämlich (ich lasse mich aber
gerne vom Gegenteil überzeugen).
Verkehrte Welt
Völlig unverständlich, weil ineffizient, ist die Art, auf
die jeweils 8 Pixel vom BIOS zu einem Byte zusammengefaßt werden.
Obwohl wie gesagt die Organisation des Bildspeichers inkompatibel zu allen
PC-Grafikmodi ist, ordneten die Entwickler in Anlehnung zum PC dem linken
Pixel einer Achtergruppe das höchstwertige Bit im Byte zu. Dagegen
wäre nichts einzuwenden, wenn nicht der LCD-Controller genau die umgekehrte
Bitreihenfolge verwenden würde. Darunter leidet bereits die Performance
der "Putpixel"-Funktion (0Ch) von Int 10h. Regelrecht bei der Arbeit zusehen
kann man aber der "Refresh"-Funktion (12h) von Int 61h, die den Grafikspeicher
zum LCD überträgt. Die Bitmuster der Achtergruppen werden dazu
auf bemerkenswert langsame Weise gespiegelt. Es sind deshalb diverse alternative
Routinen bekannt, die mit Leichtigkeit eine Beschleunigung um den Faktor
10 oder mehr erzielen (siehe [1] und [2]). Im Grunde kann man aber auch
sehr gut ganz auf diese Art der Refresh-Funktion verzichten, indem man
einen eigenen Bildspeicher anlegt, der eine identische Kopie des
LCD-RAMs enthält. Dieser Bildspeicher könnte sich durchaus bei
B800:0 befinden, einfacher ist es jedoch, direkt im Datensegment 1920 Bytes
zu reservieren. Abbildung 1 veranschaulicht hierzu die Organisation des
LCD-Bildspeichers.
Abbildung 1
Wenn das komplette Bild am Stück berechnet werden kann und nicht
wieder ausgelesen werden muß, kann man davon absehen, überhaupt
ein Abbild der Grafik im Arbeitsspeicher zu erzeugen und stattdessen die
Bilddaten direkt Byte für Byte dem LCD-Controller übergeben.
Wenn die Bilddaten noch nicht komplett im Speicher vorbereitet wurden,
kann zudem die ohnehin nötige Wartezeit (ca. 25 Taktzyklen beim Standard-Pofo,
getunt entsprechend mehr, siehe auch [6]) zwischen den Zugriffen auf den
LCD-Controller sinnvoll genutzt werden. Dieser schnellen Variante habe
ich in dem Spiel "FoliDash" den Vorzug gegeben.
Wie sag ich's meinem Controller?
Die Kommunikation mit dem bereits mehrfach angesprochenen LCD-Controller des Portfolio erfolgt über nur zwei Portadressen:
8010h = Datenregister
8011h = Befehlsregister
Ein Kommando wird übergeben, indem zuerst ein gültiger Kommandocode in das Befehlsregister und anschließend das zugehörige Argument ins Datenregister geschrieben wird. Für unsere Zwecke sind folgende Kommandos des HD61830 von Interesse:
8 = Bildspeicher-Startadresse Low festlegen
9 = Bildspeicher-Startadresse High festlegen
10 = Cursorposition Low festlegen
11 = Cursorposition High festlegen
12 = Byte(s) zum LCD übertragen
14 = Bit setzen
15 = Bit löschen
Die weiteren Erläuterungen und Programmbeispiele setzen voraus,
daß der LCD-Controller wie beschrieben in den Grafikmodus versetzt
wurde.
Die Kommandos 8 und 9 legen fest, ab welcher Adresse im 2048
Byte großen LCD-Video-RAM die 1920 dargestellten Bytes auszulesen
sind. Das Bild kann um bis zu 4 Pixelzeilen ohne Überlappung nach
oben verschoben werden, weswegen sich diese Technik zur Realisierung eines
pixelgenaues vertikalen Scrollings anbietet.
Im folgenden Beispiel soll der Bildschirm um 1 Pixelzeile gescrollt
werden. Obwohl die neue Startadresse nur 30 lautet, muß laut Datenblatt
auch das höherwertige Byte, also 0, geschrieben werden.
mov dx,8011h
mov al,8 ; Set
AdrLow
out dx,al
mov al,30 ; 1 Pixelzeile
dec dx
out dx,al
inc dx
mov al,9 ; Set
AddHigh
out dx,al
mov al,0
dec dx
out dx,al
Analog zur Auswahl der Startadresse des Bildspeichers geschieht über die Kommandos 10 und 11 die Festlegung der aktuellen "Cursorposition". Gemeint ist die Adresse im Video-RAM, auf die sich die Kommandos 12, 14 und 15 beziehen. Es ist zweckmäßig, ein Unterprogramm zur Cursorpositionierung etwa in der folgenden Art zu definieren:
Set_LCD_Cursor:
; Eingabe: si=Adresse
push ax
push dx
mov dx,8011h
mov al,10 ; Set CursorLow
out dx,al
mov ax,si
dec dx
out dx,al
inc dx
mov al,11 ; Set CursorHigh
out dx,al
mov al,ah
dec dx
out dx,al
pop dx
pop ax
ret
Soll ein kompletter Bildaufbau folgen, muß die Cursoradresse auf null gesetzt werden:
xor si,si ; si=0
call Set_LCD_Cursor
Dagegen adressiert das nächste Beispiel eine Pixel-Achtergruppe nahe der Bildschirmmitte (x=15*8; y=32):
mov si,15+32*30
call Set_LCD_Cursor
Das Kommando 12 leitet die Übertragung von Bilddaten ein. Im Anschluß daran dürfen beliebig viele Bytes (z.B. 1920) nacheinander ins Datenregister geschrieben werden, wobei aber, wie erwähnt, zwischen den Portzugriffen eine gewisse Wartezeit einzuhalten ist. Man bedenke hierbei, daß ein einziger Prozessorbefehl mit Speicherzugriff bereits etwa 12 Takte benötigt (wie z.B. LODSB). Wer ganz sicher gehen will, kann das Busyflag des LCD-Controllers als Bit 7 von Port 8011h auslesen. Hier zur Demonstration eine Routine, die den Bildschirm mit einem vertikalen Linienmuster füllt und dieses dann ständig invertiert.
mov ah,55h ; Bitmuster
Effekt:
xor si,si
call Set_LCD_Cursor
mov dx,8011h
mov al,12 ; Daten an-
out dx,al ; kündigen
mov cx,1920 ; Byte-Anzahl
L: in al,dx
test al,dh ; Busy?
jnz L
dec dx
mov al,ah ; Byte
out dx,al ; schreiben
inc dx
loop L
xor ah,255 ; invertieren
jmp Effekt
Das Ergebnis - ein nervöses Flimmern - beweist, daß der byteweise
Transfer von Bilddaten sehr effizient ist.
Trotzdem hat in manchen Fällen (Grafiken mit geringer durchschnittlicher
"Schwärzung") das gezielte Setzen und Löschen einzelner Pixel
seine Berechtigung. Unter Verwendung der Kommandos 14 und 15 des
LCD-Controllers läßt sich relativ einfach eine "PutPixel"-Routine
formulieren, die in ihrer Anwendung der Funktion 0Ch von Int 10h
entspricht. Die Ausführung von [7] verzichtet auf die Aktualisierung
des Grafikspeichers bei B800:0 und die damit verbundene Invertierungsmöglichkeit
(Farbe 128), erreicht dafür aber ungefähr die vierfache Geschwindigkeit
des BIOS-Pendants. Man sollte sich allerdings darüber im Klaren sein,
daß solcherart generierte Grafiken für Screen-Grabber-Programme
unsichtbar bleiben.
Zur Funktionsweise der Kommandos 14 und 15 ist anzumerken, daß
zunächst wieder die gewünschte Cursoradresse eingestellt werden
muß, und zwar nach wie vor bytebezogen. Das zum Kommando gehörige
Argument legt lediglich die Bit-Nummer innerhalb des spezifizierten
Bytes fest und darf somit Werte von 0 bis 7 annehmen.
Sofern die Startadresse des Bildspeichers nicht verändert wurde
(Scrolling!), aktiviert der nachfolgende Programmausschnitt das Pixel in
der rechten unteren Bildschirmecke:
mov si,1919
call Set_LCD_Cursor
mov dx,8011h
mov al,14 ; 14=Set, 15=Clear
out dx,al
dec dx
mov al,7 ; Bit-Nummer
out dx,al
Vorschau
So viel zum Thema Grafik auf dem Portfolio. Weitere Informationen rund
um den LCD-Controller finden sich in [3], [4] und [5]. Fragen und Anmerkungen
bitte an .
Ganz im Sinne von Multimedia geht es im nächsten Teil weiter mit
Wissenswertem über die Tonerzeugung beim Portfolio.
Referenzen:
[1] Prozedur "grefresh" der Pascal-Unit auf Gunnar
Thöles WWW-Seite
[2] Newsletter von Paul Jolliffe, Ausgabe 4
[3] Newsletter von Paul Jolliffe, Ausgabe 5
[4] Newsletter von Paul Jolliffe, Ausgabe 9
[5] Datenblatt zum HD61830: "H9CD0.PDF"
[6] Programm LCD_TEST.COM auf meiner WWW-Seite
(http://leute.server.de/peichl/pf.htm)
[7] Assembler-Routine "PutPixel" auf meiner WWW-Seite