borland pascal:
z realu do protektu
První věc, kterou člověk většinou zkusí, je vzít nějaký svůj
oblíbený program psaný v realu a zkompilovat ho pro protekt (v
BP.EXE je volba real / protekt / windows). Když nevzniknou žádné
syntaktické chyby (to by ani myslím neměly) a program se rozběhne,
obvykle záhy skončí s hláškou Runtime Error 216 neboli General
Protection Fault (GPF). Zklamaný programátor se vrátí do realu a
žije šťastně až do smrti.
Jak to bylo vlastně se mnou... stalo se mi to taky ? Drrrr,
drrrrr.. paměť zvyšuje obrátky. Ááá, já vlastně uvažoval jestli s tím
zapraseným zdrojákem Pařeniště seknu, budu pokračovat v realu,
přepíšu ho do Cčka nebo do protektu. První vypadl real. Potom jsem
musel ověřit, co to vůbec ten protekt je a jestli v něm může něco
jako Pařeniště fungovat. Kniha Mikroprocesory Intel Pentium a spol
od Grady podobné otázky zodpoví dost dobře, doporučuji. Poté
zbývalo prakticky ověřit, jak to s tím protektem v Pascalu vlastně
je. Dneska mi už přijde absurdní představa, že jsem to nevěděl, ale
ono to asi opravdu nikde není napsané. Výsledek průzkumu byl tento:
16bitový kód, 16bitová data (tj. xor ax,ax vynuluje ax a db 66h; xor
ax,ax vynuluje eax, lodsb čte z [si] a db 67h; lodsb z [esi], všechno
přesně jako v realu). Najednou to vypadá, že mezi protektem a
realem v Borland Pascalu není žádný rozdíl (kromě detailů jako
alokace paměti). Zkusím sepsat na jaké hlavní rozdíly jsem postupně
narazil.
Nejde zapisovat do CS.
- Dá se obejít i bez toho. Někdy to vypadá, že ne, ale jde to.
Nejde zapisovat do $A000.
- Zato jde do SegA000 (dále SegB800, Seg0040, SegB000).
Nejde číst ani zapisovat na adresy, které nejsou tvoje.
- Interrupty nastavovat slušně.
- Dávat si pozor na konce polí apod.
Nejde pracovat s neplatnými pointery (kromě nil), jinak řečeno, do
segmentového registru nejde zapsat neplatný selektor.
- Dávat si pozor.
- Když je to nutné, dá se s neplatným pointerem pracovat po
přetypování na longint (BP pak nenahrává segment do segmentového
registru).
Nejde všechnu paměť alokovat přes INT 21.
- Zkusit používat pascalský GetMem.
Nejde alokovat souvislých 64KB a víc.
- Už jsem o tom psal v tricích, nejlepší je asi použít GlobalAlloc z
unity WinAPI.
Nefungují VESA BIOS, Get ROM font, Event wait a další služby
systému (já zatím narazil jen na tyto).
- Co se stane když napíšeš INT 10h ? Nezapomeň že jsi v protektu a
obsluha INT 10h je psaná v realu. Stačilo by přepnout do realu a
provést původní obsluhu ? Asi ne.. :) Rozdíl je mezi segmenty a
selektory (odkazuji na Mikroprocesory Intel..). Když máš službě v
registrech předat pointer, předáš selektor a offset. Ale realová
obsluha chce segment a offset. I kdybys znal ke svému selektoru
příslušný segment, nemohl bys ho předat, protože ten nejde zapsat
do segmentového registru.
Od toho tu je RTM.EXE. Ještě v protektu zachytává interrupty a
tam, kde je to třeba, převádí selektory na segmenty. Stejně tak po
skončení služby konvertuje případné vrácené segmenty na selektory.
A protože většina tvých dat leží nad 1MB, ještě je musí kopírovat
do konvenční paměti pod 1MB, případně zpátky (realová obsluha nad
1MB nedosáhne).
Vypadá to promakaně (ne moc rychlé, ale funkční), tak kde je
problém ? RTM musí mít přehled o všech funkcích systému. Když
zavoláš třeba INT 21 s AX=3d00, RTM musí vědět, že tahle služba
pracuje s pointerem DS:DX na blok 256(?) bajtů, aby těch 256 bajtů
z DS:DX zkopčil do konvenční paměti a do DS dal segment. Jiná služba
může mít v DS třeba offset, takže nejde automaticky převádět
všechny segmentové registry.
RTM se snaží, ale všechny služby prostě nemůže znát, zvlášť
když vznikly později než RTM. A jsou tu i jiné záludnosti. Třeba AT
služba Event wait. Té předáš pointer na boolean a počet mikrosekund.
Služba skončí hned, ale za udanou dobu boolean nastaví. To prostě
nejde. Když je tvůj boolean nad 1MB tak nejde zařídit, aby ho
realmódová služba Event wait nastavila.
Řešení těchto problémů existuje. Jsou to DPMI služby (INT 31).
Namátkou: Alloc Dos Mem, Free Dos Mem, SegmentDescriptor, Simulate
Realmode Interrupt (dále viz interrupt list). Pomocí nich můžeš
naalokovat konvenční paměť tak, že dostaneš segment i selektor. Se
segmentem se budeš obracet na realmódové služby, se selektorem
budeš pracovat sám. Alokovat konvenční paměť jde i v unitě WinAPI a
obyčejným voláním INT 21, ale nejlepší zkušenosti mám s DPMI.
Automaticky se nastavuje {$define dpmi} (v realu define msdos).
Nejde debugovat.
- Externí debugger TDDPMI sice není tak cool jako 'internal
debugging', ale dá se s ním překvapivě rychle sžít. Dokonce má i
některé výhody (oceňuji posmrtné prohlížení lokálních proměnných).
Počítač se méně hroutí.
- Konečně nějaká výhoda. Tam kde se v realu zhroutil teď nejspíš
hodí runtime error.
Offset první proměnné v DS je jiný.
- V realu je první proměnná v datasegmentu na offsetu 2, v
protektu 16. To pro případ, že bys chtěl mít pole bajtů takové, že
index prvku je roven jeho offsetu (moc fajn vlastnost, mám ji v
Pařeništi tak zažranou, že se jí nemůžu zbavit).
Nejde číst/psát word na 0ffff apod (přes hranici segmentu).
- Dávat pozor.
- Vlastně by to nemělo jít ani v realu, ale vzpomínám si, že zrovna v
realu (ne ve V86) to nějak prošlo.
Celá paměť je přímo přístupná (žádné handlování s XMS,EMS).
- Da cool !
Dee