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 gew”hnlicher 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 m”glichst einfach programmierbar machen. Bei den Vorg„ngern 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 anschlieáend den Punkt an die gewnschten Koordinaten zu setzen. Dies erscheint komplizierter als beim Modus 13h, jedoch st”át 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, h”here Aufl”sungen. Doch trotz aller Rechenpower gibt es eine Einschr„nkung: Bei manchen, sehr rechenaufwendigen VGA-Operationen, schafft es die Hardware einfach nicht, den Bildschirm innerhalb eines Strahlrcklaufes (Retrace) vollst„ndig darzustellen. Bewegt sich nun zuf„lligerweise genau in dem Zeitpunkt, in dem das Bild aufgebaut wird, der Kathodenstrahl ber den Schirm, hat das fatale Folgen. In der oberen H„lfte des Bildschirms, die schon neu aufgebaut wurde, ist das n„chste Frame zu sehen, in der unteren H„lfte noch das alte. Man muá also vor der Bildschirmoperation abwarten, bis der Retrace vorber ist, und dann das Bild aufbauen. Was aber, wenn w„hrend 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 zus„tzliche Seiten (Pages)? In der letzen Ausgabe haben wir uns mit dem Virtual Screen besch„ftigt, 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 Sonderf„llen ben”tigt 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 zus„tzlicher Arbeitsspeicher ben”tigt und, das wichtigste, eine Page ist nur 16000 Byte groá, es wird also nur ein Viertel der Rechenzeit ben”tigt, die beim VS n”tig w„re. 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, paát 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 ben”tigt man konkrete Formeln zur Bestimmung des Offsets und der Plane, hier kommen sie: Offset = (Y-Koord * 320 + X-Koord) div 4 Dies k”nnen wir umformen zu: Offset = Y-Koord * 80 + X-Koord div 4, wobei wir das 'div 4' durch ein schnelleres 'shr 2' ersetzen k”nnen. 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 erm”glichen. 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 l”scht Bit 3. Da es natrlich unter den Grafikkartenherstellern wie so oft schwarze Schafe gibt, deren Karten nicht ganz 100% kompatibel sind (Dankesch”””n!), sollte der Speicherzugriff auf Byte-Adressierung umgeschaltet werden. Hierzu l”scht 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 anw„hlen } out dx,al inc dx in al,dx { Aktuellen Registerinhalt holen } and al,0F7h { Bit 2 setzen, Bit 3 l”schen } 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 l”schen } mov dx,3D4h mov al,14h { CRTC-Register 14h anw„hlen } out dx,al inc dx in al,dx { Aktuellen Registerinhalt holen } and al,0BFh { Bit 6 l”schen } out dx,al dec dx mov al,17h { CRTC-Register 17h anw„hlen } 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, k”nnt ihr den Kommentaren entnehmen. Nachdem wir also diese Prozedur aufgerufen haben, haben wir also einen sch”nen Mode-X Screen vor uns. Toll. Freu. Jubel. Aber was nun? Natrlich, wie w„r'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 anw„hlen } 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 dazuz„hlen } 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 erw„hnten 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 s„mtlichen 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 H„lfte der ersten und die erste H„lfte der zweiten Page auf dem Bildschirm zu haben. Merkt ihr was? Diese Prozedur l„át sich sehr elegant fr ein Scrolling einsetzen, indem man den Wert einfach stufenweise erh”ht. Das hatten wir doch schon im vorletzten Teil! Eine Erg„nzung 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 ver„ndert, 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, máte eine Repeat-Schleife zur Darstellung von Sprites auf einem Hintergrund ungef„hr 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 n„chsten 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 standardm„áig die vertikale Aufl”sung von 200 Pixeln. Das ist wohl jedem bekannt. Das Sonderbare ist nur, daá die VGA eine vertikale Aufl”sung von 200 gar nicht untersttzt. Es gibt nur die M”glichkeiten 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 Aufl”sung 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 anw„hlen } out dx,al inc dx in al,dx { Aktuellen Registerinhalt holen } and al,70h { Bits 0-3 und 7 l”schen } out dx,al end; Durch die verschiedensten Registermanipulationen lassen sich alle m”glichen Aufl”sungen erreichen, das geht von 256x200 ber 376x282 bis 512x480, wobei wir diese ca. 20 verschiedenen Aufl”sungen 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 h„ufig 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 m”glich. 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á erh”ht 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 k”nnen den Ausschnitt des Bildschirms (320x200) beliebig ber diesen groáen Bildschirm bewegen. Dazu wird das CRTC-Register 13h mit dem Wert 80 beschrieben (der normale Wert ist 40), um die virtuelle horizontale Aufl”sung zu verdoppeln. Wie dies genau funktioniert, will ich jetzt nicht erkl„ren, das wrde zu weit fhren. Wichtig ist nur, daá es funktioniert. procedure XDouble;assembler; asm mov dx,3D4h { CRTC-Register 13h anw„hlen } 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 anschlieáend 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 sch”nes 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) Auáerdem 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 erkl„rende 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 k”nnt ihr euch aber noch eine andere Referenz zu dem Thema aus den obengenannten Quellen ziehen. Im n„chsten 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 drauáen ein ziemlich langweiliger Artikel, aber ich bin sicher, daá sich einige Anf„nger, die sich die Z„hne an diesem Problem ausgebissen haben, gern einen Artikel darber h„tten. Falls ich da v”llig falsch liege, k”nnt 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. ]