Ve většině aplikací, jejichž části jsou psané v assembleru, se setkáme s nutností zpracovávat celočíselné hodnoty se znaménkem, což například v programovacím jazyce C odpovídá datovému typu signed int. Naprostá většina moderních mikroprocesorů práci s tímto datovým typem nativně podporuje, ovšem práce s celými čísly se znaménkem od programátora vyžaduje poněkud hlubší znalosti. Zejména je nutné porozumět tomu, jakým způsobem se pracuje se všemi čtyřmi příznakovými bity a zejména pak s jejich kombinacemi. Právě tímto důležitým tématem se budeme zabývat dnes.
Obsah
1. Použití assembleru v Linuxu: zpracování celých čísel se znaménkem
2. Základní aritmetické operace s celými čísly se znaménkem
3. Nastavení příznakových bitů Zero, Sign, Carry a Overflow při porovnání dvou operandů
4. První demonstrační příklad – nastavení a test příznaku Zero
5. Výsledky běhu prvního demonstračního příkladu
6. Druhý demonstrační příklad – nastavení a test příznaku Sign
7. Výsledky běhu druhého demonstračního příkladu
8. Třetí demonstrační příklad – nastavení a test příznaku Carry
9. Výsledky běhu třetího demonstračního příkladu
10. Čtvrtý demonstrační příklad – nastavení a test příznaku Overflow
11. Výsledky běhu čtvrtého demonstračního příkladu
12. Podmíněné skoky při práci s celými čísly se znaménkem
13. První skupina podmíněných skoků – test jediného příznaku
14. Druhá skupina podmíněných skoků – test kombinace příznaků
15. Repositář s demonstračními příklady
1. Použití assembleru v Linuxu: zpracování celých čísel se znaménkem
Všechny moderní typy mikroprocesorů obsahují ve svých instrukčních sadách mj. i instrukce určené pro zpracování celých čísel se znaménkem. V první řadě se pochopitelně jedná o instrukce, které lze použít pro provedení základních aritmetických operací (součet, rozdíl, součin, podíl popř. i výpočet zbytku po dělení), ovšem nesmíme zapomenout ani na operace aritmetického posunu doprava a doleva (neboli násobení a dělení mocninou dvou) a taktéž na podmíněné skoky. Právě kvůli podpoře podmíněných skoků je nutné, aby mikroprocesory kromě dvou již popsaných bitových příznaků Zero Flag a Carry Flag správně pracovaly i s příznaky Sign Flag a především pak Overflow Flag (poznámka: některé architektury, například MIPS, se zcela obejdou bez použití bitových příznaků, ovšem jak architektuře i386 a x86-64, tak i na klasické architektuře ARM, které jsou minimálně v tomto ohledu prakticky totožné, se s příznaky pracuje).
V dnešním článku se v úvodu seznámíme s významem všech čtyř zmíněných příznaků. Dále si řekneme, které základní aritmetické operace je možné použít na mikroprocesorové architektuře i386 a x86-64 a jak přesně se nastavují všechny čtyři již zmíněné příznaky. Následovat bude čtveřice poměrně jednoduchých demonstračních příkladů, na nichž je ukázán konkrétní způsob nastavení příznaků při provádění operace ADD a CMP s různými hodnotami. Ve dvanácté kapitole a v dalších dvou navazujících kapitolách si pak řekneme, jaké typy podmíněných skoků se používají při práci s celými čísly se znaménkem. Uvidíme, že se tyto skoky jsou již mnohem složitější, než tomu bylo u podmíněných skoků používaných při práci s čísly bez znaménka (je tomu tak proto, že u některých podmínek je nutné testovat kombinaci většího množství příznaků).
Poznámka: pod pojmem „čísla se znaménkem“ (signed integer) je myšlena reprezentace celých čísel v doplňkovém kódu (dvojkový doplněk).
2. Základní aritmetické operace s celými čísly se znaménkem
Pro začátek se budeme zabývat instrukční sadou mikroprocesorů s architekturou i386 a x86-64, a to z toho důvodu, aby bylo možné si všechny příklady ihned otestovat na běžném desktopu či notebooku. U těchto typů mikroprocesorů nalezneme několik aritmetických instrukcí podporujících zpracování celých čísel se znaménkem. Tyto instrukce jsou vypsány v následujících tabulkách. Většina zmíněných instrukcí dokáže zpracovat osmibitové, šestnáctibitové, 32bitové i 64bitové hodnoty, přičemž jeden operand může ležet v operační paměti a nikoli v pracovním registru:
Operace se dvěma vstupními operandy
# | Instrukce | Význam | Poznámka |
---|---|---|---|
1 | ADD dest, src | součet | použitelné i pro čísla bez znaménka |
2 | ADC dest, src | součet s přičtením CF | použitelné i pro čísla bez znaménka |
3 | SUB dest, src | rozdíl | použitelné i pro čísla bez znaménka |
4 | SBB dest, src | rozdíl | k menšiteli je nejprve přičten CF |
5 | IMUL dest, src | součin | nastavuje jen CF a OF |
6 | IMUL dest, src1, src2 | součin dest=src1*src2 | nastavuje jen CF a OF |
7 | IDIV dest | podíl + výpočet zbytku po dělení | příznaky nedefinované |
8 | CMP src1, src2 | porovnání registrů | jako SUB, ovšem bez uložení výsledku |
Poznámka1: u instrukce IDIV je první vstupní operand typicky uložen v registrovém páru AX, DX:AX či EDX:EAX. Výsledkem této instrukce je dvojice hodnot – podíl a zbytek po dělení.
Poznámka2: při striktním pohledu by se mohlo zdát, že instrukce CMP do seznamu aritmetických instrukcí nepatří, ovšem jedná se vlastně o běžnou instrukci SUB (rozdíl), s tím, že se nikam neukládá výsledek, pouze se nastaví všechny čtyři bitové příznaky. Tato instrukce je velmi užitečná a často používaná (i překladači).
Operace s jedním vstupním operandem
# | Instrukce | Význam | Poznámka |
---|---|---|---|
1 | INC | zvýšení registru či buňky v paměti o 1 | nenastavuje CF |
2 | DEC | snížení registru či buňky v paměti o 1 | nenastavuje CF |
3 | NEG | vynásobení hodnotou -1 | CF == 0 pro vstup==0, jinak CF==1 |
Poznámka1: zajímavé je, že instrukce INC a DEC nenastavují příznak CF, a to i když dojde k přetečení (hodnota CF je zachována, čehož lze v některých algoritmech využít). Lze tedy testovat pouze příznak ZF, který však má prakticky stejný význam: u ADD operand,1 se CF nastaví ve stejné chvíli jako ZF atd. (sami si to vyzkoušejte, ovšem u SUB operand,1 je to o krok posunuté).
3. Nastavení příznakových bitů Zero, Sign, Carry a Overflow při porovnání dvou operandů
V předchozí kapitole jsme se explicitně zmínili o velkém praktickém významu instrukce CMP použité při porovnání dvou operandů. Tato instrukce nastaví všechny čtyři bitové příznaky (navíc ještě příznak parity atd., ale to není tak důležité). Zajímavé je, že instrukce pracuje korektně jak pro čísla bez znaménka, tak i pro čísla se znaménkem, ovšem u čísel bez znaménka má smysl kontrolovat pouze příznaky ZF a CF, zatímco u čísel se znaménkem kombinaci tří příznaků ZF, SF a OF:
Příznak | Bez znaménka | Se znaménkem | ||
---|---|---|---|---|
Hodnota | 0 | 1 | 0 | 1 |
ZF | nerovnost | rovnost | nerovnost | rovnost |
CF | větší nebo rovno | menší než | × | × |
SF | × | × | viz další text | |
OF | × | × | viz další text |
Pro čísla se znaménkem je tedy skutečně nutné sledovat hodnoty příznaků ZF, SF a OF. Význam příznaku ZF je zřejmý – otestuje se jím rovnost obou porovnávaných operandů, protože bit ZF nabude hodnoty 1 pouze za předpokladu, že operand1==operand2 a tedy operand1-operand2==0. U příznaků SF a OF je to již poněkud komplikovanější, o čemž se můžeme přesvědčit při pohledu na následující tabulku:
SF | OF | Význam |
---|---|---|
0 | 1 | menší než |
1 | 0 | menší než |
0 | 0 | větší nebo rovno |
1 | 1 | větší nebo rovno |
Jinými slovy – podmínka operand1<operand2 je splněna ve chvíli, kdy platí SF⊕OF==1, kde ⊕ je bitový operátor XOR. Pokud naopak platí SF==OF, byla splněna podmínka operand1≥operand2. O rozhodnutí o zbylých dvou podmínkách operand1≤operand2 a operand1>operand2 se postará přečtení bitového příznaku ZF. Opět pro přehlednost:
ZF | SF | OF | Význam |
---|---|---|---|
0 | 0 | 1 | menší než |
0 | 1 | 0 | menší než |
0 | 0 | 0 | větší než |
0 | 1 | 1 | větší než |
1 | 0 | 1 | rovnost |
1 | 1 | 0 | rovnost |
1 | 0 | 0 | rovnost |
1 | 1 | 1 | rovnost |
Poznámka: ve skutečnosti všechny tyto možnosti nemohou nastat, což se týká posledních čtyř řádků tabulky (otázka pro čtenáře – proč tomu tak je?).
Pokud vás zajímá, jak přesně jsou použity výše zmíněné bitové příznaky v podmíněných skocích, pokračujte ve čtení dvanácté kapitoly. V následujících osmi kapitolách se totiž na relativně jednoduchých demonstračních příkladech přesvědčíme, za jakých okolností jsou příznaky nastavovány, což je sice užitečné pro pochopení interního chování mikroprocesoru a jeho ALU (aritmeticko-logické jednotky), ovšem u většiny programů je důležité pouze vědět, jaká instrukce skoku se má použít v návaznosti na instrukci CMP.
4. První demonstrační příklad – nastavení a test příznaku Zero
S příznakem ZF (Zero Flag) jsme se již poměrně podrobně seznámili v předchozích částech tohoto seriálu, takže si nyní pouze vytvořme demonstrační příklad, který ukáže, za jakých okolností se tento příznak nastaví instrukcemi ADD a CMP. V příkladu použijeme znalosti, které jsme získali minule a předminule, což konkrétně znamená, že příklad je rozdělen do tří souborů exit.s, writeMessage.s a zero_flag.s a používá ve velké míře makra. V prvním souboru nalezneme makro použité pro ukončení procesu:
# asmsyntax=as
# Makro pro ukonceni procesu v Linuxu.
#
# Autor: Pavel Tisnovsky
sys_exit = 1
# Deklarace makra pro ukonceni aplikace
.macro exit
mov eax, sys_exit # cislo sycallu pro funkci "exit"
mov ebx, 0 # exit code = 0
int 0x80 # volani Linuxoveho kernelu
.endm
Ve druhém souboru najdeme makro a subrutinu (volanou makrem) pro zobrazení řetězce na standardním výstupu:
# asmsyntax=as
# Makro pro tisk zpravy na standardni vystup.
#
# Autor: Pavel Tisnovsky
# Linux kernel system call table
sys_write = 4
std_output = 1
# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessage message,messageLength
mov ecx, offset \message # adresa retezce, ktery se ma vytisknout
mov edx, \messageLength # pocet znaku, ktere se maji vytisknout
call write_message # vytisknout zpravu "Zero flag not set"
.endm
# Podprogram pro vytisteni zpravy na standardni vystup
# Ocekava se, ze v ecx bude adresa zpravy a v edx jeji delka
write_message:
mov eax, sys_write # cislo syscallu pro funkci "write"
mov ebx, std_output # standardni vystup
int 0x80
ret
Třetí příklad obsahuje především dvojici maker nazvaných compareAndShowZeroFlag a addAndShowZeroFlag. Těmto makrům se předají dvě celočíselné 32bitové hodnoty se znaménkem, následně se provede vybraná operace (ADD nebo CMP) a vytiskne se hodnota příznaku ZF (Zero Flag) nastavená těmito operacemi. Tato makra jsou následně volána s různými hodnotami, viz též navazující kapitolu:
# asmsyntax=as
# Program pro otestovani chovani priznaku ZF (priznak nulovosti)
# - pouzita je "Intel" syntaxe.
#
# Autor: Pavel Tisnovsky
.intel_syntax noprefix
# Nacteni definice makra pro ukonceni aplikace
.include "exit.s"
# Nacteni maker pro tisk zpravy i prislusne subrutiny
.include "writeMessage.s"
#-----------------------------------------------------------------------------
# Deklarace makra pro porovnani dvou hodnot a vytisteni stavu priznaku ZF
.macro compareAndShowZeroFlag const1, const2
mov eax, \const1
mov ebx, \const2
cmp eax, ebx # porovnani registru a nastaveni priznaku
jz zero_set\@ # test na priznak ZF
writeMessage messageZeroNotSet, messageZeroNotSetLen
jmp end_compare\@
zero_set\@:
writeMessage messageZeroSet, messageZeroSetLen
end_compare\@:
.endm
# Deklarace makra pro soucet dvou hodnot a vytisteni stavu priznaku ZF
.macro addAndShowZeroFlag const1, const2
mov eax, \const1
mov ebx, \const2
add eax, ebx # soucet registru a nastaveni priznaku
jz zero_set\@ # test na priznak ZF
writeMessage messageZeroNotSet, messageZeroNotSetLen
jmp end_add\@
zero_set\@:
writeMessage messageZeroSet, messageZeroSetLen
end_add\@:
.endm
#-----------------------------------------------------------------------------
.section .data
messageZeroSet:
.string "Zero flag set\n"
messageZeroSetLen = $ - messageZeroSet # delka prvni zpravy
messageZeroNotSet:
.string "Zero flag not set\n"
messageZeroNotSetLen = $ - messageZeroNotSet # delka druhe zpravy
messageCmp:
.string "\nInstruction: CMP\n"
messageCmpLen = $ - messageCmp # delka treti zpravy
messageAdd:
.string "\nInstruction: ADD\n"
messageAddLen = $ - messageAdd # delka ctvrte zpravy
messagePositiveValues:
.string "\nPositive values\n"
messagePositiveValuesLen = $ - messagePositiveValues
messageNegativeValues:
.string "\nNegative values\n"
messageNegativeValuesLen = $ - messageNegativeValues
message0x7fffffffand0x80000000:
.string "\n0x7fffffff and 0x80000000\n"
message0x7fffffffand0x80000000Len = $ - message0x7fffffffand0x80000000
#-----------------------------------------------------------------------------
.section .bss
#-----------------------------------------------------------------------------
.section .text
.global _start # tento symbol ma byt dostupny i linkeru
_start:
writeMessage messageCmp, messageCmpLen
writeMessage messagePositiveValues, messagePositiveValuesLen
compareAndShowZeroFlag 0, 0
compareAndShowZeroFlag 100, 0
compareAndShowZeroFlag 0, 100
compareAndShowZeroFlag 100, 100
writeMessage messageNegativeValues, messageNegativeValuesLen
compareAndShowZeroFlag -100, 0
compareAndShowZeroFlag 0, -100
compareAndShowZeroFlag -100, -100
writeMessage messageAdd, messageAddLen
writeMessage messagePositiveValues, messagePositiveValuesLen
addAndShowZeroFlag 0, 0
addAndShowZeroFlag 100, 0
addAndShowZeroFlag 0, 100
addAndShowZeroFlag 100, 100
writeMessage messageNegativeValues, messageNegativeValuesLen
addAndShowZeroFlag -100, 0
addAndShowZeroFlag 100, -100
addAndShowZeroFlag -100, 100
addAndShowZeroFlag -100, -100
writeMessage message0x7fffffffand0x80000000, message0x7fffffffand0x80000000Len
addAndShowZeroFlag 0x7fffffff, 0x7fffffff
addAndShowZeroFlag 0x7fffffff, 0x80000000
addAndShowZeroFlag 0x80000000, 0x80000000
addAndShowZeroFlag 0x80000000, 0x80000001
exit # ukonceni aplikace
5. Výsledky běhu prvního demonstračního příkladu
Podívejme se nyní na výsledky vypsané na standardní výstup dnešním prvním demonstračním příkladem:
Instruction: CMP
Positive values
Zero flag set
Zero flag not set
Zero flag not set
Zero flag set
Negative values
Zero flag not set
Zero flag not set
Zero flag set
Instruction: ADD
Positive values
Zero flag set
Zero flag not set
Zero flag not set
Zero flag not set
Negative values
Zero flag not set
Zero flag set
Zero flag set
Zero flag not set
0x7fffffff and 0x80000000
Zero flag not set
Zero flag not set
Zero flag set
Zero flag not set
Výsledky lze shrnout následovně. U operace CMP je to nejjednodušší – pokud jsou oba porovnávané operandy totožné, nastaví se ZF na jedničku, v opačném případě na nulu. Nezáleží ani na nulovosti operandů, ani na jejich znaménku:
Operand 1 | Operand 2 | ZF po CMP | Poznámka |
---|---|---|---|
0 | 0 | 1 | shodné operandy |
100 | 0 | 0 | rozdílné operandy |
0 | 100 | 0 | rozdílné operandy |
100 | 100 | 1 | shodné operandy |
-100 | 0 | 0 | rozdílné operandy |
0 | -100 | 0 | rozdílné operandy |
-100 | -100 | 1 | shodné operandy |
U operace ADD nezáleží na původní hodnotě operandů, ale pouze na výsledku součtu. Pokud je součet nulový, je nastaven ZF na jedničku, v opačném případě na nulu. Pozor však na speciální případ, kdy dojde k přetečení výsledku – výsledek bude mít hodnotu přesně 0x100000000, což je o jedničku více, než maximální hodnota 0xffffffff reprezentovaná 32 bity. Ovšem vzhledem k tomu, že hodnota 0x100000000 má všech spodních 32bitů nulových, je i v tomto případě ZF nastaven:
Operand 1 | Operand 2 | ZF po ADD | Poznámka |
---|---|---|---|
0 | 0 | 1 | 0+0 = 0 |
100 | 0 | 0 | 100+0 ≠ 0 |
0 | 100 | 0 | 0+100 ≠ 0 |
100 | 100 | 0 | 100+100 ≠ 0 |
-100 | 0 | 0 | -100+0 ≠ 0 |
100 | -100 | 1 | 100+(-100) = 0 |
-100 | 100 | 1 | -100+100 = 0 |
-100 | -100 | 0 | -100+(-100) ≠ 0 |
0x7fffffff | 0x7fffffff | 0 | přetečení, spodních 32bitů ≠ 0 |
0x7fffffff | 0x80000000 | 0 | přetečení, spodních 32bitů ≠ 0 |
0x80000000 | 0x80000000 | 1 | přetečení, spodních 32bitů = 0 |
0x80000000 | 0x80000001 | 0 | přetečení, spodních 32bitů ≠ 0 |
6. Druhý demonstrační příklad – nastavení a test příznaku Sign
Druhý demonstrační příklad se prakticky ve všech ohledech podobá příkladu prvnímu, ovšem namísto testování příznaku ZF se zde testuje příznak SF, tedy znaménko výsledku prováděné operace (u instrukce CMP se tedy jedná o výsledek rozdílu obou operandů). Nejprve se podívejme na zdrojový kód tohoto příkladu, důležitější však bude pohled na výsledek uvedený v navazující kapitole:
# asmsyntax=as
# Program pro otestovani chovani priznaku SF (priznak znamenka)
# - pouzita je "Intel" syntaxe.
#
# Autor: Pavel Tisnovsky
.intel_syntax noprefix
# Nacteni definice makra pro ukonceni aplikace
.include "exit.s"
# Nacteni maker pro (opakovany) tisk zpravy i prislusne subrutiny
.include "writeMessage.s"
#-----------------------------------------------------------------------------
# Deklarace makra pro porovnani dvou hodnot a vytisteni stavu priznaku ZF
.macro compareAndShowSignFlag const1, const2
mov eax, \const1
mov ebx, \const2
cmp eax, ebx # porovnani registru a nastaveni priznaku
js sign_set\@ # test na priznak SF
writeMessage messageSignNotSet, messageSignNotSetLen
jmp end_compare\@
sign_set\@:
writeMessage messageSignSet, messageSignSetLen
end_compare\@:
.endm
# Deklarace makra pro soucet dvou hodnot a vytisteni stavu priznaku ZF
.macro addAndShowSignFlag const1, const2
mov eax, \const1
mov ebx, \const2
add eax, ebx # soucet registru a nastaveni priznaku
js sign_set\@ # test na priznak SF
writeMessage messageSignNotSet, messageSignNotSetLen
jmp end_add\@
sign_set\@:
writeMessage messageSignSet, messageSignSetLen
end_add\@:
.endm
#-----------------------------------------------------------------------------
.section .data
messageSignSet:
.string "Sign flag set\n"
messageSignSetLen = $ - messageSignSet # delka prvni zpravy
messageSignNotSet:
.string "Sign flag not set\n"
messageSignNotSetLen = $ - messageSignNotSet # delka druhe zpravy
messageCmp:
.string "\nInstruction: CMP\n"
messageCmpLen = $ - messageCmp # delka treti zpravy
messageAdd:
.string "\nInstruction: ADD\n"
messageAddLen = $ - messageAdd # delka ctvrte zpravy
messagePositiveValues:
.string "\nPositive values\n"
messagePositiveValuesLen = $ - messagePositiveValues
messageNegativeValues:
.string "\nNegative values\n"
messageNegativeValuesLen = $ - messageNegativeValues
message0x7fffffffand0x80000000:
.string "\n0x7fffffff and 0x80000000\n"
message0x7fffffffand0x80000000Len = $ - message0x7fffffffand0x80000000
#-----------------------------------------------------------------------------
.section .bss
#-----------------------------------------------------------------------------
.section .text
.global _start # tento symbol ma byt dostupny i linkeru
_start:
writeMessage messageCmp, messageCmpLen
writeMessage messagePositiveValues, messagePositiveValuesLen
compareAndShowSignFlag 0, 0
compareAndShowSignFlag 100, 0
compareAndShowSignFlag 0, 100
compareAndShowSignFlag 100, 100
writeMessage messageNegativeValues, messageNegativeValuesLen
compareAndShowSignFlag -100, 0
compareAndShowSignFlag 0, -100
compareAndShowSignFlag -100, -100
writeMessage messageAdd, messageAddLen
writeMessage messagePositiveValues, messagePositiveValuesLen
addAndShowSignFlag 0, 0
addAndShowSignFlag 100, 0
addAndShowSignFlag 0, 100
addAndShowSignFlag 100, 100
writeMessage messageNegativeValues, messageNegativeValuesLen
addAndShowSignFlag -100, 0
addAndShowSignFlag 100, -100
addAndShowSignFlag -100, 100
addAndShowSignFlag -100, -100
writeMessage message0x7fffffffand0x80000000, message0x7fffffffand0x80000000Len
addAndShowSignFlag 0x7fffffff, 0x7fffffff
addAndShowSignFlag 0x7fffffff, 0x80000000
addAndShowSignFlag 0x80000000, 0x80000000
addAndShowSignFlag 0x80000000, 0x80000001
exit # ukonceni aplikace
7. Výsledky běhu druhého demonstračního příkladu
Podobně jako u příkladu prvního, i zde si nejprve uvedeme zprávy vypsané demonstračním příkladem a posléze výsledky shrneme v přehlednějších tabulkách:
Instruction: CMP
Positive values
Sign flag not set
Sign flag not set
Sign flag set
Sign flag not set
Negative values
Sign flag set
Sign flag not set
Sign flag not set
Instruction: ADD
Positive values
Sign flag not set
Sign flag not set
Sign flag not set
Sign flag not set
Negative values
Sign flag set
Sign flag not set
Sign flag not set
Sign flag set
0x7fffffff and 0x80000000
Sign flag set
Sign flag set
Sign flag not set
Sign flag not set
Podívejme se nyní na shrnutí všech výsledků vypsaných tímto demonstračním příkladem. Začneme operací CMP, která odečte oba operandy a do příznaku SF dosadí znaménko výsledku (rozdílu). Pokud je výsledek záporný, je SF nastaven na jedničku, v opačném případě je nastaven na nulu:
Operand 1 | Operand 2 | SF po CMP | Poznámka |
---|---|---|---|
0 | 0 | 0 | 0 - 0 ≥ 0 |
100 | 0 | 0 | 100 - 0 ≥ 0 |
0 | 100 | 1 | 0 - 100 < 0 |
100 | 100 | 0 | 100 - 0 ≥ 0 |
-100 | 0 | 1 | -100 - 0 < 0 |
0 | -100 | 0 | 0 - (-100) ≥ 0 |
-100 | -100 | 0 | -100 - (-100) ≥ 0 |
Z této tabulky vyplývá i sémantika operace CMP a příznaku SF – testuje se platnost podmínek x - y ≥ 0 a x - y < 0.
Výsledky pro operaci ADD:
Operand 1 | Operand 2 | SF po ADD | Poznámka |
---|---|---|---|
0 | 0 | 0 | 0+0 ≥ 0 |
100 | 0 | 0 | 100+0 ≥ 0 |
0 | 100 | 0 | 0+100 ≥ 0 |
100 | 100 | 0 | 100+100 ≥ 0 |
-100 | 0 | 1 | -100+0 < 0 |
100 | -100 | 0 | 100+(-100) ≥ 0 |
-100 | 100 | 0 | -100+100 ≥ 0 |
-100 | -100 | 1 | -100+(-100) < 0 |
0x7fffffff | 0x7fffffff | 1 | nejvyšší bit výsledku má hodnotu 1 |
0x7fffffff | 0x80000000 | 1 | nejvyšší bit výsledku má hodnotu 1 |
0x80000000 | 0x80000000 | 0 | nejvyšší bit výsledku má hodnotu 0 |
0x80000000 | 0x80000001 | 0 | nejvyšší bit výsledku má hodnotu 0 |
Opět si povšimněte, že v mezních případech – při přetečení – je sice příznak SF „nějak“ nastaven, ale bez dalších testů nejsme schopni říci, k jaké situaci přesně došlo.
8. Třetí demonstrační příklad – nastavení a test příznaku Carry
Třetí demonstrační příklad již bude poněkud zajímavější, než předchozí dva příklady, protože v něm budeme sledovat nastavení příznaku přenosu (carry), a to jak v případě výpočtu rozdílu operandů (instrukce CMP), tak i při jejich součtu (instrukce ADD). Zdrojový kód tohoto příkladu je prakticky totožný s příklady předchozími, samozřejmě ovšem s tím rozdílem, že namísto podmíněného skoku jz (jump if ZF set) či js (jump if SF set) je použit podmíněný skok jc (jump if CF set):
# asmsyntax=as
# Program pro otestovani chovani priznaku CF (priznak prenosu)
# - pouzita je "Intel" syntaxe.
#
# Autor: Pavel Tisnovsky
.intel_syntax noprefix
# Nacteni definice makra pro ukonceni aplikace
.include "exit.s"
# Nacteni maker pro (opakovany) tisk zpravy i prislusne subrutiny
.include "writeMessage.s"
#-----------------------------------------------------------------------------
# Deklarace makra pro porovnani dvou hodnot a vytisteni stavu priznaku ZF
.macro compareAndShowCarryFlag const1, const2
mov eax, \const1
mov ebx, \const2
cmp eax, ebx # porovnani registru a nastaveni priznaku
jc carry_set\@ # test na priznak CF
writeMessage messageCarryNotSet, messageCarryNotSetLen
jmp end_compare\@
carry_set\@:
writeMessage messageCarrySet, messageCarrySetLen
end_compare\@:
.endm
# Deklarace makra pro soucet dvou hodnot a vytisteni stavu priznaku ZF
.macro addAndShowCarryFlag const1, const2
mov eax, \const1
mov ebx, \const2
add eax, ebx # soucet registru a nastaveni priznaku
jc carry_set\@ # test na priznak CF
writeMessage messageCarryNotSet, messageCarryNotSetLen
jmp end_add\@
carry_set\@:
writeMessage messageCarrySet, messageCarrySetLen
end_add\@:
.endm
#-----------------------------------------------------------------------------
.section .data
messageCarrySet:
.string "Carry flag set\n"
messageCarrySetLen = $ - messageCarrySet # delka prvni zpravy
messageCarryNotSet:
.string "Carry flag not set\n"
messageCarryNotSetLen = $ - messageCarryNotSet # delka druhe zpravy
messageCmp:
.string "\nInstruction: CMP\n"
messageCmpLen = $ - messageCmp # delka treti zpravy
messageAdd:
.string "\nInstruction: ADD\n"
messageAddLen = $ - messageAdd # delka ctvrte zpravy
messagePositiveValues:
.string "\nPositive values\n"
messagePositiveValuesLen = $ - messagePositiveValues
messageNegativeValues:
.string "\nNegative values\n"
messageNegativeValuesLen = $ - messageNegativeValues
message0x7fffffffand0x80000000:
.string "\n0x7fffffff and 0x80000000\n"
message0x7fffffffand0x80000000Len = $ - message0x7fffffffand0x80000000
#-----------------------------------------------------------------------------
.section .bss
#-----------------------------------------------------------------------------
.section .text
.global _start # tento symbol ma byt dostupny i linkeru
_start:
writeMessage messageCmp, messageCmpLen
writeMessage messagePositiveValues, messagePositiveValuesLen
compareAndShowCarryFlag 0, 0
compareAndShowCarryFlag 100, 0
compareAndShowCarryFlag 0, 100
compareAndShowCarryFlag 100, 100
writeMessage messageNegativeValues, messageNegativeValuesLen
compareAndShowCarryFlag -100, 0
compareAndShowCarryFlag 0, -100
compareAndShowCarryFlag -100, -100
writeMessage messageAdd, messageAddLen
writeMessage messagePositiveValues, messagePositiveValuesLen
addAndShowCarryFlag 0, 0
addAndShowCarryFlag 100, 0
addAndShowCarryFlag 0, 100
addAndShowCarryFlag 100, 100
writeMessage messageNegativeValues, messageNegativeValuesLen
addAndShowCarryFlag -100, 0
addAndShowCarryFlag 100, -100
addAndShowCarryFlag -100, 100
addAndShowCarryFlag -100, -100
writeMessage message0x7fffffffand0x80000000, message0x7fffffffand0x80000000Len
addAndShowCarryFlag 0x7fffffff, 0
addAndShowCarryFlag 0x7fffffff, 1
addAndShowCarryFlag 0x7fffffff, 2
addAndShowCarryFlag 0x7fffffff, 0x7fffffff
addAndShowCarryFlag 0x7fffffff, 0x80000000
addAndShowCarryFlag 0x80000000, 0x80000000
addAndShowCarryFlag 0x80000000, 0x80000001
exit # ukonceni aplikace
9. Výsledky běhu třetího demonstračního příkladu
Po překladu a spuštění tohoto příkladu získáme na standardním výstupu následující zprávy:
Instruction: CMP
Positive values
Carry flag not set
Carry flag not set
Carry flag set
Carry flag not set
Negative values
Carry flag not set
Carry flag set
Carry flag not set
Instruction: ADD
Positive values
Carry flag not set
Carry flag not set
Carry flag not set
Carry flag not set
Negative values
Carry flag not set
Carry flag set
Carry flag set
Carry flag set
0x7fffffff and 0x80000000
Carry flag not set
Carry flag not set
Carry flag not set
Carry flag not set
Carry flag not set
Carry flag set
Carry flag set
Shrnutí výsledků bude podobné, jako u předchozích dvou příkladů.
Operace CMP:
Operand 1 | Operand 2 | CF po CMP | Poznámka |
---|---|---|---|
0 | 0 | 0 | nedošlo k přenosu |
100 | 0 | 0 | nedošlo k přenosu |
0 | 100 | 1 | při operaci 0-100 je potřeba výpůjčka (borrow) |
100 | 100 | 0 | nedošlo k přenosu |
-100 | 0 | 0 | nedošlo k přenosu (-100 v dvojkovém doplňku) |
0 | -100 | 1 | při operaci 0-(-100) je potřeba výpůjčka (borrow) |
-100 | -100 | 0 | rozdíl dvou stejných čísel – nedojde k přenosu |
Povšimněte si, že příznak carry (přenos) je nastavován i v případě, že byla použita výpůjčka (borrow). U některých typů mikroprocesorů je tomu přesně naopak! (ze stále používaných čipů se jedná o řadu PIC).
Operace ADD:
Operand 1 | Operand 2 | CF po ADD | Poznámka |
---|---|---|---|
0 | 0 | 0 | bez přenosu |
100 | 0 | 0 | bez přenosu |
0 | 100 | 0 | bez přenosu |
100 | 100 | 0 | bez přenosu |
-100 | 0 | 0 | bez přenosu |
100 | -100 | 1 | s přenosem! (-100 v dvojkovém doplňku) |
-100 | 100 | 1 | s přenosem! (-100 v dvojkovém doplňku) |
-100 | -100 | 1 | s přenosem! (-100 v dvojkovém doplňku) |
0x7fffffff | 0 | 0 | bez přenosu |
0x7fffffff | 1 | 0 | bez přenosu |
0x7fffffff | 2 | 0 | bez přenosu |
0x7fffffff | 0x7fffffff | 0 | bez přenosu |
0x7fffffff | 0x80000000 | 0 | bez přenosu (na hranici) |
0x80000000 | 0x80000000 | 1 | přenos a výsledkem je 0 |
0x80000000 | 0x80000001 | 1 | přenos a výsledkem je 1 |
Zde můžeme vidět dvě zajímavosti:
- U operací typu -100+100 je sice výsledkem nula, ale ve skutečnosti dojde k přenosu, protože hodnota -100 je reprezentována v dvojkovém doplňku.
- U čísel bez znaménka nedává (u porovnávání operací CMP) moc smysl testovat právě CF, protože je výsledek nekonzistentní.
10. Čtvrtý demonstrační příklad – nastavení a test příznaku Overflow
Čtvrtý a současně i dnešní poslední demonstrační příklad je v kontextu tohoto článku nejdůležitější, protože ukazuje, jakým způsobem je nastavován příznak přetečení (overflow) u operací ADD a CMP. Podívejme se nejprve na zdrojový kód tohoto příkladu:
# asmsyntax=as
# Program pro otestovani chovani priznaku OF (priznak preteceni)
# - pouzita je "Intel" syntaxe.
#
# Autor: Pavel Tisnovsky
.intel_syntax noprefix
# Nacteni definice makra pro ukonceni aplikace
.include "exit.s"
# Nacteni maker pro (opakovany) tisk zpravy i prislusne subrutiny
.include "writeMessage.s"
#-----------------------------------------------------------------------------
# Deklarace makra pro porovnani dvou hodnot a vytisteni stavu priznaku ZF
.macro compareAndShowOverflowFlag const1, const2
mov eax, \const1
mov ebx, \const2
cmp eax, ebx # porovnani registru a nastaveni priznaku
jo overflow_set\@ # test na priznak OF
writeMessage messageOverflowNotSet, messageOverflowNotSetLen
jmp end_compare\@
overflow_set\@:
writeMessage messageOverflowSet, messageOverflowSetLen
end_compare\@:
.endm
# Deklarace makra pro soucet dvou hodnot a vytisteni stavu priznaku ZF
.macro addAndShowOverflowFlag const1, const2
mov eax, \const1
mov ebx, \const2
add eax, ebx # soucet registru a nastaveni priznaku
jo overflow_set\@ # test na priznak OF
writeMessage messageOverflowNotSet, messageOverflowNotSetLen
jmp end_add\@
overflow_set\@:
writeMessage messageOverflowSet, messageOverflowSetLen
end_add\@:
.endm
#-----------------------------------------------------------------------------
.section .data
messageOverflowSet:
.string "Overflow flag set\n"
messageOverflowSetLen = $ - messageOverflowSet # delka prvni zpravy
messageOverflowNotSet:
.string "Overflow flag not set\n"
messageOverflowNotSetLen = $ - messageOverflowNotSet # delka druhe zpravy
messageCmp:
.string "\nInstruction: CMP\n"
messageCmpLen = $ - messageCmp # delka treti zpravy
messageAdd:
.string "\nInstruction: ADD\n"
messageAddLen = $ - messageAdd # delka ctvrte zpravy
messagePositiveValues:
.string "\nPositive values\n"
messagePositiveValuesLen = $ - messagePositiveValues
messageNegativeValues:
.string "\nNegative values\n"
messageNegativeValuesLen = $ - messageNegativeValues
message0x7fffffffand0x80000000:
.string "\n0x7fffffff and 0x80000000\n"
message0x7fffffffand0x80000000Len = $ - message0x7fffffffand0x80000000
#-----------------------------------------------------------------------------
.section .bss
#-----------------------------------------------------------------------------
.section .text
.global _start # tento symbol ma byt dostupny i linkeru
_start:
writeMessage messageCmp, messageCmpLen
writeMessage messagePositiveValues, messagePositiveValuesLen
compareAndShowOverflowFlag 0, 0
compareAndShowOverflowFlag 100, 0
compareAndShowOverflowFlag 0, 100
compareAndShowOverflowFlag 100, 100
writeMessage messageNegativeValues, messageNegativeValuesLen
compareAndShowOverflowFlag -100, 0
compareAndShowOverflowFlag 0, -100
compareAndShowOverflowFlag -100, -100
writeMessage messageAdd, messageAddLen
writeMessage messagePositiveValues, messagePositiveValuesLen
addAndShowOverflowFlag 0, 0
addAndShowOverflowFlag 100, 0
addAndShowOverflowFlag 0, 100
addAndShowOverflowFlag 100, 100
writeMessage messageNegativeValues, messageNegativeValuesLen
addAndShowOverflowFlag -100, 0
addAndShowOverflowFlag 100, -100
addAndShowOverflowFlag -100, 100
addAndShowOverflowFlag -100, -100
writeMessage message0x7fffffffand0x80000000, message0x7fffffffand0x80000000Len
addAndShowOverflowFlag 0x7fffffff, 0
addAndShowOverflowFlag 0x7fffffff, 1
addAndShowOverflowFlag 0x7fffffff, 2
addAndShowOverflowFlag 0x7fffffff, 0x7fffffff
addAndShowOverflowFlag 0x7fffffff, 0x80000000
addAndShowOverflowFlag 0x80000000, 0x80000000
addAndShowOverflowFlag 0x80000000, 0x80000001
exit # ukonceni aplikace
11. Výsledky běhu čtvrtého demonstračního příkladu
Výsledky čtvrtého příkladu jsou nejzajímavější a poněkud méně předvídatelné, takže je vhodné je sledovat pečlivěji. Nejprve se podívejme, jaké zprávy se vypíšou na standardní výstup:
Instruction: CMP
Positive values
Overflow flag not set
Overflow flag not set
Overflow flag not set
Overflow flag not set
Negative values
Overflow flag not set
Overflow flag not set
Overflow flag not set
Instruction: ADD
Positive values
Overflow flag not set
Overflow flag not set
Overflow flag not set
Overflow flag not set
Negative values
Overflow flag not set
Overflow flag not set
Overflow flag not set
Overflow flag not set
0x7fffffff and 0x80000000
Overflow flag not set
Overflow flag set
Overflow flag set
Overflow flag set
Overflow flag not set
Overflow flag set
Overflow flag set
Operace CMP:
Operand 1 | Operand 2 | OF po CMP | Poznámka |
---|---|---|---|
0 | 0 | 0 | nedošlo k přetečení |
100 | 0 | 0 | nedošlo k přetečení |
0 | 100 | 1 | nedošlo k přetečení |
100 | 100 | 0 | nedošlo k přetečení |
-100 | 0 | 0 | nedošlo k přetečení |
0 | -100 | 1 | nedošlo k přetečení |
-100 | -100 | 0 | nedošlo k přetečení |
Na rozdíl od příznaku CF, který v případě záporných čísel dával nekonzistentní výsledky, je příznak OF konzistentní – všechny porovnávané hodnoty jsou tak malé, že u jejich rozdílu k přetečení nedojde.
Operace ADD:
Operand 1 | Operand 2 | CF po ADD | Poznámka |
---|---|---|---|
0 | 0 | 0 | bez přetečení |
100 | 0 | 0 | bez přetečení |
0 | 100 | 0 | bez přetečení |
100 | 100 | 0 | bez přetečení |
-100 | 0 | 0 | bez přetečení |
100 | -100 | 0 | bez přetečení |
-100 | 100 | 0 | bez přetečení |
-100 | -100 | 0 | bez přetečení |
0x7fffffff | 0 | 0 | bez přetečení (mezní případ) |
0x7fffffff | 1 | 1 | došlo ke změně znaménka výsledku – přetečení |
0x7fffffff | 2 | 1 | došlo ke změně znaménka výsledku – přetečení |
0x7fffffff | 0x7fffffff | 1 | došlo ke změně znaménka |
0x7fffffff | 0x80000000 | 0 | výsledkem je -1, ovšem druhý vstupní operand je záporný |
0x80000000 | 0x80000000 | 1 | změna znaménka a současně i přenos (carry) |
0x80000000 | 0x80000001 | 1 | změna znaménka a současně i přenos (carry) |
Z tabulky je patrné, že při nastavování příznaku OF se testují jak znaménka vstupních operandů, tak i operandů výstupního. Vzorec pro výpočet vypadá takto:
OF = not (((A31 nor B31) and C30) nor ((A31 nand B31) nor C30))
C30 je přenos z třicátého do třicátého prvního bitu, A a B jsou vstupní operandy.
12. Podmíněné skoky při práci s celými čísly se znaménkem
Nyní již máme k dispozici všechny informace potřebné pro to, abychom se seznámili s podmíněnými skoky používanými při práci s celými čísly. Připomeňme si, že podmíněné skoky jsou v assembleru základním prvkem pro řízení běhu programu, tedy pro tvorbu programových smyček i rozhodovacích konstrukcí typu if-then-else. V případě celých čísel bez znaménka jsme si (většinou) vystačili s pouhým porovnáním a otestováním příznaku ZF a/nebo CF. Příkladem může být již dříve popsaná programová smyčka určená pro vytištění sekvence hvězdiček:
mov ecx, offset buffer # zapis se bude provadet do tohoto bufferu
mov ebx, rep_count # pocet opakovani znaku
mov al, '*' # zapisovany znak
loop:
mov [ecx], al # zapis znaku do bufferu
inc ecx # uprava ukazatele do bufferu
dec ebx # zmenseni pocitadla
jnz loop # pokud jsme se nedostali k nule, skok na zacatek smycky
U čísel se znaménkem je to složitější, neboť je nutné testovat kombinaci tří příznaků: ZF, SF a OF, což je naznačeno v poslední tabulce uvedené ve třetí kapitole. Sémantika skoků je platná pro operaci CMP a samozřejmě též SUB.
13. První skupina podmíněných skoků – test jediného příznaku
Některé podmíněné skoky si po porovnání dvou operandů se znaménkem vystačí s testováním jediného příznaku. Jedná se o následující skoky:
# | Instrukce | Alias | Test | Podmínka |
---|---|---|---|---|
1 | JZ | JE | ZF == 1 | op1 == op2 |
2 | JNZ | JNE | ZF == 0 | op1 ≠ op2 |
Mnemotechnické pomůcky:
- JE - Jump if equal
- JNE - Jump if not equal
14. Druhá skupina podmíněných skoků – test kombinace příznaků
Ostatní podmíněné skoky po porovnání dvou operandů se znaménkem musí testovat kombinaci většího množství příznaků. Jedná se o následující skoky:
# | Instrukce | Alias | Test | Podmínka |
---|---|---|---|---|
1 | JL | JNGE | SF <> OF | op1 < op2 |
2 | JGE | JNL | SF == OF | op1 ≥ op2 |
3 | JLE | JNG | ZF = 1 or SF <> OF | op1 ≤ op2 |
4 | JG | JNLE | ZF = 0 and SF == OF | op1 > op2 |
Poznámka: interně se podmínka SF <> OF realizuje pomocí bitové operace SF ⊕ OF.
Mnemotechnické pomůcky:
- JL – Jump if less
- JNGE – Jump if not greater or equal
- JGE – Jump if greater or equal
- JNL – Jump if not less
- JLE –Jump if less or equal
- JNG – Jump if not greater
- JG – Jump if greater
- JNLE – Jump if not less or equal
15. Repositář s demonstračními příklady
Všechny dnes popisované demonstrační příklady byly, podobně jako ve všech předchozích částech tohoto seriálu, společně s podpůrnými skripty určenými pro jejich překlad či naopak pro disassembling, uloženy do GIT repositáře dostupného na adrese https://github.com/tisnik/presentations/. Všechny příklady jsou určeny pro GNU Assembler a používají Intel syntaxi, která je čitelnější, než původní AT&T syntaxe. Následuje tabulka s odkazy na zdrojové kódy příkladů i na již zmíněné podpůrné skripty:
První demonstrační příklad – nastavení a test příznaku Zero
# | Soubor | Popis | Odkaz do repositáře |
---|---|---|---|
1 | zero_flag.s | hlavní program pro GNU Assembler | https://github.com/tisnik/presentations/blob/master/assembler/28_zero_flag/zero_flag.s |
2 | exit.s | program pro GNU Assembler, který se vkládá do prvního souboru | https://github.com/tisnik/presentations/blob/master/assembler/28_zero_flag/exit.s |
3 | writeMessage.s | program pro GNU Assembler, který se vkládá do prvního souboru | https://github.com/tisnik/presentations/blob/master/assembler/28_zero_flag/writeMessage.s |
4 | assemble | skript pro překlad na procesorech i386 | https://github.com/tisnik/presentations/blob/master/assembler/28_zero_flag/assemble |
5 | disassemble | skript pro disassembling | https://github.com/tisnik/presentations/blob/master/assembler/28_zero_flag/disassemble |
Druhý demonstrační příklad – nastavení a test příznaku Sign
# | Soubor | Popis | Odkaz do repositáře |
---|---|---|---|
1 | sign_flag.s | hlavní program pro GNU Assembler | https://github.com/tisnik/presentations/blob/master/assembler/29_sign_flag/sign_flag.s |
2 | exit.s | program pro GNU Assembler, který se vkládá do prvního souboru | https://github.com/tisnik/presentations/blob/master/assembler/29_sign_flag/exit.s |
3 | writeMessage.s | program pro GNU Assembler, který se vkládá do prvního souboru | https://github.com/tisnik/presentations/blob/master/assembler/29_sign_flag/writeMessage.s |
4 | assemble | skript pro překlad na procesorech i386 | https://github.com/tisnik/presentations/blob/master/assembler/29_sign_flag/assemble |
5 | disassemble | skript pro disassembling | https://github.com/tisnik/presentations/blob/master/assembler/29_sign_flag/disassemble |
Třetí demonstrační příklad – nastavení a test příznaku Carry
# | Soubor | Popis | Odkaz do repositáře |
---|---|---|---|
1 | carry_flag.s | hlavní program pro GNU Assembler | https://github.com/tisnik/presentations/blob/master/assembler/30_carry_flag/carry_flag.s |
2 | exit.s | program pro GNU Assembler, který se vkládá do prvního souboru | https://github.com/tisnik/presentations/blob/master/assembler/30_carry_flag/exit.s |
3 | writeMessage.s | program pro GNU Assembler, který se vkládá do prvního souboru | https://github.com/tisnik/presentations/blob/master/assembler/30_carry_flag/writeMessage.s |
4 | assemble | skript pro překlad na procesorech i386 | https://github.com/tisnik/presentations/blob/master/assembler/30_carry_flag/assemble |
5 | disassemble | skript pro disassembling | https://github.com/tisnik/presentations/blob/master/assembler/30_carry_flag/disassemble |
Čtvrtý demonstrační příklad – nastavení a test příznaku Overflow
# | Soubor | Popis | Odkaz do repositáře |
---|---|---|---|
1 | overflow_flag.s | hlavní program pro GNU Assembler | https://github.com/tisnik/presentations/blob/master/assembler/31_overflow_flag/overflow_flag.s |
2 | exit.s | program pro GNU Assembler, který se vkládá do prvního souboru | https://github.com/tisnik/presentations/blob/master/assembler/31_overflow_flag/exit.s |
3 | writeMessage.s | program pro GNU Assembler, který se vkládá do prvního souboru | https://github.com/tisnik/presentations/blob/master/assembler/31_overflow_flag/writeMessage.s |
4 | assemble | skript pro překlad na procesorech i386 | https://github.com/tisnik/presentations/blob/master/assembler/31_overflow_flag/assemble |
5 | disassemble | skript pro disassembling | https://github.com/tisnik/presentations/blob/master/assembler/31_overflow_flag/disassemble |
16. Odkazy na Internetu
- X86 Assembly/Arithmetic
https://en.wikibooks.org/wiki/X86_Assembly/Arithmetic - Art of Assembly - Arithmetic Instructions
http://oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-2.html - The GNU Assembler Tutorial
http://tigcc.ticalc.org/doc/gnuasm.html - The GNU Assembler - macros
http://tigcc.ticalc.org/doc/gnuasm.html#SEC109 - ARM subroutines & program stack
http://www.toves.org/books/armsub/ - Generating Mixed Source and Assembly List using GCC
http://www.systutorials.com/240/generate-a-mixed-source-and-assembly-listing-using-gcc/ - Calling subroutines
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.kui0100a/armasm_cihcfigg.htm - ARM Assembly Language Programming
http://peter-cockerell.net/aalp/html/frames.html - ASM Flags
http://www.cavestory.org/guides/csasm/guide/asm_flags.html - Status Register
https://en.wikipedia.org/wiki/Status_register - Intel x86 JUMP quick reference
http://unixwiz.net/techtips/x86-jumps.html - Linux assemblers: A comparison of GAS and NASM
http://www.ibm.com/developerworks/library/l-gas-nasm/index.html - Programovani v assembleru na OS Linux
http://www.cs.vsb.cz/grygarek/asm/asmlinux.html - Is it worthwhile to learn x86 assembly language today?
https://www.quora.com/Is-it-worthwhile-to-learn-x86-assembly-language-today?share=1 - Why Learn Assembly Language?
http://www.codeproject.com/Articles/89460/Why-Learn-Assembly-Language - Is Assembly still relevant?
http://programmers.stackexchange.com/questions/95836/is-assembly-still-relevant - Why Learning Assembly Language Is Still a Good Idea
http://www.onlamp.com/pub/a/onlamp/2004/05/06/writegreatcode.html - Assembly language today
http://beust.com/weblog/2004/06/23/assembly-language-today/ - Assembler: Význam assembleru dnes
http://www.builder.cz/rubriky/assembler/vyznam-assembleru-dnes-155960cz - Assembler pod Linuxem
http://phoenix.inf.upol.cz/linux/prog/asm.html - AT&T Syntax versus Intel Syntax
https://www.sourceware.org/binutils/docs-2.12/as.info/i386-Syntax.html - Linux Assembly website
http://asm.sourceforge.net/ - Using Assembly Language in Linux
http://asm.sourceforge.net/articles/linasm.html - vasm
http://sun.hasenbraten.de/vasm/ - vasm – dokumentace
http://sun.hasenbraten.de/vasm/release/vasm.html - The Yasm Modular Assembler Project
http://yasm.tortall.net/ - 680x0:AsmOne
http://www.amigacoding.com/index.php/680x0:AsmOne - ASM-One Macro Assembler
http://en.wikipedia.org/wiki/ASM-One_Macro_Assembler - ASM-One pages
http://www.theflamearrows.info/documents/asmone.html - Základní informace o ASM-One
http://www.theflamearrows.info/documents/asminfo.html - Linux Syscall Reference
http://syscalls.kernelgrok.com/ - Programming from the Ground Up Book - Summary
http://savannah.nongnu.org/projects/pgubook/ - IBM System 360/370 Compiler and Historical Documentation
http://www.edelweb.fr/Simula/ - IBM 700/7000 series
http://en.wikipedia.org/wiki/IBM_700/7000_series - IBM System/360
http://en.wikipedia.org/wiki/IBM_System/360 - IBM System/370
http://en.wikipedia.org/wiki/IBM_System/370 - Mainframe family tree and chronology
http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_FT1.html - 704 Data Processing System
http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_PP704.html - 705 Data Processing System
http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_PP705.html - The IBM 704
http://www.columbia.edu/acis/history/704.html - IBM Mainframe album
http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_album.html - Osmibitové muzeum
http://osmi.tarbik.com/ - Tesla PMI-80
http://osmi.tarbik.com/cssr/pmi80.html - PMI-80
http://en.wikipedia.org/wiki/PMI-80 - PMI-80
http://www.old-computers.com/museum/computer.asp?st=1&c=1016 - The 6502 overflow flag explained mathematically
http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html - X86 Opcode and Instruction Reference
http://ref.x86asm.net/coder32.html