MS Visual Studio je velice všestranný nástroj a jeho použití může sahat až do oblastí, pro které nejspíš nebylo určeno. Tento článek ukazuje možnosti Visual Studia pro vývoj programu v assembleru. Na příkladu jednoduchého prográmku "Hello, World !!" napsaného v assembleru si ukážeme, že komfort vyvíjení programu v tomto nízkoúrovňovém jazyce je skoro tak vysoký jako při vyvíjení programu psaného v C nebo v C++.
Úkolem tohoto článku není popisovat programování v assembleru pod Windows, proto zde uvedu pouze výpis programu "Hello, world !!". Pro informace o assembleru, fóra a hlavičkové soubory zkuste třeba MasmForum.
.586 .MODEL FLAT,STDCALL OPTION CASEMAP:NONE UNICODE=0 include inc/windows.inc include inc/user32.inc includelib lib/user32.lib ?WINDOWS=1 .data szMessage db "Hello, world !!!", 0 szTitle db "Assembler",0 .code WinMain PROC hInstance :HANDLE, hPrevInstance:HANDLE, lpszCmdParam :LPSTR, nCmdShow :WORD INVOKE MessageBox, NULL, offset szMessage, offset szTitle, MB_OK ret WinMain ENDP end
Program je velice primitivní, zobrazí pouze jeden message box. Syntaxe souboru odpovídá překladači Macro Assembler od Microsoftu (ml.exe).
Vytvoříme nový projekt jako aplikaci pro Win32, nazveme jej třeba "AsmHello". Na další záložce zvolíme "An empty project". Visual Studio nám oznámí, že v tomto případě za nás nic neudělá a vytvoří prázdný projekt.
Do složky, která byla vytvořena, nakopírujte soubor AsmHello.asm a adresáře inc a lib (Získáte je z příkladu ke stažení). Do projektu ve Visual C++, do složky "Source Files" vložte soubor AsmHello.asm.
Pokud se pokusíte přeložit takto napsaný projekt, nedostanete sice žádnou chybu, ale ani žádný EXE soubor. Pro soubory soubory s příponou ASM totiž není definovaná žádná akce. Aby Visual Studio takový soubor přeložilo, musíte nadefinovat tzv. Custom Build Step, tedy postup, jak má prostředí se souborem naložit. Otevřete nastavení projektu (Menu "Project\Settings" a v levé části dialogu vyberte soubor AsmHello.asm. Do pole "Description" můžete napsat v podstatě cokoliv, Visual Studio tento text vypisuje při kompilaci souboru. Do pole "Commands" vepište příkazovou řádku pro zkompilování assemblerovkého souboru:
Pro Debug Verzi:
ml.exe /c /nologo /coff /Zd /Zi /Fo Debug/AsmHello.obj /I inc AsmHello.asm
Pro Release Verzi:
ml.exe /c /nologo /coff /Fo Release/AsmHello.obj /I inc AsmHello.asm
K tomu, aby překlad fungoval, musíte mít soubor ml.exe (Kompilátor assembleru) v nějakém adresáři, ke kterému je nastavena proměnná PATH. Význam jednotlivých parametrů zjistíte, pokud na příkazovou řádku napíšete
ml /?
Do pole "Outputs" vepište "Debug\AsmHello.obj" (pro Debug Verzi) a "Release\AsmHello.obj" (pro Release verzi). Toto pole slouží k tomu, aby Visual Studio vědělo, který soubor bude výstupem po zkompilování. Pokud bude vstupní soubor (AsmHello.asm) novější než výstupní soubor (Debug\AsmHello.obj), bude proveden překlad.
Přeložení proběhne v pořádku, ale linker nahlásí chybu
--------------------Configuration: AsmHello - Win32 Debug-------------------- Kompiluji .\AsmHello.asm Assembling: AsmHello.asm Linking... LINK : error LNK2001: unresolved external symbol _WinMainCRTStartup Debug/AsmHello.exe : fatal error LNK1120: 1 unresolved externals Error executing link.exe. AsmHello.exe - 2 error(s), 0 warning(s)
Tato chyba vznikla proto, že linker očekává někde v programu funkci WinMainCRTStartup, která je implicitně použita jako vstupní bod programu. V programech napsaných v C/C++ je tato funkce implementována ve startovacích knihovnách C a má na starosti převod příkazové řádky na proměnné __argc a __argv, spouštění funkcí před WinMain-em aj. V našem případě tyto funkce nepotřebujeme. Musíme ale sdělit linkeru, že má použít funkci WinMain, implementovanou v AsmHello.asm, jako vstupní bod programu. Toho docílíme v nastavení linkeru ("Project\Settings", karta "Link", kategorie "Output", do pole "Entry-point symbol" vepište WinMain). Nezapomeňte stejnou změnu provést také v Release verzi. Dále ještě zrušte volbu "Link incrementally" v kategorii "Customize". To způsobí, že linker nebude generovat tabulku odkazů, kvůli které by se při ladění otevíralo okno "Assembly". Tato volba bývá implicitně nastavena v Debug verzi programu.
No a zbývá spustit debugger klávesou F10 a kurzor se objeví na začátku funkce WinMain:
Celý příklad v assembleru si můžete stáhnout (241 KB). K souboru jsou také přibaleny hlavičkové soubory a knihovna.
Programy psané v assembleru jsou pověstné svojí malou velikostí. Náš má ale v Release verzi 16384 bytů, což je celkem dost na to, že volá jenom jednu API funkci. Je to způsobeno tím, že EXE soubory obsahují sekce, které jsou zarovnány na 8192 bytů (a jsou naplněny většinou nulami - zkuste program zkomprimovat do ZIPu !). Pokud ale toto zarovnání snížíme, snížíme tím i velikost výsledného EXE souboru. Do nastavení linkeru ("Project\Settings", karta "Link", pole "Project Options" na konec všech parametrů připište:
/ALIGN:0x1000 /OPT:REF
a to jak do Debug verze, tak i do Release verze. Nyní bude mít výsledný EXE soubor po sestavení něco málo přes 2500 bytů. Při tomto nastavení zarovnání bude linker hlásit varování
LINK : warning LNK4108: /ALIGN specified without /DRIVER or /VXD; image may not run
Pod Windows řady 9x je minimální funkční zarovnání 4096 bytů (0x1000), pod Windows řady NT lze program spustit i se zarovnáním 16 bytů (0x10). Velikost výsledného EXE souboru je pak extrémně malá, jen něco přes 700 bytů. To je asi limit, který se nám podaří u EXE souboru dosáhnout (hlavička EXE souboru má 512 bytů). Volba /OPT:REF říká linkeru, že by měl z výsledného souboru vyhodit odkazy na knihovny DLL, které nejsou použity.
Na závěr jeden trik: Pokud si myslíte, že v C není možné vytvořit takto malý EXE soubor, mýlíte se. Pokud do projektu vložíte jediný soubor C nebo CPP, obsahující pouze WinMain, nastavíte vstupní bod programu v nastavení linkeru na WinMain a nastavíte i stejné zarovnání, bude velikost výsledného souboru v Release verzi srovnatelná s velikostí programu napsaného v assembleru.
Copyright (c) Ladislav Zezula 11.10.2003