VESA-Pixel setzen in Assembler
                ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

Hier geht's um die praktische Durchfhrung des Theorietextes ber
das Punktesetzen in den Modi 101h/103h/105h. Ich stelle hier nur
die Routine vor, ein Programm, das diese einbindet, findet Ihr
im Pfad 'SOURCE'.

Also, die Routi:


DOSSEG

.MODEL SMALL
.STACK 256

ScreenSeg equ 0A000h

.DATA

Granu       db          ?
xAufloesung dw          ?
Puffer      db 256 dup (?)
FensterProc dw          ?
            dw          ?

PUBLIC InitSetVESAPixASM
PUBLIC SetVESAPixASM

.CODE

InitSetVESAPixASM PROC NEAR
 MOV AX,4F03h
 INT 10h             ;VESA-Modus kommt in BX zurck

 MOV CX,BX           ;Modus nach CX
 MOV AX,@Data
 MOV ES,AX           ;ES mit Segment (Datensegment) von Puffer
                     ; laden
 LEA DI,Puffer       ;DI mit Offset     "            "    "
                     ;   "
 MOV AX,4F01h
 INT 10h             ;Infos holen und in Puffer ablegen

 MOV AX,WORD PTR Puffer+12h
 MOV XAufloesung,AX  ;XAufloesung aus Puffer in Variable

 MOV AX,WORD PTR Puffer+04h
 MOV CX,6            ;Granularit„t aus Puffer nach CX
Schleife:
  SHR AX,1
  JC Weiter2
 LOOP Schleife
Weiter2:
 MOV Granu,CL      ;Bearbeitete 'Granularit„t' nach Granu2

 MOV AX,WORD PTR Puffer+0Ch
 MOV FensterProc,AX
 MOV AX,WORD PTR Puffer+0Eh
 MOV FensterProc+2,AX
RET
InitSetVESAPixASM ENDP

SetVESAPixASM PROC NEAR
 SFrame STRUC
  BP0         dw ?
  Back        dd ?
  Farbe       dw ?
  y           dw ?
  x           dw ?
 SFrame ENDs

 Frame EQU [BP - BP0]
 PUSH BP
 MOV BP,SP
 MOV AX,ScreenSeg
 MOV ES,AX

 MOV AX,xAufloesung
 MOV BX,Frame.y
 MUL BX
 ADD AX,Frame.x
 JNC Weiter
 INC DX
Weiter:
 MOV CL,Granu
 SHL DX,CL
 MOV DI,AX                            ;Offset nach DI
 XOR BX,BX                            ;Fenster 0 setzen
 CALL DWORD PTR FensterProc           ;Das richtige Fenster ein-
                                      ;stellen
 MOV AL,BYTE PTR Frame.Farbe          ;Farbe holen
 STOSB                                ;Farbe in Videospeicher

 POP BP
RET 6
SetVESAPixASM ENDP

END


Wenn Ihr sie gut durchgeschaut habt und nebenbei noch mit der
Routine aus der Pascal-Rubrik verglichen habt..., dann ist
Euch sicherlich aufgefallen, daá sie anders ist. Sie funktioniert
aber trotzdem!

Es gibt zwei Prozeduren: Die eine (InitSetVESAPixASM) initialis-
iert die Variablen Granu, xAufloesung und FensterProc.
Die andere (SetVESAPixASM) benutzt dann diese 'Startwerte' und
setzt die bergebenen Punkte.
Zuerst, weil es fr's Verst„ndnis einfacher ist, die Prozedur
SetVESAPixASM:
 Die Prozedur baut zu Anfang einen Stack-Rahmen auf, sodaá man
 leicht ber Frame.VARIABLE auf eine der drei bergebenen Variab-
 len (x,y,Farbe) zugreifen kann. ES wird mit der Segmentadresse
 des Videosegments geladen. Weiter geht's mit der Berechnung des
 'PseudoOffsets'. Die Vorgehensweise ist die gleiche, die auch
 im Theorieartikel beschrieben wurde. Doch gibt es da eine
 Schwierigkeit, die sich aber leicht in ein Vorteil umfunktionie-
 ren l„át: Man muá ja eine Multiplikation der y-Koordinate mit
 der x-Aufl”sung durchfhren. Doch kann man in Assembler fr
 eine Multiplikation nur das AX-Register verwenden. Nun ist das
 AX-Register aber nur 16 Bit breit, kann also maximal den Wert
 65535 annehmen. Das reicht aber fr die Berechnung des Pseudo-
 Offsets nicht, die dieser selbst in dem 'kleinsten' Modus
 (101h) bis zu 307199 groá sein kann. Doch: multipliziert man
 eine Zahl, und das Ergebnis wird gr”áer als 65535, paát also
 nicht mehr ins AX-Register, so wird dieses sogesehen auf 32-
 Bit ausgebaut: das DX-Register springt ein. Man kann also in
 DX ablesen, wie oft AX 'bergelaufen' ist. So, und diese Zahl,
 die in DX abzulesen ist, hilft uns nun weiter: Man weiá nun, daá
 der Punkt, der gesetzt werden soll, im DX'ten Videofenster
 liegt, wenn man von einer Granularit„t von 64 KB ausgeht. Das
 ist jetzt wahrscheinlich nur den Wenigsten klar, doch vielleicht
 hilft dieses Beispiel weiter:

 Ich will den Punkt (0/400) setzen.
 PseudoOffset : 400*640 = 256000. Nun wrde in AX E800h und in DX
 3 stehen, da 256000 in Hex 3E800h ist (ehrlich!). Das wrde be-
 deuten, ich máte daá Videofenster, um 3 * 65536 Byte verschie-
 ben, um ber 0A000h:0E800h den gewnschten Punkt zu setzen.
 Naja, war auch kein besonderns gutes Beispiel, aber egal. Halten
 wir fest: In DX k”nnen wir ablesen, um wieviele Segmente (um
 wieviele 65536er-Schritte) wir das Videofenster im Videospei-
 cher der Grafikkarte verschieben mssen. Halten wir durch das
 Beispiel auch noch fest: AX gibt nun direkt den Offset an, wo
 wir den Punkt hinsetzen sollen.
 Nun kommt nur noch eine Sache hinzu: Die x-Koordinate muá ad-
 diert werden. Auch hier kann es passieren, daá AX 'berl„uft',
 also einen Wert annehmen wrde, der gr”áer als 65535 ist. Da
 das nun mal aber nicht geht, wird in solch einem Fall das Carry-
 Flag gesetzt. Man fragt also einfach ab, ob das Carry-Flag auf
 1 steht - und wenn ja, erh”ht man DX nochmals um eins.
 Nun hat man zwar die Anzahl der 'Segmente', um die das Video-
 fenster verschoben werden muá, doch ist ja nicht gegeben, daá
 die Granularit„t ebenfalls Segment-Gr”áe, also 64Kb aufweist.
 Bei mir kann ich das Videofenster zum Beispiel nur mit einer
 Granularit„t von 4Kb verschieben, daá heiát, ich máte das Vi-
 deofenster 16 mal verschieben (64/4=16), um es um ein Segment
 verschoben zu haben. Also máte man an sich 65536 durch die
 Granularit„t teilen und dann die Zahl, die man in DX berechnet
 hat, mal dem Ergebnis nehmen. Nehmen wir das Beispiel von oben,
 wo DX 3 war, meine Granularit„t von 4Kb und die daraus erfol-
 genen 16 Verschiebungen pro Segment und multiplizieren nun DX
 mit diesen 16. Das Ergebnis w„re also 48 - Ich máte mein Vi-
 deofenster 48 mal verschieben, um den oben genannten Punkt rich-
 tig zu setzen. Alles klar? Gut! Nun dauert es aber einfach zu
 lange, eine Multiplikation durchzufhren. Und da gibt es auch
 eine L”sung: Schneller als der MUL-Befehl ist der Befehl SHL,
 der einen Bitweisen Linksshift durchfhrt, was einer Multipli-
 kation zur Basis 2 erm”glicht - also mit 2, 4, 8 etc. Und da
 die Granularit„t ebenfalls immer eine Potenz von 2 ist, l„át
 sich das gut vereinen. Wie das genau abl„uft, steht unten, da
 daá in der Prozedur InitSetVESAPixASM gemacht wird. Hier findet
 SetVESAPixASM nur ein Zahl wieder (in Granu bergeben), die an-
 gibt, um wieviele Stellen DX nach links geshiftet werden muá,
 um die richtige Anzahl der Fenster zu bekommen. Irgendwie habe
 ich den Eindruck, daá das relativ schwierig zu verstehen ist...
 Aber weiter: Hat man nun alles, was man zum Setzen des Punktes
 ben”tigt, berechnet, schiebt man den Offset, der sich noch in
 AX befindet nach DI, setzt BX auf null (also: 1. Fenster setzen)
 und ruft dann mittels einem FAR-CALL die Routine auf, die das
 Videofenster verschiebt. Dies w„re auch mit der Unterfunktion
 05h m”glich gewesen, doch ist es so um einiges schneller. Zum
 Schluá wird dann noch die Farbe in ES:DI, also in 0A000h:Offset
 geschrieben, und der Punkt erscheint am Monitor!

InitSetVESAPixASM:
 Die Routine liest zuerst den VESA-Modus aus, der zur Zeit aktu-
 ell ist. Dieser kommt in BX zurck und wird dann gleich nach CX
 'ge-mov-t'. Dann wird die Unterfunktion 01h (hole Infos) vorbe-
 reitet: ES:DI zeigt auf Puffer. An der Adresse 12h findet sich
 ein WORD, daá die x-Aufl”sung angibt. Eigentlich máte man noch
 abfragen, ob diese berhaupt zurckgeliefert wurden (ber's
 erste Byte im Puffer). Das spare ich mir aber, da diese Info
 eigentlich immer vorhanden ist. An der Pufferstelle 04h befindet
 sich die Granularit„t. Doch k”nnen wir sie so, wie sie dort
 steht, nicht gebrauchen(s.o.). Die Speicherstelle gibt an, in
 welchen Schritten das Fenster verschoben werden kann. Was wir
 aber brauchen, ist, wie oben beschrieben, die Anzahl der Bits,
 um die wir die Anzahl der berechneten Segmente, um die wir das
 Videofenster verschieben mssen, nach links shiften mssen, um
 die reale Anzahl der Verschiebungen herauszubekommen, um die
 das Videofenster verschoben werden muá.... ;->. Toller Satzbau!
 Lest das Ding mindestens noch 50 mal, vielleicht wird dann deut-
 lich, was ich meine - zumindest steht alles drin! Um diese An-
 zahl also zu erhalten, habe ich mir folgenden Zusammenhang klar
 gemacht:

   Vorgefundene  Bin„r-        Nummer der '1'  Anzahl der Links-
   Granularit„t  schreibweise                  shifte
   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
    1            0000 0001     1               6
    2            0000 0010     2               5
    4            0000 0100     3               4
    8            0000 1000     4               3
   16            0001 0000     5               2
   32            0010 0000     6               1
   64            0100 0000     7               0


 Was soll uns diese Tabelle sagen? Sie soll den Zusammenhang
 zwischen der bin„ren Schreibweise der Granularit„t, die man im
 Puffer bei 04h vorfindet, und der Anzahl der Linksshifte, die
 man sp„ter ben”tigt, um die Anzahl der Verschiebungen fr das
 Videofenster zu ermitteln, erl„utern. Wie man sieht, ergibt sich
 diese Anzahl aus 7 minus der Stelle, wo die '1' in der bin„ren
 Schreibweise steht. Also: haben wir eine Granularit„t von 4Kb,
 befindet sich die '1' bei 00000100 an der 3. Stelle. Das heiát,
 wir mssen DX um 4 Stellen (7-3) nach links shiften, bzw. mal
 16 nehmen (s.o.), um das richtige Videofenster einzustellen.
 Haben wir eine Granularit„t von 64Kb, so braucht man DX natr-
 lich nicht mehr verschieben, was man auch aus der oberen Tabelle
 entnehmen kann. Wie das gemacht wird, kann man dem Listing ent-
 nehmen. Zum Schluá der Prozedur wird noch Sprungadresse fr den
 FAR-CALL ermittelt, der im Puffer ab 0Ch steht (Offset und Seg-
 ment).


Tja, damit w„re eigentlich alles erkl„rt. Ich gebe zu, daá es
nicht ganz einfach ist, aber glaubt mir: auch nicht fr mich! Es
ist ziemlich schwierig, ein Problem zu erl„utern, daá man mit-
lerweile in- und auswendig kennt. Aber: Lest den Text einfach
noch ein paar mal. Irgendwann wird schon etwas h„ngenbleiben. Und
wenn nicht, gibt's ja immer noch den Brief an uns...!


                                                 Kemil