Assembler v Linuxu se v současnosti používá převážně v těch situacích, kdy je zapotřebí efektivně provést pouze určité specifické paměťově či výpočetně náročné operace. Zbytek aplikace se přitom vytváří v některém vyšším programovacím jazyku, například v C či C++. Z tohoto důvodu je užitečné vědět, jak je možné assembler (resp. kód psaný v assembleru) kombinovat se zdrojovým kódem psaným v C či C++. Právě této problematice je věnován dnešní článek.
Obsah
1. Použití assembleru v Linuxu: assembler a jazyk C
3. Jak se příkazy asm a __asm__ zpracovávají?
4. Blok vytvořený v assembleru bez vstupních a výstupních operandů
5. Specifikace výstupních operandů
6. Přesné určení registrů pro výstupní operandy
7. Specifikace vstupních operandů
8. Explicitní určení registru pro výstupní operand
9. Explicitní určení registrů pro vstupní operandy i výstupní operand
10. Stejný registr použitý pro vstupní i výstupní operand
11. Vliv optimalizací na generovaný kód
12. Použití syntaxe používané firmou Intel
13. Repositář s demonstračními příklady
1. Použití assembleru v Linuxu: assembler a jazyk C
Pokud je nutné v nějaké nativní aplikaci, tj. v aplikaci překládané do strojového kódu, určitou kritickou část naprogramovat v assembleru, mají vývojáři k dispozici hned několik možností:
- Ta část, která je psaná v assembleru, může být přeložena ze samostatných zdrojových kódů libovolným assemblerem (GNU Assembler, NASM atd.) a následně staticky slinkována se zbytkem aplikace, což jsme si již v tomto seriálu ukázali. Připomeňme si, že kód v assembleru lze v případě potřeby přímo překládat zavoláním gcc, nikoli as, což nám mj. umožní implicitně volat linker (pokud je to zapotřebí). V assembleru jsme v tomto případě omezeni na tvorbu subrutin, které se volají z céčkového kódu, tj. nelze například naprogramovat jen vnitřní smyčku v assembleru.
- Část psanou v assembleru lze přeložit běžným způsobem (opět s využitím GNU Assembleru, NASMu atd.) a následně objektový kód uložit do dynamické knihovny (so). Tato knihovna se bude k aplikaci linkovat až po spuštění programu. Platí pro nás prakticky stejná omezení, jaká byla zmíněna v předchozím bodu.
- V jediném zdrojovém kódu lze kombinovat céčko (či C++) a assembler. Tato možnost přináší některé výhody, například zápis instrukcí v assembleru přímo do funkce (optimalizace vnitřních smyček), ovšem i některé nevýhody, protože zdrojový kód se stane nepřenositelným na další platformy (s využitím podmíněného překladu je však možné pro další platformy připravit alternativní kód). I přesto si tento způsob dnes vysvětlíme; taktéž si ukážeme, jak se do části psané v assembleru předávají parametry.
Poznámka: všechny demonstrační příklady byly vyzkoušeny s překladačem GCC a nemusí být plně přenositelné na další typy překladačů:
<strong>gcc --version</strong>
gcc (GCC) 6.3.1 20161221 (Red Hat 6.3.1-1)
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
2. Příkazy asm a __asm__
V mnoha překladačích programovacího jazyka C popř. jazyka C++ nalezneme příkaz asm, za kterým následuje buď programový blok umístěný ve složených závorkách (tento způsob se používal například u kdysi populárních překladačů společnosti Borland) nebo blok umístěný v závorkách kulatých. V případě překladače GNU C se používá druhý způsob. Ovšem vzhledem k tomu, že asm není rezervované klíčové slovo specifikované ve standardech C, nebude tento blok správně rozeznán při překladu s volbami -ansi a/nebo -std. Z tohoto důvodu se doporučuje namísto asm používat __asm__ a pro překlad zdrojového kódu na jiných překladačích navíc do pro jistotu hlavičky či na úvod doplnit:
#ifndef __GNUC__
#define __asm__ asm
#endif
Bližší informace o této problematice je možné v případě potřeby najít například na stránce Alternate Keywords.
Poznámka: v demonstračních příkladech navíc používám i deklaraci __volatile__ zabezpečující, že se blok v assembleru nebude žádným způsobem optimalizovat (a tím pádem ani odstraňovat). To je důležité, protože se vyhnete nemilým překvapením při překladu s volbami -Ox apod.
3. Jak se příkazy asm a __asm__ zpracovávají?
Instrukce zapsané v blocích asm či __asm__ se překladačem céčka zpracovávají způsobem, který může vzdáleně připomínat expanzi maker. V podstatě se provádí pouze náhrady čísel parametrů za jména registrů a takto upravený kód se předá do assembleru, a to dokonce včetně znaků pro konce řádků, mezer na začátcích řádků atd. Z tohoto důvodu se již tradičně celý program zapisuje formou řetězce, kde se na každém řádku explicitně uvádí znak pro odřádkování \n a znak tabulátoru \t ve chvíli, kdy se nezapisuje řádek s návěštím (label); zde by naopak tabulátor překážel. Za tímto řetězcem se zapisuje nepovinný seznam výstupních registrů, seznam vstupních registrů a konečně seznam registrů používaných uvnitř kódu (tuto problematiku si blíže vysvětlíme v dalším textu, takže se následujícího kódu moc nelekněte). Jednotlivé seznamy se od sebe oddělují dvojtečkou. Celý zápis může vypadat následovně:
#include <stdio.h>
int main()
{
__asm__ __volatile__(
"nop \n\t"
" nop \n\t"
" nop \n\t"
" nop \n\t"
" nop # komentar \n\t"
: /* zadne vystupni registry */
: /* zadne vstupni operandy */
: /* zadne registry pouzivane uvnitr kodu */
);
return 0;
}
Podívejme se, jakým způsobem se tento blok zpracuje překladačem céčka. Překladač musíme zavolat s volbou -S, aby se ze zdrojového kódu vygeneroval mezivýsledek předávaný interně do GNU assembleru as. Tento mezivýsledek obsahuje přeložený céčkový kód, případné ladicí informace a taktéž expandované bloky asm:
gcc -S asm_in_c_0.c
Mezivýsledek vypadá zhruba následovně. Povšimněte si zvýrazněných řádků, z nichž je patrné, že se náš „program“ skutečně pouze vložil na správné místo, a to včetně všech mezer a komentářů:
.file "asm_in_c_0.c"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
#APP
# 5 "asm_in_c_0.c" 1
<strong> nop</strong>
<strong> nop</strong>
<strong> nop</strong>
<strong> nop</strong>
<strong> nop # komentar </strong>
# 0 "" 2
#NO_APP
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 6.3.1 20161221 (Red Hat 6.3.1-1)"
.section .note.GNU-stack,"",@progbits
Poznámka: pozor na to, že komentáře jsou na jiných platformách zapisovány odlišnými znaky!
4. Blok vytvořený v assembleru bez vstupních a výstupních operandů
V případě, že je v assemblerovském bloku potřeba vykonat nějaké instrukce, a to nezávisle na okolním céčkovém kódu, není nutné specifikovat žádné vstupní registry ani výstupní operandy. Jestliže se navíc v takovém bloku nemění obsah jiných registrů (samozřejmě kromě PC neboli čítače instrukcí), je i poslední seznam registrů prázdný. Celý blok lze zapsat dvěma způsoby. Rozsáhlejším s explicitním uvedením prázdných seznamů (k vidění je méně často):
#include <stdio.h>
int main()
{
__asm__ __volatile__(
"nop \n\t"
: /* zadne vystupni registry */
: /* zadne vstupni operandy */
: /* zadne registry pouzivane uvnitr kodu */
);
return 0;
}
Nebo jednodušším a taktéž mnohem kratším způsobem, kde zcela chybí tři seznamy oddělené dvojtečkami. Celý assemblerovský blok je tedy tvořen jediným (obecně víceřádkovým) řetězcem:
#include <stdio.h>
int main()
{
__asm__ __volatile__(
"nop \n\t"
);
return 0;
}
5. Specifikace výstupních operandů
Nyní se podívejme na poněkud složitější příklad, v němž bude vytvořen blok v assembleru, jehož úkolem bude zapsat konstantu 42 do globální proměnné nazvané result. Postup je jednoduchý:
- Načteme konstantu 42 do pracovního registru RBX (připomeňme si, že se nacházíme na platformě x86-64).
- Uložíme obsah registru RBX do proměnné result.
Při zápisu tohoto bloku musíme vyřešit dva problémy. První problém spočívá ve specifikaci proměnné result, což lze řešit zápisem "=" (result) v seznamu výstupních operandů. Tento zápis znamená: ulož obsah automaticky vybraného pracovního registru (například RAX) do proměnné result. Interně je první výstupní operand v assembleru reprezentován znaky %0, případný druhý operand znaky %1 atd. Druhý problém spočívá v tom, že přepisujeme obsah registru RBX, což překladač céčka neví. Musíme ho o tom informovat, aby překladač věděl, že nesmí počítat s tím, že bude obsah tohoto registru nezměněn. To se provede jednoduše – v posledním (třetím) seznamu se uvede jméno tohoto registru popř. většího množství registrů:
#include <stdio.h>
unsigned long long result;
int main()
{
__asm__ __volatile__(
"mov $42, %%rbx; \n\t"
"mov %%rbx, %0; \n\t"
: "=r" (result) /* vystupni operand */
: /* zadne vstupni operandy */
: "%rbx" /* registry pouzivane uvnitr kodu */
);
printf("%Ld\n", result);
return 0;
}
Ve skutečnosti se náš assemblerovský blok transformuje do třech strojových instrukcí, protože je nutné uložit obsah automaticky vybraného pracovního registru do proměnné result. Konkrétně může vypadat výsledek transformace takto (poslední instrukce ukládá obsah registru RAX do globální proměnné):
#APP
# 7 "asm_in_c_3.c" 1
mov $42, %rbx;
mov %rbx, %rax;
# 0 "" 2
#NO_APP
movq %rax, result(%rip)
Poznámka: pokud vám nevyhovuje používání %0 pro výstupní operand, lze provést jeho pojmenování následujícím způsobem:
__asm__ (
"mov $42, %%rbx \n\t"
"mov %%rbx, <strong>%[result_ident]</strong> \n\t"
: <strong>[result_ident]</strong> "=r" (result)
:
: "%rbx");
printf("%Ld\n", result);
return 0;
6. Přesné určení registrů pro výstupní operandy
V předchozím příkladu jsme nechali na céčkovém překladači, ať si sám zvolí registr používaný pro výstupní operand. Samozřejmě je však možné registr zvolit explicitně, a to náhradou znaku "r" v řetězci "=r" za jiný znak podle následující tabulky. Povšimněte si, že (alespoň prozatím) není možné explicitně použít vyšších osm pracovních registrů, tj. registry pojmenované R8 až R15. V samotném assemblerovském kódu však tyto registry lze využít:
Náhrada "r" | Význam |
---|---|
a | %rax, %eax, %ax, %al |
b | %rbx, %ebx, %bx, %bl |
c | %rcx, %ecx, %cx, %cl |
d | %rdx, %edx, %dx, %dl |
S | %rsi, %esi, %si |
D | %rdi, %edi, %di |
Podívejme se nyní na to, co se stane, když pro výstupní operand explicitně zvolíme registr RCX:
#include <stdio.h>
unsigned long long result;
int main()
{
__asm__ __volatile__(
"mov $42, %%rbx; \n\t"
"mov %%rbx, %0; \n\t"
: "=c" (result) /* vystupni operand */
: /* zadne vstupni operandy */
: "%rbx" /* registry pouzivane uvnitr kodu */
);
printf("%Ld\n", result);
return 0;
}
Tento kód se přeloží následujícím způsobem:
#APP
# 7 "test.c" 1
mov $42, %rbx;
mov %rbx, %rcx;
# 0 "" 2
#NO_APP
movq %rcx, %rax
movq %rax, result(%rip)
Nenechte se zmást použitím RAX, po optimalizaci (-O) tento mezikrok zmizí.
Alternativně můžeme zcela vynechat meziuložení výsledku do registru RBX a prohlásit tento registr za registr obsahující výstupní operand:
#include <stdio.h>
unsigned long long result;
int main()
{
__asm__ __volatile__(
"mov $42, %%rbx; \n\t"
: "=b" (result) /* vystupni operand */
: /* zadne vstupni operandy */
: /* registry pouzivane uvnitr kodu */
);
printf("%Ld\n", result);
return 0;
}
Překlad bude vypadat takto:
#APP
# 7 "test.c" 1
mov $42, %rbx;
# 0 "" 2
#NO_APP
movq %rbx, %rax
movq %rax, result(%rip)
7. Specifikace vstupních operandů
Zkusme si nyní vytvořit složitější příklad, v němž (samozřejmě v assembleru) sečteme obsah dvou celočíselných proměnných a uložíme výsledek do proměnné třetí. Pro jednoduchost se budou vstupní proměnné jmenovat x a y, proměnná výstupní se bude jmenovat result. Blok napsaný v assembleru se vlastně nebude příliš odlišovat od předchozího příkladu, ovšem kromě výstupního operandu musíme specifikovat i operandy vstupní. Používá se podobný způsob zápisu, ovšem bez znaku "=". Vstupní operandy vytváří se vstupními operandy jednu ucelenou řadu, takže v tomto konkrétním příkladu bude mít výstupní operand označení %0, první vstupní operand označení %1 a druhý operand pochopitelně označení %2:
#include <stdio.h>
unsigned long long x = 10;
unsigned long long y = 20;
unsigned long long result;
int main()
{
__asm__ __volatile__(
"mov %1, %%rbx; \n\t"
"add %2, %%rbx; \n\t"
"mov %%rbx, %%rcx \n\t"
: "=c" (result) /* vystupni operand */
: "r" (x), "r" (y) /* dva vstupni operandy */
: "%rbx" /* registry pouzivane uvnitr kodu */
);
printf("%Ld\n", result);
return 0;
}
Výsledek transformace provedené překladačem céčka vypadá takto:
movq x(%rip), %rax
movq y(%rip), %rdx
#APP
# 9 "test.c" 1
mov %rax, %rbx;
add %rdx, %rbx;
mov %rbx, %rcx
# 0 "" 2
#NO_APP
movq %rcx, %rax
movq %rax, result(%rip)
Na začátku vidíme automaticky vygenerované instrukce pro umístění vstupních operandů do vybraných registrů (vybral si je sám překladač), dále pak vlastní výpočet a konečně uložení výsledku do proměnné result.
8. Explicitní určení registru pro výstupní operand
Opět můžeme trošku experimentovat a explicitně určit, že výstupní operand je uložen v registru RAX. Tím si ušetříme jednu instrukci MOV:
#include <stdio.h>
unsigned long long x = 10;
unsigned long long y = 20;
unsigned long long result;
int main()
{
__asm__ __volatile__(
"mov %1, %%rax; \n\t"
"add %2, %%rax; \n\t"
: "=a" (result) /* vystupni operand */
: "r" (x), "r" (y) /* dva vstupni operandy */
: /* registry pouzivane uvnitr kodu */
);
printf("%Ld\n", result);
return 0;
}
Výsledek transformace provedené překladačem céčka (bez zapnutých optimalizací) vypadá následovně:
movq x(%rip), %rax
movq y(%rip), %rdx
#APP
# 9 "test.c" 1
mov %rax, %rbx;
add %rdx, %rbx;
mov %rbx, %rcx
# 0 "" 2
#NO_APP
movq %rax, result(%rip)
movq result(%rip), %rax
9. Explicitní určení registrů pro vstupní operandy i výstupní operand
Pokračujme v našich úpravách dále. Nyní budeme požadovat, aby výstupní operand (součet) byl umístěn v registru RAX, první vstupní operand v registru RBX a druhý vstupní operand v registru RCX. Zápis bude vypadat následovně (připomeňme si tabulku ze šesté kapitoly):
#include <stdio.h>
unsigned long long x = 10;
unsigned long long y = 20;
unsigned long long result;
int main()
{
__asm__ __volatile__(
"mov %%rbx, %%rax; \n\t"
"add %%rcx, %%rax; \n\t"
: "=a" (result) /* vystupni operand */
: "b" (x), "c" (y) /* dva vstupni operandy v registrech rbx a rcx */
: /* registry pouzivane uvnitr kodu */
);
printf("%Ld\n", result);
return 0;
}
Výsledek transformace provedené překladačem céčka (bez zapnutých optimalizací) vypadá následovně:
movq x(%rip), %rax
movq y(%rip), %rdx
movq %rax, %rbx
movq %rdx, %rcx
#APP
# 9 "test.c" 1
mov %rbx, %rbx;
add %rcx, %rbx;
mov %rbx, %rcx
# 0 "" 2
#NO_APP
movq %rax, result(%rip)
movq result(%rip), %rax
10. Stejný registr použitý pro vstupní i výstupní operand
Samozřejmě je možné – a často se s tím setkáme – použít jediný registr jak pro vstupní, tak i pro výstupní operand. Celý blok psaný v assembleru se nám v takovém případě zjednoduší na jedinou instrukci a navíc nebudeme muset specifikovat žádný pracovní registr:
#include <stdio.h>
unsigned long long x = 10;
unsigned long long y = 20;
unsigned long long result;
int main()
{
__asm__ __volatile__(
"add %%rbx, %%rax; \n\t"
: "=a" (result) /* vystupni operand */
: "a" (x), "b" (y) /* dva vstupni operandy v registrech rbx a rcx */
: /* registry pouzivane uvnitr kodu */
);
printf("%Ld\n", result);
return 0;
}
11. Vliv optimalizací na generovaný kód
Velký vliv na výslednou podobu kódu mají optimalizace prováděné céčkovým překladačem. Ten sice nezasahuje do našeho kódu psaného v assembleru (což je samozřejmě dobře), ovšem přípravu operandů a uložení výsledků již může být optimalizováno. Předchozí příklad přeložený bez optimalizací vypadá takto:
movq x(%rip), %rax
movq y(%rip), %rdx
movq %rdx, %rbx
#APP
# 9 "asm_in_c_9.c" 1
add %rbx, %rax;
# 0 "" 2
#NO_APP
movq %rax, result(%rip)
movq result(%rip), %rax
Výsledek překladu s volbou -O:
movq x(%rip), %rax
movq y(%rip), %rbx
#APP
# 9 "asm_in_c_9.c" 1
add %rbx, %rax;
# 0 "" 2
#NO_APP
movq %rax, result(%rip)
Tento kód je již přímočarý a přesně odpovídá tomu, co jsme požadovali: vstupní operandy ulož do registrů RAX a RBX, proveď součet těchto registrů a následně ulož RAX do proměnné result.
12. Použití syntaxe používané firmou Intel
Poslední zajímavostí, o které se dnes zmíníme, je použití syntaxe firmy Intel. Tato syntaxe je podle mého názoru čitelnější, protože zmizí nepěkná procenta atd., navíc se prohodí operandy všech instrukcí. Tuto syntaxi můžeme použít:
#include <stdio.h>
unsigned long long x = 10;
unsigned long long y = 20;
unsigned long long result;
int main()
{
__asm__ __volatile__(
"add rax, rbx; \n\t"
: "=a" (result) /* vystupni operand */
: "a" (x), "b" (y) /* dva vstupni operandy v registrech rbx a rcx */
: /* registry pouzivane uvnitr kodu */
);
printf("%Ld\n", result);
return 0;
}
Nesmíme však zapomenout při překladu použít volbu -masm=intel. Podoba výsledného transformovaného kódu se v tomto případě radikálně pozmění:
mov rax, QWORD PTR x[rip]
mov rdx, QWORD PTR y[rip]
mov rbx, rdx
#APP
# 9 "test.c" 1
add rax, rbx;
# 0 "" 2
#NO_APP
mov QWORD PTR result[rip], rax
13. Repositář s demonstračními příklady
Všechny demonstrační příklady byly, podobně jako v předchozích částech tohoto seriálu, společně s podpůrným skriptem připraveným pro jejich překlad, uloženy do GIT repositáře dostupného na adrese https://github.com/tisnik/presentations/. Všechny zmíněné příklady jsou určeny pro překladač GNU C:
14. Odkazy na Internetu
- How to Use Inline Assembly Language in C Code
https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C - GCC-Inline-Assembly-HOWTO
http://ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html - A Brief Tutorial on GCC inline asm (x86 biased)
http://www.osdever.net/tutorials/view/a-brief-tutorial-on-gcc-inline-asm - GCC Inline ASM
http://locklessinc.com/articles/gcc_asm/ - Cortex-A35
https://www.arm.com/products/processors/cortex-a/cortex-a35-processor.php - Cortex-A53
https://www.arm.com/products/processors/cortex-a/cortex-a53-processor.php - Cortex-A57
https://www.arm.com/products/processors/cortex-a/cortex-a57-processor.php - Cortex-A72
https://www.arm.com/products/processors/cortex-a/cortex-a72-processor.php - Cortex-A73
https://www.arm.com/products/processors/cortex-a/cortex-a73-processor.php - System cally pro AArch64 na Linuxu
https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h - Architectures/AArch64 (FedoraProject.org)
https://fedoraproject.org/wiki/Architectures/AArch64 - SIG pro AArch64 (CentOS)
https://wiki.centos.org/SpecialInterestGroup/AltArch/AArch64 - The ARMv8 instruction sets
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html - A64 Instruction Set
https://developer.arm.com/products/architecture/instruction-sets/a64-instruction-set - Switching between the instruction sets
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html - The A64 instruction set
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html - Introduction to ARMv8 64-bit Architecture
https://quequero.org/2014/04/introduction-to-arm-architecture/ - MCU market turns to 32-bits and ARM
http://www.eetimes.com/document.asp?doc_id=1280803 - Cortex-M0 Processor (ARM Holdings)
http://www.arm.com/products/processors/cortex-m/cortex-m0.php - Cortex-M0+ Processor (ARM Holdings)
http://www.arm.com/products/processors/cortex-m/cortex-m0plus.php - ARM Processors in a Mixed Signal World
http://www.eeweb.com/blog/arm/arm-processors-in-a-mixed-signal-world - ARM Architecture (Wikipedia)
https://en.wikipedia.org/wiki/ARM_architecture - ARM Documentation: B, BL, BX, BLX, and BXJ
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0204j/Cihfddaf.html - Branch and Call Sequences Explained
https://community.arm.com/groups/processors/blog/2013/09/25/branch-and-call-sequences-explained - Improving ARM Code Density and Performance
New Thumb Extensions to the ARM Architecture Richard Phelan - Aarch64 Register and Instruction Quick Start
https://wiki.cdot.senecacollege.ca/wiki/Aarch64_Register_and_Instruction_Quick_Start - Exploring AArch64 assembler – Chapter 1
http://thinkingeek.com/2016/10/08/exploring-aarch64-assembler-chapter1/ - Exploring AArch64 assembler – Chapter 2
http://thinkingeek.com/2016/10/08/exploring-aarch64-assembler-chapter-2/ - The ARM Processor Architecture
http://www.arm.com/products/processors/technologies/instruction-set-architectures.php - Thumb-2 instruction set
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344c/Beiiegaf.html - Introduction to ARM thumb
http://www.eetimes.com/discussion/other/4024632/Introduction-to-ARM-thumb - ARM, Thumb, and ThumbEE instruction sets
http://www.keil.com/support/man/docs/armasm/armasm_CEGBEIJB.htm - An Introduction to ARM Assembly Language
http://dev.emcelettronica.com/introduction-to-arm-assembly-language - Processors - ARM
http://www.arm.com/products/processors/index.php - The ARM Instruction Set
http://simplemachines.it/doc/arm_inst.pdf - ARM Architecture (Wikipedia)
http://en.wikipedia.org/wiki/ARM_architecture - C Functions Without Arguments
https://eklitzke.org/c-functions-without-arguments - GNU Assembler Examples
http://cs.lmu.edu/~ray/notes/gasexamples/ - Simply FPU
http://www.website.masmforum.com/tutorials/fptute/ - Art of Assembly language programming: The 80x87 Floating Point Coprocessors
https://courses.engr.illinois.edu/ece390/books/artofasm/CH14/CH14-3.html - Art of Assembly language programming: The FPU Instruction Set
https://courses.engr.illinois.edu/ece390/books/artofasm/CH14/CH14-4.html - INTEL 80387 PROGRAMMER'S REFERENCE MANUAL
http://www.ragestorm.net/downloads/387intel.txt - x86 Instruction Set Reference: FLD
http://x86.renejeschke.de/html/file_module_x86_id_100.html - x86 Instruction Set Reference: FLD1/FLDL2T/FLDL2E/FLDPI/FLDLG2/FLDLN2/FLDZ
http://x86.renejeschke.de/html/file_module_x86_id_101.html - x86 Instruction Set Reference: FLD
http://x86.renejeschke.de/html/file_module_x86_id_100.html - x86 Instruction Set Reference: FST/FSTP
http://x86.renejeschke.de/html/file_module_x86_id_117.html - x86 Instruction Set Reference: BTC
http://x86.renejeschke.de/html/file_module_x86_id_23.html - x86 Instruction Set Reference: BTR
http://x86.renejeschke.de/html/file_module_x86_id_24.html - x86 Instruction Set Reference: BTS
http://x86.renejeschke.de/html/file_module_x86_id_25.html - x86 Instruction Set Reference: BSF
http://x86.renejeschke.de/html/file_module_x86_id_19.html - x86 Instruction Set Reference: BSR
http://x86.renejeschke.de/html/file_module_x86_id_20.html - x86 Instruction Set Reference: BSWAP
http://x86.renejeschke.de/html/file_module_x86_id_21.html - x86 Instruction Set Reference: XCHG
http://x86.renejeschke.de/html/file_module_x86_id_328.html - x86 Instruction Set Reference: SETcc
http://x86.renejeschke.de/html/file_module_x86_id_288.html - 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