
                             VGA-Kurs - Part #5

Herzlich Willkommen zu 'T.C.P.'s Beginners Guide To VGA Coding', Part V.
In diesem Teil werden wir den in der Demo-Scene am meisten genutzten
Videomodus besprechen, den Mode-X.
Der Mode-X ist, wie viele sicherlich vermuten, kein Standardmodus der VGA.
Trotzdem ist er eigentlich ein sehr gewhnlicher Videomodus, der wie die
Hires-Modi aufgebaut ist, d.h. er wird nicht direkt, wie beim Modus 13h,
sondern ber Planes adressiert.
Eigentlich ist der Modus 13h ja auch eine Abart des Mode-X, und nicht
umgekehrt. Die Entwickler der VGA-Karte wollten den Modus 13h mglichst einfach
programmierbar machen. Bei den Vorgngern der VGA wurde der Bildschirmspeicher
immer in Verbindung mit Planes angesprochen. Diese sind im Modus 13h durch
Abschalten des sog. Chain-4-Modus nicht aktiv, und man kann den
Bildschirmspeicher direkt ansprechen.
Eine Plane ist sozusagen ein Bildschirm. Im EGA-Modus z.B. mu man, um ein Pixel
zu setzen, erst die Plane bestimmen, auf die der Punkt gesetzt werden soll, um
anschlieend den Punkt an die gewnschten Koordinaten zu setzen. Dies erscheint
komplizierter als beim Modus 13h, jedoch stt man, wenn man nur in diesem
Modus 13h programmiert, sehr bald auf Grenzen. Ein Beispiel:
Der PC wurde im Laufe der Jahre immer weiter entwickelt, genau wie die
VGA-Karten. Schneller, mehr Speicher, hhere Auflsungen. Doch trotz aller
Rechenpower gibt es eine Einschrnkung: Bei manchen, sehr rechenaufwendigen
VGA-Operationen, schafft es die Hardware einfach nicht, den Bildschirm
innerhalb eines Strahlrcklaufes (Retrace) vollstndig darzustellen.
Bewegt sich nun zuflligerweise genau in dem Zeitpunkt, in dem das Bild
aufgebaut wird, der Kathodenstrahl ber den Schirm, hat das fatale Folgen.
In der oberen Hlfte des Bildschirms, die schon neu aufgebaut wurde, ist das
nchste Frame zu sehen, in der unteren Hlfte noch das alte. Man mu also vor
der Bildschirmoperation abwarten, bis der Retrace vorber ist, und dann das
Bild aufbauen. Was aber, wenn whrend der kurzen Zeit, in der kein Neuaufbau
des Bildes erfolgt, nicht genug Zeit ist, um z.B. ein komplexes Vektorgebilde
darzustellen?
Jetzt mu eine zweite Bildschirmseite her, auf der man das Bild berechnen, und
dann -schwupp- nach dem Bildschirmaufbau auf den Screen kopieren kann.
Woher aber bekommt man zustzliche Seiten (Pages)? In der letzen Ausgabe haben
wir uns mit dem Virtual Screen beschftigt, einer simulierten zweiten Page,
die in den Arbeitsspeicher eingeblendet wird.
Diese Methode erfllt zwar ihren Zweck, jedoch hat sie einige gravierende
Nachteile:
1. Ein solcher Virtual Screen beansprucht 64000 Byte Arbeitsspeicher. Das ist
an sich nicht so viel, in einigen Sonderfllen bentigt man allerdings
mehr als einen VS, und dann kann's schon mal eng werden.
2. Um den Inhalt des VS auf den Screen zu kopieren, mssen 64000 Byte
verschoben werden, was einige Rechenzeit beansprucht, und auf langsameren
Rechnern zu Problemen fhren kann.
Diese Nachteile haben wir beim Mode-X nicht, hier werden ganze 4 Pages in die
64 KB des Bildschirmspeichers gepackt, d.h. es wird kein zustzlicher
Arbeitsspeicher bentigt und, das wichtigste, eine Page ist nur 16000 Byte
gro, es wird also nur ein Viertel der Rechenzeit bentigt, die beim VS ntig
wre.
Aber wie in aller Welt soll das funktionieren?
Ganz einfach, wie zu Beginn gesagt, mu beim Mode-X erst die Plane bestimmt
werden, auf die geschrieben/von der gelesen werden soll. Also steht ein Byte
im Bildschirmspeicher fr vier Pixel auf dem Screen. Die ersten 16000 Byte
stehen demnach fr die 64000 Pixel der ersten Page, die Bytes 16000 bis
31999 fr die der zweiten Page, usw.
Ach so, pat auf, da ihr die Begriffe 'Page' und 'Plane' nicht durcheinander
bringt, das sind zwei grundverschiedene Dinge.
Man kann sich die Planes wie vier bereinandergelegte Ebenen vorstellen. Sucht
man sich einen Punkt auf der ersten Ebene, hat man noch 3 auf den anderen
Ebenen, die die gleichen Koordinaten haben. Will man den Punkt also genau
definieren, mu auch die Plane dazu angegeben werden.
Auf der ersten Plane liegen alle Punkte, die durch 4 dividiert den Rest 0
ergeben, auf der zweiten die, die den Rest 1 ergeben, usw.
Ein Beispiel: Will man das dritte Pixel der ersten Page lesen, selektiert man
zuerst Plane 3 (3 div 4 = 0, Rest 3), und liest nun das erste Byte des
Bildschirmspeichers.
Soll stattdessen das 23456. Pixel der dritten Page beschrieben werden, wird es
etwas komplizierter, hier bentigt man konkrete Formeln zur Bestimmung des
Offsets und der Plane, hier kommen sie:
Offset = (Y-Koord * 320 + X-Koord) div 4
Dies knnen wir umformen zu:
Offset = Y-Koord * 80 + X-Koord div 4,
wobei wir das 'div 4' durch ein schnelleres 'shr 2' ersetzen knnen.
Die Formel zur Bestimmung der Plane:
Plane = X-Koord mod 4,
also der Rest der Division X-Koord div 4.
Im Modus 13h wird diese Berechnung automatisch vorgenommen, um den einfachen
Zugriff zu ermglichen.
Doch nun zuerst zum Wichtigsten, dem Setzen des Mode-X. Dazu mssen zuerst der
Chain-4-Mechanismus und der Odd/Even-Mode, d.h. die Plane Selektion per
unterstem Offset-Bit, wie es im Modus 13h blich ist, abgeschaltet werden.
Man beschreibt das TS-Register 4, und zwar setzt man Bit 2 und lscht Bit 3.
Da es natrlich unter den Grafikkartenherstellern wie so oft schwarze Schafe
gibt, deren Karten nicht ganz 100% kompatibel sind (Dankeschn!), sollte der
Speicherzugriff auf Byte-Adressierung umgeschaltet werden.
Hierzu lscht man im CRTC-Register 14h das Bit 6 und setzt es im CRTC 17h.

procedure SetModeX;assembler;
asm
  mov     ax,13h        { Zuerst den normalen Modus 13h per BIOS aktivieren }
  int     10h
  mov     dx,3C4h
  mov     al,4                         { TS-Register 4 anwhlen }
  out     dx,al
  inc     dx
  in      al,dx                        { Aktuellen Registerinhalt holen }
  and     al,0F7h                      { Bit 2 setzen, Bit 3 lschen }
  or      al,4
  out     dx,al                        { Werte zurckschreiben }
  dec     dx
  mov     ax,0F02h
  out     dx,ax                        { Alle Planes selektieren }
  mov     ax,0A000h                    { VGA-Segment nach ES }
  mov     es,ax
  xor     di,di
  xor     ax,ax
  mov     cx,0FFFFh
  cld
  rep     stosw                        { Bildschirm lschen }
  mov     dx,3D4h
  mov     al,14h                       { CRTC-Register 14h anwhlen }
  out     dx,al
  inc     dx
  in      al,dx                        { Aktuellen Registerinhalt holen }
  and     al,0BFh                      { Bit 6 lschen }
  out     dx,al
  dec     dx
  mov     al,17h                       { CRTC-Register 17h anwhlen }
  out     dx,al
  inc     dx
  in      al,dx
  or      al,40h                       { Bit 6 setzen }
  out     dx,al
end;

Ziemlich langer Code nur fr das Setzen eines Videomodus, aber es lohnt sich.
Wie das Setzen erfolgt, knnt ihr den Kommentaren entnehmen.
Nachdem wir also diese Prozedur aufgerufen haben, haben wir also einen schnen
Mode-X Screen vor uns. Toll. Freu. Jubel. Aber was nun?
Natrlich, wie wr's, wenn wir munter und unverfroren ein paar Pixel auf den
Screen knallen wrden?

procedure XPutPixel(x,y:integer;col:byte);assembler;
asm
  mov     ax,0A000h                    { VGA-Segment nach ES }
  mov     es,ax
  mov     cx,x                         { X-Koord nach CX }
  and     cx,3                         { Plane bestimmen }
  mov     ax,1
  shl     ax,cl                        { Entsprechendes Bit setzen }
  mov     ah,al
  mov     dx,03C4h                     
  mov     al,2                         { Timing-Sequencer Reg. 2 anwhlen }
  out     dx,ax                        { Plane setzen }
  mov     ax,80                        { Pixel-Offset bestimmen }
  mul     y                            { Offset = y * 80 + x div 4 }
  mov     di,ax                        { y * 80 nach DI }
  mov     ax,x
  shr     ax,2                         { entspricht AX div 4 }
  add     di,ax                        { x div 4 dazuzhlen }
  mov     al,col                       { Farbe nach AL }
  mov     es:[di],al                   { Pixel setzen }
end;

Die Prozedur selektiert automatisch die richtige Plane und setzt das Pixel.
Logisch, da die Adressierung des Pixels mehr Zeit braucht als im Modus 13h,
diese Geschwindigkeitsnachteile werden aber durch die oben erwhnten 4 Pages
wieder wettgemacht.
Wollen wir nun auf die zweite Page ein Pixel setzen, mssen wir nur beim
Aufrufen der XPutPixel-Prozedur 200 auf den Y-Wert aufaddieren.
Wenn wir auf eine andere Page umschalten wollen, so da diese auf dem
Bildschirm sichtbar wird, brauchen wir diese Prozedur:

procedure XSetStart(Adr:word);assembler;
asm
  mov     dx,3D4h
  mov     al,0Ch                       { CRTC-Register 0Ch }
  mov     ah,byte ptr Adr + 1          { Bits 15-8 abschicken }
  out     dx,ax
  mov     al,0Dh                       { CRTC-Register 0Dh }
  mov     ah,byte ptr Adr              { Bits 7-0 abschicken }
  out     dx,ax
end;

Diese Prozedur kann brigens in smtlichen Videomodi angewendet werden,
allerdings macht es nur Sinn, wenn man mehrere Bildschirmseiten hat, wie z.B.
im Modus 640x480x16 oder auch im Textmodus.
Will man also auf Page 2 umschalten, geben wir als Parameter 16000 an, bei
Page 3 32000 und bei Page 4 48000. Mit dem Parameter 0 gelangen wir wieder
zurck auf die erste Page. Natrlich kann man auch einen Wert wie 8000 angeben,
um die zweite Hlfte der ersten und die erste Hlfte der zweiten Page auf dem
Bildschirm zu haben. Merkt ihr was? Diese Prozedur lt sich sehr elegant fr
ein Scrolling einsetzen, indem man den Wert einfach stufenweise erhht.
Das hatten wir doch schon im vorletzten Teil! Eine Ergnzung dieser Technik
findet ihr weiter unten.
Am Anfang war die Rede davon, da man fr eine saubere Spritedarstellung oder
hnliches sorgen kann, indem man das Bild unsichtbar auf der zweiten Page
berechnet und dann auf den Screen kopiert. Der Code dazu:

procedure CopyPage(ziel,quelle:word);assembler;
asm
  push    ds                           { DS wird verndert, also sichern }
  mov     dx,3C4h
  mov     ax,0F02h
  out     dx,ax
  mov     dx,3CEh                      { Write Mode 1 }
  mov     ax,4105h
  out     dx,ax
  mov     ax,0A000h             { VGA-Segment nach DS (Quelle) und ES (Ziel) }
  mov     ds,ax
  mov     es,ax
  mov     si,quelle                    { Quelloffset nach SI }
  mov     di,ziel                      { Zieloffset nach DI }
  mov     cx,16000                     { 16000 Bytes kopieren }
  rep     movsb
  mov     dx,3CEh
  mov     ax,4005h
  out     dx,ax
  pop     ds                           { DS wiederherstellen }
end;

Als Parameter werden wieder die Offsets der Pages bergeben.
Wenn man also das Hintergrundbild auf Page 2 hat, mte eine Repeat-Schleife
zur Darstellung von Sprites auf einem Hintergrund ungefhr so aussehen:

repeat
  WaitRetrace;
  CopyPage(0,16000);
  DrawTheSprites;
until Bedingung = true;

Dabei mu man sich keine Gedanken darber machen, da man den Hintergrund
wiederherstellen mu, da er im nchsten Schleifendurchlauf sowieso neu kopiert
und damit der alte berschrieben wird. Und da nur 16000 Byte kopiert werden
mssen, geht das Ganze auch relativ schnell.
Das war der wichtigste Grund fr die Benutzung des Mode-X. Aber er hat noch
andere Vorzge, die der Modus 13h nicht bietet.
Der Modus 13h hat standardmig die vertikale Auflsung von 200 Pixeln. Das
ist wohl jedem bekannt. Das Sonderbare ist nur, da die VGA eine vertikale
Auflsung von 200 gar nicht untersttzt. Es gibt nur die Mglichkeiten 350, 400
oder 480. Aber wie kommt der Modus 13h dann zustande? Tja, die schlauen
IBM-Leute haben sich das so berlegt: Da 320x400 (=128000) Byte nicht in den
Bildschirmspeicher passen, lie man sich das sog. Double-Scan-Verfahren
einfallen. Es wird durch das Setzen der Bits 0-3 und 7 des CRTC-Reg. 9 aktiviert.
Es tut nichts anderes, als jede Zeile einfach 2 mal darzustellen. Dadurch
kommen wir auf die Auflsung von 200 Zeilen. Aber was hilft uns das jetzt?
Ganz einfach, wenn das Register gesetzt wird, um die Zeilenanzahl zu halbieren,
brauchen wir die Bits nur wieder zu deaktivieren, um wieder 400 Zeilen zu
erhalten. Denn im Mode-X passen die 320x400 Pixel ohne weiteres 2 mal in den
Bildschirmspeicher, auch wenn wir dann auf 2 Pages verzichten mssen.

procedure XEnter400;assembler;
asm
  mov     dx,3D4h
  mov     al,9                         { CRTC-Register 9 anwhlen }
  out     dx,al
  inc     dx
  in      al,dx                        { Aktuellen Registerinhalt holen }
  and     al,70h                       { Bits 0-3 und 7 lschen }
  out     dx,al
end;

Durch die verschiedensten Registermanipulationen lassen sich alle mglichen
Auflsungen erreichen, das geht von 256x200 ber 376x282 bis 512x480, wobei wir
diese ca. 20 verschiedenen Auflsungen hier nicht besprechen wollen, das wrde
nun wirklich zu weit fhren. Wer sich fr diese VGA-Spielereien interessiert,
soll sich eine ausfhrliche VGA-Referenz kaufen oder eine der zahlreichen guten
Mode-X Dokumentationen holen, die durch viele Mailboxen geistern.
Kommen wir zu einem weiteren Punkt, wegen dem der Mode-X hufig und gerne
eingesetzt wird: Full-Screen-Scrolling.
Dies ist im Mode-X dank der 4 Pages wirklich kinderleicht zu programmieren.
Es ist ein Scrolling in alle denkbaren Richtungen mglich. Dabei gibt es
allerdings zwei verschiedene Sichtweisen. Aus der Sicht des Programmierers
wird der Screen wie ein Ausschnitt ber den Hintergrund bewegt. Aus der Sicht
des Users wird der Hintergrund unter dem Screen bewegt. Dies erscheint
belanglos, es gibt aber einen gravierenden Unterschied zwischen diesen beiden
Sichtweisen, der leicht Verwirrung stiften kann. Ein Beispiel: Ein Scrolly
scrollt zur besseren Lesbarkeit von unten nach oben. Doch der Programmierer
bewegt dabei den Screen von oben nach unten, d.h. die Startadresse des Screens
mu erhht werden, um das Bild von unten nach oben wandern zu lassen.
Will man allerdings nicht nur nach oben oder nach unten, sondern in alle
Richtungen scrollen, z.B. in einem Actionspiel o.., mu der Bildschirm erst
so vorbereitet werden, als ob die vier Pages ein Quadrat bilden wrden. Dies
erreichen wir durch das Aktivieren des 160-Byte-Modus. Nach dem Aufrufen der
folgenden Prozedur ist der VGA-Speicher wie ein 640x400-Screen aufgebaut, d.h.
wir knnen den Ausschnitt des Bildschirms (320x200) beliebig ber diesen groen
Bildschirm bewegen. Dazu wird das CRTC-Register 13h mit dem Wert 80 beschrieben
(der normale Wert ist 40), um die virtuelle horizontale Auflsung zu verdoppeln.
Wie dies genau funktioniert, will ich jetzt nicht erklren, das wrde zu weit
fhren. Wichtig ist nur, da es funktioniert.

procedure XDouble;assembler;
asm
  mov     dx,3D4h                      { CRTC-Register 13h anwhlen }
  mov     ax,5013h                     { auf 80 setzen (doppelte Breite) }
  out     dx,ax
end;

Um das Prinzip des Scrollings in verschiedene Richtungen zu verdeutlichen,
folgt nun zum Schlu noch einmal ein Beispiel-Programm, das den kompletten
virtuellen 640x400-Screen mit Pixeln vollschreibt und anschlieend scrollt.

uses crt;
var x,y,dirx,diry : word;
{ Hier die Prozeduren SetModeX, WaitRetrace, XSetStart, XDouble und
  XPutPixel einfgen }
begin
  setmodex;                            { Mode-X setzen }
  xdouble;                             { 160-Byte Modus einschalten }
  for x := 0 to 319 do                 { Alle 4 Pages mit Mll fllen }
    for y := 0 to 799 do xputpixel(x,y,random(256));
  x := 1;                              { Startposition }
  y := 160;
  dirx := 1;
  diry := 160;
  repeat
    inc(x,dirx);                       { Bildausschnitt weiterbewegen }
    inc(y,diry);
    delay(10);
    WaitRetrace;
    XSetStart(y+x);                     { Neue Startadresse setzen }
    if (x = 80) or (x = 1) then dirx := -dirx;
    if (y = 32000) or (y = 160) then diry := -diry;
    { Richtung umkehren, wenn am Rand angekommen }
  until keypressed;
  readkey;
  asm mov ax,3; int 10 end;
end.

Wer einen Loader fr 640x400-Bilder hat, kann ja statt dem Pixelmll mal ein
schnes Hintergrundbild laden.

Weitere empfehlenswerte Mode-X Kurse:
'Assembler-Kurs - Der Mode X' von Midnight/Knockout, Deutsch (in Blackmail #10)
'Introduction to Mode X' von Robert Schmidt, Englisch (in PCGPE 1.0)
Auerdem die Mode-X-Libraries von Draeden/VLA und Matt Pritchard.

OK, Freunde, das war's fr diesmal, fr mich war es der am schwersten zu
erklrende Teil bis jetzt, ich hoffe, ihr habt alles kapiert, wenn nicht, lest
es euch noch mal durch, es ist wirklich nicht leicht. Wenn ihr es dann immer
noch nicht versteht, liegt es wohl an meinen Formulierungen. In dem Fall knnt
ihr euch aber noch eine andere Referenz zu dem Thema aus den obengenannten
Quellen ziehen.
Im nchsten Teil geht's um ein Thema, ber das ich schon viele Fragen aber
wenig Antworten gelesen habe: Wie stelle ich eigentlich ein Bild dar, das ich
gezeichnet habe?
Wir werden Loader fr PCX und RAW-Grafiken besprechen und vielleicht werde ich
noch einiges ber Bilddatenkompression schreiben.
Das wird zwar fr die VGA-Freaks da drauen ein ziemlich langweiliger Artikel,
aber ich bin sicher, da sich einige Anfnger, die sich die Zhne an diesem
Problem ausgebissen haben, gern einen Artikel darber htten. Falls ich da
vllig falsch liege, knnt ihr's mir ja sagen. Bis dann!





[ This text copyright (c) 1995-96 Johannes Spohr. All rights reserved. ]
[ Distributed exclusively through PC-Heimwerker, Verlag Thomas Eberle. ]
[                                                                      ]
[ No  part   of  this   document  may  be   reproduced,   transmitted, ]
[ transcribed,  stored in a  retrieval system,  or translated into any ]
[ human or computer language, in any form or by any means; electronic, ]
[ mechanical,  magnetic,  optical,   chemical,  manual  or  otherwise, ]
[ without the expressed written permission of the author.              ]
[                                                                      ]
[ The information  contained in this text  is believed  to be correct. ]
[ The text is subject to change  without notice and does not represent ]
[ a commitment on the part of the author.                              ]
[ The author does not make a  warranty of any kind with regard to this ]
[ material, including,  but not limited to,  the implied warranties of ]
[ merchantability  and fitness  for a particular  purpose.  The author ]
[ shall not be liable for errors contained herein or for incidental or ]
[ consequential damages in connection with the furnishing, performance ]
[ or use of this material.                                             ]
