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

16. Odkazy na Internetu

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:

  1. 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.
  2. 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:

  1. JE - Jump if equal
  2. 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:

  1. JL – Jump if less
  2. JNGE – Jump if not greater or equal
  3. JGE – Jump if greater or equal
  4. JNL – Jump if not less
  5. JLE –Jump if less or equal
  6. JNG – Jump if not greater
  7. JG – Jump if greater
  8. 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

  1. X86 Assembly/Arithmetic
    https://en.wikibooks.org/wiki/X86_Assembly/Arithmetic
  2. Art of Assembly - Arithmetic Instructions
    http://oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-2.html
  3. The GNU Assembler Tutorial
    http://tigcc.ticalc.org/doc/gnuasm.html
  4. The GNU Assembler - macros
    http://tigcc.ticalc.org/doc/gnuasm.html#SEC109
  5. ARM subroutines & program stack
    http://www.toves.org/books/armsub/
  6. Generating Mixed Source and Assembly List using GCC
    http://www.systutorials.com/240/generate-a-mixed-source-and-assembly-listing-using-gcc/
  7. Calling subroutines
    http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.kui0100a/armasm_cihcfigg.htm
  8. ARM Assembly Language Programming
    http://peter-cockerell.net/aalp/html/frames.html
  9. ASM Flags
    http://www.cavestory.org/guides/csasm/guide/asm_flags.html
  10. Status Register
    https://en.wikipedia.org/wiki/Status_register
  11. Intel x86 JUMP quick reference
    http://unixwiz.net/techtips/x86-jumps.html
  12. Linux assemblers: A comparison of GAS and NASM
    http://www.ibm.com/developerworks/library/l-gas-nasm/index.html
  13. Programovani v assembleru na OS Linux
    http://www.cs.vsb.cz/grygarek/asm/asmlinux.html
  14. Is it worthwhile to learn x86 assembly language today?
    https://www.quora.com/Is-it-worthwhile-to-learn-x86-assembly-language-today?share=1
  15. Why Learn Assembly Language?
    http://www.codeproject.com/Articles/89460/Why-Learn-Assembly-Language
  16. Is Assembly still relevant?
    http://programmers.stackexchange.com/questions/95836/is-assembly-still-relevant
  17. Why Learning Assembly Language Is Still a Good Idea
    http://www.onlamp.com/pub/a/onlamp/2004/05/06/writegreatcode.html
  18. Assembly language today
    http://beust.com/weblog/2004/06/23/assembly-language-today/
  19. Assembler: Význam assembleru dnes
    http://www.builder.cz/rubriky/assembler/vyznam-assembleru-dnes-155960cz
  20. Assembler pod Linuxem
    http://phoenix.inf.upol.cz/linux/prog/asm.html
  21. AT&T Syntax versus Intel Syntax
    https://www.sourceware.org/binutils/docs-2.12/as.info/i386-Syntax.html
  22. Linux Assembly website
    http://asm.sourceforge.net/
  23. Using Assembly Language in Linux
    http://asm.sourceforge.net/articles/linasm.html
  24. vasm
    http://sun.hasenbraten.de/vasm/
  25. vasm – dokumentace
    http://sun.hasenbraten.de/vasm/release/vasm.html
  26. The Yasm Modular Assembler Project
    http://yasm.tortall.net/
  27. 680x0:AsmOne
    http://www.amigacoding.com/index.php/680x0:AsmOne
  28. ASM-One Macro Assembler
    http://en.wikipedia.org/wiki/ASM-One_Macro_Assembler
  29. ASM-One pages
    http://www.theflamearrows.info/documents/asmone.html
  30. Základní informace o ASM-One
    http://www.theflamearrows.info/documents/asminfo.html
  31. Linux Syscall Reference
    http://syscalls.kernelgrok.com/
  32. Programming from the Ground Up Book - Summary
    http://savannah.nongnu.org/projects/pgubook/
  33. IBM System 360/370 Compiler and Historical Documentation
    http://www.edelweb.fr/Simula/
  34. IBM 700/7000 series
    http://en.wikipedia.org/wiki/IBM_700/7000_series
  35. IBM System/360
    http://en.wikipedia.org/wiki/IBM_System/360
  36. IBM System/370
    http://en.wikipedia.org/wiki/IBM_System/370
  37. Mainframe family tree and chronology
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_FT1.html
  38. 704 Data Processing System
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_PP704.html
  39. 705 Data Processing System
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_PP705.html
  40. The IBM 704
    http://www.columbia.edu/acis/history/704.html
  41. IBM Mainframe album
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_album.html
  42. Osmibitové muzeum
    http://osmi.tarbik.com/
  43. Tesla PMI-80
    http://osmi.tarbik.com/cssr/pmi80.html
  44. PMI-80
    http://en.wikipedia.org/wiki/PMI-80
  45. PMI-80
    http://www.old-computers.com/museum/computer.asp?st=1&c=1016
  46. The 6502 overflow flag explained mathematically
    http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html
  47. X86 Opcode and Instruction Reference
    http://ref.x86asm.net/coder32.html