3. cvičení
Registry
Registr je speciální paměťové místo přímo v procesoru. Práce s nimi je tedy mnohem rychlejší než s pamětí. Procesor v nich uchovává hodnoty, s kterými právě pracuje.
-
datové registry - mají všeobecná použití (jsou univerzální), níže uvedené
jsou jejich vyžadovaná použití při některých operacích, 32-bitové, ke spodním 16 bitům lze
přistupovat pod jménem bez E, a tuto 16-bitovou část je možné ještě rozdělit na
poloviny po osmi bitech, dolních osm bitů označuje jméno
končící L, horních pak jméno končící H. Horních 16 bitů z
celého 32-bitového registru nelze přímo adresovat (nejsou
přístupné přes nějaké jméno). Např. EAX je
32-bitový registr, ke spodním 16 bitům lze přistupovat pod
jménem AX, a z těchto 16 bitů dolních 8 označujeme jako registr AL,
horních pak AH.
- EAX, AX, AH ,AL (Accumulator) - střadač pro násobení a dělení, vstupně-výstupní operace
- EBX, BX, BH, BL (Base) - nepřímá adresace paměti
- ECX, CX, CH, CL (Counter) - počitadlo při cyklech, posuvech a rotacích
- EDX, DX, DH, DL (Data) - nepřímá adresace vstupů/výstupů
-
ukazatele a indexregistry - používají se pro umístění
adresy, 32-bitové, spodních 16 bitů označuje jméno bez E a
nelze je dále dělit (na rozdíl od předchozích
univerzálních registrů):
- EBP, BP (Base Pointer) - bázový registr, adresace parametrů funkcí a lokálních proměnných na zásobníku (adresa dna zásobníku), není rozumné jej libovolně měnit
- ESP, SP (Stack Pointer) - ukazatel na vrchol zásobníku (adresa vrcholu zásobníku), tento také není rozumné libovolně měnit
- EDI, DI (Destination Index) - adresa cíle
- ESI, SI (Source Index) - adresa zdroje
- EIP, IP (Instruction Pointer) - ukazatel na aktuální místo programu, adresa instrukce následující za právě prováděnou instrukcí, není možné jej přímo měnit (jen patřičnými instrukcemi)
Převod mezi datovými typy
- zúžení na menší typ provedeme bez ohledu na to, zda je číslo znaménkové, či neznaménkové. Použijeme buď menší část registru nebo v případě práce s pamětí použijeme operátor ptr
int main() { char p1; short p2; int p4 = 0xdeadbeef; _asm { mov ebx, 0xdeadbeef mov p1, bl mov p2, bx mov al, byte ptr p2 mov al, byte ptr p4 mov ax, word ptr p4 } }
- pří rozšíření hodnoty z menší na vetší u beznaménkových hodnot doplníme nuly na nově přidané pozice:
mov eax, 0 mov al, bl
Případně můžeme použít operacimovzx
, která je variantou operacemov
a umožnuje, aby druhý operand byl menší než první. - pří rozšíření hodnoty z menší na vetší u znaménkových hodnot doplníme na nově přídané pozice hodnotu nejvyššího bitu. K tomuto účelu slouží operace
movsx
. - při dělení můžeme použít následující operace, které se postarají o vhodné rozšíření hodnot:
- cbw - rozšíří al->ax
- cwd - rozšíří ax->dx:ax
- cdq - rozšíří eax->edx:eax
- cwde - rozšíří ax->eax
- Vyzkoušejte si: převody mezi různě velkými hodnotami.
Podmíněné a nepodmíněné skoky
- Návěstí: označuje místo v paměti (kódu). Zapisuje se ve tvaru:
label:
- Nepodmíněný skok:
jmp misto
-- provede nepodmíněný skok na část kódu uvozenou návěstím `misto'
Podmíněný skok (Jcc - jump conditional code)
Jedná se o skok podmíněný stavem jednoho nebo více bitů registru příznaků EF. Jen tímto způsobem je možné provádět v Assembleru přímé větvení programu. Před instrukcí podmíněného skoku proto vždy provedeme instrukci (např. CMP, TEST, aj.), která použitý příznak nastaví. V případě, že není splněna podmínka skoku, pokračuje program dál, jako by zde instrukce skoku vůbec nebyla. Instrukce podmíněného skoku začínají vždy písmenkem J. Za ním je zkratka udávající na jakých bitech registru ef(lags) je skok závislý.
- JE / JZ návěští - skok, je-li rovno, ZF = 1
- JNE / JNZ návěští - skok, není-li rovno, ZF = 0
- JS návěští - skok, je-li znaménko, SF = 1
- JNS návěští - skok, není-li znaménko, SF = 0
- JO návěští - skok, je-li přetečení, OF = 1
- JNO návěští - skok, není-li přetečení, OF = 0
- JP / JPE návěští - skok, je-li sudá parita, PF = 1
- JNP / JPO návěští - skok, je-li lichá parita, PF = 0
- JA / JNBE návěští - skok, je-li větší, (CF = 0) AND (ZF = 0)
- JAE / JNB / JNC návěští - skok, je-li větší nebo rovno, CF = 0
- JB / JNAE / JC návěští - skok, je-li menší, CF = 1
- JBE / JNA návěští - skok, je-li menší nebo rovno, (CF = 1) OR (ZF = 1)
- JG / JNLE návěští - skok, je-li větší, (SF = OF) AND (ZF = 0)
- JGE / JNL návěští - skok, je-li větší nebo rovno, SF = OF
- JL / JNGE návěští - skok, je-li menší, SF <> OF
- JLE / JNG návěští - skok, je-li menší nebo rovno, (SF <> OF) OR (ZF = 1)
int abs(int a) { int i; _asm { mov eax, a cmp eax, 0 jge konec neg eax konec: mov i, eax } return i; }
Úkoly
- Napište funkci
int avg_int(int a, int b, int c)
pro výpočet aritmetického průměru tří čísel typu int. Vyzkoušejte, že funguje správně pro kladná i záporná čísla (i jejich mix). - Napište funkci
short avg_short(short a, short b, short c)
pro výpočet aritmetického průměru tří čísel typu short. Vyzkoušejte, že funguje správně pro kladná i záporná čísla (i jejich mix). - Napište funkci
int sgn(int i)
, která vrací hodnoty -1, 0, 1, v závislosti na tom, zda-li je hodnotai
záporná, nulová nebo kladná. - Napište funkci
int min3(unsigned char a, short b, int c)
, která vrací nejmenší hodnotu. - Napište funkci
int kladne(int a, int b, int c)
, která vrací 1, pokud jsou všechny argumenty kladné, jinak 0. - Napište funkci
int mocnina(int n, unsigned int m)
vracející mocninu nm.
Pro získání bodového hodnocení je potřeba splnit úkoly 2, 4 a 6.