

                   In VESA-Modi Pixel setzen
                   


Tja, ich habe lange berlegt, ob ich hier eine Routine fr einen
speziellen VESA-Modus vorstellen soll, so zum Beispiel fr den
640x480x256-Modus (101h), oder ob ich eine Routine bringen soll,
die fr jeden Grafikmodus benutzt werden kann. Dabei stellte
sich mir allerdings die Frage, ob so eine Routine

 a) berhaupt mglich ist, ohne ganz billig einfach abzufragen,
    in welchem Modus man sich befindet, und dann verschiedene
    Prozeduren einfach anspringt und dort die Punkte setzt

 b) ob es sinnvoll ist, solch eine Prozedur zu schreiben, da man
    schlielich einiges an Rechenzeit verschwenden wird, wenn
    man immer erst unterscheiden mu, in welchem Modus man ist
    und dem entsprechend verschieden verfahren mu.

Ich bin darauf gekommen, eine Routine zu schreiben, die bei allen
256-Farben Modi funktioniert. Dabei mu ich nicht allzu viel fr
die verschiedenen Prozeduren verndern, habe EINE Routine fr
alles und bin noch relativ schnell weg. Allerdings: Wenn man eine
optimale Routine haben will, sollte man sie sich tatschlich ganz
speziell fr den gewnschten Modus schreiben, alles Andere kostet
einfach zuviel Zeit.

Also: 256 Farben Modi, was sagt uns das?
In einem solchen Modus nimmt ein Pixel immer genau 1 Byte ein,
das heit: Der Adresse A000h:0000h steht immer fr den links oben
erscheinenden Pixel, die Adresse A000h:0001h immer fr den rechts
daneben stehenden usw.  Da ein Byte genau 256 verschiedene Werte
aufnehmen kann (2^8 = 256 / 0 - 255), sollte dieses klar sein.
Der einzige Unterschied: Die Anzahl der Zeilen und Spalten sind
anders. Im Modus 101h habe ich 640 Spalten und 480 Zeilen, im
Modus 103h hat 800 Spalten und 600 Zeilen und der Modus 105h hat
1024 Spalten und 768 Zeilen.
Daraus ergibt sich, da natrlich mehr Speicher fr den Modus
105h als fr den Modus 103h und dieser mehr als fr den Modus
101h bentigt. Das wiederum heit, da ich, wenn ich den aller
letzten Punkt in jedem Modus setzen will, ich bei den hheren
Modi das 'Videofenster' in den Videospeicher der Grafikkarte
weiter verschieben mu, als in den niedrigeren. Aber dazu sp-
ter noch mehr.

Was uns nun beschftigen soll, ist: Wie finde ich heraus, in wel-
chem Modus ich mich befinde, wenn ich mich innerhalb meiner
'SetzePunkt' Prozedur befinde und nicht extra angeben will,
welcher Modus es ist (was zwar auch mglich ist, doch wre das
sicherlich nicht so 'elegant', als wrde diese Info die Pro-
zedur alleine herausfinden). Dafr gibt es eine ganz einfache
Lsung: Die Unterfunktion 01h des VESA-Treibers, der ber die
Funktion 4Fhh des Video-Interrupts 10h angesprochen wird, liefert
einem eine ganze Menge an Informationen ber einen angegebenen
Modus zurck. Unter anderem auch die x/y-Auflsung. Allerdings
ist dies leider keine Pflicht fr jeden Treiber. Doch ist mir
bisher keine andere Mglichkeit bekannt, die Auflsung dann doch
zu erhalten. Das einzige, was wir dafr noch bentigen, ist der
Modus selber, und den erhalten wir mit der Unterfunktion 03h.

Also: Zuerst fragen wir in unserer Routine die aktuellen Video-
modus ab, indem wir die Unterfunktion 03h befragen. Der Modus
wird uns in BX zugeteilt. Hier sollten wir dann kurz berprfen,
ob der Modus 101h, 103h oder 105h ist, und ansonsten die Pro-
zedur (eventuell mit einem Fehlercode) beenden.
Diesen Wert mssen wir dann ins CX-Register laden und die Unter-
funktion 02h aufrufen. Gleichzeitig mssen wir aber mit ES:DI
einen 29 Byte groen Puffer gekennzeichnet haben. Nun knnen wir
im Bit 1 (das 2. Bit) des Offsets 0 im Puffer ablesen, ob die
optionalen Informationen (darunter auch die Angabe ber die
x/y-Auflsung) mitgeliefert wurde. Wenn ja, ist das Bit 1 ge-
setzt. Ansonsten mssen wir wiederum die Prozedur verlassen.

Nungut, wir haben jetzt die x/y-Auflsung des gesetzten Modus.
Im Puffer an dem Offset 12h steht die x-Auflsung, und an dem
Offset 14h finden wir die vertikale (y-) Auflsung.

Was nun?
Der Prozedur sollte die x und die y Koordinate des gewnschten
Punktes sowie dessen Farbe bergeben werden. Um nun an die ge-
wnschte Offsetadresse im Videosegment bei A000h zu kommen, mu
man die y-Koordinate mit der x-Auflsung multiplizieren und dann
die x-Koordinate hinzu addieren.
Beispiel: Modus 101h: x-Auflsung  : 640, y-Auflsung  : 480;
                      x-Koordinate :  56, y-Koordinate :  60.
yK * xA = 60 * 640 = 38400. 38400 + xK = 38400 + 56 = 38456.
Der Pixel mu also an die Adresse A000h:38456d bzw. A000h:9638h
geschrieben werden.
Nimmt man aber nun das Beispiel xK = 56 und yK = 400, dann wrde
als Offset die Zahl 256056d bzw. 3E838h herauskommen. Nicht
schlimm? Doch! Denn dieser Offset wrde schon in einem ganz an-
deren Segment liegen! Man mu also einen Wert erhalten, der
innerhalb der Segmentgrenzen liegt, also grer oder gleich 0
und kleiner oder gleich 65535 sein mu.
Nun wird es kompliziert: Liegt der bestimmte Offset auerhalb
dieser Schranken, dann mu er in dieser 'hereingeschoben' werden.
Das heit, er mu verringert werden. Aber nicht ohne Ausgleich!
Um den gleichen Wert, um den der Offset verringert wird, mu
gleichzeitig die Adresse des Videofensters vergrert werden.
Zur Erinnerung: Das Videofenster dient als Zeiger in den Grafik-
speicher der Grafikkarte. Es wird beim Segment A000h im Haupt-
speicher eingeblendet. Da dieses Fenster im Allgemeinen auch nur
maximal 64 Kilobyte gro ist (also ein Segment umfat), mu es im
Videospeicher der Grafikkarte verschoben werden, um Modi, die
mehr als 64 Kb Speicher verbrauchen zu adressieren. Wenn ich also
zum Beispiel im Modus 101h den Pixel mit der 'Offsetadresse'
65536 setzen will, so mte ich das Videofenster ersteinmal z.B.
auf da zweite Segment im Speicher der Grafikkarte setzen (es
zeigt nun also auf die Pixel 65536-131071) und dann an die Off-
setadresse 0000h den Pixel plazieren. Ich knnte das Videofenster
aber auch so verlegen, da es auf die Pixel 32768-98303 zeigt und
dann die Offsetadresse 32767 einen Punkt plazieren. Der Effekt
wre der gleiche. ABER: WENN IHR DAS JETZT NICHT VOLLSTNDIG ODER
VIELLEICHT NICHT EINMAL ANSATZWEISE VERSTANDEN HABT: MACHT NIX!
LEST ES NOCH 50 ODER 60 MAL, VIELLEICHT BLEIBT DANN ZUMINDEST
ETWAS HNGEN... (Ich hoffe nur, da ich mich bei den Zahlen nicht
verrechnet habe).
Was aber auf jeden Fall nun klar sein sollte: Ich kann nicht ein-
fach den ermittelten Offset verringern, wenn ich nicht gleich-
zeitig das Videofenster nach 'rechts' verschiebe, also dessen
Lage im Grafikspeicher der Karte auf eine um den gleichen Wert
erhhten Offset setze.
Wie gehe ich nun dabei vor? Das einfachste ist, wenn man nun die
Granularitt ermittelt, mit der das Fenster verschoben werden
kann. Sie steht an der Offsetadresse 04h des Puffers. Zu beachten
ist, da diese in Kb angegeben ist, man mu den Wert also noch
mit 1024 multiplizieren, um auf die Byteanzahl zu kommen. Man
kann sich aber auch die beliebte binre Logik als Brainstorming
an tun, und die dort angegebene Zahl einfach um 10 Bits nach
links verschieben - geht schneller und ist schner...
Also: Granularitt nehmen und um 10 Stellen nach links shiften.
Das Ergebnis nehmen, und wenn der Offsetwert grer als die
oben angegebenen Grenzen sein sollten, diese solange um das
'Granularittsergebnis' (das so eben berechnete, um 10 Stellen
nach links geshiftete Ergebnis) verringern, bis es innerhalb
der genannten Grenzen liegt. Dabei mu man sich aber unbedingt
die Anzahl der Abzge merken. Nun nehme ich diese Anzahl, stecke
sie ins DX-Register, und verschiebe das Videofenster mittels der
Unterfunktion 05h.
Nun ist fast alles geschafft! Ich mu jetzt nur noch an die neue
(eventuell verringerte) Offsetadresse (A000h:Offset) den Wert der
Farbe schreiben, die zuvor der Routine bergeben wurde.

Beispiel: xA : 1024, yA : 768; xK : 376, yK : 677.
Ermittlung des 'PseudoOffsets': yK * xA = 677 * 1024 = 639248.
639248 + xK = 639248 + 376 = 639624 = PseudoOffset.
Nun schaue man im Puffer nach und hole sich die Granularitt.
Granu z.B. = 32. 32 * 1024 = 32768.

         Offset              Anzahl
         
         639624                   0
         606856                   1
         574088                   2
         541320                   3
         508552                   4
         475784                   5
         443016                   6
         410248                   7
         377480                   8
         344712                   9
         311944                  10
         279176                  11
         246408                  12
         213640                  13
         180872                  14
         148104                  15
         115336                  16
          82568                  17
          49800                  18

Man mu das Videofenster also um 18 multipliziert mit der Granu-
laritt verschieben (hier : um 589824 Bytes). Hat man das getan,
ldt man an die Adresse A000h:49800d bzw. A000h:C288h den Wert
der Farbe und darf sich glcklich schtzen. Natrlich mu man
den Wert der Granularitt nicht unbedingt jedesmal abziehen. Man
kann den 'PseudoOffset' auch die die Granularitt teilen, das
Videofenster um das ganzzahlige Ergebnis multipliziert mit der
Granularitt verschieben, und den Rest der Division als neuen
Offset benutzen. Geschmackssache!

Tja, nun. Ich glaube, da war relativ schwierig. Auch fr mich.
Wahrscheinlich verstehe ich beim nchsten durchlesen selber nicht
mehr, was ich da alles meinte. Pech! Das gute: Ich werde gleich
ersteinmal ausprobieren, ob die Sachen berhaupt stimmen...
Weitere Ergebnisse bekommt Ihr dann in der Assembler und
vielleicht auch noch in der Pascalrubrik zu sehen. Schaut mal
rein, wenn Euch die Theorie nicht schon ganz abgeschreckt hat.
Vielleicht versteht Ihr im Programm dann ja auch dies oder das
noch besser.
Mein Tip: Unbedingt noch einmal lesen!

Nun noch eine Komplettbersicht, die Euch als Zusammenfassung
dienen soll: Um einen Pixel in einem der Modi 101h, 103h oder
105h zu setzen, sind folgende Schritte zu tun:
- Man multipliziert die y-Koordinate mit der x-Auflsung und
  addiert zum Ergebnis die x-Koordinate. Das Ergebnis wird im
  folgenden PseudoOffset genannt.
- Man finde die Granularitt heraus, und multipliziere sie mit
  1024 = GRANU.
- Man ziehe vom PseudoOffset GRANU sooft ab, bis dieser innerhalb
  der Schranken von 0 bis 65535 liegt.
- Man merke sich gleichzeitg aber, wie oft man GRANU abgezogen
  hat = ANZAHL.
- Man verschiebe das Videofenster mittels der Unterfunktion 05h
  der VESA-Treiber um ANZAHL*GRANU (ANZAHL wird ins DX-Register
  geladen).
- Offset = eventuell verringerter Pseudooffset.
  Man lade die Farbe an die Adresse A000h:Offset und freu sich
  ber einen neuen Klecks am Monitor.

                                                 Kemil


* Hier ein paar Dinge, die eventuell behilflich sein knnten:

 - Lest den Text, in dem die VESA-Treiber-Funktionen erlutert
   werden (ein Button ber diesem!).
 - Die Koordinaten reichen immer von 0 bis zu ihrer Auflsung-1!
   Das heit: Auflsung 640x480: xK von 0 bis 639, yK von 0 bis
   479.
 - 2^10 = 1024
 - Schnelle Punkte setzen in einzelnen Modi erst in der nchsten
   Ausgabe
 - Es gibt (k?)ein Leben nach dem Selbstmord!

PS: Ich habe hier auch irgendwo gesagt, da die Unterfunktion 01h
einen Zeiger ES:DI braucht, der auf einen 29 Byte groen Puffer
zeigt (dafr sind Zeiger schlielich da...). Nun reichen diese
29 Byte aber irgendwie einfach nicht aus. Daher: nehmt einen
Puffer mit einer Grer von 256 Byte. Das klappt (zumindest bei
mir).

