Výuka assembleru

7. Procedury a funkce

<< Předchozí díl
Další díl >>

V tomto díle si něco řekneme o procedurách a jejich volání. Procedury a funkce jsou samozřejmou součástí každého programu. Dokonce i ten nejjednodušší prográmek, který v assembleru (nebo v nějakém vyšším programovacím jazyce) napíšeme, bude mít aspoň jednu proceduru. Ano, WinMain je procedura jako každá jiná. Od ostatních se liší pouze tím, že je volána operačním systémem jako vstupní bod programu.
Pozn.: Ve vyšších programovacích jazycích se obvykle mluví o procedurách, funkcích, rutinách nebo i podprogramech. Protože tyto pojmy jsou do značné míry podobné, nebudou v tomto článku rozlišovány. Bude-li řeč o proceduře nebo funkci, rozumí se tím stejný element programu.

Důvody, proč psát procedury

Existuje několik důvodů, proč je dobré program dělit na procedury namísto "lineárního" kódu, který je celý pouze ve WinMain:

Volání procedur

Pro volání procedury se používá instrukce call. Tato instrukce uloží do zásobníku adresu následující instrukce za instrukcí call a provede skok na adresu, která je parametrem instrukce call. Důvodem ukládání návratové adresy do zásobníku je nutnost znát adresu, kam má být proveden návrat z funkce. Instrukce pro návrat z funkce, ret (obdoba příkazu "return" z C nebo "Exit" z Pascalu), jednoduše vybere z vrcholu zásobníku návratovou adresu a provede skok na tuto adresu. Tento princip zajišťuje, že je možné funkci volat z libovolného místa (i z jiné funkce) a návrat bude vždy proveden na správné místo. Dokonce i volání procedury ze sebe samé (tzv. rekurze) díky tomuto principu funguje. Jediné, co funkcionalitu návratu z funkce spolehlivě zničí, je přepsání zásobníku na "vhodném" místě nebo prostá změna adresy uložené v registru ESP.

Parametry procedur

Aby byla procedura co nejuniverzálnější, je nutné její činnost řídit vstupními parametry. Např. procedura CopyFile by nebyla příliš použitelná, pokud by kopírovala pouze soubor "C:\Dokument.txt" do "A:\Dokument.txt". Proto např. funkce CopyFile, kterou poskytují Windows, má tři parametry (jméno zdrojového souboru, jméno cílového souboru a zda má funkce přepsat cílový soubor, pokud existuje). V praxi je většina procedur v programu s jedním nebo více parametry.
Parametry se do procedury mohou předávat dvěma způsoby (oba se používají i ve vyšších programovacích jazycích):

Předávání parametrů funkce do zásobníku se provádí sérií instrukcí push, seřazených od posledního parametru k prvnímu. Volání funkce MessageBox, zapsané v C/C++ jako

    MessageBox(NULL, szMessage, szTitle, MB_OK);

provedeme takto:

        push    MB_OK
        push    offset szTitle
        push    offset szMessage
        push    0
        call    MessageBox
nebo jednodušeji pomocí direktivy INVOKE, která automaticky vytvoří potřebnou sekvenci instrukcí push, následovanou instrukcí call:
        INVOKE  MessageBox, NULL, offset szTitle, offset szMessage, MB_OK

Na první instrukci uvnitř funkce MessageBox bude stav zásobníku následující:

Adresa [ESP] [ESP+04h] [ESP+08h] [ESP+0Ch] [ESP+10h]
Obsahuje RetAddr
(Návratová adresa)
hParent
(NULL)
szMessage szTitle nFlags
(MB_OK)

Kdo a jak ale odstraní hodnoty, které jsou do zásobníku vloženy před zavoláním funkce MessageBox ? Závisí to na tzv. volací konvenci funkce:

Volací konvence Kdo odstraňuje parametry Jak budou parametry odstraněny
_cdecl Volající, těsně za instrukcí call Posunutím zásobníku na správnou hodnotu,
např. add esp, 10h
_stdcall Volaná funkce Instrukcí ret XX, kde XX je počet parametrů násobený čtyřmi

Při volání funkce je nutné vědět, kterou volací konvenci musíme použít. Obecně platí, že funkce API Windows používají volací konvenci _stdcall (označovanou také jako WINAPI), a standardní funkce knihovny C používají konvenci _cdecl. V případě, že zvolíme volací konvenci nesprávně, dojde většinou k pádu programu.

Návratová hodnota procedury

Návratová hodnota z funkce je "výsledek" této funkce. Některé funkce vracejí výsledek výpočtu, jiné zase hodnotu, která informuje o úspěchu nebo neúspěchu činnosti funkce. Není nutné, aby každá funkce měla návratovou hodnotu - např. funkce, centrující okno buďto skončí s úspěchem nebo s neúspěchem (např. pokud okno neexistuje).
Návratová hodnota je vracena v registrech procesoru podle následující tabulky:

Velikost vracené hodnoty Registr
8 bitů (byte, char) AL
16 bitů (word, short) AX
32 bitů (dword, long) EAX
64 bitů (qword, __int64) EDX (horních 32 bitů)
EAX (spodních 32 bitů)

Co bude procedura dělat ?

Než napíšeme proceduru, měli bychom si rozmyslet několik věcí:

Píšeme proceduru

Následující příklad ukazuje způsob zápisu procedury a její volání. Jde o funkci, která násobí dvě čísla (v praxi bychom to samozřejmě provedli pomocí jediné instrukce, zde jde o jednoduchý příklad):

.code


Multiply PROC dwNumber1 :DWORD,
              dwNumber2 :DWORD

        mov eax, dwNumber1
        mul dwNumber2

        ret
Multiply ENDP



WinMain PROC    hInstance    :HANDLE,
                hPrevInstance:HANDLE,
                lpszCmdParam :LPSTR,
                nCmdShow     :WORD

        INVOKE  Multiply, 10, 20

        ret
WinMain ENDP

Některé podrobnosti volání funkcí v assembleru se do dnešní lekce již nevejdou, probereme je příště.

<< Předchozí díl
Další díl >>