Aleš Keprt - Elektronická učebnice Assembleru
http://student.inf.upol.cz/keprt/vyuka/asm/asm6.aspx
Assembler - 6. část poslední změna této stránky: 9.2.2007 Zpět
Poslední šestá část učebních textů k Assembleru se týká specificky operačního systému MS-DOS. Když jsem Assembler učil poprvé, to bylo v r 1999-2000, bylo to tehdy vůbec prvně, co bylo do výuky zařazeno programování ve 32bitovém prostředí Windows. Nebylo jednoduché přesvědčit garanta předmětu, že DOSu již dávno odzvonilo, proto bylo tehdy 16bitové programování v DOSu stále součástí výuky, ale mělo již menší rozsah. Po letech se postupně sekce o programování v DOSu dostaly na vedlejší kolej, až byly úplně vypuštěny a pro svou archaičnost se nyní již neučí vůbec. Tato část učebních textů je tedy jakýmsi archívem starých časů :-), ale také krásnou ukázkou některých programovacích technik, které již odvál čas a v dnešních operačních systémech je již nepotkáte. Praktický význam je nulový, doporučuji k prostudování jen zájemcům o hlubší pochopení, jak počít vlastně funguje.
1. Segmentové registry Segmentové registry (seg.reg.) jsou speciální registry určené k řízení adresace paměti. V praxi se často pletou označení segment, desktriptor a selektor. My se budeme tímto zabývat jen v DOSu, proto hovoříme jen o segmentech a segmentových registrech. Hodnota seg.reg. se při každém přístupu do paměti násobí 16 a přičítá k offsetu, čili adrese v instrukci. Např. seg.reg. ds (data segment) se používá pro adresaci globálních a statických proměnných. Když např. chceme adresovat obrazovku, která je na fyzické adrese 640KB, dáme do seg.reg. hodnotu 640KB/16 a adresujeme jakoby od nuly. short videoram=640*1024/16; _asm { push es mov es,videoram mov [es:0],7 ;bod vlevo nahoře na obrazovce mov [es:1],14 ;bod napravo od něj pop es }
Nezapomeňte, že seg.reg. musíte vždy chránit dvojicí push-pop. Uvedený příklad v DOSu moc nevyzkoušíte, protože předpokládá, že obrazovka je VGA a je přepnutá do grafického režimu. Chcete-li si vyzkoušet totéž v textovém režimu, použijte adresu videoram 0xB8000 (736KB), anebo přepněte VGA do grafického režimu 320x200.
1z7
19.2.2007 7:52
Aleš Keprt - Elektronická učebnice Assembleru
http://student.inf.upol.cz/keprt/vyuka/asm/asm6.aspx
_asm { ;přepne VGA do grafického režimu 320x200 mov ax,13h int 10h
}
;přepne VGA zpět do textového režimu 80x25 mov ax,3 int 10h
Jak vidíte na ukázce, funkce grafické karty se v DOSu volají tak, že nastavíte určité hodnoty do registrů a vykonáte instrukci int 10h. Více informací vám podá Sysman nebo ABSHelp. Následuje tabulka vysvětlující význam jednotlivých seg.reg. značka
název
asociované registry
poznámky
cs
code segment eip
adresování kódu
ds
data segment všechny ostatní
adresování statických dat (ve small modelu i dynamických)
es
extra segment edi v řetězcových instrukcích adresování dynamických dat (přes pointery)
fs, gs
-
ss
stack segment esp, ebp
-
k volnému použití (podobně jako es) adresování zásobníku, segment roste dolů
Jak ukazuje uvedený příklad, seg.reg. se použije při adresaci pomocí dvojtečky, např. [es:si] je totéž jako [si], jenže se bude adresovat podle segmentu es, namísto obvyklého ds. Segmenty mají význam pouze v paměťových modelech jiných než small, kdy umožňují snadno adresovat paměť větší než 64KB. K tomuto účelu jsou také instrukce pro načtení efektivní adresy včetně segmentu lds, les, lfs, lgs. Okopírování hodnoty typu long z pointeru na pointer se tedy provede takto: void copy_long(long *dest,long *src); copy_long proc dest:dword,src:dword uses si,di,es,ds les si,src lfs di,dest mov ax,es:[si] mov fs:[di],ax mov ax,es:[si+2] mov fs:[di+2],ax ret copy_long endp
2z7
19.2.2007 7:52
Aleš Keprt - Elektronická učebnice Assembleru
http://student.inf.upol.cz/keprt/vyuka/asm/asm6.aspx
Všimněte si, že absence registrů fs a gs na CPU 8086 až 80286 znamenala docela problém, protože byl k dispozici pouze es, takže jste při práci se dvěma pointery museli měnit i ds, který je jinak vyhrazen pro práci se statickými proměnnými.
2. Segmentové direktivy Tyto direktivy patří logicky k těm, které již byly probrány v jedné z předchozích kapitol. Ve Windows se však, jak víte, segmenty téměř nepoužívají a kromě jednoho řádku assume, kterým zpřístupňujeme data segment v MASM, se nám tyto direktivy mohou hodit opravdu jedině v DOSu.
assume segreg:data, assume segreg:code, assume segreg:nothing Pomocí assume určujete přiřazení regmentových registrů k segmentům. Používá se to především pro adresování dat. Standardně je nastaveno toto: assume cs:code assume ds:data assume ostatní:nothing
Pokud chcete rezervovat ds na něco specifického a použít na adresování dat např. gs, jednoduše napíšete toto: assume gs:data assume ds:nothing
segcs, segds, seges, segfs, seggs Toto jsou pro změnu instrukční prefixy, které ovlivňují vždy následující instrukci a určují použití jiného registru. Je to potřeba pouze u instrukcí movsb a spol., které nemají jinou možnost, jak určit, se kterými segmenty se pracuje. movsb seggs movsb
;přesun ds -> es ;přesun gs -> es
@code, @data Tyto dva symboly se vyhodnocují na segment kódu resp. dat programu. Využití je zřejmě hlavně na místech přerušení apod., kde je třeba obnovit "náš" data segment do registru ds.
3z7
19.2.2007 7:52
Aleš Keprt - Elektronická učebnice Assembleru
http://student.inf.upol.cz/keprt/vyuka/asm/asm6.aspx
3. Alokace paměti a další funkce MS-DOSu Funkcí DOSu není až tak mnoho jako funkcí Windows, ale stejně jich je celá řada. Zatímco Windows má aplikační rozhraní (API) navrženo v jazyku C a používá proto klasické funkce se jmény a vstupními parametry, starý DOS byl navržen v prehistorických dobách a jeho rozhraní kopíruje styl používaný ještě předtím na 8bitových počítačích v systému CP/M. Rozhraní pro volání funkcí operačního systému je tedy plně v Assembleru a funkce nemají ani jména, ani žádné vstupní parametry, jak jsme zvyklí z Windows nebo jiných novějších operačních systémů. Pro ukázku, jak tedy volání funkcí DOSu funguje, uvádím práci s pamětí - alokace paměti a její uvolnění. (Víme přitom, že ve Windows nejčastěji paměť alokujeme použitím stejných konstrukcí jako v jazyku C - tedy funkcemi malloc a free.) Jako příklad si ukážeme alokování paměti pomocí funkce 48h. char *p; _asm { mov ah,48h mov bx,(velikost+15)/16 int 21h jnc ok ;zde ošetřit chybu ok: mov word ptr p+2,ax mov word ptr p,0 }
;ah = číslo funkce DOSu, kterou chceme volat ;INT 21h = "zavolej DOS"
Takto jsme získali far pointer v proměnné p. Takto alokovanou paměť uvolníme následujícím kódem. _asm { mov ah,49h push es mov es,word ptr p+2 int 21h pop es }
Zastavme se u alokace paměti podrobněji. Spuštění programu v DOSu proběhne tak, že se nejprve alokuje největší volný blok paměti, potom se do něj umístí program, ten se spustí a další už je na něm. Spuštěný (tedy váš) program musí na začátku nejprve zmenšit blok alokované paměti (DOS funkce 4Ah) na takovou velikost, kterou ve skutečnosti potřebuje. Tím se uvolní místo v paměti pro dynamicky alokované proměnné apod. Tuto "inicializaci paměti" provede vyšší jazyk (C++), pokud assembler pouze vkládáte do něj. Pokud celý program máte v assembleru, musíte si vše zařídit sami. Viz Sysman, DOS funkce 4Ah a PSP. Můžete si na ukázku vyzkoušet tento program - přeložte jej do EXE a krokujte v Turbo Debuggeru (TD.EXE, klávesa F8).
4z7
19.2.2007 7:52
Aleš Keprt - Elektronická učebnice Assembleru
http://student.inf.upol.cz/keprt/vyuka/asm/asm6.aspx
model small codeseg ;zkusime alokovat 64KB pamet mov ah,48h mov bx,1000h int 21h ;pamet nelze alokovat - CF=1 a AX=8 (chyba "nedostatek pameti") ;zmensime blok PSP na 64KB ;pri startu programu plati DS=ES=PSP mov ah,4ah mov bx,1000h int 21h ;znovu zkousime alokovat pamet mov ah,48h mov bx,1000h int 21h ;tentokrat se to povedlo - CF=0, AX=pocatecni segment alokovaneho bloku ;ukoncime program (alokovana pamet se uvolni automaticky) mov ah,4ch int 21h end
Pro kompletní popis funkcí DOSu doporučuji jednoznačně prohlédnout interaktivní help ABSHELP nebo TECHHELP (to jsou názvy programů - kdysi bývaly přístupné někde na síťovém disku, musíte pohledat... :-).
4. Vytváření samostatných EXE a COM souborů V DOSu můžete velmi snadno vytvářet samostatné COM nebo EXE soubory, tedy kompilovat assembler bez použití C++ nebo jiného vyššího jazyka. Takové programy jsou pak velmi krátké, zvláště v případě COM souborů. Zde je ukázka "hello world" - program se zkompiluje na 28 bajtů. (Šlo by to zkrátit, ale to by vám komplikovalo pochopení programu.) Tento program použijte jako kostru .asm souboru, ze kterého můžete vytvořit spustitelný COM soubor. hello.asm - zdrojový text i s podrobným komentářem model tiny
5z7
19.2.2007 7:52
Aleš Keprt - Elektronická učebnice Assembleru
http://student.inf.upol.cz/keprt/vyuka/asm/asm6.aspx
.486 .data text db 'Hello World',13,10,'$' .code org 100h start: push cs pop ds mov ah,9 mov dx,offset text int 21h mov ah,4ch int 21h end start
Všimněte si, že zdrojový text je principiálně rozdělen na tři části. První dva řádky obsahují direktivy řídící překlad (tj. něco jako nastavení konfigurace překladače pro tento konkrétní .asm soubor). Direktivou .data nebo také dataseg (obojí je totéž) začíná prostor pro deklaraci dat (obvykle zjednodušeně nazýván datovým segmentem). Direktivou .code nebo také codeseg začíná kód programu. Direktiva end označuje konec souboru. Data se v assembleru dělí do tří sekcí - .const označuje konstanty, .data označuje inicializovaná data, která však nejsou konstantní a konečně .data? označuje neinicializovaná data.
5. Čtení parametrů z příkazové řádky Tato kapitola tady původně vůbec neměla být, ale jelikož programy studentů, kteří se pokoušeli o čtení parametrů z příkazové řádky, nebyly moc robustní, rozhodl jsem se poradit vám, jak správně na to. Čtení parametrů je velmi jednoduché a provedete ho ve dvou krocích: 1. získáte pointer na příkazovou řádku (je někde v paměti), 2. zpracujete obsah příkazové řádky. Parametry příkazové řádky jsou součástí 256bajtového bloku na adrese PSP (Program Segment Prefix - jakási hlavička procesu v paměti). Při spuštění programu je nastaveno DS=ES=PSP, takže jej nemusíte hledat. :-) Jinak PSP získáte pomocí funkce DOSu 62h. Samotné parametry příkazové řádky jsou na offsetu 81h a na offsetu 80h je jejich délka (max.127 bajtů). Pozn.: V memory modelu tiny je celý program v jednom segmentu, takže CS=DS=ES=PSP. Zpracování parametrů příkazové řádky je v okamžiku, kdy znáte PSP, velmi snadné. Nejprve přečtete délku z offsetu 80h. Potom "odříznete" tzv.whitespaces ze začátku i konce řetězce parametrů a to, co zbude, je řetězec zadaný jako parametr z příkazové řádky. Bylo-li zadáno více slov, jsou všechna za sebou (MS-DOS slova jako parametry nerozlišuje, narozdíl od C++). Pokud opomenete odříznout whitespaces, váš program se bude chovat odlišně ve Windows NT, protože originální MS-DOS nechává jako první znak parametru vždy první znak za názvem souboru, tedy mezeru. Tato mezera je však z funkčního hlediska přebytečná a Windows NT a možná i další systémy ji do PSP neukládají. Za whitespaces můžete pro jednoduchost považovat všechny znaky, jejichž číselná hodnota je < 32, tedy nezobrazitelné znaky.
6z7
19.2.2007 7:52
Aleš Keprt - Elektronická učebnice Assembleru
http://student.inf.upol.cz/keprt/vyuka/asm/asm6.aspx
Zpět
7z7
19.2.2007 7:52