Výuka assembleru

10. Zásobníkový rámec a lokální proměnné

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

Ve vyšších programovacích jazycích je běžné použití proměnných, jejichž platnost je omezena pouze na funkci, ve které jsou deklarovány. Podobné proměnné je možné použít i v assembleru, v této lekci si ukážeme, na jakém principu lokální proměnné fungují.

Lokální proměnné jsou na zásobníku

Schéma zásobníku Z jedné z předchozích lekcí víme, jak funguje zásobník. Pokud program právě běží uvnitř nějaké funkce, můžeme stav zásobníku znázornit obrázkem vpravo:

Pokud po vstupu do funkce snížíme hodnotu v registru ESP o určitý počet dvojslov, získáme volný prostor o této velikosti. Ten slouží pro ukládání lokálních proměnných. Protože před návratem z funkce musíme ukazatel zásobníku obnovit do původního stavu, lokální proměnné zaniknou.

MyFunction PROC Param1:DWORD,
                Param2:PTR

        sub     esp, 12         ; Snížíme ukazatel zásobníku o 12 bytů (3x4 byty)
                                ; První proměnná bude na adrese [esp+00h], druhá
                                ; na adrese [esp+04h], třetí na [esp+08h]

        ...

        add     esp, 12         ; Vrátíme ukazatel zpět na původní pozici.
                                ; (Lokální proměnné tím zaniknou)
        ret                     ; Návrat z funkce
MyFunction ENDP
Schéma zásobníku uvnitř funkce

Obrázek vpravo zobrazuje uspořádání zásobníku během funkce MyFunction. Registr ESP ukazuje na lokální proměnnou uloženou v zásobníku nejvýše.

Menším problémem při používání lokálních proměnných tímto způsobem je jejich adresování. Hodnota v registru ESP se často mění (hlavně kvůli ukládání registrů nebo vkládání parametrů volaných funkcí), a na tyto změny je třeba brát zřetel, což může být např. u větších funkcí obtížné. Pokud v takovémto kódu uděláme chybu, velmi těžko ji pak budeme hledat. Přesto se toto adresování někdy používá, většinou v kódu generovaném automaticky překladačem vyššího programovacího jazyka při zapnutí vhodné volby (Ve Visual C++ je to volba v nastavení "Project\Settings", karta "C/C++", kategorie "Optimizations", volba "Frame-Pointer Ommission").

Potíže s adresováním pomocí registru ESP je možné odstranit postupem nazvaným zásobníkový rámec (angl. stack frame). Na začátku funkce zkopírujeme hodnotu registru ESP do registru EBP, který pak používáme k adresování. Postup ukazuje následující funkce:

MyFunction PROC Param1:DWORD,
                Param2:PTR

        push    ebp             ; Uložíme hodnotu EBP do zásobníku
        mov     ebp, esp        ; Zkopírujeme hodnotu registru ESP to EBP
        sub     esp, 12         ; Snížíme ukazatel zásobníku o 12 bytů (3x4 byty)
                                ; První proměnná bude na adrese [ebp-04h], druhá
                                ; na adrese [ebp-08h], třetí na [ebp-0Ch]

        ...

        mov     esp, ebp        ; Vrátíme ukazatel zpět na původní pozici.
                                ; (Lokální proměnné tím zaniknou)
        pop     ebp             ; Obnovíme původní hodnotu registru EBP
        ret                     ; Návrat z funkce
MyFunction ENDP

Zásobníkový rámec je vytvářen překladači vyšších programovacích jazyků i překladačem assembleru. Jestliže nadeklarujeme jednu nebo více lokálních proměnných pomocí direktivy LOCAL, zásobníkový rámec je pak vytvořen automaticky:

MyFunction PROC Param1:DWORD,
                Param2:PTR

LOCAL   dwCount : DWORD         ; Lokální proměnná "dwCount" typu DWORD
LOCAL   hWin : HWND             ; Lokální proměnná "hWin" typu HWND
LOCAL   ps : PAINTSTRUCT        ; Lokální proměnná "ps" typu PAINSTRUCT

        ...

        ret                     ; Návrat z funkce
MyFunction ENDP

Překladač automaticky spočítá velikost nadeklarovaných lokálních proměnných a vyhradí pro ně místo v zásobníku. Lokální proměnné můžeme používat stejně jako proměnné v sekci .data. Jedinou výjimkou je získání adresy proměnné, pro kterou musíme použít direktivu ADDR místo direktivy OFFSET. Při použití lokálních proměnných je nutné zvyknout si tato omezení:

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