Výuka assembleru

11. Instrukce pro logické operace

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

Jako další skupinu instrukcí v assembleru probereme jsou instrukce pro logické operace. Jejich význam může být pro začínající programátory podceňován, jsou ale velice často používané hlavně tam, kde je potřeba pracovat s jednotlivými bity. Tyto instrukce by se daly přiřadit k aritmetickým instrukcím; mají podobné operandy a všechny nastavují hodnoty příznaků v registru EFLAGS. Logické operace mohou být provedeny pouze s operandy stejné velikosti.

Jak fungují logické operace

Při logické operaci se porovnávají jednotlivé bity obou operandů. Do daného bitu výsledku se dosadí výsledek logické operace příslušných bitů obou operandů. Výsledek závisí na hodnotě bitů obou operandů, a na typu logické operace. V assembleru jsou podporovány operace and (logické "a"), or (logické "nebo"), xor (exkluzivní "nebo") a bitová negace. Každá operace je probrána podrobněji níže; pro každou operaci je uvedena i tzv. pravdivostní tabulka, která uvádí, jakou bude mít hodnotu výsledný bit při konkrétní kombinaci obou operandů. Pokud provádíme logickou operaci mezi např. dvěma 8mibitovými čísly, je nutné provést 8 logických operací (mezi příslušnými bity obou operandů).

Mezi programátory se logické operace označují téměř výhradně anglickými výrazy, je dobré si na to zvyknout.

Logické AND - jeden i druhý

Při operaci "logické and" je výsledný bit nastaven na 1, pokud hodnota obou bitových operandů je rovna 1. Pravdivostní tabulka vypadá takto:

Bit 1 0 0 1 1
Bit 2 0 1 0 1
Výsledek 0 0 0 1

Instrukce pro AND

V assembleru jsou k dispozici instrukce and a test. Instrukce and provede operaci logické AND a její výsledek uloží do prvního operandu. Instrukce test provede logickou operaci jenom "jako" - výsledek nikam neukládá, pouze nastaví příznaky v registru EFLAGS. Příklady instrukcí:

        and     eax, 1                  ; Provede AND obsahu registru EAX a konstanty 1,
                                        ; výsledek uloží do EAX
        and     eax, ebx                ; Provede AND obsahu registru EAX a registru EBX,
                                        ; výsledek uloží do EAX
        and     eax, eax                ; Provede operaci mezi dvěma stejnými hodnotami,
                                        ; výsledek uloží do EAX
        test    eax, 800h               ; Provede AND obsahu registru EAX a konstanty 800h
        test    eax, ebx                ; Provede AND obsahu registru EAX a registru EBX
        test    eax, eax                ; Provede operaci mezi dvěma stejnými hodnotami

Využití operace AND

Operace AND má dva způsoby praktického využití. Prvním (jednodušším z nich) je porovnávání nějaké hodnoty na nulu. Tato porovnávací operace se při programování používá dost často a překladače vyšších programovacích jazyků ji optimalizují na použití instrukce test. Výsledná instrukce je zpracována rychleji a její velikost v bytech je menší než velikost instrukce cmp registr, 0:

        mov     eax, dwValue            ; Načteme hodnotu proměnné "dwValue"
        test    eax, eax                ; Je to nula ?
        jz      __JeNula                ; Pokud ano, tak proveď skok

Zkuste si logicky zdůvodnit, proč uvedený postup funguje.

Druhým využitím logického AND je častá nutnost testování jednoho bitu v určité hodnotě. Velké množství informací ze systému se zjišťuje (i nastavuje) jako 32bitová hodnota, která je kombinací několika příznaků - flagů. Jako příklad si můžeme uvést parametr funkce CreateWindow "dwStyles". Při testování, zda je daný flag nastaven, se používá právě instrukce and nebo test:

        INVOKE  GetWindowLong hWnd, GWL_STYLES
        test    eax, WS_VSCROLL         ; Definováno jako 00200000h
        je      __HasVerticalScrollBar

Logické OR - jeden nebo druhý

Při operaci "logické or" je výsledný bit nastaven na 1, pokud hodnota jednoho z bitových operandů je rovna 1. Pravdivostní tabulka vypadá takto:

Bit 1 0 0 1 1
Bit 2 0 1 0 1
Výsledek 0 1 1 1

Instrukce pro OR

Pro tuto operaci je k dispozici instrukce or. Jeji funkce je obdobná jako u instrukce and, příklady zde uvádět nebudu.

Využití operace OR

Instrukci or je možné podobně jako test použít k porovnávání na nulu nebo nenulovou hodnotu (zdůvodněte, proč to funguje!). Její nejčastější využití je ale pro nastavení jednoho nebo více bitů v souboru příznaků. Jako příklad uvedu opět 32bitovou hodnotu obsahující vlastnosti okna. Následující kód přidá do vlastností okna příznak WS_VSCROLL:

        INVOKE  GetWindowLong hWnd, GWL_STYLES
        or      eax, WS_VSCROLL           
        INVOKE  SetWindowLong hWnd, GWL_STYLES, eax

Místo instrukce or by bylo možné použít i instrukci add. Výhoda instrukce or je v tom, že její výsledek je stejný bez ohledu na to, zda pokud příznak WS_VSCROLL ve vlastnostech okna již existuje. Funkce add by celou skupinu příznaků kompletně zničila, pokud by bit byl již nastaven.

Logické XOR - buďto jeden nebo druhý

Při operaci "logické xor" je výsledný bit nastaven na 1, pokud je hodnota obou operandů různá. Pravdivostní tabulka vypadá takto:

Bit 1 0 0 1 1
Bit 2 0 1 0 1
Výsledek 0 1 1 0

Instrukce pro XOR

Pro tuto logickou operaci máme k dispozici instrukci xor.

Využití operace OR

Nejjednodušším využitím instrukce xor je rychlé vynulování registru. Pokud provedeme operaci XOR na dvě stejná čisla, výsledkem je vždy nula:

        xor     eax, eax                ; V EAX bude nula bez ohledu
                                        ; na to, jaká byla předchozí hodnota
        sub     eax, eax                ; Podobně jako xor eax, eax
        mov     eax, 0                  ; Velikost instrukce je větší, její provádění
                                        ; trvá déle, a nenastavuje příznaky v EFGAFS

V porovnání instrukcí xor eax, eax a mov eax, 0 je nutné pamatovat na to, že instrukce mov nenastavuje příznaky v registru EFLAGS, zatímco instrukce xor ano. Vhodnost obou instrukcí záleží na situaci. Pokud je nutné stav registru EFLAGS zachovat, použijeme sub, jinak použijeme xor.

Operace XORování se často využívá u různých šifrovacích algoritmů. Pokud totiž hodnotu X (šifrovaná data) XORujeme dvakrát libovolnou hodnotou Y (klíčem), dostaneme původní hodnotu X. Následující příklad provede zašifrování a dešifrování bloku dat:

.data
        szText   db  'Toto je text ktery bude zasifrovan', 0
        dwLength dd  $ - offset szText
        
.code

        mov     esi, offset szText      ; Šifrovaný text
        mov     ecx, dwLength           ; Délka šifrovaného textu
        mov     ebx, 0A5h               ; Šifrovací klíč

__EncryptLoop:
        mov     al,  [esi]
        xor     al, bl                  ; Zašifruj znak
        mov     [esi], al
        inc     esi
        loop    __EncryptLoop           ; Zmenší ECX o 1 a pokud není nula,
                                        ; skočí na __EncryptLoop

        mov     esi, offset szText      ; Šifrovaný text
        mov     ecx, dwLength           ; Délka šifrovaného textu

__DecryptLoop:
        mov     al,  [esi]
        xor     al, bl                  ; Dešifruj znak
        mov     [esi], al
        inc     esi
        loop    __DecryptLoop           ; Zmenší ECX o 1 a pokud není nula,
                                        ; skočí na __DecryptLoop

Všiměte si, že cyklus pro šifrování je úplně stejný jako cyklus pro dešifrování. Uvedené šifrování je zamozřejmě velice primitivní, v praxi se používají mnohem dokonalejší šifry. Přesto jsou velmi často postaveny na operaci xorování.

Logické NOT - bitová negace

Operace "bitová negace" změní nulu na jedničku a naopak. Zde je pravdivostní tabulka:

Bit 1 0 1
Výsledek 1 0

Pro bitovou negaci je k dispozici instrukce not, která má pouze jeden operand. Instrukce se moc často nepoužívá.

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