|
SVGA in Qbasic
Autor: Andre Klein
Vorwort
|
letzte Aktualisierung: 11.07.2003
| |
Wer viel mit Grafiken arbeitet kennt das Problem, entweder hat man zu wenig Farben(SCREEN 12) oder eine zu kleine Auflösung(SCREEN 13).
Hier die Lösung:
Schreib dir einfach eigene Grafik-Routinen die es möglich machen Auflösungen bis zu 1600x1200x16Mc zu benutzen.
wie du das machst wird hier Schritt für Schritt erklärt.
Abschnitt 1
|
Womit fange ich an?
| |
Bevor wir anfangen müßen wir uns einmal "Grafik" als solches vornehmen. Zum Erklären beziehe ich mich in allen Beispielen
auf SCREEN 13 (320x200x256).
Abschnitt 2
|
Was ist VRAM?
| |
VRAM wird der Speicher genannt der auf der Grafik-Karte eingebaut ist und wird in MB angegeben. Diese Angabe ist sehr wichtig
und steht meistens direkt auf der Verpackung der Karte. Wenn es dort nicht steht kann man die Speichergröße auch anders herausfinden, aber dazu später.
Im VRAM selber ist das gespeichert was später auf dem Bildschirm zu sehen ist.
Abschnitt 3
|
Wie arbeitet der VRAM mit Grafik?
| |
Als Beispiel nehmen wir einen VRAM mir der Größe von 1 MB.
Jetzt ist es so, das immer ein 64KB-Fenster dieser 1 MB im normalen Arbeitsspeicher zur Verfügung steht.
Jedesmal wenn man der Grafikkarte sagt welches Fenster gelesen werden soll, dann kopiert sie die jeweiligen 64KB in
das sogenannte VIDEO-SEGMENT.
Und dann, mehrmals in der Sekunde, wird dieses Video-Segment wieder von der Grafikkarte ausgelesen und wieder in
den VRAM geschrieben.
Das ganze bewirkt, das wenn man im Video-Segment etwas verändert, es auch automatisch in den VRAM übertragen wird.
Dieses Video-Segment hat in IBM-Kompatiblen PC's immer das SEGMENT &HA000 und fängt beim OFFSET &H0 an.
Das was später auf dem Bildschirm zu sehen ist, sind mehrere Fenster aus dem VRAM. Auch dort kann man einstellen welche
Fenster zu sehen sind.
Das hat den Vorteil das man in nicht sichtbaren Fenstern, Grafiken zwischenspeichern kann.
Wenn man dann diese Grafiken in Spielen benutzt, geht das natürlich schneller, da die Grafiken ja nicht erst aus dem RAM gehölt werden müssen.
Abschnitt 4
|
Was ist eine PALETTE?
| |
PSET (100,100),15
Wenn du in QB einen Pixel malst der die Farbnummer 15 hat dann erscheint auf dem Bildschirm ein weißer Pixel.
Aber woher weiß QB oder die Grafikkarte das 15 = weiß ist?
Und zwar ist es so das es eine Tabelle gibt. Wenn die Grafikkarte die Farbe 15 darstellen soll, schaut sie einfach in diese
Tabelle an die Position 15. Dort stehen die Rot, Grün und Blauanteile der Farbe. Auch RGB genannt. Und aus diesen
Anteilen setzt die Grafikkarte die Farben zusammen die auf dem Bildschirm zu sehen sind.
Diese Tabelle mit den Farbanteilen nennt man PALETTE!
Natürlich kann man diese Farbanteile auch verändern. In QB geht das z.B. mit dem PALETTE-Befehl!
Ein Palette kann maximal 256 Farbnummern speichern. Also von 0-255.
Abschnitt 5
|
Das erste Beispiel: PSET mal anders
| |
Das folgende Beispielprogramm zeichnet einen weißen Punkt in die linke, obere Ecke des Bildschirms.
SCREEN 13 |
Auflösung setzen (320x200x256) |
DEF SEG = &HA000 |
SEGMENT festlegen mit dem wir arbeiten wollen |
offset& = 0 |
Das OFFSET im SEGMENT bestimmen |
farbe% = 15 |
Die Farbe des Punktes festlegen |
POKE offset&, (farbe% AND 255) |
Pixel in den Speicher schreiben |
DEF SEG |
QB-Standart-Segment wiederherstellen |
SLEEP |
Damit dir das ganze Programm ein wenig verständlicher wird, spiel mal mit den Werten offset& und farbe% herum.
Nimm z.B. für das offset& bei einem Test 319 und beim nächsten Test 320. Wenn du das machst, müßtest du sehen
das der Speicher linear aufgebaut ist. Also ein Offset von 319 hat die XY-Koordinaten 319,0 und ein Offset von 320
hat die XY-Koordinaten von 0,1.
Das heißt wenn du einen Pixel an eine bestimmte Position setzen willst dann mußt du das dazugehörige OFFSET berechnen.
Das macht man so:
x& = 200 |
y& = 100 |
offset& = (y&*320) + x& |
Die 320 stehen für die X-Auflösung |
Das komplette Programm sieht dann so aus:
SCREEN 13 |
Auflösung setzen (320x200x256) |
DEF SEG = &HA000 |
SEGMENT festlegen mit dem wir arbeiten wollen |
x& = 200 |
y& = 100 |
offset& = (y&*320) + x& |
Die 320 stehen für die X-Auflösung |
farbe% = 15 |
Die Farbe des Punktes festlegen |
POKE offset&, (farbe% AND 255) |
Pixel in den Speicher schreiben |
DEF SEG |
QB-Standart-Segment wiederherstellen |
SLEEP |
Abschnitt 6
|
Das zweite Beispiel: POINT mal anders
| |
Das folgende Beispielprogramm gibt die Farbe eines Pixels zurück:
SCREEN 13 |
Auflösung setzen (320x200x256) |
DEF SEG = &HA000 |
SEGMENT festlegen mit dem wir arbeiten wollen |
x& = 200 |
y& = 100 |
offset& = (y&*320) + x& |
Die 320 stehen für die X-Auflösung |
farbe% = PEEK(offset&) |
Pixel aus dem Speicher lesen |
DEF SEG |
QB-Standart-Segment wiederherstellen |
PRINT farbe% |
|
SLEEP |
Abschnitt 7
|
SVGA jetzt gehts los.
| |
Um SVGA zu benutzen ist es das einfachste mit der VESA-Schnittstelle zu arbeiten.
Was ist die VESA-Schnittstelle?
VESA-Schnittstelle bedeutet einfach nur das das BIOS schon (halb)-fertige Programme besitzt die der Anwender
selber benutzen kann. So spart man sich das direkte Ansteuern der Grafik-Karte.
Diese Schnittstelle kann man benutzen wenn man den Software-Interrupt &H10 des BIOS benutzt.
Was ist ein Interrupt?.
Abschnitt 8
|
Dinge die ich für folgende Beispiele voraussetze:
| |
1. QB ab Version 4.0
2. QB muß mit der Option /L aufgerufen werden um die QB.QLB zu laden.
siehe "Was ist eine Bibliothek?".
3. folgendes Programm muß am Anfang der QB-Datei stehen.
TYPE regtypex |
ax AS INTEGER |
bx AS INTEGER |
cx AS INTEGER |
dx AS INTEGER |
bp AS INTEGER |
si AS INTEGER |
di AS INTEGER |
flags AS INTEGER |
ds AS INTEGER |
es AS INTEGER |
END TYPE |
DIM SHARED reg AS regtypex |
Abschnitt 9
|
Überprüfen ob unsere Grafik-Karte VESA-kompatibel ist.
| |
Mit dem folgenden Programm kann man überprüfen ob die Grafik-Karte VESA-kompatibel ist. Das muß sein, weil
die anderen Programme sonst nicht laufen würden.(logischerweise)
puffer$ = SPACE$(280) |
Datenpuffer für Daten anlegen |
reg.ax = &H4F00 |
als Funktion AH = &H4F festlegen |
reg.es = VARSEG(puffer$) |
SEGMENT von puffer$ speichern |
reg.di = SADD(puffer$) |
OFFSET von puffer$ speichern |
CALL INTERRUPTX (&H10, reg, reg) |
INT &H10 aufrufen (BIOS-Grafik-INT) |
IF reg.ax <> &H4F THEN |
Wenn reg.ax = &H4F dann ist VESA vorhanden. |
PRINT "kein VESA!" |
END |
END IF |
puffer$="" |
Speicher von puffer$ freigeben (muß nicht!) |
Abschnitt 10
|
Welche VESA-Auflösungen unterstützt meine Grafikkarte?
| |
Das kann man mit dem folgenden Programm herausfinden.
CLS |
Bildschirm löschen |
FOR i% = 256 TO 304 |
Modi in diesem Bereich überprüfen, denn dort sind die besten |
dat$ = SPACE$(259) |
Puffer für Daten anlegen |
reg.ax = &H4F01 |
Funktion AH = &H4F und Unterfunktion AL = &01 festlegen |
reg.cx = i% |
reg.cx ist die jeweilige Modusnummer |
reg.es = VARSEG(dat$) |
SEGMENT von dat$ merken |
reg.di = SADD(dat$) |
OFFSET von dat$ merken |
CALL INTERRUPTX(&H10, reg, reg) |
BIOS-Grafik-Int &H10 aufrufen |
IF reg.ax = &H4F THEN |
Wenn reg.ax = &H4F dann wird dieser Modus von der Grafikkarte unterstützt |
Xres% = CVI(MID$(dat$, 19, 2)) |
X-Resolution, X-Auflösung rausfinden |
Yres% = CVI(MID$(dat$, 21, 2)) |
Y-Resolution, Y-Auflösung rausfinden |
bpp& = ASC(MID$(dat$, 26, 1)) |
BitsPerPixel rausfinden |
farben& = 2 ^ bpp& |
Anzahl der Farben berechnen |
PRINT "&H";HEX$(i%); " "; Xres%; "x"; Yres%; "x"; farben& |
Die wichtigsten Daten auf den Bildschirm schreiben |
END IF |
NEXT i% |
nächsten Modus überprüfen |
Anmerkungen:
Jede Auflösung hat eine andere Modus-Nummer. Zwischen den Nummern 256 (&H100) und 304(&H130)
befinden sich die besten SVGA-Moden. Darunter und darüber gibt es auch noch welche aber sind kaum zu gebrauchen.
Abschnitt 11
|
Wie setze ich einen Grafik-Modus?
| |
Bevor man einen Grafik-Modus setzt muß natürlich geschaut werden ob man eine VESA-kompatible Grafikkarte hat.
Desweiteren mußt du unbedingt BEVOR du einen SVGA-Modus setzt z.B. SCREEN 13 aufrufen. Das ganze dient
dazu das QB ja nicht weiß das wir einen SVGA-Modus gesetzt haben und deswegen schaltet es auch nicht automatisch zurück.
Wenn man vorher z.B. SCREEN 13 setzt, dann weiß QB das es nach beenden des QB-Programmes wieder in den Textmodus
zurückschalten muß!
!!!WICHTIG!!! |
Für ab hier beschriebene Programme übernehme ich in Schadensfällen keine Haftung!
|
Es kann nämlich sein das der MONITOR die gewählte Auflösung nicht unterstützt. Das merkt man z.B. daran das
auf dem Bildschirm plötzlich überlagerte Bilder angezeigt werden oder der Bildschirm sich abschaltet während das
Programm LÄUFT!
Mir ist zwar während der ganzen Programmierung kein Schadensfall bekannt geworden, aber ich möchte trotzdem darauf hinweisen!!!
|
Hier das Programm zum setzen eines SVGA-Modus:
CLS |
SCREEN 13 |
reg.ax = &H4F02 |
reg.ax auf AH = &H4F und AL = &H02 setzen |
reg.bx = &H101 |
Modusnummer setzen, (hier als Beispiel &H101, 640x480x256) |
CALL INTERRUPTX (&H10, reg, reg) |
BIOS-Grafik-Int &H10 aufrufen |
IF reg.ax <> &H4F THEN |
nochmal kontrollieren ob der Modus unterstützt wird! |
PRINT "Modus nicht vorhanden!" |
END |
END IF |
Abschnitt 12
|
Wie gehts jetzt weiter?
| |
Wenn du jetzt mal die QB-Befehler PSET, LINE, PRINT usw ausprobierst müßtest du sehen das das alles nicht mehr richtig
funktioniert. Ist ja auch logisch, da QB ja kein SVGA kennt. Also müssen wir uns alle Grafikroutinen selber schreiben.
Das ist ein Haufen Arbeit und ich werde dir hier jetzt nur noch zeigen wie man einen Pixel setzt und liest. Alles andere
kann man ja davon ableiten.
Abschnitt 13
|
Wie setze ich einen SVGA-Pixel?
| |
Genauso wie im SCREEN 13 von QB wie es am Anfang des Tutorials beschrieben wurde. Also mit dem direkten schreiben
der Farbnummer ins Video-Segment (&HA000).
zur Erinnerung nochmal das Programm:
SCREEN 13 |
Auflösung setzen (320x200x256) |
DEF SEG = &HA000 |
SEGMENT festlegen mit dem wir arbeiten wollen |
x& = 200 |
y& = 100 |
offset& = (y&*320) + x& |
Die 320 stehen für die X-Auflösung |
farbe% = 15 |
Die Farbe des Punktes festlegen |
POKE offset&, (farbe% AND 255) |
Pixel in den Speicher schreiben |
DEF SEG |
QB-Standart-Segment wiederherstellen |
SLEEP |
Anstatt der 320 müssen wir jetzt 640 eintragen da wir ja eine Auflösung von 640x480x256 haben. Den SCREEN 13-Befehl können wir jetzt
wegnehmen da wir ja schon einen SVGA-Modus (wie oben beschrieben) gesetzt haben.
Das Programm sieht dann so aus:
DEF SEG = &HA000 |
SEGMENT festlegen mit dem wir arbeiten wollen |
x& = 200 |
y& = 100 |
offset& = (y&*640) + x& |
Die 640 stehen für die X-Auflösung |
farbe% = 15 |
Die Farbe des Punktes festlegen |
POKE offset&, (farbe% AND 255) |
Pixel in den Speicher schreiben |
DEF SEG |
QB-Standart-Segment wiederherstellen |
SLEEP |
Dann probieren wir das ganze mal aus mit den XY-Koordinaten 0,0 ; 639,0 und 0,479!
Ahhhh! Was ist das? ein Überlauf bei den letzten Koordinaten? Wo kommt der denn her?
Und zwar:
wenn wir mal offset& per "Hand" berechnen kommen wir auf einen Wert von 306560. Und jetzt haben wir ein
Problem, denn POKE kann ja nur an Adressen schreiben die einen Wert von 0-65535 haben.
Wieso hat es dann beim SCREEN 13 funktioniert?
weil da das maximale OFFSET 63999 war(319,199). Und das funktioniert ja noch einwandfrei!
Wie lautet die Lösung für das Problem?
Und zwar hat man es so gemacht das der VRAM auf der Grafikkarte in sogenannte "Bänke" unterteilt ist.
Die 1. Bank (0) ist ganz vorne im Speicher und enthält die OFFSET's von 0-65535, die 2. Bank(1) enthält alle
OFFSET's von 65536-131071 usw.
Also muß man jetzt ausrechnen in welcher Bank das OFFSET liegt, diese Bank zum schreiben aktivieren und den Pixel reinschreiben!
Wie berechne ich die Bank?
bank& = offset& \ 65536 |
damit schauen wir wieviel mal die Bank ins Offset passt |
oder |
bank& = FIX(offset& / 65536) |
Dann müssen wir das OFFSET (neu) berechnen
offset& = offset& - bank& * 65536 |
damit ziehen wir vom Offset die "Banken ab" |
So, erstmal fertig. Jetzt haben wir eine bestimmte BANK und ein bestimmtes OFFSET.
In dem Beispiel mit 0,479 ist BANK jetzt 4 und das OFFSET 44416.
wie setze ich jetzt eine Bank und was bewirkt das eigentlich?
Das Programm zum Bank setzen:
reg.ax = &H4F05 |
reg.ax auf AH = &H4F und AL = &H05 setzen |
reg.bx = 0 |
reg.dx =bank& |
Bank die gesetzt werden soll |
CALL INTERRUPTX (&H10, reg, reg) |
BIOS-Grafik-Int &H10 aufrufen |
Das ganze bewirkt jetzt das, das was im Video-Segment liegt, genau der BANK-Bereich ist den wir beschreiben wollen.
Das gleiche funktioniert auch fürs Lesen von Pixeln.
Jetzt noch den Pixel an das OFFSET schreiben, und siehe da er erscheint an der richtigen Position.
Wenn er nicht an die richtige Position gesetzt wurde dann kann das folgenden Grund haben:
Wir haben ja vorweggenommen das eine BANK eine Größe von 65536 Byte hat.
ABER das ist leider nicht immer so!!!!
bei einer Grafik-Karte von mir z.B. ist die Bank-Größe nur 4096 Byte. Und da muß man dann fürs Rechnen auch diese 4096 Byte nehmen,
sonst sieht das ganze nicht so toll aus auf dem Bildschirm.
Wie finde ich heraus was für eine Bank-Größe meine Grafik-Karte im Moment hat?
Das macht man so:
dat$ = SPACE$(259) |
Puffer für Daten anlegen |
reg.ax = &H4F01 |
Funktion AH = &H4F und Unterfunktion AL = &01 festlegen |
reg.cx = &H101 |
reg.cx ist die Modusnummer |
reg.es = VARSEG(dat$) |
SEGMENT von dat$ merken |
reg.di = SADD(dat$) |
OFFSET von dat$ merken |
CALL INTERRUPTX(&H10, reg, reg) |
BIOS-Grafik-Int &H10 aufrufen |
banksize& = CVI(MID$(dat$,5,2)) * 1024 |
Bank-Größe herausfinden |
banksize& setzt man dann in die Berechnung ein.
Beim Lesen eines Pixels ist es das genau das gleiche Prinzip. Bloß das man anstatt POKE -> PEEK benutzt.
Und das wars auch schon. Jetzt kann man sich seine eigenen Grafik-Routinen schreiben für 256Farben-Moden.
Wie mache ich das aber mit 32768, 65536 und 16777216 Farben?
Da machst du fast genau das gleiche.
Moden mit 32768 und 65536 Farben benötigen 2 Byte im Speicher anstatt 1 Byte wie mit 256 Farben. Also
mußt du einfach bei 640x480 offset& * 2 nehmen und dann 2 Byte in den Speicher schreiben.
Und bei 16Mc * 3 nehmen. Und 3 Byte in den Speicher schreiben.
Zu sagen ist noch das Moden die mehr als 256 Farben haben, keine Palette mehr besitzen. Dort werden die Farbanteile direkt in das
Video-Segment geschrieben.
bei 32768 und 65536 Farben sind jeweils 5 Bit für RGB zuständig. Bei 16Mc steht ein Byte für Rot, eins für Grün und eins für Blau.
Abschnitt 14
|
Sichtbaren Bereich festlegen:
| |
Man kann mit der beschriebenen Pixel-Routine natürlich auch außerhalb des Bildschirms einen Pixel setzen.
ein Beispiel wäre an den XY-Koordinaten 100,1000.
Das Programm führt das auch korrekt aus und der Pixel "landet" auf der Grafikkarte. Aber in einem Bereich der im Moment nicht an den Bildschirm
übertragen wird.
Und so gibt es natürlich die möglich der Grafikkarte zu sagen ab wo denn nun der sichtbare Bereich anfängt.
Das ganze macht man so:
reg.ax = &H4F07 |
reg.ax auf AH = &H4F und AL = &H07 setzen |
reg.bx = 0 |
reg.bx auf 0 setzen |
reg.cx = 0 |
reg.cx auf 0 setzen |
reg.dx = y& |
reg.dx ist die Y-Koordinate die in der allerersten Bildschirmzeile zu sehen ist |
CALL INTERRUPTX (&H10, reg, reg) |
BIOS-Grafik-Int &H10 aufrufen |
Wenn man dann weiterüberlegt dann kommt man auf die Idee einfach mehrere Bildschirme auf der Grafikkarte abzuspeichern.
Das heißt für den 1. Bildschirm wird y&+0 genommen (Da sie ja ganz vorne ist).
Für den 2. Bildschirm wird y& + Yres& genommen (also + die Y-Auflösung) usw usw...
Und wenn man das macht hat man am Ende sogenannte BILDSCHIRMSEITEN.
Aber wozu sind die gut?
Man kann z.B. Grafiken aus Dateien laden und sie erstmal auf einer Nichtsichtbaren Bildschirmseite unterbringen.
Da dann nicht immer von der Festplatte gelesen werden muß kann man so Grafiken schneller aufbauen.
Oder aber man benutzt es für PAGE-FLIPPING oder DOUBLE-BUFFERING.
Was nichts anderes heißt als das man den kompletten Bildschirm auf einer nichtsichtbaren Bildschirmseite aufbaut und wenn das fertig ist
wird einfach nur der sichtbare Bereich auf die jeweilige Seite gelegt. So kann man auch das Flackern von Animationen verhindern usw usw...
Wieviele Bildschirmseiten man anlegen kann ist abhängig vom VRAM auf der Grafikkarte!
Wenn man einen Wert nimmt der ausserhalb des VRAM's liegt dann fängt der VRAM einfach wieder von vorne an.
Abschluss
|
letzte Aktualisierung: 11.07.2003
| |
So ich hoffe das war verständlich genug. Wenn du weitere Fragen hast oder ich irgend etwas falsch beschrieben habe, dann
Mail an Webmaster.
|
|
|