Aleš Keprt - Elektronická učebnice Assembleru
http://student.inf.upol.cz/keprt/vyuka/asm/asm3.aspx
Assembler - 3.část poslední změna této stránky: 9.2.2007 Zpět
1. Externí assembler O externí assembler se jedná v případě, že máte zdroják v samostatném souboru s příponou ASM (některé překladače používají jiné přípony, ale to je jedno). V Borland C++ 3.1 (DOS 16bit) se jednoduše přidá do projektu nový .ASM soubor a můžete programovat. Výhoda je v tom, že v takovém externím souboru můžete používat i příkazy asm, které inline asm v BC 3.1 neumí (všechny 32bitové instrukce, makra apod.). BC volá svůj externí kompilátor assembleru TASM (Turbo Assembler), který vyrobí z .ASM souboru .OBJ, který se už přilinkuje do EXE klasickým způsobem. Ve Visual C++ (Windows 32bit) používáme jeho MASM (Macro Assembler). Do verze Visual C++ 6.0 byl MASM distribuovaný zvlášť, ve Windows DDK (driver development kit). Buď si tedy MASM můžete doinstalovat z DDK, nebo můžete alternativně používat i TASM. Od verze Visual C++ 7.0/.NET /2002 je MASM součástí C++ instalace. Jelikož dnes se běžně používá verze 2003 nebo novější, pro použití externího assembleru MASM ve Visual Studiu nepotřebujete nic dalšího instalovat. Ve VC++ můžete do projektu přidat .ASM soubor, bohužel se však soubor nebude automaticky kompilovat assemblerem. Musíte to nastavit ručně. Zde uvedený postup platí pro Visual C++ 2003: V seznamu soburů (Solution Explorer) klikněte na váš .ASM soubor pravým tlačítkem a zvolte "Properties". Zde v "Custom Build Step " nastavte: MASM: (doporučené řešení) command line: ml /Zd /c /Cx /coff /Fo"$(OutDir)\$(InputName).obj" "$(InputPath)" outputs: $(OutDir)\$(InputName).obj TASM: (náhradní řešení) command line: tasm /t /zi /mx $(InputPath),$(OutDir)\$(InputName).obj outputs: $(OutDir)\$(InputName).obj Pokud máte starší verzi Visual Studia než 2002, musíte ještě udělat jednu věc: Zřístupnit TASM resp. MASM někde v pathu (nebo aspoň spouštěcí baťák). Verze 2002 a novější to už má automaticky.
1z7
19.2.2007 7:51
Aleš Keprt - Elektronická učebnice Assembleru
http://student.inf.upol.cz/keprt/vyuka/asm/asm3.aspx
Proč je Borlandí TASM horší: Používá totiž jinou syntaxi chybových hlášení a jiný formát OBJ souborů. První problém znamená, že VC++ vám vypíše jen seznam chyb a čísel řádků, ale nebudete moci chyby prohlížet stejně pohodlně jako chyby v C++ kódu. Druhý problém znamená, že MS Linker sice provede import OBJ souboru, ale bude vypisovat warning (varování) o špatném formátu OBJ souboru při každém linkování. V GCC (DOS/Windows 32bit) musíte použít MASM, protože GCC linker neumí importovat OBJ soubory z TASM. Parametry příkazové řádky jsou stejné jako ve VC++. Týká se to DJGPP, MinGW, CygWin a vůbec asi všech GCC portů na MS-operační systémy. V Linuxu je to s používáním MASM/TASM překladačů ještě složitější. Na závěr ještě vysvětlení parametrů MASM: /Zd generuje informace o číslech řádků (debug info) /c pouze přeložit, ale nelinkovat /Cx rozlišovat velká a malá písmena pouze u exportovaných symbolů /coff generuje formát souborů COFF /Fo jméno výstupního souboru
2. Struktura ASM souboru Soubor ASM může být v různých formátech a taky může mít různou strukturu. My si ukážeme jednu z možných (pro MASM a TASM), kterou budeme pro začátek používat. Tato struktura souboru mi připadá vůbec nejjednodušší. .486 ;budeme používat procesor 486 .model small,c ;paměťový model small, jazyk C jumps ;POUZE TASM - automatické řešení vzdálených podmíněných skoků locals ;POUZE TASM - lokální návěští (v rámci funkce) za @@ .const
;začátek konstant
;deklarace konstant .data
;začátek inicializovaných dat
;deklarace proměnných .data?
;začátek neinicializovaných dat
;deklarace proměnných
2z7
19.2.2007 7:51
Aleš Keprt - Elektronická učebnice Assembleru
.code
http://student.inf.upol.cz/keprt/vyuka/asm/asm3.aspx
;začátek kódu
;kód programu end
;konec souboru
V případě použití 32bitové verze MASM, což je nejobvyklejší situace při práci ve Windows, vynecháváme třetí a čtvrtý řádek, neboť irektivy jumps a locals zde nejsou potřeba. Překladač se totiž takto chová automaticky (tj. vzdálené skoky jsou vždy řešeny a návěští jsou vždy lokální). V TASM tyto direktivy pro pohodlnou práci potřeba jsou. Zatímco v TASM jsou datové segmenty automaticky seskupeny a je možno je adresovat bez uvádění segmentového prefixu ds:, MASM toto nedělá a ani nedovoluje seskupovat segmenty v režimu s přepínačem /coff. Tento poněkud složitě znějící popis se snaží vyjádřit jediné: Při použití MASM budete čelit problémům s globálními proměnnými. Pouze jediný ze tří datových segmentů můžete přiřadit registru ds. Ostatní doporučuji umístit do samostatných zdrojových souborů a odkazovat se na ně pomocí extrn (čímž překladač tak trochu ošálíte, neboť mu vsugerujete představu, že konstanty i inicializované proměnné jsou v sekci .data?, což sice není pravda, ale funguje to, neboť všechny datové segmenty jsou linkerem stejně seskupeny dohromady), nebo u ostatních segmentů prostě používat prefix ds: u každé adresace. Při používání globálních proměnných tedy kostra souboru pro MASM následovně. .486 .model small,c
;budeme používat procesor 486 ;paměťový model small, jazyk C
.data?
;začátek neinicializovaných dat
;deklarace proměnných assume ds:_bss
;přiřazení ds registru k sekci .data?
.code
;začátek kódu
;kód programu end
;konec souboru
Poznámka: První řádek (v případě TASM i MASM) určuje typ procesoru, zde tedy 80486. Tato verze procesoru je podporována všemi překladači assembleru ve všech verzích, na které můžete narazit. Pouze novější překladače však podporují i Pentium (direktiva .586 - starý TASM nepodporuje, MASM ve verzi 6.11 ano) nebo Pentium Pro (direktiva .686 - opět TASM nepodporuje, MASM ve verzi 8.00 ano). Procesory Pentium Pro a kompatibilní 3z7
19.2.2007 7:51
Aleš Keprt - Elektronická učebnice Assembleru
http://student.inf.upol.cz/keprt/vyuka/asm/asm3.aspx
přinesly řadu zajímavých instrukcí, je to však nad rámec výuky v jednom semestru.
3. Funkce V C++ jsou standardně všechna jména public, čili jsou přístupná i z ostatních souborů. V asm je tomu naopak. Standardně jsou všechna jména lokální (jakoby "static" v C++) a deklarací public jméno je zveřejníte. To taky musíte udělat u funkcí, které chcete volat z C++ do assembleru. public mocnina mocnina proc value:dword mov eax,value imul eax,value ret mocnina endp
;jméno mocnina je public ;deklarace funkce s jedním parametrem ;eax=value ;eax*=value ;návrat ;konec funkce
Na uvedeném příkladu vidíme několik důležitých věcí: 1. Deklarace public může být klidně ještě před vlastní definicí jména. Obvykle se tyto deklarace pro větší přehlednost píší na začátek asm souboru. 2. Funkce/procedura začíná konstruktem jméno proc. První slovo je vámi zvolené jméno, za ním následuje klíčové slovo proc. 3. Funkce končí konstruktem jméno endp ("end procedure"). 4. Vstupní parametry se píší za klíčové slovo proc, formát je jako v Pascalu. Tzn. jméno:typ. 5. Návratová hodnota se nikde nespecifikuje, ale nechává se v registru eax (typ dword), případně ax (word) nebo al (byte). V 16bit prostředí se 32bitová hodnota vrací ve dvojici registrů dx:ax. 6. Na konci funkce VŽDY musí být instrukce ret, jinak program běží dál (pokračuje za koncem funkce!). 7. Na tomto příkladu to není vidět, ale nezapomínejte schovávat registry, které použijete. Volající funkci by mohly chybět! Nejjednodušší je použít instrukce pushad a popad, které uchovají resp. obnoví všechny registry kromě segmentových (ty zatím stejně nepoužíváme). Tyto instrukce však uloží na zásobník všechny registry, včetně eax!
4. Vstupní parametry funkce Vstupní parametry, je-li jich víc, se oddělují čárkami.
4z7
19.2.2007 7:51
Aleš Keprt - Elektronická učebnice Assembleru
http://student.inf.upol.cz/keprt/vyuka/asm/asm3.aspx
theproc proc a:dword,b:dword ;deklarace funkce jménem "theproc" se dvěma parametry pushad ;uchování všech registrů ... popad ;obnovení všech registrů ret ;návrat theproc endp ;konec funkce jménem "theproc"
5. Paměťové modely Jelikož naše programy v asm linkujeme s C++, používáme stejné paměťové modely. Tzn. v 32bit prostředí je to vždy model small (zvaný také flat), v 16bit prostředí je to jeden z tiny, small, medium, compact, large. Model huge v asm neexistuje. Uvědomte si, že zatím umíte používat jen model small! Uvedením C do deklarace modelu určíme, že chceme spolupracovat s jazykem C. To je důležité pro názvy funkcí (aby se funkce mezi C a asm vůbec "našly") a při předávání parametrů funkcí. Funkce v C++ potom deklarujeme jako extern "C", protože assembler by naše C++ funkce jinak nenašel. //budeme z C++ kódu volat naši funkci napsanou v assembleru extern "C" void theproc(int a, int b); int main() { //funkci teď zavoláme stejně, jako každou jinou theproc(1,2); }
return 0;
6. Volání funkcí Instrukce call address Instrukce call zavolá podprogram, tedy funkci. Adresu můžete zadat buď jako jméno funkce nebo i nepřímo, tak že si do registru dáte adresu funkce a zavoláte ji přes tento registr. Následující ukázka kódu obsahuje mnoho nových věcí.
5z7
19.2.2007 7:51
Aleš Keprt - Elektronická učebnice Assembleru
http://student.inf.upol.cz/keprt/vyuka/asm/asm3.aspx
.const text db "Ahoj",0
;definujeme řetězec (na konci řetězce musíme uvést binárí nulu!)
.code extrn printf:proc
;funkce printf z Céčka
public theproc theproc proc push offset text mov eax,offset printf call eax pop eax ret theproc endp
;vstupní parametry funkcí se předávají přes zásobník ;načteme adresu printf ;voláme printf nepřímým voláním ;po volání print musíme uklidit zásobník
Poznámka: V TASM můžeme použít pseudoinstrukci call k volání procedur včetně parametrů. Naproti tomu MASM má pseudoinstrukci invoke, která slouží k volání procedur s parametry, ovšem invoke vyžaduje uvedení prototypu (deklarace) volané funkce pomocí konstrukce proto. Syntaxe proto je stejná jako u proc. Ukažme tedy stejný příklad s využitím invoke. .const text db "Ahoj",0
;definujeme řetězec (na konci řetězce musíme uvést binárí nulu!)
.code printf proto text:dword
;funkce printf z Céčka - zjednodušená deklarace s jedním parametrem
public theproc theproc proc invoke printf,offset text ;voláme printf pomocí pseudoinstrukce invoke ret theproc endp
7. Deklarace proměnných Proměnnou (s názvem text) jsme deklarovali již v předchozím příkladě. Je to tedy již jasné. jméno db hodnota
6z7
;1bajtová proměnná = typ byte (db=declare byte), také může být řetězec
19.2.2007 7:51
Aleš Keprt - Elektronická učebnice Assembleru jméno dw hodnota jméno dd hodnota
http://student.inf.upol.cz/keprt/vyuka/asm/asm3.aspx
;2bajtová proměnná = typ word (dw=declare word) ;4bajtová proměnná = typ dword (dd=declare double word)
Proměnné takto deklarujeme pouze v externím assembleru. Nezapoměňte je psát do sekce "const", "data?" nebo "data". Neinicializované proměnné deklarujeme tak, že místo hodnoty napíšeme otazník.
8. Externí deklarace Externí jména deklarujeme, jak ukazuje příklad, pomocí klíčového slova extrn (jen jedno e!). extrn extrn extrn extrn extrn extrn extrn
jméno:byte jméno:word jméno:dword jméno:proc jméno:pointer jméno:ptr jméno:integer
;proměnná typu byte ;proměnná typu word ;proměnná typu dword ;procedura (bez parametrů) ;proměnná typu pointer (TASM) ;proměnná typu pointer (MASM) ;proměnná typu integer (TASM)
Poznámka: Deklarace prototypu funkce pomocí proto (viz výše) zcela nahradí deklaraci pomocí extrn. To berte jako návod pro práci v MASM, týkající se jen funkcí, nikoliv proměnných. (Je přímo zakázáno použít současně extrn i proto.) Zpět
7z7
19.2.2007 7:51