Téma kombinace assembleru a programovacího jazyka C na mikroprocesorech s 32bitovou architekturou ARM dnes dokončíme. Ukážeme si způsob přístupu na haldu (heap), volání služeb jádra, použití programových smyček v assembleru a nezapomeneme ani na poměrně rozsáhlou problematiku práce s hodnotami s plovoucí řádovou čárkou s využitím technologie VFP (Vector Floating Point).
Obsah
1. Kombinace assembleru a programovacího jazyka C na procesorech ARM (dokončení)
2. Přístup z assembleru na haldu
3. Volání služeb jádra z assembleru
4. Použití počítaných smyček v assemblerovském bloku
5. Vylepšení zápisu u assemblerovských bloků s návěštími
6. Základ práce s hodnotami s plovoucí řádovou čárkou
7. Přenos druhého FP operandu do operandu výstupního
8. Použití symbolických jmen vstupních a výstupních operandů
9. Využití pracovních registrů VFP (Vector Floating Point)
11. Součet dvou FP hodnot instrukcí vadd.f32
12. Podíl dvou FP hodnot instrukcí vdiv.f32
13. Makefile určený pro překlad dnešních demonstračních příkladů
14. Repositář s demonstračními příklady
1. Kombinace assembleru a programovacího jazyka C na procesorech ARM (dokončení)
Téma, jemuž jsme se věnovali v předchozích dvou částech seriálu o assembleru v Linuxu, dnes dokončíme. Ukážeme si totiž některé nepatrně složitější příklady bloků napsaných v assembleru 32bitových mikroprocesorů s architekturou ARM. Nejdříve si řekneme, jakým způsobem je možné přistupovat k datům umístěným na haldě (heapu), způsob zápisu programových smyček ve vkládaném assembleru a na konec se zaměříme na stručný popis technologie VFP (Vector Floating Point) a její využití při provádění základních aritmetických operací s hodnotami s plovoucí řádovou čárkou.
2. Přístup z assembleru na haldu
Ve vkládaných assemblerovských blocích je samozřejmě možné přistupovat i k těm datům, které jsou umístěny na haldě. My si konkrétně ukážeme způsob změny některých znaků v řetězci. Při vytváření řetězce je nutné zaručit, aby byl skutečně předán přes ukazatel, takže se pro vstupní operand použije modifikátor „m“. Adresa prvního znaku v řetězci se načte do pracovního registru instrukcí ldr (load register), o zápis jednoho znaku se postará instrukce strb (store byte). Povšimněte si, že je možné k počátečnímu znaku řetězce (k jeho adrese) přičíst offset (4 pro pátý a 7 pro osmý znak):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *message = NULL;
int main()
{
message = (char*) malloc(20);
strcpy(message, "Hello world!");
puts(message);
__asm__ __volatile__(
"ldr r1, %0 @ adresa retezce \n\t"
"mov r2, #'*' @ zapisovany znak \n\t"
"strb r2, [r1,#4] @ prepis jednoho 'o' \n\t"
"strb r2, [r1,#7] @ prepis druheho 'o' \n\t"
: /* zadne vystupni operandy */
: "m" (message) /* vstupni operandy */
: "r1", "r2" /* registry pouzivane uvnitr kodu */
);
puts(message);
return 0;
}
Po překladu a spuštění příkladu by se na standardním výstupu měl objevit původní řetězec a řetězec upravený – s přepsanými dvěma znaky:
Hello world!
Hell* w*rld!
Podívejme se ještě na to, jak se tento příklad přeložil do assembleru, tj. jakým způsobem se naplnil vstupní operand:
ldr r3, .L2
#APP
@ 13 "asm_in_c_7.c" 1
ldr r1, [r3, #0] @ adresa retezce
mov r2, #'*' @ zapisovany znak
strb r2, [r1,#4] @ prepis jednoho 'o'
strb r2, [r1,#7] @ prepis druheho 'o'
@ 0 "" 2
ldr r3, .L2
ldr r3, [r3, #0]
mov r0, r3
bl puts
3. Volání služeb jádra z assembleru
Ve druhém demonstračním příkladu zavoláme funkci jádra sloužící pro výpis sekvence bajtů do zvoleného zařízení či do souboru. Pokud použijeme číslo souboru #1, bude se jednat o standardní výstup, což již známe z „čistě assemblerovských“ příkladů. Data, která se budou na standardní výstup zapisovat, jsou přečtena z běžného céčkovského řetězce, který je v tomto případě umístěn v sekci konstant. Povšimněte si, že nula ukončující céčkovský řetězec není žádným způsobem jádrem zpracována; naopak musíme explicitně předat délku řetězce (počet zapisovaných bajtů):
#include <stdio.h>
const char *message = "Hello world!";
int main()
{
__asm__ __volatile__(
"mov r7, #4 @ cislo syscallu pro funkci write \n\t"
"mov r0, #1 @ standardni vystup \n\t"
"ldr r1, %0 @ adresa retezce, ktery se ma vytisknout \n\t"
"mov r2, #12 @ pocet znaku, ktere se maji vytisknout \n\t"
"svc 0 @ volani Linuxoveho kernelu \n\t"
: /* zadne vystupni operandy */
: "m" (message) /* zadne vstupni operandy */
: "r0", "r1", "r2", "r7" /* registry pouzivane uvnitr kodu */
);
return 0;
}
Překlad provedený GCC vyprodukuje následující sekvenci instrukcí:
ldr r3, .L2
#APP
@ 7 "asm_in_c_8.c" 1
mov r7, #4 @ cislo syscallu pro funkci write
mov r0, #1 @ standardni vystup
ldr r1, [r3, #0] @ adresa retezce, ktery se ma vytisknout
mov r2, #12 @ pocet znaku, ktere se maji vytisknout
svc 0 @ volani Linuxoveho kernelu
4. Použití počítaných smyček v assemblerovském bloku
Ve třetím příkladu si vytvoříme jednoduchou programovou smyčku, která postupně změní několik znaků v řetězci umístěného na haldu. Všechny instrukce již známe, povšimněte si však toho, že návěští smyčky (label loop) musí být zapsáno na začátku řádku, takže se před něj nezapisuje znak tabulátoru. Výsledek není příliš čitelný, to však opravíme v další kapitole:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *message = NULL;
int main()
{
message = (char*) malloc(20);
strcpy(message, "Hello world!");
puts(message);
__asm__ __volatile__(
"ldr r1, %0 @ adresa retezce \n\t"
"mov r2, #'*' @ zapisovany znak \n\t"
"mov r3, #2 @ pocitadlo smycky \n\t"
"\n"
"loop:\n\t"
"strb r2, [r1,r3] @ prepis jednoho znaku \n\t"
"add r3, r3, #2 @ zvyseni hodnoty pocitadla \n\t"
"cmp r3, #12 @ porovnani s ocekavanou koncovou hodnotou \n\t"
"bne loop @ podmineny skok na zacatek smycky \n\t"
: /* zadne vystupni operandy */
: "m" (message) /* vstupni operandy */
: "r1", "r2", "r3" /* registry pouzivane uvnitr kodu */
);
puts(message);
return 0;
}
Po překladu a spuštění získáme na standardním výstupu tyto dva řádky:
Hello world!
He*l* *o*k*!
Překlad provedený GCC vyprodukuje následující sekvenci instrukcí:
#APP
@ 14 "asm_in_c_9.c" 1
ldr r1, [r0, #0] @ adresa retezce
mov r2, #'*' @ zapisovany znak
mov r3, #2 @ pocitadlo smycky
loop:
strb r2, [r1,r3] @ prepis jednoho znaku
add r3, r3, #2 @ zvyseni hodnoty pocitadla
cmp r3, #12 @ porovnani s ocekavanou koncovou hodnotou
bne loop @ podmineny skok na zacatek smycky
5. Vylepšení zápisu u assemblerovských bloků s návěštími
Předchozí příklad je možné přepsat do mnohem čitelnější podoby takovým způsobem, že nepoužijeme znaky tabulátoru na začátku řádku, ale namísto toho se spokojíme se starými dobrými mezerami. Takový vkládaný assemblerovský blok je již mnohem čitelnější – ostatně posuďte sami:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *message = NULL;
int main()
{
message = (char*) malloc(20);
strcpy(message, "Hello world!");
puts(message);
__asm__ __volatile__(
" ldr r1, %0 @ adresa retezce \n"
" mov r2, #'*' @ zapisovany znak \n"
" mov r3, #2 @ pocitadlo smycky \n"
"loop: \n"
" strb r2, [r1,r3] @ prepis jednoho znaku \n"
" add r3, r3, #2 @ zvyseni hodnoty pocitadla \n"
" cmp r3, #12 @ porovnani s ocekavanou koncovou hodnotou \n"
" bne loop @ podmineny skok na zacatek smycky \n"
: /* zadne vystupni operandy */
: "m" (message) /* vstupni operandy */
: "r1", "r2", "r3" /* registry pouzivane uvnitr kodu */
);
puts(message);
return 0;
}
Takto vypadá kód vygenerovaný překladačem GCC:
#APP
@ 14 "asm_in_c_10.c" 1
ldr r1, [r0, #0] @ adresa retezce
mov r2, #'*' @ zapisovany znak
mov r3, #2 @ pocitadlo smycky
loop:
strb r2, [r1,r3] @ prepis jednoho znaku
add r3, r3, #2 @ zvyseni hodnoty pocitadla
cmp r3, #12 @ porovnani s ocekavanou koncovou hodnotou
bne loop @ podmineny skok na zacatek smycky
Poslední úprava se týká prvního řádku s instrukcí ldr, který je posunutý doprava. Nad tento řádek umístíme jediný znak pro odřádkování:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *message = NULL;
int main()
{
message = (char*) malloc(20);
strcpy(message, "Hello world!");
puts(message);
__asm__ __volatile__(
"\n"
" ldr r1, %0 @ adresa retezce \n"
" mov r2, #'*' @ zapisovany znak \n"
" mov r3, #2 @ pocitadlo smycky \n"
"loop: \n"
" strb r2, [r1,r3] @ prepis jednoho znaku \n"
" add r3, r3, #2 @ zvyseni hodnoty pocitadla \n"
" cmp r3, #12 @ porovnani s ocekavanou koncovou hodnotou \n"
" bne loop @ podmineny skok na zacatek smycky \n"
: /* zadne vystupni operandy */
: "m" (message) /* vstupni operandy */
: "r1", "r2", "r3" /* registry pouzivane uvnitr kodu */
);
puts(message);
return 0;
}
Výsledek:
#APP
@ 14 "asm_in_c_11.c" 1
ldr r1, [r0, #0] @ adresa retezce
mov r2, #'*' @ zapisovany znak
mov r3, #2 @ pocitadlo smycky
loop:
strb r2, [r1,r3] @ prepis jednoho znaku
add r3, r3, #2 @ zvyseni hodnoty pocitadla
cmp r3, #12 @ porovnani s ocekavanou koncovou hodnotou
bne loop @ podmineny skok na zacatek smycky
6. Základ práce s hodnotami s plovoucí řádovou čárkou
Ve druhé části dnešního článku o použití assembleru společně s jazykem C se zaměříme na popis způsobu zpracování hodnot s plovoucí řádovou čárkou (FP = Floating Point). Pokud programujeme v assembleru, máme při zpracování FP hodnot dvě možnosti – můžeme použít běžné celočíselné 32bitové pracovní registry se jmény r0 až r15, mezi nimiž lze FP hodnoty jen přenášet a popř. i v omezené míře porovnávat, či můžeme použít skutečný matematický koprocesor, který u dnešních 32bitových čipů ARM většinou implementuje instrukční sadu VFPx (vector floating point). Nejprve se seznámíme s první možností, kdy s FP hodnotami pracujeme naprosto stejným způsobem, jakoby se jednalo o hodnoty celočíselné. V prvním příkladu tohoto typu je implementován assemblerovský blok se dvěma vstupními operandy (proměnnými typu float) a jediným operandem výstupním (jenž je taktéž typu float). Uvnitř bloku pouze přesuneme první vstupní operand do operandu výstupního, žádná další operace se zde neprovádí:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
float x = 0.5f;
float y = 1.0f;
float z = 1000.0f;
int main()
{
printf("%f\n", z);
__asm__ __volatile__(
" mov %0, %1\n" /* presunuti prvniho vstupniho operandu na vystup */
: "=r" (z) /* vystupni operand */
: "r" (x), "r" (y) /* vstupni operandy */
: /* registry pouzivane uvnitr kodu */
);
printf("%f\n", z);
return 0;
}
V přeloženém kódu vyprodukovaném překladačem GCC vypadá náš assemblerovský blok poněkud komplikovaně, což je způsobeno načítáním vstupních operandů nepřímo přes adresy a uložením výstupního operandu taktéž přes adresu:
ldr r3, .L2+8
ldr r3, [r3, #0] @ float
ldr r2, .L2+12
ldr r2, [r2, #0] @ float
#APP
@ 13 "asm_in_c_12.c" 1
<strong>mov r2, r3</strong>
@ 0 "" 2
ldr r3, .L2+4
str r2, [r3, #0] @ float
První dvě instrukce načtou první vstupní operand (registr r3 zde slouží jak pro adresování, tak i jako místo pro první operand). Druhé dvě instrukce provedou totéž, ale pro druhý vstupní operand. Následuje přenos (mov) a opětovné uložení výsledku z registru r2 na adresu dočasně umístěnou v registru r3.
7. Přenos druhého FP operandu do operandu výstupního
Ve výpisu přeloženého kódu uvedeného v předchozí kapitole jste si pravděpodobně povšimli toho, že jeden pracovní registr byl použit jak pro uložení vstupního operandu, tak i pro uložení operandu výstupního. Totéž bude platit ve chvíli, kdy v našem assemblerovském bloku přeneseme do výstupního operandu druhý vstupní operand, tj. hodnotu uloženou v proměnné y. Příklad se změní pouze minimálně:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
float x = 0.5f;
float y = 1.0f;
float z = 1000.0f;
int main()
{
printf("%f\n", z);
__asm__ __volatile__(
" mov %0, %2\n" /* presunuti prvniho vstupniho operandu na vystup */
: "=r" (z) /* vystupni operand */
: "r" (x), "r" (y) /* vstupni operandy */
: /* registry pouzivane uvnitr kodu */
);
printf("%f\n", z);
return 0;
}
V přeloženém kódu nalezneme i část odpovídající našemu assemblerovskému bloku, provedená instrukce je vlastně zbytečná, protože převádí data do stejného registru:
ldr r3, .L2+8
ldr r3, [r3, #0] @ float
ldr r2, .L2+12
ldr r2, [r2, #0] @ float
#APP
@ 13 "asm_in_c_13.c" 1
<strong>mov r2, r2</strong>
@ 0 "" 2
ldr r3, .L2+4
str r2, [r3, #0] @ float
8. Použití symbolických jmen vstupních a výstupních operandů
Ještě stojí za připomenutí možnost použít symbolická jména vstupních i výstupních operandů v assemblerovském bloku. To je ukázáno v dalším demonstračním příkladu, který se ve všech ohledech podobá oběma předchozím příkladům, až na to, že namísto indexů operandů se použijí jejich symbolická jména:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
float x = 0.5f;
float y = 1.0f;
float z = 1000.0f;
int main()
{
printf("%f\n", z);
__asm__ __volatile__(
" mov %[var_z], %[var_x]\n" /* presunuti prvniho vstupniho operandu na vystup */
: [var_z]"=r" (z) /* vystupni operand */
: [var_x]"r" (x),
[var_y]"r" (y) /* vstupni operandy */
: /* registry pouzivane uvnitr kodu */
);
printf("%f\n", z);
return 0;
}
Přeložená část programu s assemblerovským blokem a jeho inicializační fází vypadá takto (k žádné podstatné změně nedošlo):
ldr r3, .L2+8
ldr r3, [r3, #0] @ float
ldr r2, .L2+12
ldr r2, [r2, #0] @ float
#APP
@ 13 "asm_in_c_14.c" 1
mov r2, r3
@ 0 "" 2
ldr r3, .L2+4
str r2, [r3, #0] @ float
9. Využití pracovních registrů VFP (Vector Floating Point)
Mnohem častěji se s FP hodnotami pracuje s využitím VFP. Matematické koprocesory VFP obecně obsahují šestnáct pracovních registrů, každý o šířce 64 bitů. Tyto registry lze použít buď pro práci s hodnotami s dvojitou přesností (double) – potom se tyto registry v assembleru označují jmény d0 až d15. Ovšem taktéž je možné libovolný registr rozdělit na dva registry o šířce 32 bitů, z nichž každý dokáže pojmout číselnou hodnotu s jednoduchou přesností (single/float). Díky tomuto rozdělení se počet registrů pro formát single zvětšil na dvojnásobek – tyto registry jsou v assembleru pojmenovány s0 až s31. Podle konvence dodržované jak překladači, tak i v programových knihovnách se při volání subrutin používají registry d0 až d7 pro předávání parametrů subrutině, popř. pro získání návratových hodnot ze subrutiny. Samozřejmě se tyto registry taktéž používají při výpočtech v subrutině. Ostatní registry lze taktéž použít, ovšem jejich hodnota by měla být při návratu ze subrutiny obnovena.
10. Základní instrukce VFP
Nyní se již konečně dostáváme k popisu jednotlivých instrukcí, které lze nalézt v instrukční sadě VFP. Zajímavý je především fakt, že každá instrukce obsahuje příznak, zda se má provádět operace s hodnotami/registry typu single či double (postfix .f32 resp. .f64). Instrukce taktéž obsahují příznakové bity určující, za jakých podmínek se instrukce má provést. Význam těchto příznakových bitů je poněkud odlišný od bitů použitých v instrukční sadě ARM, a to především z toho důvodu, že příznak V má jiný význam (přetečení hodnoty celého čísla do znaménkového bitu versus dvě FP hodnoty nelze porovnat). To, zda se bude vybraná operace provádět nad dvojicí skalárních hodnot nebo nad dvojicí registrů, je určeno hodnotami LEN a STRIDE) v řídicím registru FPSCR. Ve výchozím nastavení je LEN rovno jedné a operace se neprovádí s vektory, ale se skaláry.
Při změně hodnoty pouze některých bitů registru FPSCR se používá následující sekvence instrukcí:
VMRS r3,FPSCR ; přesun hodnoty FPSCR do registru R3
BIC r3,r3,#0x00370000 ; vynulování bitových polí STRIDE a LEN
ORR r3,r3,#0x00030000 ; nastavení STRIDE = 1 a LEN = 4
VMSR FPSCR,r3 ; zpětný přesun hodnoty z R3 do registru FPSCR
V následující tabulce jsou vypsány základní aritmetické operace:
# | Instrukce | Význam | Prováděný výpočet |
---|---|---|---|
1 | VADD Fd, Fn, Fm | součet | Fd := Fn + Fm |
2 | VSUB Fd, Fn, Fm | rozdíl | Fd := Fn - Fm |
3 | VNEG Fd, Fm | změna znaménka | Fd := - Fm |
4 | VABS Fd, Fm | absolutní hodnota | Fd := abs(Fm) |
5 | VSQRT Fd, Fm | druhá odmocnina | Fd := sqrt(Fm) |
6 | VDIV Fd, Fn, Fm | dělení | Fd := Fn / Fm |
7 | VMUL Fd, Fn, Fm | násobení | Fd := Fn * Fm |
8 | VMLA Fd, Fn, Fm | násobení + akumulace | Fd := Fd + (Fn * Fm) |
9 | VMLS Fd, Fn, Fm | odečtení součinu | Fd := Fd - (Fn * Fm) |
10 | VNMUL Fd, Fn, Fm | násobení + změna znaménka | Fn := - (Fn * Fm) |
11 | VNMLA Fd, Fn, Fm | kombinace VNMUL a VMLA | Fd := - Fd - (Fn * Fm) |
12 | VNMLS Fd, Fn, Fm | kombinace VNMUL a VMLS | Fd := - Fd + (Fn * Fm) |
Poznámka: povšimněte si především šesti různých variant násobení. To je zapříčiněno tím, že se VFP používá například i pro filtraci obrazu, FFT atd., kde lze tyto operace s výhodou použít.
11. Součet dvou FP hodnot instrukcí vadd.f32
Zkusme si nyní vytvořit nepatrně složitější assemblerovský blok, v němž se sečte obsah dvou globálních FP proměnných x, y a výsledek se uloží do třetí proměnné z. Pro součet se využije instrukce vadd.f32, kde suffix .f32 určuje, že se bude pracovat s registry typu single (jednoduchá přesnost):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
float x = 0.5f;
float y = 1.0f;
float z = 1000.0f;
int main()
{
printf("%f\n", z);
__asm__ __volatile__(
" vadd.f32 %[var_z], %[var_x], %[var_y]\n" /* soucet obou vstupnich operandu */
: [var_z]"=w" (z) /* vystupni operand */
: [var_x]"w" (x),
[var_y]"w" (y) /* vstupni operandy */
: /* registry pouzivane uvnitr kodu */
);
printf("%f\n", z);
return 0;
}
Opět se podívejme na výsledný kód, který vznikl překladem původního zdrojového kódu demonstračního příkladu. Můžeme zde vidět, že se obsah FP registru načítá dvojicí instrukcí ldr+flds a ukládá dvojicí ldr+fsts. Překladač si zvolil použití registrů s14 a s15:
ldr r3, .L2+8
flds s15, [r3, #0]
ldr r3, .L2+12
flds s14, [r3, #0]
#APP
@ 13 "asm_in_c_15.c" 1
<strong>vadd.f32 s15, s15, s14</strong>
@ 0 "" 2
ldr r3, .L2+4
fsts s15, [r3, #0]
12. Podíl dvou FP hodnot instrukcí vdiv.f32
Poslední příklad používá instrukci vdiv pro vydělení dvou hodnot 100,0 a -0,1; výsledkem bude -1000,0:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
float x = 100.0f;
float y = -0.1f;
float z = 0.0f;
int main()
{
printf("%f\n", z);
__asm__ __volatile__(
" vdiv.f32 %[var_z], %[var_x], %[var_y]\n" /* soucet obou vstupnich operandu */
: [var_z]"=w" (z) /* vystupni operand */
: [var_x]"w" (x),
[var_y]"w" (y) /* vstupni operandy */
: /* registry pouzivane uvnitr kodu */
);
printf("%f\n", z);
return 0;
}
Vygenerovaný kód je prakticky totožný s předchozím příkladem, samozřejmě s tím rozdílem, že se namísto instrukce vadd použije instrukce vdiv:
ldr r3, .L2+8
flds s15, [r3, #0]
ldr r3, .L2+12
flds s14, [r3, #0]
#APP
@ 13 "asm_in_c_16.c" 1
<strong>vdiv.f32 s15, s15, s14</strong>
@ 0 "" 2
ldr r3, .L2+4
fsts s15, [r3, #0]
13. Makefile určený pro překlad dnešních demonstračních příkladů
Všechny demonstrační příklady, které jsme si v dnešním i předchozím článku ukázali, lze přeložit příkazem make s využitím následujícího souboru Makefile. Tento soubor si raději stáhněte přímo z repositáře, neboť jen tak budete mít jistotu, že se nenahradí znaky pro Tab za mezery:
# Parametry prekladace.
CFLAGS=-Wall -O9 -g
# Vychozi pravidlo pro vytvoreni vysledne spustitelne aplikace.
all: asm_in_c_0.s asm_in_c_0 \
asm_in_c_1.s asm_in_c_1 \
asm_in_c_2.s asm_in_c_2 \
asm_in_c_3.s asm_in_c_3 \
asm_in_c_4.s asm_in_c_4 \
asm_in_c_5.s asm_in_c_5 \
asm_in_c_6.s asm_in_c_6 \
asm_in_c_7.s asm_in_c_7 \
asm_in_c_8.s asm_in_c_8 \
asm_in_c_9.s asm_in_c_9 \
asm_in_c_10.s asm_in_c_10 \
asm_in_c_11.s asm_in_c_11 \
asm_in_c_12.s asm_in_c_12 \
asm_in_c_13.s asm_in_c_13 \
asm_in_c_14.s asm_in_c_14 \
asm_in_c_15.s asm_in_c_15 \
asm_in_c_16.s asm_in_c_16
clean:
rm -f *.o
rm -f *.s
rm -f asm_in_c_[0-9]+
# Pravidlo pro slinkovani vsech objektovych souboru a vytvoreni
# vysledne spustitelne aplikace.
$(PROGNAME): $(PROGNAME).o
$(CC) -o $@ $(LDFLAGS) $<
# Pravidlo pro preklad kazdeho zdrojoveho souboru do prislusneho
# objektoveho souboru.
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Pravidlo pro preklad kazdeho zdrojoveho souboru do prislusneho
# assemblerovskeho vysledku.
%.s: %.c
$(CC) $(CFLAGS) -S -c $< -o $@
14. Repositář s demonstračními příklady
Všechny demonstrační příklady byly, podobně jako v prakticky všech 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 (podrobnosti o konkrétních verzích jsou uvedeny v úvodní kapitole):
15. Odkazy na Internetu
- ARM GCC Inline Assembler Cookbook
http://www.ethernut.de/en/documents/arm-inline-asm.html - Extended Asm - Assembler Instructions with C Expression Operands
https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html - ARM inline asm secrets
http://hardwarebug.org/2010/07/06/arm-inline-asm-secrets/ - 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