Az elveszett VESA |
|
Anno, még a kilencvenes évek elején hatalmas káosz kezdett el eluralkodni a videovezérlők piacán, már ami ezeknek a videovezérlőknek a programozását jelenti. Minden egyes hardver gyártó cégnek megvoltak a saját megoldásai, amelyeknek természetesen meglehetősen kevés közös tulajdonsága volt a konkurens cégek hasonló termékeivel, minden videokártya gyártó más módon inicializáltatta a bizonyos SuperVGA felbontásokat. Mivel nem volt egy jól átgondolt és logikus rendszer, mindenki csinálta amit jónak látott. Szegény programozók meg csak ültek középen, és fogták a fejüket: ha megírom a programot a Trident videokártyás gépemen, biztos hogy futni fog a kliens S3 kártyáival? Ha nem használok SuperVGA módot, lehet, hogy nem fogja átvenni a programot, mert csúnyának tartja a 16 színt? Ilyen és ehhez hasonló kérdések ezrei merültek fel a kétségbeesett programozók fejében, amikor valaki "magasabb szinteken" is megelégelte a káoszt, és a Video Electronics Standards Association bevezette a standardizált SuperVGA felbontásokat, ezeknek a beállítási módszerét, lekérdezését, és más hasonló hasznosságokat. Mivel azonban a Microsoft ® Windows™ is
ezekben az időkben kezdett lábra kapni, a világ inkább a Windows
Driverek felé fordult, és a VESA valahogy a háttérbe szorult. Mi mégis úgy
gondoltuk, hogy legalább egy cikk erejéig fogunk ezzel a méltatlanul
elfeledett standardizálási kísérlettel, és bemutatjuk, miként lehetett annak
idején "csúcsgrafikázni". Ahhoz, hogy belevághassunk a munkába, szükségünk lesz egy DOS alatti C fordítóra, kevés ASM illetve C tudásra, és rengeteg lelkesedésre. Ha mindezeket beszereztük, akkor kezdjük el… |
|
Én itt vagyok... |
|
Ahhoz, hogy megtudjuk, hogy a videokártyánk támogatja-e a VESA-t (ugyanis nem minden videokártya gyártó fogadta el a VESA-t, de ne aggódjunk, minden mai kártya valamilyen mértékben támogatja) a következőt kell művelnünk: int Init() { char tmp; asm {
mov ax,0x4f00
mov di, offset InfRec
push ds
pop es
int 0x10
mov tmp,al } return (tmp==0x4f); } vagyis, hogy érthetőbb legyen: a 10h megszakításnak adjunk át egy 4f00h-t az AX-be, illetve az ES:DI –be egy TInfRec struktúrának a címét, amely a következőképpen néz ki: struct TInfRec { char _Signature[4]; unsigned int _Version; char* _pszOEM; unsigned long _Abilities; unsigned int* _Modes; unsigned char _FormInfoRec[18]; unsigned char _Data[238]; unsigned char _nothing[256]; } InfRec;
Most jöjjenek a magyarázatok, mert gondolom, ez így nagyon száraz volt. A _Signature mindig azt tartalmazza, hogy "VESA". A _Version magasabbik rendű bájtjában a nagyobbik, alacsonyabb rendű bájtjában a kisebbik verziószám (ne feledjük, DOS alatt vagyunk, ahol az int 16 bites). A _pszOEM tartalmazza a gyártó azonosítóját, vagy bármi más információt, amit a gyártó szövegként a kártyához mellékelt. Az _Abilities nulladik bitje, ha be van állítva, akkor a DAC szélességén tudunk változtatni, ha nem akkor nem. A _Modes tömb tartalmazza az összes videómódot, amit a kártya kezelni tud, a legutolsó ffffh. Ez nem videómód. A _Data első négy karakteréből alkotott long értéke megmondja, hogy hány darab 64k-s memóriablokk van a videokártyán, a többi nem publikus. Az egész eddig leírtakból a legérdekesebb talán az a tömb, amely a videómódokat tárolja. És valóban, hisz ez mondja meg, hogy egyes videómódok mire képesek, mekkora a felbontásuk, a színmélységük, satöbbi. Természetesen, a következő kérdés, ami felmerül: Jó, Jó, de abba a tömbbe csak számok vannak, honnan tudom én meg egy számból, hogy a hozzárendelt videómód mire képes. A válasz pofonegyszerű, a következő függvénnyel: void GetModeInfo(unsigned
int mode) { asm {
mov di,offset mdInf
push ds
pop es
mov ax,0x4f01;
mov cx,mode
int 0x10 } } ahol a mdInf a következő struktúra: struct TModInf { unsigned int _ModeAttrs; unsigned char _WinAAtrs; unsigned char _WinBAttrs; unsigned int _WinGran, _WinSize, _WinASeg, _WinBSeg; unsigned long _Handler; unsigned int _ScanLineW; unsigned int _HorzRes, _VertRes; unsigned char _CharWidth, _CharHeight, _NumPlanes, _BPP, _NumBanks, _MemModel, _BankSize, _Reserved[227], _Rest[256]; } mdInf;
Kicsivel bővebben: A _ModeAttrs bitjei a következőket árulják el: be lehet állítani (0. Bit), a 10h megszakítás 0ah illetve 0eh karakterkiíró függvényeit támogatja (2.bit), színes mód (3. bit), illetve grafikus mód (4. bit). Ami még érdekes ebből a struktúrából, az a _HorzRes illetve a _VertRes, amelyek magát a képernyő felbontást adják meg, illetve a _BPP ami a színmélységet tartalmazza. A _CharWidth illetve _CharHeight mezők határozzák meg egy karakter szélességét illetve magasságát ebben a felbontásban. A _NumBanks megmondja, hány bank fogadja be a képernyőre kirakható pixelek összességét. A bank alatt egy lineáris zónát kell érteni, amelynek mérete általában 64k, de inkább a _BankSize-t használjuk erre, mivel ez biztosabb. A videomemória több, ilyen egymás utáni bankból áll, balról jobbra, és fentről lefele, és mindig meg kell vizsgálnunk, hogy az aktuális pixel, amit éppen ki akarunk rakni a hányadik bankba fog kerülni, de ez a folyamat lejjebb még bemutatásra kerül. Most, hogy mindent tudunk a videómódról, nem ártana beállítani: void SetMode(unsigned
int mode) { oldbank = 0 ; asm {
mov di,offset mdInf
push ds
pop es
mov ax,0x4f01;
mov cx,mode
int 0x10
mov ax,0x4f02
mov bx,mode
int 0x10 } }
Amint látjuk, a függvény nemcsak beállítja a videomódot, hanem lekérdezi a módhoz tartozó információkat is. És most, hogy be van állítva ez a szépséges videomód, az alapművelet, egy pixel kirakása lesz a következő műveletünk. Viszont kijelentem, hogy ez nem a legoptimálisabb SVGA putpixel metódus, sokkal inkább oktatási célokra alkalmas: hogy NE írjunk SVGA putpixelt. Na, tessék: void PutPixel(unsigned
int x, unsigned int y, unsigned char col) { register unsigned long addr=((long)mdInf._ScanLineW*y)+x; register unsigned int raddr = addr % 65536; newbank=addr>>16; if(oldbank!=newbank) {
oldbank=newbank; asm {
mov ax,0x4f05
xor bx,bx
mov dx,newbank
int 0x10 } } asm {
mov ax,mdInf._WinASeg
mov es,ax
mov di,raddr
mov al,col
stosb } }
Ez egy teljesen optimaliizálatlan putpixel, de akinek kedve van az csak fejlesztgesse tovább. Megfigyelhetjük, hogy miként kell vizsgálni, hogy túlléptünk-e az aktuális bankunkból, és ha igen, akkor hogy váltunk bankot. Ezt a legtöbb helyen nagyon leoptimizálják az aktuális felbontásra, és hajlamosak a programozók konstansokat használni, viszont ez a putpixel működik bármilyen felbontásra, ami 256 színen alapul. Igaz, hogy én is használtam egy 65536-ot, mint a bankméret, de általában ez mindenütt ennyi. Utoljára meg kijelentem, hogy a mellékelt .h fájlt mindenki saját belátása szerint használhatja, viszont én nem vagyok felelős semmiféle mellékhatásért, kárért, bármiért, amit ennek a fájlnak a használata nyomán fedeztek fel a kedves olvasók. Még azt sem garantálom, hogy működik egyáltalán. Aránylag rég írtam, és nem nagyon fejlesztettem tovább, úgyhogy ez limitáltságában marad meg ilyennek amilyen. |
|
Deák Ferenc |