QuickBASIC als auch GWBASIC.EXE besitzen für Grafik den
SCREEN
-Befehl, um den Grafik-Videomodus setzen zu können.
Dabei geben Sie eine Nummer an, um die gewünschte Auflösung
wählen zu können. Leider :-( ist die
Liste der verfügbaren Grafikmodi sehr beschränkt. Somit bietet Ihnen
GWBASIC.EXE lediglich 640×350 Bildpunkte bei 16 aus 64
Farben als beste Auflösung an, was dem technischen Stand von 1985
entspricht, als IBM den Enhanced Graphics Adapter (EGA) mit
256 KB Videospeicher vorstellte. Und QuickBASIC beschränkt sich von Haus
aus ebenfalls auf Standard-VGA, so dass 320×200 Bildpunkte bei 256 Farben
sowie 640×480 Pixel bei 16 Farben aus jeweils 262'144 Farben das
Höchste der Gefüge darstellen.
Mittlerweile besitzen aber die meisten PC für Windows 95/98/NT Grafikkarten, welche viel bessere Auflösungen und Farbtiefen erlauben. In diesem Artikel soll gezeigt werden, wie Sie dieses Potential aus QuickBASIC heraus nutzen können.
Microsoft BASIC leidet wie die meisten alten Programme an einem fehlenden Treiberkonzept, weil so etwas wie eine SCRMODES.INI-Datei mit Einträgen wie
1: XRes=320 YRes=200 ColorDpt=2 Pages=1 Drv=CGA.DRV 2: XRes=640 YRes=200 ColorDpt=1 Pages=1 Drv=CGA.DRV 3: XRes=720 YRes=348 ColorDpt=1 Pages=2 Drv=HERCULES.DRV 4: XRes=640 YRes=400 ColorDpt=4 Pages=1 Drv=OLIVETTI.DRV 7: XRes=320 YRes=200 ColorDpt=4 Pages=8 Drv=EGA.DRV 8: XRes=640 YRes=200 ColorDpt=4 Pages=4 Drv=EGA.DRV 9: XRes=640 YRes=350 ColorDpt=4 Pages=2 Drv=EGA.DRV 10: XRes=640 YRes=350 ColorDpt=2 Pages=2 Drv=EGA.DRV 11: XRes=640 YRes=480 ColorDpt=4 Pages=1 Drv=VGA.DRV 12: XRes=640 YRes=480 ColorDpt=4 Pages=1 Drv=VGA.DRV 13: XRes=320 YRes=200 ColorDpt=8 Pages=1 Drv=VGA.DRV
komplett fehlt. Stattdessen sind die Grafikroutinen fest im Maschinencode von GWBASIC.EXE bzw. QBASIC.EXE bzw. BRUN45.EXE eincodiert, auch als hard coded bezeichnet.
Um die Bedeutung der VESA-Schnittstelle besser zu verstehen, ist es von Vorteil, einen kurzen Blick in die Entstehungsgeschichte vom IBM-PC zu werfen.
Im Jahre 1981 stellte IBM seine erste PC-Generation mit Intel x86-Prozessor
vor. Diese ersten Geräte waren mit einem Monochrome
Display Adapter (MDA) als Grafikkarte ausgestattet. Etwas später
folgte dann der IBM-XT, bei welchem die Kunden bei der Grafikkarte zwischen
Hercules Graphics Card (HGC => SCREEN 3
,
720×348 Pixel schwarzweiss) und Color Graphics Adapter
(CGA => SCREEN 1
mit 320×200/4 Farben und SCREEN
2
mit 640×200/2 Farben) auswählen konnten. Zugleich hat IBM
Konkurrenz von Compaq und
Amstrad bekommen, so dass die bekannten PC-Clones erstmals aufgetaucht
waren. Von IBM folgte 1985 der IBM AT mit Intel 80286-Prozessor und Enhanced Graphics Adapter (EGA => SCREEN 7
mit
320×200/16 Farben, SCREEN 8
mit 640×200/16 Farben,
SCREEN 9
mit 640×350/16 Farben und SCREEN 10
mit 640×350/4 Monochrom-Farben). 1987 führte IBM mit der
PS/2-Rechnerserie den Video Graphics Array (VGA =>
SCREEN 11
mit 640×480/2 Farben, SCREEN 12
mit
640×480/16 Farben und SCREEN 13
mit 320×200/256
Farben) der Öffentlichkeit vor.
Bis Ende der 80er-Jahre hatte IBM immer den Ton angegeben, was die Einführung von neuen technischen Standards in der PC-Welt betrifft, die Konkurrenz hat also immer nur voll kompatible Hardware nachgebaut. Danach hatte jedoch IBM diese führende Position bei der Schaffung solcher Industrie-Standards verloren, denn der erste 80386er-PC wurde von der Konkurrenz gebaut. Gleichzeitig überboten sich die Hersteller von Grafikkarten laufend, in dem jeder Karten mit noch besseren Grafikauflösungen entwickelte. Leider geschah dies total unkoordiniert, so dass ein riesiges Chaos an »Standards« im PC-Markt herrschte. Diese unbefriedigende Situation machte es den Software-Entwicklern natürlich unmöglich, aus jeder Grafikkarte die volle Leistung auszuschöpfen. Da all diese SuperVGA-Karten immerhin voll kompatibel zu Standard-VGA von IBM sind, beschränkten sich die meisten Software-Hersteller auf die maximale Unterstützung von Standard-VGA, darunter auch QuickBASIC.
Die grosse Rettung aus diesem Durcheinander erfolgte durch einen Zusammenschluss der Grafikkarten-Hersteller zur Video Electronics Standards Association (VESA), welche eine einheitliche Software-Schnittstelle zur Ansteuerung der Grafikkarten in den SuperVGA-Auflösungen herausgab. Daraufhin implementierte beinahe jeder Grafikkarten-Hersteller diese Spezifikationen im VGA-BIOS als VESA BIOS Extension (VBE). Für die älteren Grafikkarten waren dann immerhin entsprechende Treiber in Form von Terminate and Stay Resident (TSR)-Programmen erhältlich, welche in die AUTOEXEC.BAT eingetragen werden konnten. So konnte man beispielsweise bei einer Spea/V7 Mercury Lite durch eine Zeile
LH C:\SPEA\GDC\V7LITVBE.EXE
die VESA-Kompatibilität nachrüsten.
Mit MSD.EXE (Microsoft Diagnostics) können Sie dies wunderbar überprüfen. MSD.EXE liegt jedem MS-DOS 6.2x sowie jedem Windows 3.1x bei, bei Windows 95 und 98 finden Sie dieses Werkzeug auf der Installations-CD unter \TOOLS\OLDMSDOS. Beispiel einer Grafikkarte, welche VESA unterstützt:
____________________ ____________________ Computer... _-------------- Video ---------------+s... _ A: B: C: ____________________| Video Adapter Type: VGA |________ E: F: G: ____________________| Manufacturer: ATI |_______ Memory... | Model: |... _ 1 ____________________| Display Type: VGA Color |________ ____________________| Video Mode: 3 |_______ Video... | Number of Columns: 80 |... _ 2 ____________________| Number of Rows: 25 |________ ____________________| Video BIOS Version: |_______ Network... | Video BIOS Date: 98/05/25 |.. _ 3.10 ____________________| VESA Support Installed: Yes |________ Enhanced ____________________| VESA Version: 2.00 |_______ OS Version... | VESA OEM Name: ATI MACH64 |s... _ ____________________| Secondary Adapter: None |________ ____________________+------------------------------------|_______ Mouse... | OK _ |ms... _ ____________________| ________ |________ ____________________+------------------------------------+_______ Other Adapters... _ Game Adapter Device Drivers... _ _____________________ _____________________ Finds files, prints reports, exits.
Und hier ein Beispiel einer nicht VESA-fähigen Grafikkarte:
____________________ ____________________ Computer... _ Olivetti/Olivetti Disk Drives... _ A: C: D: _---------------------------------- Video --------------------------------+ | Video Adapter Type: VGA | | Manufacturer: Olivetti | | Model: | | Display Type: VGA Color | | Video Mode: 3 | | Number of Columns: 80 | | Number of Rows: 25 | | Video BIOS Version: VETTI 1990 IBM VGA Compatible EVC rev. 1.09.U01u| | Video BIOS Date: | | VESA Support Installed: No | | Secondary Adapter: None | +-------------------------------------------------------------------------| | OK _ | | ________ | +-------------------------------------------------------------------------+ ____________________ ____________________ Other Adapters... _ Device Drivers... _ _____________________ _____________________ Press ALT for menu, or press highlighted letter, or F3 to quit MSD.
Zuerst sollten Sie sich auf den mitgelieferten Treiberdisketten bzw. Treiber-CD auf die Suche nach einem solchen TSR-Programm wie V7LITVBE.EXE vom zuvor gezeigten Beispiel machen. Werden Sie nicht fündig, so schauen Sie unter technischer Unterstützung im Treiber-Downloadbereich des Grafikkarten-Herstellers (z.B. ATI, Matrox, Diamond Multimedia) nach einer entsprechenden Datei und installieren diese entsprechend den Herstelleranweisungen. Vereinzelt können Sie auch mit einem Flash-BIOS-Update Ihre Grafikkarte VESA-tauglich aktualisieren, so dass Sie auch danach im Download-Bereich suchen sollten.
Falls nichts weiterhilft, so bietet SciTech Software Inc. das Produkt SciTech Display Doctor an, welches auch bei ziemlich exotischen Grafik-Chipsätzen eine VESA-Unterstützung anbietet. Unter anderem konnte ich damit auf meinem alten 80386/25 MHz-PC mit Chips and Technologies Inc. 82c452-Onbard-VGA-Controller immerhin noch 640×480/256 Farben herausholen.
Die aktuellen Spezifikationen liegen mittlerweile in der Version 3.0 (Stand Mai 2000) vor. Gegenüber der Version 1.2 betrifft dies einzig und alleine die Protected Mode-Schnittstelle beim 80386 im 32-Bit-Modus, wo Sie den Videospeicher linear adressieren können. Auch Version 2.0 unterscheidet sich nur in der Einführung dieses linearen Adressierungsmodells gegenüber dem Vorgänger 1.2. Da wir in QuickBASIC ausschliesslich im Real Mode der CPU arbeiten, verwende ich im Beispielprogramm die 1.2er-Schnittstelle, was auch zugleich die minimale Anforderung für Ihre Grafikkarte darstellt. Bei Bedarf sollten Sie also Ihren VBE-Treiber aktualisieren, wofür Sie wie oben beschrieben auf die Heimseite des Grafikkarten-Herstellers gehen sollten.
Die aktuellen VBE-Spezifikation in der Version 3.0 finden Sie im FTP-Bereich der VESA-Heimseite als vbe3.pdf, welche Sie mit dem Adobe Acrobat Reader lesen können. Falls Sie sich für ältere Versionen interessieren, finden Sie diese noch auf zahlreichen Web-Seiten und Download-Archiven, wovon ich Ihnen hier einige aufzähle:
Aus meiner Sicht sind die älteren Versionen in der Regel wesentlich übersichtlicher. In den folgenden Ausführungen beziehe ich mich daher immer auf Version 1.2. Um möglichst viel zu profitieren, sollten Sie jedoch auch einen Blick in die übrigen Versionen werfen, insbesondere sollten Sie die Veränderungen zwischen den einzelnen Versionen genauer unter die Lupe nehmen, denn Sie lernen dabei, worauf Sie achten müssen, damit Ihre QuickBASIC-Anwendung mit SuperVGA auch in Zukunft voll kompatibel bleibt.
VESA stellt in diesem Sinne keine Grafikbibliothek dar, fertige
Befehle wie LINE
und CIRCLE
suchen Sie daher
vergeblich. VESA stellt Ihnen eigentlich nur die Funktionalitäten auf der
niedrigsten Stufe zur Verfügung. Dies sind:
Vielleicht kennen Sie noch den Commodore 64, welcher keine Grafikbefehle in
seinem BASIC hatte, so dass Sie direkt mit POKE
die Pixel in den
Videospeicher schreiben mussten. Vielleicht ist Ihnen dieses Programmfragment
dabei noch in guter Erinnerung:
1000 REM PIXEL SETZEN (COMMODORE 64 HIRES-MODUS) 1010 ADR=8192+8*INT(X/8)+320*INT(Y/8)+(Y AND 7) 1020 POKE ADR, PEEK(ADR)OR 2^(7-(X AND 7)) 1030 RETURN
Die Grafikprogrammierung mit VESA funktioniert im Grunde genommen sehr
ähnlich. Im Gegensatz zum Commodore 64, wo jedes Gerät genau gleich
ausgestattet ist, existieren in der PC-Welt sehr vielfältige und
unterschiedliche Grafikkarten-Fabrikate.
Gäbe es keine VESA-Schnittstelle, so dass wir heute immer noch dieses
wilde Chaos bezüglich Standards hätten, so müssten Sie den
obigen Algorithmus auf jedem PC individuell anpassen. Ein wesentliches
Grundmerkmal von VESA war auch damals die Anforderung, dass die
Grafikkarten-Hersteller ihre bereits bestehenden Produkte nachrüsten
konnten, mit Hilfe der oben beschriebenen TSR-Programmen. Bei VESA wird dieses
Ziel dadurch erreicht, dass Sie einen ähnlichen, jedoch viel universeller
geschriebenen POKE
-Algorithmus wie oben dargestellt verwenden,
welcher vollständig ausparametrisiert ist, d.h. Sie ersetzen sämtliche Zahlenkonstanten durch
Variablen. Die VESA-Schnittstelle liefert Ihnen dann eine Beschreibung des
Videomodus in Form der ModeInfoBlock
-Struktur, aus welcher
Sie die konkreten Werte dieser Variablen auslesen können.
Der Zugriff auf die VESA-Schnittstelle erfolgt über DOS-Interrupts, und zwar ist dies INT
10h
mit Funktionsnummer AH=4Fh und AL=Unterfunktion im Detail:
' Allgemeiner Funktionsaufruf ' $INCLUDE: 'qb.bi' DIM dosIntEin AS RegTypeX, dosIntAus AS RegTypeX dosIntEin.ax = &H4Fnn CALL INTERRUPTX(&H10, dosIntEin, dosIntAus) IF dosIntAus.ax <> &H4F THEN PRINT "Fehler beim Aufrufen" END IF
Für das Demonstrationsprogramm benötigen Sie wegen
den CALL INTERRUPT
-Aufrufen die Compilerversion von QuickBASIC!
Andernfalls müssen Sie diese Aufrufe durch eine Maschinensprache-Routine
wie unter DOS-Interrupts beschrieben
ersetzen.
Das Demonstrationsprogramm können Sie hier herunterladen, anschliessend sollten Sie Windows komplett beenden, also Start -> Beenden..., Im MS-DOS-Modus neustarten oder beim Einschalten des PC F8 drücken, da Windows (in meinem Fall mit einer ATI xpert@WORK) keine solchen Videomoduswechsel beim MS-DOS-Eingabeaufforderungsfenster mag.
Das Programm liefert Ihnen zu Beginn wie MSD.EXE generelle Informationen zu Ihrer Grafikkarte sowie die Liste aller verfügbaren Videomodi, anschliessend wird Ihnen jeder Videomodus demonstriert, in dem der Bildschirm mit Farbquadraten gefüllt wird. Auf meiner ATI xpert@WORK mit 8 MB Videospeicher kann ich beispielsweise als beste Auflösung 1280×1024 mit 16,7 Millionen Farben (24 Bit TrueColor) dadurch nutzen, bei entsprechendem Monitor wären sogar 1600×1200 Bildpunkte möglich! :-)
Im folgenden wird nur auf die wichtigen Stellen eingegangen, daher finden Sie auch nur einen Teil der Zeilen zitiert.
' VESA-Demonstration: Demonstriert die Verwendung der SuperVGA-Grafikmodi ' aus QuickBASIC heraus unter Zuhilfenahme der VESA-Schnittstelle ' (c) 2000 by Andreas Meile, CH-8242 Hofen SH ' e-Mail: info@dreael.ch WWW: http://www.hofen.ch/~andreas/ ' $INCLUDE: 'qb.bi'
Beachten Sie lediglich die Verwendung von /l qb beim Laden
des Compilers QB.EXE, da viele CALL INTERRUPT
-Aufrufe
zum Einsatz kommen.
TYPE VgaInfoBlock VESASignatur AS STRING * 4 VESAVersionMinor AS STRING * 1 VESAVersionMajor AS STRING * 1 OEMStringOffs AS INTEGER OEMStringSeg AS INTEGER Faehigkeiten AS LONG VideoModeListeOffs AS INTEGER VideoModeListeSeg AS INTEGER TotalSpeicher AS INTEGER Reserviert AS STRING * 236 END TYPE TYPE ModeInfoBlock ModusAttribute AS INTEGER FenstAAttr AS STRING * 1 FenstBAttr AS STRING * 1 FenstGranularitaet AS INTEGER FenstGroesse AS INTEGER FenstASeg AS INTEGER FenstBSeg AS INTEGER FenstFktPtrOffs AS INTEGER FenstFktPtrSeg AS INTEGER ByteProPixelZeile AS INTEGER ' ab hier die früher einmal noch freiwilligen Felder xAufl AS INTEGER YAufl AS INTEGER XZeiGroe AS STRING * 1 YZeiGroe AS STRING * 1 AnzBitPlanes AS STRING * 1 AnzBitProPixel AS STRING * 1 AnzSpeiBaenke AS STRING * 1 SpeiModell AS STRING * 1 BankGroesse AS STRING * 1 AnzBildSeiten AS STRING * 1 Reserviert1 AS STRING * 1 ' ab hier die seit VESA 1.2 eingeführten Farbfelder RotMaskenGroesse AS STRING * 1 RotFeldPos AS STRING * 1 GruenMaskenGroesse AS STRING * 1 GruenFeldPos AS STRING * 1 BlauMaskenGroesse AS STRING * 1 BlauFeldPos AS STRING * 1 ReservMaskenGroesse AS STRING * 1 ReservFeldPos AS STRING * 1 DirektFarbInfo AS STRING * 1 Reserviert AS STRING * 216 END TYPE
Hier finden Sie in QuickBASIC eine direkte Abbildung der in der Spezifikation aufgeführten
Strukturen. Beachten Sie insbesondere die Umsetzung der einzelnen Datentypen
aus den dortigen Assembler-Datentypen; aus db
(einzelnes Byte!)
wird beispielsweise STRING * 1
, also Zeichenkette mit fester
Länge von 1, dw
wird zu Integer%
und
dd
zu Longint&
. FAR-Adresszeiger sind als
Kombination von Seg%
und Offs%
dargestellt. Auf die
STRING * 1
-Werte wird sinnvollerweise immer über
ASC()
/CHR$()
zugegriffen, weil es ja in Wirklichkeit
numerische Werte sind.
w% = 128 FOR i% = 0 TO 7 XPot%(i%) = w% w% = w% \ 2 NEXT i%
Die Variable XPot%() wird aus
Geschwindigkeitsgründen wegen den bei QuickBASIC fehlenden
SHL
/SHR
-Operatoren erzeugt.
dosIntEin.ax = &H4F00 ' Funktion 4fh (VESA-BIOS), Unterfunktion 00h ' (SuperVGA-Informationen zurückliefern) dosIntEin.es = VARSEG(infBlk) dosIntEin.di = VARPTR(infBlk) CALL INTERRUPTX(&H10, dosIntEin, dosIntAus) IF dosIntAus.ax <> &H4F THEN ' AL=4Fh und AH=00h? PRINT "Fehler: Keine VESA-fähige Grafikkarte vorhanden!" .. END END IF
Und hier erfolgt der allererste Zugriff auf die VESA-Schnittstelle: Wir
holen generelle Informationen zur eingebauten Grafikkarte ein. Als Puffer wird
direkt eine Strukturvariable vom Typ ModeInfoBlock
übergeben,
sodass die Felder beim Aufruf an der korrekten Position abgefüllt
werden. Beachten Sie dabei noch die Auswertung des Rückgabewertes in AX,
ob der Aufruf erfolgreich war oder nicht.
Im Anschluss wird die Struktur ausgewertet, wobei bei den Adresszeigern mit
DEF SEG
und PEEK()
gearbeitet wird.
PeekWord%()
stellt dabei eine selber definierte Funktion dar, um
einen 16-Bit-Wert auslesen zu können.
nModes% = 0 WHILE PeekWord%(i%) <> -1 vModes%(nModes%) = PeekWord%(i%) PRINT " " + FormatHex$(vModes%(nModes%), 4) + "h"; i% = i% + 2 nModes% = nModes% + 1 WEND
Einen wichtigen Schritt stellt dabei noch das Auslesen der vorhandenen Videomodi dar. Dazu gibt die VESA-Spezifikation folgende Programmierregel:
Gehen Sie niemals von einer festen Zuordnung der Videomodus-Nummern zu bestimmten Grafikauflösungen und Farbtiefen aus! Fragen Sie stattdessen immer die Liste der verfügbaren Videomodi ab und wählen Sie den für Ihre Anwendung möglichst gut passenden Videomodus aus.
Ebenfalls sehr wichtig:
' Version prüfen ma% = ASC(infBlk.VESAVersionMajor) mi% = ASC(infBlk.VESAVersionMinor) IF ma% < 1 OR ma% = 1 AND mi% < 2 THEN PRINT "Es wird im Minimum VESA Version 1.2 benötigt!" PRINT "Bitte im WWW nach einem BIOS-Update für die Grafikkarte oder" PRINT "neueren VESA-TSR-Programmversion nachschauen" END END IF
Entscheiden Sie sich stets anhand Ihren Anforderungen auf eine bestimmte VESA-Version und fragen Sie diese ab, ob die Mindestanforderung erfüllt ist. Dabei sollten Sie höhere Versionen immer zulassen, da das VESA-Konsortium Abwärtskompatibilität bei neuen Versionen garantiert!
Durch Einhaltung dieser Programmdesign-Regel ersparen Sie zum einen Ihren Endanwender unklare Abstürze infolge von Zugriffen auf bei älteren VESA-Versionen noch nicht vorhandene Funktionen, und zum anderen vermeiden Sie damit die Notwendigkeit solcher Flickschustereien wie SETVER.EXE von MS-DOS oder neuerdings APCOMPAT.EXE bei Windows 2000, denn aus meiner Sicht zeugt solche Software überhaupt nicht von Fachkompetenz! :-(
FOR i% = 1 TO nModes% PRINT "Modus"; i%; "von"; nModes%; ": "; FormatHex$(vModes%(i% - 1), 4); "h" PRINT "====================================" dosIntEin.ax = &H4F01 ' Funktion SuperVGA-Modus zurückliefern dosIntEin.cx = vModes%(i% - 1) dosIntEin.es = VARSEG(vModBesch) dosIntEin.di = VARPTR(vModBesch) CALL INTERRUPTX(&H10, dosIntEin, dosIntAus) IF dosIntAus.ax <> &H4F THEN COLOR 30 PRINT "Fehler: Nicht unterstützt"
In diesem Demonstrationsprogramm gehen wir jeden einzelnen Modus einmal
durch, dabei werden zuerst allgemeine Informationen zum Videomodus
eingeholt. Im nachfolgenden PRINT
-Zeilen-Programmteil sehen Sie
selber, welche Informationen es dazu gibt und wie man sie extrahiert.
dosIntEin.ax = &H4F03 ' SuperVGA-Modus zurückliefern CALL INTERRUPTX(&H10, dosIntEin, dosIntAus) IF dosIntAus.ax <> &H4F THEN PRINT "Fehler bei Videomodus lesen" END END IF AlterModus% = dosIntAus.bx
Denken Sie daran: Sauberes Programmdesign bedeutet, dass wir jeweils am Schluss der Arbeit wieder alles so hinterlassen, wie es am Anfang war, unter anderem auch der Videomodus (meistens 80×25-Textmodus).
dosIntEin.ax = &H4F02 ' SuperVGA-Modus setzen dosIntEin.bx = vModes%(i% - 1) CALL INTERRUPTX(&H10, dosIntEin, dosIntAus) IF dosIntAus.ax <> &H4F THEN PRINT "Fehler bei Videomodus setzen: ax="; FormatHex$(dosIntAus.ax, 4); "h"
Hier kommt der SCREEN
-Ersatz zum Zug: Setzen des Videomodus.
Bevor Sie darauf los zeichnen können, müssen noch die Variablen
dieses universell gestalteten PSET
-Algorithmus
(Unterprogramme VesaPset
und VesaPsetPlanar
)
einmalig vorbereitet werden:
IF (ASC(vModBesch.FenstAAttr) AND 5) = 5 THEN SchrFenst% = 0 DEF SEG = vModBesch.FenstASeg ELSEIF (ASC(vModBesch.FenstBAttr) AND 5) = 5 THEN SchrFenst% = 1 DEF SEG = vModBesch.FenstBSeg ELSE SchrFenst% = -1 ' Kein Fenster END IF IF (ASC(vModBesch.FenstAAttr) AND 3) = 3 THEN LeseFenst% = 0 ELSEIF (ASC(vModBesch.FenstBAttr) AND 3) = 3 THEN LeseFenst% = 1 ELSE LeseFenst% = -1 ' Kein Fenster END IF ' In der Demo wird jedoch kein separates Schreib-/Lese- ' Speicherfenster benötigt dosIntEin.ax = &H4F05 dosIntEin.bx = SchrFenst% OR 256 CALL INTERRUPTX(&H10, dosIntEin, dosIntAus) AktFenst% = dosIntAus.dx IF LeseFenst% <> SchrFenst% THEN ' separates Lese- und Schreibfenster: Sind beide identisch? dosIntEin.ax = &H4F05 dosIntEin.bx = LeseFenst% OR 256 CALL INTERRUPTX(&H10, dosIntEin, dosIntAus) IF dosIntAus.dx <> AktFenst% THEN ' Nein => auf gleichen Wert setzen dosIntEin.ax = &H4F05 dosIntEin.bx = LeseFenst% dosIntEin.dx = AktFenst% CALL INTERRUPTX(&H10, dosIntEin, dosIntAus) END IF END IF
Beachten Sie hierbei die unterschiedlichen Architekturmerkmale einzelner
Grafikkarten-Fabrikate. Einige Hersteller verwenden für das Lesen
(PEEK()
!) und Schreiben (POKE
!) separate
Fenster für den Zugriff auf das RAM der Grafikkarte, andere
Grafikkarten-Produkte bieten nur ein gemeinsames Speicherfenster an.
Einzelne Fabrikate bieten sogar zwei Speicherfenster an, in welchen
sowohl gelesen als auch geschrieben werden kann.
Diesem Umstand müssen wir volle Rechnung tragen. Bei dieser Demo wird allgemein nur ein Schreibfenster benötigt. Die einzige Ausnahme stellt der aus 4 Bitebenen zusammengesetzte planare 16-Farben-Modus dar, wo es eine Leseoperation braucht. Die Abfrage sowie das bedingte Setzen des Lesefensters soll sicherstellen, dass beide Fenster auf denselben Speicherbereich zeigen.
xSpr% = (ASC(vModBesch.AnzBitProPixel) + 7) \ 8 ySpr& = CLNG(vModBesch.ByteProPixelZeile) yJmp& = CLNG(ASC(vModBesch.BankGroesse)) * 1024& yInterleav% = ASC(vModBesch.AnzSpeiBaenke) FenstGroe& = CLNG(vModBesch.FenstGroesse) * 1024& GranSpr% = vModBesch.FenstGroesse \ vModBesch.FenstGranularitaet xAufl% = vModBesch.xAufl YAufl% = vModBesch.YAufl rPos& = Pot2&(ASC(vModBesch.RotFeldPos)) rDiv% = CINT(Pot2&(8 - ASC(vModBesch.RotMaskenGroesse))) gPos& = Pot2&(ASC(vModBesch.GruenFeldPos)) gDiv% = CINT(Pot2&(8 - ASC(vModBesch.GruenMaskenGroesse))) bPos& = Pot2&(ASC(vModBesch.BlauFeldPos)) bDiv% = CINT(Pot2&(8 - ASC(vModBesch.BlauMaskenGroesse)))
Hier erfolgt die eigentliche Parameterinitialisierung. Beachten Sie hierbei, dass die Parameterstruktur zugunsten einer möglichst hohen Ausführgeschwindigkeit der Punktsetz-Routinen optimiert ist und daher entsprechend durch Umrechnungen aufbereitet werden muss. Weiter ist noch bei Auflösungen wie 15 bpp zu bemerken, dass der X-Bytesprung (xSpr%) immer auf ganze Bytes aufzurunden ist.
keinDemo% = 0 SELECT CASE ASC(vModBesch.SpeiModell) CASE &H0
Die Auswahl der Demo erfolgt anhand des Speichermodells, also der Datenstrukturierung im RAM der Grafikkarte.
FOR y% = 1 TO YAufl% PrintText y%, 1, 1, 2, FuehrNull$(y%, 3) NEXT y% FOR x% = 4 TO xAufl% h% = x% FOR y% = 3 TO 1 STEP -1 PrintText y%, x%, 13, 0, CHR$(48 + h% MOD 10) h% = h% \ 10 NEXT y% NEXT x% PrintText 5, 5, 12, 0, "Zeichenvorrat:" PrintText 5, 22, 3, 0, "Farben:" FOR y% = 0 TO 15 FOR x% = 0 TO 15 PrintText 6 + y%, 5 + x%, 7, 0, CHR$(16 * y% + x%) PrintText 6 + y%, 22 + x%, x%, y%, "A" NEXT x% NEXT y%
Im Textmodus sollen die Dimensionen des Bildschirms sowie der Zeichensatz
gezeigt werden. Da viele moderne Grafikkarten mittlerweile keine
BIOS-Unterstützung anbieten, kommt hier ein eigener
PRINT
-Ersatz zur Anwendung, bei welchem Sie die Cursorposition
zusammen mit den Farben und dem Text angeben können. Die entsprechende
Routine verwendet dann direkt POKE
auf den Videospeicher.
CASE &H3 ' Sequenz aus initPlanar(), um den sog. Write Mode auf 2 zu setzen OUT &H3C4, &H2 OUT &H3C5, &HF OUT &H3CE, &H3 OUT &H3CF, &H0 OUT &H3CE, &H5 OUT &H3CF, &H2
Und hier erfolgt im Falle eines 16-Farben-Videomodus die Demo. Beachten Sie
hierbei die nötigen Vorbereitungen bei den VGA-Registern, um Farbpixel
setzen zu können. Diese OUT
-Sequenz habe ich direkt aus dem
C-Programmbeispiel aus Anhang 2 der VESA-Spezifikationen Version 3
übernommen.
FOR y% = 0 TO YAufl% - 1 FOR x% = 0 TO xAufl% - 1 VesaPsetPlanar x%, y%, (x% + y%) \ 20 AND 15 NEXT x% NEXT y%
Als Grafikdemo wird ein ganz einfaches Diagonalstreifenmuster mit allen 16 Farben und 20 Pixel breiten Streifen erzeugt.
' Wieder auf BIOS-Vorgabewerte setzen gemäss setWriteMode0() OUT &H3CE, &H8 OUT &H3CF, &HFF OUT &H3CE, &H5 OUT &H3CF, &H0
Sauberkeit beim Beenden ist alles :-), daher werden hier die VGA-Register wie im C-Beispiel der VESA-Spezifikationen wieder zurückgesetzt.
CASE &H6 FOR y% = 0 TO YAufl% - 1 FOR x% = 0 TO xAufl% - 1 VesaPset x%, y%, Farbe&(y% AND 255, x% AND 255, 133) NEXT x% NEXT y%
Freunde von rasterfreier Echtfarbgrafik aufgepasst: Hier erfolgt die Demonstration des Echtfarbmodus (HiColor und TrueColor), wo Sie jedem Bildpunkt individuelle Rot-Grün-Blau-Werte verpassen können. Als Beispiel werden zweidimensionale Farbkeile in Form von Quadraten dargestellt. Mit einem guten Monitor können Sie dabei die Unterschiede zwischen HiColor und TrueColor sehr gut sehen! Bei mir ist sogar der Unterschied zwischen 32K- (15 bpp) und 64K-HiColor (16 bpp) noch gut zu erkennen. Falls Sie die Grafikauflösung ablesen möchten: Ein Quadrat entspricht immer 256 Pixel.
CASE &H4 FOR y% = 0 TO YAufl% - 1 FOR x% = 0 TO xAufl% - 1 VesaPset x%, y%, CLNG((x% \ 5 AND 15) + 16 * (y% \ 5 AND 15)) NEXT x% NEXT y%
Und hier erfolgt die 256-Palettengrafik-Demo: Alle 256 Farben je als Farbquadrat von 16×16 Felder, jedes Feld mit 5 Pixel Seitenlänge => Seitenlänge eines ganzen »Ornaments« 80 Pixel, so dass Sie durch Abzählen auch wieder die Grafikauflösung ablesen können.
dosIntEin.ax = &H4F02 ' SuperVGA-Modus setzen dosIntEin.bx = AlterModus% CALL INTERRUPTX(&H10, dosIntEin, dosIntAus)
Und hier verlassen wir wieder den SuperVGA-Videomodus und gehen zum ursprünglichen Videomodus zurück.
Nachfolgend noch alle wichtigen Unterprogramme:
FUNCTION Farbe& (r%, g%, b%) Farbe& = rPos& * CLNG(r% \ rDiv%) OR gPos& * CLNG(g% \ gDiv%) OR bPos& * CLNG(b% \ bDiv%) END FUNCTION
Diese Umwandlungsroutine wird nur bei den Echtfarb-Grafikmodi benötigt, um den Speicherinhalt-Farbwert zu berechnen. Beachten Sie dabei die Verwendung der Parametervariablen, insbesondere das offene und neutrale Programmdesign, so dass diese Routine mit jeder nur erdenklichen, auch exotischen Farbwert-Bit-Unterteilung funktioniert. Aus diesem Grund wurden bei VESA 1.2 diese Felder hinzugefügt.
FUNCTION PeekWord% (Adr%) PeekWord% = CVI(CHR$(PEEK(Adr%)) + CHR$(PEEK(Adr% + 1))) END FUNCTION
Die zu Beginn erwähnte 16-Bit-PEEK()
-Funktion.
FUNCTION Pot2& (e%) h& = 1& FOR i% = 1 TO e% h& = h& * 2 NEXT i% Pot2& = h& END FUNCTION
Auch diese Funktion dient nur als Ersatz für die bei QuickBASIC fehlenden Bit-Verschiebeoperatoren.
SUB PrintText (y%, x%, VFar%, HFar%, t$) ' Diese Routine läuft auch ohne BIOS-Unterstützung Adr% = (y% - 1) * CINT(ySpr&) + 2 * (x% - 1) fa% = 16 * HFar% OR VFar% FOR i% = 1 TO LEN(t$) POKE Adr%, ASC(MID$(t$, i%, 1)) POKE Adr% + 1, fa% Adr% = Adr% + 2 NEXT i% END SUB
Hier der BIOS-unterstützungsunabhängige PRINT
-Ersatz
für eine Textausgabe in Text-Bildschirmmodi.
SUB VesaPset (x%, y%, Farb&) DIM dosIntEin1 AS RegType, dosIntAus1 AS RegType IF x% >= 0 AND y% >= 0 AND x% < xAufl% AND y% < YAufl% THEN AbsAdr& = CLNG(x% * xSpr%) + CLNG(y% \ yInterleav%) * ySpr& + yJmp& * (y% MOD yInterleav%) f1& = Farb& FOR i% = 1 TO xSpr% NeuFenst% = CINT(AbsAdr& \ FenstGroe&) * GranSpr% IF AktFenst% <> NeuFenst% THEN dosIntEin1.ax = &H4F05 dosIntEin1.bx = SchrFenst% dosIntEin1.dx = NeuFenst% CALL INTERRUPT(&H10, dosIntEin1, dosIntAus1) AktFenst% = NeuFenst% END IF POKE AbsAdr& MOD FenstGroe&, CINT(f1& AND 255&) AbsAdr& = AbsAdr& + 1 f1& = f1& \ 256& NEXT i% END IF END SUB
Die wichtigste Kernfunktionalität in der VESA-Grafikprogrammierung: Die
universell gestaltete POKE
-Routine für das Setzen
eines Bildpunktes. Beachten Sie hierbei das Setzen des Schreibfensters, welches
aus Geschwindigkeitsgründen nur bei Bedarf erfolgt. Dabei ist noch zu
bemerken, dass mit der an und für sich langsameren
INT-Methode der Aufruf erfolgt anstelle eines
FAR-Sprungs. QuickBASIC bietet zwar CALL ABSOLUTE
,
jedoch können Sie dort keine Parameter in den Registern der CPU
hinterlegen.
SUB VesaPsetPlanar (x%, y%, Farb%) DIM dosIntEin1 AS RegType, dosIntAus1 AS RegType IF x% >= 0 AND y% >= 0 AND x% < xAufl% AND y% < YAufl% THEN AbsAdr& = CLNG(x% \ 8) + CLNG(y% \ yInterleav%) * ySpr& + yJmp& * (y% MOD yInterleav%) NeuFenst% = CINT(AbsAdr& \ FenstGroe&) * GranSpr% IF AktFenst% <> NeuFenst% THEN dosIntEin1.ax = &H4F05 dosIntEin1.bx = SchrFenst% dosIntEin1.dx = NeuFenst% CALL INTERRUPT(&H10, dosIntEin1, dosIntAus1) IF SchrFenst% <> LeseFenst% THEN ' Separates Schreib- und Lesefenster => Lesefenster auch setzen dosIntEin1.ax = &H4F05 dosIntEin1.bx = LeseFenst% dosIntEin1.dx = NeuFenst% CALL INTERRUPT(&H10, dosIntEin1, dosIntAus1) END IF AktFenst% = NeuFenst% END IF OUT &H3CE, 8 OUT &H3CF, XPot%(x% AND 7) dummy% = PEEK(AbsAdr& MOD FenstGroe&) POKE AbsAdr& MOD FenstGroe&, Farb% END IF END SUB
Spezialfall planarer 16-Farben-Videomodus mit 4 bpp: Die Programmierung entspricht weitgehend der Standard-VGA-Programmierung, ausser dass die Grössen (Auflösung!) als neutrale Parameter erscheinen und ausserdem das Lese- und Schreibfenster bei Bedarf gesetzt werden.
Dieses Beispielprogramm ist eigentlich nicht als Bibliothek gedacht, sondern es soll Ihnen mehr die Grundprinzipien der SuperVGA-Programmierung aufzeigen. Dies haben Sie sicherlich auch bei der Grafikaufbaugeschwindigkeit sehr gut bemerkt. Und dies ist auch der Grund, weshalb fast alle diese im Internet auffindbaren SuperVGA-Grafikbibliotheken auf schnellen Assemblerroutinen basieren.
Aber mit etwas Flair für Software-Design können Sie dieses Beispielprogramm ohne weiteres in eine Bibliothek umwandeln, welche Sie dann nach Belieben in eigenen Projekten verwenden können.
Gerade bei der Programmierung mit VESA kann es Ihnen recht schnell passieren, dass Sie Ihr Programm ganz unbewusst spezifisch auf die Grafikkarte Ihres PC anpassen und somit Fälle übersehen, mit welchen Sie gemäss Spezifikationen auch immer voll damit rechnen müssen. Typisches Beispiel wäre das Speicherfenster, wenn Sie beispielsweise davon ausgehen, dass dieses für Lesen und Schreiben identisch ist. Daher einige Tips:
Auch ich verwendete zum Test ausser der ATI xpert@WORK auch einmal mein
alter 386er mit dem SciTech Display Doctor. Ebenso probierte ich das
Demoprogramm noch mit einer Trident 9440 (PCI, 1 MB) sowie mit einer alten
Diamond SpeedStar 24 aus. Bei der SpeedStar 24 stiess ich sogar auf einem
Fehler in der VBE-Implementation: Bei den Textmodi werden die Pixel statt
Textzeilen und -spalten in der ModeInfoBlock
-Struktur geliefert,
was klar die VESA-Spezifikationen missachtet. Auch Compaq passierte bei der
Implementation von CPQVESA.EXE
für einen DeskPro XE450 einen Lapsus: Im 800×600/16 Farben-Videomodus
werden 800 statt 100 Bytes pro Zeile gemeldet, was entsprechende
Darstellungsfehler (immer 7 Pixelzeilen leer gelassen) bewirkt. Ein anderer Bug
bewirkt dort ausserdem, dass vom Bild nur Streifen erscheinen. Wenn man die
Demonstrationsroutinen in VBETEST.EXE
von SciTech vorher einmal laufen
gelassen hat, dann verschwindet dieses Problem seltsamerweise, als würde
VBETEST.EXE eine »heilende« Wirkung auf die
VBE-Routinen im RAM ausüben... Sie sehen: Auch bei den
Grafikkarten-Herstellern arbeiten nur Menschen wie Sie und ich... :-)
In diesem kleinen Beitrag zeige ich Ihnen noch einen Trick, mit welchem Sie
auch in GWBASIC.EXE die Auflösungen SCREEN 11
,
SCREEN 12
und SCREEN 13
hinzaubern können, und
zwar ohne ein einziges Byte Assembler/Maschinensprache. Der Trick beruht auf
der Verwendung des con-Gerätes von MS-DOS im Zusammenhang mit
ANSI.SYS. Dazu ergänzen Sie zuerst die Datei
CONFIG.SYS um eine Zeile
DEVICEHIGH=C:\DOS\ANSI.SYS
bei MS-DOS Version 5.x und 6.x bzw.
DEVICEHIGH=C:\WINDOWS\COMMAND\ANSI.SYS
bei Windows 95 und 98, und starten anschliessend Ihren Rechner neu. Laden Sie dann das Beispielprogramm VGADEMO.BAS. Kurz daraus die wichtigsten Stellen:
150 KEY OFF:SCREEN 1:SCREEN 0
In Zeile 150 wird ein von der Anzahl Zeichen kompatibler Textmodus gesetzt.
Dies erlaubt es dann mit PRINT
und COLOR
arbeiten zu
können.
160 OPEN "con" FOR OUTPUT AS 1 170 PRINT#1,CHR$(27);"[19h"; 180 CLOSE 1
Hier erfolgt die maschinensprachefreie Videomodusaktivierung durch Ausgabe der passenden Steuersequenz an den ANSI.SYS-Treiber. Unter MS-DOS 6.2x können Sie mit help ansi.sys genauere Details zu diesen Steuersequenzen nachschlagen. In diesem Fall wird der 320×200/256 Farben-Videomodus verwendet.
190 FOR I%=0 TO 240 STEP 16 200 DEF SEG=&HA000+I%*15 210 FOR J%=0 TO 15:C%=I%+J%:B%=20*J% 220 FOR K%=B% TO B%+3520 STEP 320:FOR L%=K% TO K%+19:POKE L%,C%:NEXT:NEXT:NEXT:NEXT
Die Grafikpixel müssen ganz analog dem VESA-Beispielprogramm direkt mit
POKE
gesetzt werden. Da GWBASIC.EXE nichts von
unserem Videomoduswechsel weiss, steht auch dementsprechend PSET
nicht zur Verfügung!
230 LOCATE 25,1 240 PRINT"VGA-Demo in GW-BASIC * 1991 by A. Meile";
Weil PRINT
im Hintergrund über das BIOS geht, und das BIOS
im Gegensatz zu GWBASIC.EXE selber vom Videomodus-Wechsel
»Bescheid« weiss, kommt die Textausgabe entsprechend richtig.
270 OPEN "con" FOR OUTPUT AS 1 280 PRINT#1,CHR$(27);"[2J"; 290 CLOSE 1
Auch die CLS
-Anweisung erfolgt über eine
ANSI.SYS-Steuersequenz. Zeilen 300-370 stellen lediglich die
zweite Demonstration dar.
380 OPEN "con" FOR OUTPUT AS 1 390 PRINT#1,CHR$(27);"[?1h"; 400 CLOSE 1
Sauberes Aufräumen ist oberes Gebot! Daher wird der ursprünglich, »offiziell« gesetzte 40×25-Textmodus mittels Steuerzeichenfolge zurückgesetzt.
410 SCREEN 2:KEY ON:SCREEN 0
Und hier wird GWBASIC.EXE-seitig wieder in den 80×25-Zeichenmodus zurückgekehrt.