= Używanie inline assemblera w gcc =
== Wstęp ==
Za pomocą inline assemblera można dodawać wstawki asemblerowe do programów w
C. 

Uwaga: gcc domyślnie używa asemblera gas, który ma zupełnie inną składnię
niż NASM. W szczególności:
- Kolejność argumentów jest odwrócona, np. movl %eax, %edx oznacza skopiowanie
eax do edx, a nie odwrotnie (jak w NASM).
- Nazwy rejestrów należy poprzedzić znakiem %, np. pushl %eax
- Nazwy stałych należy poprzedzić znakiem $.
- Nazwy instrukcji zawierają przyrostek wskazujący na rozmiar argumentu, np.
movl oznacza mov z argumentem typu long (czyli 32 bity).
Bardziej szczegółowy opis składni znajduje na stronach:
http://www.redhat.com/docs/manuals/enterprise/RHEL-3-Manual/gnu-assembler/i386-syntax.html
http://en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax
http://sourceware.org/binutils/docs-2.17/as/index.html

Przy pisaniu wstawek asemblerowych należy zwrócić szczególną uwagę na to, by
wskazać kompilatorowi co robi dana wstawka (jakie rejestry modyfikuje, czy
można ją optymalizować, itp.), jeśli tego nie zrobimy, to kompilator może ją
usunąć, użyć rejestrów, które zmieniliśmy we wstawce do przechowania swoich
zmiennych, itp. Generalnie kończy się to dziwnym zachowaniem programu,
wywracaniem, itp. które trudno zlokalizować. Dlatego należy koniecznie
testować program ze wstawką asemblerową z włączonymi w kompilatorze
optymalizacjami, żeby to od razu wychwycić.

== Składnia wstawki ==

__asm__ __volatile__ (
	kod_asemblerowy 
	: parametry wyjściowe                  /* opcjonalne */
	: parametry wejściowe                   /* opcjonalne */
	: lista zmienianych rejestrów      /* opcjonalne */
        );

Słowo kluczone __volatile__ powoduje, że kompilator nie będzie próbował
przesuwać kodu asemblerowego w inne miejsce.

=== Kod asemblerowy ===

Kod asemblerowy to kod w składni asemblera gas jako napis w C.
Instrukcje oddziela się znakami końca linii "\n\t", bo tak to ma być
przekazywane do asemblera. Należy zwrócić uwagę, że to jest zwykły napis w
C, więc podlega takim samym prawom rozwijania sekwencji "escape" (takich jak
\n) jak wszędzie w C.

Najprostszy przykład:
        __asm__ __volatile__ (
                "movl $0, %ebx\n\t" //kod powrotu
                "movl $1, %eax\n\t" //numer funkcji
                "int $0x80\n\t"
        );
Nie ma parametrów wejściowych, wyjściowych ani zmienianych rejestrów (bo
program się kończy).

== Lista zmienianych rejestrów ==
Lista zmienianych rejestrów wskazuje kompilatorowi, które rejestry są
zmieniane we wstawce asemblerowej, tak żeby kompilator nie próbował ich użyć
do przechowywania swoich zmiennych tymczasowych.
Należy zwrócić uwagę, żeby podać wszystkie rejestry, które mogą być
zmienione implicite (np. rejestry esi i edi w wypadku funkcji CMPSB) oraz w
kodzie wywoływanym ze wstawki asemblerowej.

== Parametry wejściowe i wyjściowe ===

Parametry wejściowe to zmienne, adresy, itp. z C, które zostaną przekazane
do kodu asemblerowego i mogą tam zostać użyte. Muszą one być przekazane w
określony sposób tak, by można ich było użyć w instrukcji asemblerowej,
ponieważ nie wszystkie instrukcje mogą np. brać adres w pamięci (np. 
"add [adres1], parametr1" nie zadziała jeśli oba argumenty są wskaźnikami do
pamięci).

Parametry definiuje się w postaci:
"ograniczenia" (nazwa_zmiennej_C)
np.
"r" (licznik)
Jeśli jest ich więcej, oddziela się je przecinkami.

Odwołuje się do parametrów przez %numer, przy czym numerowane są parametry w
takiej kolejności, że najpierw parametry wyjściowe, a potem wejściowe.

Przykład:
	int kod_powrotu = 8;
	__asm__ __volatile__ (
		"movl %0, %%ebx\n\t" //kod powrotu 
		"movl $1, %%eax\n\t" //numer funkcji
		"int $0x80\n\t"
		: /* brak parametrów wyjściowych */
		: "r" (kod_powrotu)
	);

Przydatne ograniczenia:
"r" - rejestr ogólnego przeznaczenia
"m" - parametr będzie adresem w pamięci
"g" - rejestr ogólnego przeznaczenia, adres w pamięci lub wartość
natychmiastowa

W przypadku parametrów wyjściowych poprzedza się typ jednym ze znaczników:
"=" - ten parametr będzie tylko zapisywany, np. "=r".
"+" - ten parametr będzie odczytywany i zapisywany we wstawce

Przykład:
	char *napis = "ala ma kota\n";
	int dlugosc = strlen(napis);
	int wynik_funkcji;
	__asm__ __volatile__ (
		"movl %1, %%ecx\n\t" //adres
		"movl %2, %%edx\n\t" //dlugosc
		"movl $1, %%ebx\n\t" //numer deskryptora
		"movl $4, %%eax\n\t" //numer funkcji
		"int $0x80\n\t"
		"movl %%eax, %0\n\t" //wynik funkcji w eax
		: "=r"(wynik_funkcji)
		: "m" (napis), "g" (dlugosc)
		: "%eax", "%ebx", "%ecx", "%edx"
        );
Adres napisu jest przekazywany jako parametr "m" (adres w pamięci).
Długość jest przekazywana w rejestrze lub w pamięci (typ "g").
Wynik jest przekazywany z powrotem do kodu C przez zapisanie rejestru i
zawartość tego rejestru będzie wartością zmiennej wynik_funkcji.

Lista zmienianych rejestrów obejmuje rejestry eax, ebx, ecx, edx, ponieważ
one są zmieniane w ciele funkcji.

Uwagi:
- Nie należy przekazywać jako parametrów do asemblera bezpośrednio
parametrów funkcji, trzeba je najpierw skopiować na zmienną lokalną. Z
niewiadomych powodów GCC się buntuje.
- Należy zwrócić uwagę na kolejność wpisywania parametrów rejestrowych 
do rejestrów, ponieważ one też są w rejestrach, więc zapisując można je
sobie zamazać. Kompilator nie wie, co się dzieje wewnątrz wstawki
asemblerowej.

 
= Makefile =
Załączony Makefile generuje oprócz pliku wykonywalnego 2 pliki z
wygenerowanym przez kompilator kodem asemblera dla programu: 
main_c.s (kod zoptymalizowany z -O3) i main_c.s0 (kod bez optymalizacji).

Literatura:
- http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
- http://www-128.ibm.com/developerworks/library/l-ia.html

Zadania:
- Zamienić programy z zajęć 06_napisy na ich odpowiedniki w postaci wstawek
asemblerowych w C.
- Zamienić program rozmywający w piksmapę w pionie z zajęć 9 na wersję w C
używającą wstawki asemblerowej do instrukcji MMX.
