Výuka assembleru
11. Instrukce pro logické operace
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.
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.
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 |
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
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
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 |
Pro tuto operaci je k dispozici instrukce or. Jeji funkce je obdobná jako u instrukce and, příklady zde uvádět nebudu.
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.
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 |
Pro tuto logickou operaci máme k dispozici instrukci xor.
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í.
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á.
Copyright Ladislav Zezula 2004