Lekce 11 - Assembler - Kombinace skoků a příznaky
V minulé lekci, Assembler - Podmíněné a nepodmíněné skoky, jsme si řekli něco o nepodmíněných a podmíněných skocích, pomocí kterých můžeme vytvořit podmínky a cykly známé z vyšších jazyků.
V dnešním ASM tutoriálu se naučíme kombinovat podmíněné a nepodmíněné skoky, zjistíme, jak skokové instrukce získají výsledek z CMP a jak porovnávat signed čísla.
Příklad - Podmíněné a nepodmíněné skoky dohromady
Říkali jsme si, že můžeme podmíněné a nepodmíněné skoky snadno kombinovat. Ukažme si tedy na příkladu, jak se používají dohromady:
zacatek: mov cx, 5 .znovu: mov si, message call print_string dec cx cmp cx, 0 jz .return jmp .znovu .return: mov si, konec call print_string ret print_string: lodsb cmp al, 0 jz .return mov ah, 0eh mov bx, 7h int 10h jmp print_string .return: ret message db 'CX != 0', 13, 10, 0 konec db 'CX == 0', 0
Z kódy byste již měli vyčíst, že vypisuje text 5x, tedy dokud není
CX
rovno 0
. Vyzkoušeli jsme si tak i instrukci
DEC
ke snížení hodnoty a vlastně jsme si napsali instrukci
loop
.
Všimněte si použití funkce print_string
, kterou
jsem již používali, jako příkladu. A také. že pokud dáme před
návěstí .
, stane se z něj "pod-návěstí" a nemusíme tak
používat očíslování apod. (Platí pro NASM).
Příznaky
Již víme, že CMP
vykoná operaci A - B
a na
její výsledek pak pomocí dalších instrukcí reagujeme. Jak ale tyto
instrukce vědí, jaký výsledek byl? CMP
totiž podle výsledku
porovnání nastaví tzv. flagy (příznaky) a následující skákací
instrukce reagují na hodnoty uložené v těchto příznacích.
Pokud znáte principy počítačů, víte, že odčítání se vnitřně provádí jako přičtení dvojkového doplňku druhého čísla.
Flagy nastavuje kromě CMP
i několik dalších
instrukcí a nemusí nastavit všechny, ale jen některé z nich. Nejsou tedy
jen záležitost porovnávání, ale i několika dalších instrukcí, např. i
těch aritmetických.
Flagy mohou být vždy buď 1
nebo 0
. V registru
EFLAGS
je velké množství flagů, ale v podmíněných skocích
se používají jen tyto flagy:
ZF
- Zero flag - Nastaví se, pokud je výsledek0
.SF
- Sign flag - Nastaví se, pokud je nejvyšší bit výsledku operace1
(MSB jako Most Significant Bit, obvykle ten nejvíce vlevo). Název pochází od toho, že pokud je číslo uložené jako signed, tak tento bit v čísle označuje, zda je číslo záporné či nikoli.CF
- Carry flag - Nastaví se, pokud při operaci došlo k přetečení (např. přičteme1
k255
v proměnné o velikosti1
bajt). Výsledek bude tedy0
, ale zapne se flag Carry. Pokud máme signed číslo, tak tam se o přetečení dozvíme z flagu overflow níže.OF
- Overflow flag - Nastaví se, pokud dojde k přetečení hodnoty signed čísla (pro tento typ čísel nelze k ověření přetečení použít Carry flag).PF
- Parity flag - Flag parity se nastaví, pokud je v nejnižším bajtu výsledku sudý počet jedničkových bitů.
Tabulka skoků včetně příznaků a porovnávání signed čísel
Jelikož již známe flagy, můžeme si uvést další, kompletnější
tabulku skoků a uvést do ní na jaký flag nastavený pomocí
CMP
daná skoková instrukce reaguje. Také si
přidáme instrukce pro práci se signed čísly. Takových
tabulek nás čeká ještě spoustu, ASM je hodně o tabulkách instrukcí
Instrukce | Porovnání | Popis | Typ čísel | Flagy |
---|---|---|---|---|
JO (Jump if Overflow) | Skočí, pokud došlo k přetečení | OF == 1 | ||
JNO (Jump if Not Overflow) | Skočí, pokud nedošlo k přetečení | OF == 0 | ||
JS (Jump if Sign) | Skočí, pokud byl nastaven sign bit | SF == 1 | ||
JNS (Jump if Not Sign) | Skočí, pokud nebyl nastaven sign bit | SF == 0 | ||
JE (Jump if Equal) JZ (Jump if Zero) |
A == B | Skočí, pokud byly hodnoty rovné Skočí, pokud byl rozdíl hodnot 0 |
ZF == 1 | |
JNE (Jump if Not Equal) JNZ (Jump if Not Zero) |
A != B | Skočí, pokud nebyly hodnoty rovné Skočí, pokud nebyl rozdíl hodnot 0 |
ZF == 0 | |
JB (Jump if Below) JNAE (Jump if Not Above or Equal) JC (Jump if Carry) |
A < B | Skočí, pokud byl rozdíl hodnot záporný Skočí, pokud byla hodnota menší Skočí, pokud byl nastaven flag carry |
unsigned | CF == 1 |
JNB (Jump if Not Below) JAE (Jump if Above or Equal) JNC (Jump if Not Carry) |
A >= B | Skočí, pokud nebyla hodnota menší Skočí, pokud nebyl rozdíl hodnot větší nebo roven 0 Skočí, pokud nebyl nastaven flag carry |
unsigned | CF == 0 |
JBE (Jump if Below or Equal) JNA (Jump if Not Above) |
A <= B | Skočí, pokud byla hodnota menší nebo rovná Skočí, pokud byl rozdíl hodnot menší nebo roven 0 |
unsigned | CF == 1 nebo ZF == 1 |
JA (Jump if Above) JNBE (Jump if Not Below or Equal) |
A > B | Skočí, pokud byla hodnota větší Skočí, pokud byl rozdíl hodnot větší nebo roven 0 |
unsigned | CF == 0 a ZF == 0 |
JL (Jump if Less) JNGE (Jump if Not Greater or Equal) |
A < B | Skočí, pokud byla hodnota menší Skočí, pokud hodnota nebyla větší nebo rovna |
signed | SF != OF |
JGE (Jump if Greater or Equal) JNL (Jump if Not Less) |
A >= B | Skočí, pokud byla hodnota větší nebo rovna Skočí, pokud hodnota nebyla menší |
signed | SF == OF |
JLE (Jump if Less or Equal) JNG (Jump if Not Greater) |
A <= B | Skočí, pokud byla hodnota menší nebo rovná Skočí, pokud hodnota nebyla větší |
signed | ZF == 1 nebo SF != OF |
JG (Jump if Greater) JNLE (Jump if Not Less or Equal) |
A > B | Skočí, pokud byla hodnota větší Skočí, pokud hodnota nebyla menší nebo rovná |
signed | ZF == 0 a SF == OF |
JP (Jump if Parity) JPE (Jump if Parity Even) |
Skočí, pokud byl nastaven parity bit Skočí, pokud byla sudá parita |
PF == 1 | ||
JNP (Jump if Not Parity) JPO (Jump if Parity Odd) |
Skočí, pokud nebyl nastaven parity bit Skočí, pokud byla lichá parita |
PF == 0 |
Tato tabulka je stále zjednodušená a neobsahuje instrukce pro všechny podmíněné skoky, například ty podle obsahu registrů. Nám však takto bohatě stačí
Některé uvedené skoky podle příznaků se nepoužívají tak často. My je budeme používat např. k tomu, abychom věděli, jestli se nějaká operace zdařila, či nikoliv.
V příští lekci, Assembler - Signed a Unsigned čísla, budeme pracovat s celými čísly se znaménkem.