Výuka assembleru

8. Kombinování assembleru a C/C++

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

Jestliže potřebujeme do programu začlenit časově kritickou funkci, je možné tuto funkci napsat v assembleru a přidat ji do projektu. Chceme napsat např. funkci pro zjišťování délky řetězce. Tato funkce je v C k dispozici (strlen), ale pro názornost ji napíšeme sami. Řetězec bude předán podle céčkovských zvyklostí ukazatelem na začátek řetězce. Konec řetězce poznáme podle znaku z kódem 0 (který se do délky nepočítá).

Způsob první - assembler přímo do zdrojového souboru v C/C++

Většina překladačů C++ umožňuje psát assembler přímo do zdrojového textu. Tato varianta míchání C a assembleru je jednodušší než napsání modulu v assembleru (který je navíc potřeba zařadit do projektu speciálním způsobem).
Pro zápis assembleru v C je nutné použít příkaz _asm:

    _asm
    {
        mov    eax, 0
        mov    ebx, eax
    }

Tímto způsobem můžeme napsat kostru funkce (návratová hodnota a parametry) v C, a tělo funkce napsat v assembleru. Nejsou žádné starosti s volací konvencí, typy parametrů ani s linkováním funkce do projektu. Funkci pro spočítání délky řetězce napíšeme takto:

int StringLength(char * szString)
{
    int strlength = 0;

    _asm
    {

        mov     esi, szString;          // Adresa řetězce
        mov     ecx, 0                  // Počet znaků

__LoadChar:
        mov     al, [esi];              // Do AL načteme první/další znak
        cmp     al, 0                   // Je to konec řetězce ?
        jz      __EndString             // Ano => skok
        inc     esi                     // Ukazatel v ESI přesuneme na další znak
        inc     ecx                     // Zvýšíme počet znaků o 1
        jmp     __LoadChar

__EndString:
        mov     strlength, ecx          // Uložíme délku řetězce
    }

    return strlength;
}

void main(void)
{
    int length = StringLength("Toto je pokusný řetězec pro assembler");

    printf("Delka retezce je %i\n", length);
}

Způsob druhý - funkce v samostatném modulu

Druhý způsob vyžaduje zařazení funkce do samostatného modulu, který bude kompletně napsán v assembleru. Nejdříve si ukážeme céčkovskou část:

#include <stdio.h>

extern "C" int _cdecl StringLength(char * szString);

void main(void)
{
    int length = StringLength("Toto je pokusný řetězec pro assembler");

    printf("Delka retezce je %i\n", length);
}

Není zde nic zvláštního - pro nás neobvyklá je deklarace funkce s extern "C" (která je potřeba k tomu, aby překladač nedoplňoval ke jménu funkce dekoraci z C++), a dále klíčové slovo _cdecl, které zdůrazňuje volací konvenci CDECL, která se používá v programovacím jazyce C.
Modul v assembleru bude vypadat takto:

.586
.MODEL FLAT,STDCALL

OPTION CASEMAP:NONE

include ../inc/windows.inc

.code

StringLength PROC C szString:LPSTR

        push    esi                     ; Uložíme obsah registru ESI
        mov     esi, szString           ; Adresa řetězce
        mov     eax, 0                  ; Počet znaků

__LoadChar:
        mov     cl, [esi]               ; Do AL načteme první/další znak
        cmp     cl, 0                   ; Je to konec řetězce ?
        jz      __EndString             ; Ano => skok
        inc     esi                     ; Ukazatel v ESI přesuneme na další znak
        inc     eax                     ; Zvýšíme počet znaků o 1
        jmp     __LoadChar

__EndString:
        ; Návratová hodnota (počet znaků) je v EAX
        pop     esi                     ; Obnovíme obsah registru ESI
        ret
StringLength ENDP

end

Všimněte si deklarace PROC C za jménem funkce StringLength. Tato deklarace říká, že funkce bude používat volací konvenci _cdecl. Volací konvence u funkce z assembleru se musí shodovat s volací konvencí nadeklarovanou v céčkovském modulu, jinak bude program padat.
Funkce také ukládá a obnovuje obsah registru ESI; Obsah registrů ESI a EDI je lepší ukládat, protože volající funkce někdy předpokládá, že nebudou uvnitř funkce změněny.

Po vložení assemblerovského modulu do projektu ve Visual C++ je nutné nastavit překlad tohoto modulu, protože Visual C++ to implicitně neumí. Jakmile vložíte assemblerovský modul do projektu, nastavte v "Project\Settings", soubor StrLen.asm, karta "Custom Build" takto:

Pokud znáte assembler trochu lépe a zdá se vám, že celá funkce by se dala ještě více zoptimalizovat, máte pravdu. K optimalizaci se ale dostaneme v některém z příštích dílů.

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