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)

10. Základní instrukce VFP

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

15. Odkazy na Internetu

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 r0r15, 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
        mov r2, r3

@ 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
        mov r2, r2

@ 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 d0d15. 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 s0s31. Podle konvence dodržované jak překladači, tak i v programových knihovnách se při volání subrutin používají registry d0d7 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
        vadd.f32 s15, s15, s14

@ 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
        vdiv.f32 s15, s15, s14

@ 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 [email protected] $(LDFLAGS) $<

# Pravidlo pro preklad kazdeho zdrojoveho souboru do prislusneho
# objektoveho souboru.
%.o:    %.c
        $(CC) $(CFLAGS) -c $< -o [email protected]

# Pravidlo pro preklad kazdeho zdrojoveho souboru do prislusneho
# assemblerovskeho vysledku.
%.s:    %.c
        $(CC) $(CFLAGS) -S -c $< -o [email protected]

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):

# Soubor Odkaz do repositáře
1 asm_in_c_0.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_0.c
2 asm_in_c_1.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_1.c
3 asm_in_c_2.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_2.c
4 asm_in_c_3.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_3.c
5 asm_in_c_4.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_4.c
6 asm_in_c_5.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_5.c
7 asm_in_c_6.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_6.c
8 asm_in_c_7.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_7.c
9 asm_in_c_8.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_8.c
10 asm_in_c_9.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_9.c
11 asm_in_c_10.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_10.c
12 asm_in_c_11.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_11.c
13 asm_in_c_12.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_12.c
14 asm_in_c_13.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_13.c
15 asm_in_c_14.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_14.c
16 asm_in_c_15.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_15.c
17 asm_in_c_16.c https://github.com/tisnik/presentations/blob/master/assembler/asm_in_c_arm/asm_in_c_16.c

15. Odkazy na Internetu

  1. ARM GCC Inline Assembler Cookbook
    http://www.ethernut.de/en/documents/arm-inline-asm.html
  2. Extended Asm – Assembler Instructions with C Expression Operands
    https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
  3. ARM inline asm secrets
    http://hardwarebug.org/2010/07/06/arm-inline-asm-secrets/
  4. 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
  5. GCC-Inline-Assembly-HOWTO
    http://ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
  6. A Brief Tutorial on GCC inline asm (x86 biased)
    http://www.osdever.net/tutorials/view/a-brief-tutorial-on-gcc-inline-asm
  7. GCC Inline ASM
    http://locklessinc.com/articles/gcc_asm/
  8. Cortex-A35
    https://www.arm.com/products/processors/cortex-a/cortex-a35-processor.php
  9. Cortex-A53
    https://www.arm.com/products/processors/cortex-a/cortex-a53-processor.php
  10. Cortex-A57
    https://www.arm.com/products/processors/cortex-a/cortex-a57-processor.php
  11. Cortex-A72
    https://www.arm.com/products/processors/cortex-a/cortex-a72-processor.php
  12. Cortex-A73
    https://www.arm.com/products/processors/cortex-a/cortex-a73-processor.php
  13. System cally pro AArch64 na Linuxu
    https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h
  14. Architectures/AArch64 (FedoraProject.org)
    https://fedoraproject.org/wiki/Architectures/AArch64
  15. SIG pro AArch64 (CentOS)
    https://wiki.centos.org/SpecialInterestGroup/AltArch/AArch64
  16. The ARMv8 instruction sets
    http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html
  17. A64 Instruction Set
    https://developer.arm.com/products/architecture/instruction-sets/a64-instruction-set
  18. Switching between the instruction sets
    http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html
  19. The A64 instruction set
    http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html
  20. Introduction to ARMv8 64-bit Architecture
    https://quequero.org/2014/04/introduction-to-arm-architecture/
  21. MCU market turns to 32-bits and ARM
    http://www.eetimes.com/document.asp?doc_id=1280803
  22. Cortex-M0 Processor (ARM Holdings)
    http://www.arm.com/products/processors/cortex-m/cortex-m0.php
  23. Cortex-M0+ Processor (ARM Holdings)
    http://www.arm.com/products/processors/cortex-m/cortex-m0plus.php
  24. ARM Processors in a Mixed Signal World
    http://www.eeweb.com/blog/arm/arm-processors-in-a-mixed-signal-world
  25. ARM Architecture (Wikipedia)
    https://en.wikipedia.org/wiki/ARM_architecture
  26. ARM Documentation: B, BL, BX, BLX, and BXJ
    http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0204j/Cihfddaf.html
  27. Branch and Call Sequences Explained
    https://community.arm.com/groups/processors/blog/2013/09/25/branch-and-call-sequences-explained
  28. Improving ARM Code Density and Performance
    New Thumb Extensions to the ARM Architecture Richard Phelan
  29. Aarch64 Register and Instruction Quick Start
    https://wiki.cdot.senecacollege.ca/wiki/Aarch64_Register_and_Instruction_Quick_Start
  30. Exploring AArch64 assembler – Chapter 1
    http://thinkingeek.com/2016/10/08/exploring-aarch64-assembler-chapter1/
  31. Exploring AArch64 assembler – Chapter 2
    http://thinkingeek.com/2016/10/08/exploring-aarch64-assembler-chapter-2/
  32. The ARM Processor Architecture
    http://www.arm.com/products/processors/technologies/instruction-set-architectures.php
  33. Thumb-2 instruction set
    http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344c/Beiiegaf.html
  34. Introduction to ARM thumb
    http://www.eetimes.com/discussion/other/4024632/Introduction-to-ARM-thumb
  35. ARM, Thumb, and ThumbEE instruction sets
    http://www.keil.com/support/man/docs/armasm/armasm_CEGBEIJB.htm
  36. An Introduction to ARM Assembly Language
    http://dev.emcelettronica.com/introduction-to-arm-assembly-language
  37. Processors – ARM
    http://www.arm.com/products/processors/index.php
  38. The ARM Instruction Set
    http://simplemachines.it/doc/arm_inst.pdf
  39. ARM Architecture (Wikipedia)
    http://en.wikipedia.org/wiki/ARM_architecture
  40. C Functions Without Arguments
    https://eklitzke.org/c-functions-without-arguments
  41. GNU Assembler Examples
    http://cs.lmu.edu/~ray/notes/gasexamples/
  42. Simply FPU
    http://www.website.masmforum.com/tutorials/fptute/
  43. Art of Assembly language programming: The 80×87 Floating Point Coprocessors
    https://courses.engr.illinois.edu/ece390/books/artofasm/CH14/CH14-3.html
  44. Art of Assembly language programming: The FPU Instruction Set
    https://courses.engr.illinois.edu/ece390/books/artofasm/CH14/CH14-4.html
  45. INTEL 80387 PROGRAMMER’S REFERENCE MANUAL
    http://www.ragestorm.net/downloads/387intel.txt
  46. x86 Instruction Set Reference: FLD
    http://x86.renejeschke.de/html/file_module_x86_id_100.html
  47. x86 Instruction Set Reference: FLD1/FLDL2T/FLDL2E/FLDPI/FLDLG2/FLDLN2/FLDZ
    http://x86.renejeschke.de/html/file_module_x86_id_101.html
  48. x86 Instruction Set Reference: FLD
    http://x86.renejeschke.de/html/file_module_x86_id_100.html
  49. x86 Instruction Set Reference: FST/FSTP
    http://x86.renejeschke.de/html/file_module_x86_id_117.html
  50. x86 Instruction Set Reference: BTC
    http://x86.renejeschke.de/html/file_module_x86_id_23.html
  51. x86 Instruction Set Reference: BTR
    http://x86.renejeschke.de/html/file_module_x86_id_24.html
  52. x86 Instruction Set Reference: BTS
    http://x86.renejeschke.de/html/file_module_x86_id_25.html
  53. x86 Instruction Set Reference: BSF
    http://x86.renejeschke.de/html/file_module_x86_id_19.html
  54. x86 Instruction Set Reference: BSR
    http://x86.renejeschke.de/html/file_module_x86_id_20.html
  55. x86 Instruction Set Reference: BSWAP
    http://x86.renejeschke.de/html/file_module_x86_id_21.html
  56. x86 Instruction Set Reference: XCHG
    http://x86.renejeschke.de/html/file_module_x86_id_328.html
  57. x86 Instruction Set Reference: SETcc
    http://x86.renejeschke.de/html/file_module_x86_id_288.html
  58. X86 Assembly/Arithmetic
    https://en.wikibooks.org/wiki/X86_Assembly/Arithmetic
  59. Art of Assembly – Arithmetic Instructions
    http://oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-2.html
  60. The GNU Assembler Tutorial
    http://tigcc.ticalc.org/doc/gnuasm.html
  61. The GNU Assembler – macros
    http://tigcc.ticalc.org/doc/gnuasm.html#SEC109
  62. ARM subroutines & program stack
    http://www.toves.org/books/armsub/
  63. Generating Mixed Source and Assembly List using GCC
    http://www.systutorials.com/240/generate-a-mixed-source-and-assembly-listing-using-gcc/
  64. Calling subroutines
    http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.kui0100a/armasm_cihcfigg.htm
  65. ARM Assembly Language Programming
    http://peter-cockerell.net/aalp/html/frames.html
  66. ASM Flags
    http://www.cavestory.org/guides/csasm/guide/asm_flags.html
  67. Status Register
    https://en.wikipedia.org/wiki/Status_register
  68. Intel x86 JUMP quick reference
    http://unixwiz.net/techtips/x86-jumps.html
  69. Linux assemblers: A comparison of GAS and NASM
    http://www.ibm.com/developerworks/library/l-gas-nasm/index.html
  70. Programovani v assembleru na OS Linux
    http://www.cs.vsb.cz/grygarek/asm/asmlinux.html
  71. Is it worthwhile to learn x86 assembly language today?
    https://www.quora.com/Is-it-worthwhile-to-learn-x86-assembly-language-today?share=1
  72. Why Learn Assembly Language?
    http://www.codeproject.com/Articles/89460/Why-Learn-Assembly-Language
  73. Is Assembly still relevant?
    http://programmers.stackexchange.com/questions/95836/is-assembly-still-relevant
  74. Why Learning Assembly Language Is Still a Good Idea
    http://www.onlamp.com/pub/a/onlamp/2004/05/06/writegreatcode.html
  75. Assembly language today
    http://beust.com/weblog/2004/06/23/assembly-language-today/
  76. Assembler: Význam assembleru dnes
    http://www.builder.cz/rubriky/assembler/vyznam-assembleru-dnes-155960cz
  77. Assembler pod Linuxem
    http://phoenix.inf.upol.cz/linux/prog/asm.html
  78. AT&T Syntax versus Intel Syntax
    https://www.sourceware.org/binutils/docs-2.12/as.info/i386-Syntax.html
  79. Linux Assembly website
    http://asm.sourceforge.net/
  80. Using Assembly Language in Linux
    http://asm.sourceforge.net/articles/linasm.html
  81. vasm
    http://sun.hasenbraten.de/vasm/
  82. vasm – dokumentace
    http://sun.hasenbraten.de/vasm/release/vasm.html
  83. The Yasm Modular Assembler Project
    http://yasm.tortall.net/
  84. 680×0:AsmOne
    http://www.amigacoding.com/index.php/680×0:AsmOne
  85. ASM-One Macro Assembler
    http://en.wikipedia.org/wiki/ASM-One_Macro_Assembler
  86. ASM-One pages
    http://www.theflamearrows.info/documents/asmone.html
  87. Základní informace o ASM-One
    http://www.theflamearrows.info/documents/asminfo.html
  88. Linux Syscall Reference
    http://syscalls.kernelgrok.com/
  89. Programming from the Ground Up Book – Summary
    http://savannah.nongnu.org/projects/pgubook/
  90. IBM System 360/370 Compiler and Historical Documentation
    http://www.edelweb.fr/Simula/
  91. IBM 700/7000 series
    http://en.wikipedia.org/wiki/IBM_700/7000_series
  92. IBM System/360
    http://en.wikipedia.org/wiki/IBM_System/360
  93. IBM System/370
    http://en.wikipedia.org/wiki/IBM_System/370
  94. Mainframe family tree and chronology
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_FT1.html
  95. 704 Data Processing System
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_PP704.html
  96. 705 Data Processing System
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_PP705.html
  97. The IBM 704
    http://www.columbia.edu/acis/history/704.html
  98. IBM Mainframe album
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_album.html
  99. Osmibitové muzeum
    http://osmi.tarbik.com/
  100. Tesla PMI-80
    http://osmi.tarbik.com/cssr/pmi80.html
  101. PMI-80
    http://en.wikipedia.org/wiki/PMI-80
  102. PMI-80
    http://www.old-computers.com/museum/computer.asp?st=1&c=1016
  103. The 6502 overflow flag explained mathematically
    http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html
  104. X86 Opcode and Instruction Reference
    http://ref.x86asm.net/coder32.html