Assembler (Jazyk symbolických adres)

(2. část)

Aritmetické instrukce

Programátor při své činnosti potřebuje nejen přesuny dat. V každém programu jsou nutné i výpočty a to s běžnými daty, nebo s adresami. Ty se v Assembleru provádějí jen s celými čísly. Operace s desetinnými čísly jsou zdlouhavé, i když jsou proveditelné pomocí určitých algoritmů nebo instrukcí matematického koprocesoru. Většina matematických operací se provádí s čísly v registrech nebo v paměti. Označení operandů je shodné jako při přesunech. Zároveň tyto instrukce nastavují indikátory registru F. Umožní tak větvit program. Informace o nastavovaných indikátorech najdeme v seznamu instrukcí.

Sčítání

Při tvorbě programu si musíme ujasnit, jestli chceme k hodnotě na cílovém místě přičíst 1, nebo jiné číslo. Podle toho volíme instrukci:

Příklady:

INC AX - přičti k registru AX hodnotu 1
INC WORD PTR [EBX + 2] - přičti k slovu na adrese určené EBX + 2 hodnotu 1
ADD AX, BX - ke slovu v registru AX přičti obsah registru BX (slovo)
ADD AH, 8 - k slabice v registru AH přičti číslo 8
ADD DX, WORD PTR [EBX] - k registru DX přičti slovo na adrese EBX
ADD BYTE PTR [ESI], 30 - k slabice na adrese ESI přičti 30
ADD promenna, 5 - k deklarované proměnné přičti 5

Pokud při těchto operacích dojde k přeplnění cíle, nastaví se flagy CF a OF do log. 1 (kromě případu INC).
Aby při odlaďování vašich programů nedošlo ke zbytečným hádkám s překladačem, uvědomte si, že zdroj i cíl musí mít stejný počet bitů.

Otázka: V EBX:EAX je první 64-bitové číslo a EDX:ECX druhé. Jaké instrukce je sečtou s výsledkem v EBX:EAX?

Odčítání

Instrukce sloužící k odčítání jsou zápisem operandů shodné s instrukcemi pro sčítání. Proto si uvedeme jen jejich seznam: Příklady by byly shodné se sčítáním.
Navíc jsou zde tyto specifické instrukce:

Instrukce CMP porovnává dvě čísla odečtením. Protože ale nedojde k jejich změně, použijeme tuto instrukci před větvením programu. Za CMP totiž většinou následují instrukce skoku závislé na stavu příznaků registru F.

Příklad:
      #include <stdio.h>

      unsigned short a, b, s, r;

      int main()
      {
      printf("a=");
      scanf("%i", &a);
      printf("b=");
      scanf("%i", &b);
      _asm {
      mov ax, a
      add ax, b
      mov s, ax
      mov ax, a
      sub ax, b
      mov r, ax
      inc a
      dec b
      }
      printf("a+b=%u\na-b=%u\na+1=%u\nb-1=%u\n", s, r, a, b);
      return 0;
      }
    
Uvedený příklad ukazuje nejjednodušší použití instrukcí ADD, SUB, INC, DEC.

Násobení

I když programátoři neradi používají (nebo spíše neradi používali, a to hodně dávno) instrukce násobení a dělení pro jejich dlouhou dobu provádění (na procesoru 8086, u jiných procesorů je už rychlejší, ale stejně relativně pomalé), Assembler je samozřejmě má. I tyto operace jsou definovány jen na celých číslech. Rozlišujeme také, jestli je provádíme se znaménkem, nebo bez znaménka. POZOR! O kolikabitové násobení se jedná určuje označení místa zdroje.

Dělení

Tato operace je jednou z nejzdlouhavějších. Její provádění trvá 43 period hodin (na 8086 až 184) (sčítání trvá 2 periody). Jeho výhodou je ale to, že je možné zjistit jak výsledek po celočíselném dělení (celočíselný podíl), tak i zbytek po celočíselném dělení (modulo). A to všechno jen jedinou instrukcí.

Použití těchto instrukcí je podobné jako násobení. Program si musíme ošetřit tak, aby nemohlo dojít k dělení nulou. Jestliže k němu přesto dojde, procesor zavolá přerušení INT 0 a dojde k ukončení programu.

Příklad:
      #include <stdio.h>

      unsigned short a, b, d, z;
      unsigned long s;

      int main()
      {
      printf("a=");
      scanf("%i", &a);
      printf("b=");
      scanf("%i", &b);
      _asm {
      mov ax, a
      mul b
      push ax
      mov eax, 0
      mov ax, dx
      mov ecx, 0
      pop cx
      mov edx, 0x10000
      mul edx
      add eax, ecx
      mov s, eax
      mov dx, 0
      mov ax, a
      div b
      mov d, ax
      mov z, dx
      }
      printf("a*b=%u\na div b=%u, a mod b=%u\n", s, d, z);
      return 0;
      }
    

Př. Napište program, který vypočítá obsah trojúhelníka podle Heronova vzorce:

P = sqrt(s*(s-a)*(s-b)*(s-c)), s = (a+b+c)/2.
Programu zadáte strany a, b, c (v jazyce C), pak se výpočet provede v Assembleru až po závěrečnou odmocninu, kterou provedete pomocí funkce sqrt v C (musíte vložit hlavičkový soubor math.h). Výsledek vypište (pomocí funkce printf v C).

Instrukce logických operací

Logické instrukce jsou jednou z nepostradatelných pomůcek programátorů. V Assembleru je možné provádět všechny běžné logické operace mezi hodnotami v registrech, v paměti nebo zadanými přímo. Chybí zde tedy instrukce pro jednotlivé bity (od procesoru 386 jsou už i tyto instrukce). Ty však volbou vhodných algoritmů můžeme lehce nahradit. Kolikabitová operace je, určuje opět specifikace zdroje a cíle.
Instrukci TEST použijeme k nastavení příznakového registru, a tak můžeme větvit program, aniž bychom ovlivnili hodnoty zdroje a cíle.

Použití logických operací

Vymaskování slabiky nebo slova
Často potřebuje programátor nastavit některé bity do hodnoty log. 1, nebo 0. K tomu mu velmi dobře poslouží právě logické operace AND nebo OR.
Máme-li slabiku ve tvaru XXXXAXXX v registru AL a chceme, aby bity X měly hodnotu 0 a hodnota bitu A zůstala zachována, provedeme instrukci AND AL, 08h (=00001000b).
Máme-li slabiku ve tvaru XXXXAXXX v registru AL a chceme, aby bity X měly hodnotu 1 a hodnota bitu A zůstala zachována, provedeme instrukci OR AL, F7h (=11110111b).

Otázka: Máme slabiku ve tvaru XXXXAXXX v registru AL a chceme, aby bity X zůstaly zachovány a hodnota bitu A byla opačná. Jakou instrukci provedeme?

Nulování registru
Zajímavější než instrukcí MOV registr, 0 je nulování pomocí XOR registr, registr. Efekt je stejný, doba vykonání operace je kratší (jen na 8086, na novějších je to už stejné).
Kódování
Každý rád chrání svá data před neoprávněným přístupem kódováním. K tomu dobře slouží logická operace XOR. Postup kódování:
Provedeme-li operaci XOR s konstantou a číslem, získáme kódované číslo. Pokud s kódovaným číslem provedeme opět XOR se stejnou konstantou, získáme zpět původní číslo.
Kódovaná čísla přidáme do binárního souboru programu. Před jejich použitím je dekódujeme. Protože tato čísla mohou nést např. jméno autora (v ASCII), je jméno pro běžného uživatele po zakódování nečitelné (a tedy lehce nepřepsatelné).
Pozor! Hodnota konstanty musí být při kódování i dekódování stejná.
Tento postup můžeme libovolně pozměňovat podle úrovně našich znalostí (např. XORovat první znak s druhým, druhý s třetím, ...).

Př. Ověřte si toto kódování jednoduchým programem. Programu zadáte číslo a konstantu, ten číslo zakóduje a dekóduje (v Assembleru) a obojí vypíše.



Jan Outrata
outrataj@phoenix.inf.upol.cz