= Instrukcje MMX i SSE =
== Wprowadzenie ==
Są to instrukcje typu SIMD (Single Instruction, Multiple Data), czyli jedna
instrukcja wykonuje zadaną operację jednocześnie na kilku danych (np.
dodawanie, shift, porównanie, itp.).
Służą do obrabiania dużych ilości danych, najczęściej
multimedialnych (obraz, dźwięk), ale przydaje się też w innych sytuacjach,
gdy obrabiamy dane strumieniowo (np. w DSP).

Większość instrukcji działa „pionowo” (np. dodajemy 4 słowa 2-bajtowe z
jednego rejestru do 4 słów 2-bajtowych z 2 rejestru)
Niektóre instrukcje działają „poziomo” (np. dodajemy 4 słowa 2-bajtowe w
jednym rejestrze).
Instrukcje SIMD mogą używać liczb całkowitych lub zmiennoprzecinkowych (my
się zajmujemy tylko całkowitymi).

Więcej o instrukcjach MMX/SSE2:
- http://en.wikipedia.org/wiki/MMX
- http://en.wikipedia.org/wiki/SSE2

== Użycie ==
Instrukcje MMX używają 8-miu rejestrów MMn (od MM0 do MM7), które są
64-bitowe i są współdzielone z koprocesorem zmiennoprzecinkowym (dlatego nie
można ich używać naprzemiennie z operacjami zmiennoprzecinkowymi).

Instrukcje SSE2 są rozszerzeniem MMX dla operacji na liczbach całkowitych,
używają 8 rejestrów 128-bitowych oznaczonych XMM0 do XMM7 (w późniejszych
wersjach liczba rejestrów została zwiększona do 16). Te rejestry nie są
współdzielone z koprocesorem. 
Wszystkie 64-bitowe instrukcje MMX mają swoje 128-bitowe odpowiedniki w SSE2
więc skupimy się na instrukcjach MMX.

Do przesłania zawartości pamięci do rejestrów MMX używa się instrukcji MOVQ
(64-bitowe MOV), np.
mov mm0, [eax]

Do przesłania danych do rejestru 128-bitowego używa się instrukcji MOVDQA
jeśli dane są wyrównane do granicy 16-bajtów albo MOVDQU jeśli nie są
wyrównane.

Instrukcje operujące na danych SIMD przeważnie mają w nazwie "packed".
Przydatne instrukcje:
PSRAW (Packed Data Bit Shift Right Arithmetic)
	Traktuje rejestr jako 4 słowa 16 bitowe i wykonuje operację SAR na  
	każdym z nich z osobna
PSRAD - analogicznie jak PSRAW, ale dla słów 32-bitowych
PADDB/PADDW/PADDD (Add Packed Integers)
	Traktuje rejestry/pamięć jako 8*1bajt/4*2bajty/2*4bajty i dodaje parami
	pomiędzy rejestrami. Ewentualnie bity przepełnienia są tracone,
	znaczniki nie są ustawiane. Wynik jest w docelowym rejestrze.
PADDQ - Dodaje 2 wartości 64-bitowe
PADDSB/PADDSW (Add Packed Signed Integers With Saturation)
	Traktuje rejestry jako 8*1bajt/4*2bajty ze znakiem, dodaje parami
	ale w razie przepełnienia ustawia wartość na maksymalną wartość ze
	zgodnym znakiem.
PADDUSB/PADDUSW - analogicznie, ale dla liczb bez znaku
PAVGB/PAVGW (Average Packed Integers)
	Traktuje rejestry jako 8*1bajt/4*2bajty bez znaku, wykonuje parami
	operację  (L + R + 1) shr 1
PCMPEQB/PCMPEQW/PCMPEQD (Compare Packed Integers)
	Traktuje rejestry/pamięć jako 8*1bajt/4*2bajty/2*4bajty i porównuje
	wartości parami. Jeśli wartości są równe, ustawia docelową wartość
	na same jedynki.
PCMPGTB/PCMPGTW/PCMPGTD - analogicznie, ale porównuje czy jedna wartość jest
	większa od drugiej
PMAXSW (Packed Signed Integer Word Maximum)
	Traktuje rejestry jako 4*2bajty ze znakiem, porównuje parami i
	zapisuje w rejestrze docelowym większą z wartości (oblicza maksimum
	z par)
PMULLW (Multiply Packed 16-bit Integers, and Store)
	Traktuje rejestry jako 4*2bajty bez znaku, mnoży parami i zapisuje w
	docelowym rejestrze dolne 16 bitów 32-bitowego wyniku mnożenia
	(czyli wykonuje mnożenie z obcięciem do 16 bitów)
POR/PXOR/PAND - wykonują OR/XOR/AND na rejestrach MMX.
PMADDWD (MMX Packed Multiply and Add)
	Wykonuje operację przemnożenia i dodania:
	dst[0-31]   := (dst[0-15] * src[0-15]) + (dst[16-31] * src[16-31]) 
	dst[32-63]  := (dst[32-47] * src[32-47]) + (dst[48-63] * src[48-63])	

== Uwagi ==
- Nie każdy procesor obsługuje dany zestaw instrukcji, więc w produkcyjnych
programach należy zrobić przynajmniej wersję SIMD i zwykłą (w asemblerze 
x86).
- Instrukcje MMX są obsługiwane przez praktycznie każdy procesor dzisiaj w
użyciu, więc najbezpieczniej pisać w tym zestawie instrukcji
- Niektóre kompilatory (np. GCC 4) są w stanie wygenerować kod w MMX, pod
warunkiem użycia odpowiedniej docelowej architektury.
- Czasem kod w C lub za pomocą wcześniejszego zestawu instrukcji  jest
szybszy!
- Po użyciu instrukcji MMX (nie SSE) należy wykonać instrukcję EMMS, ponieważ
MMX używają rejestrów zmiennoprzecinkowych koprocesora. Nie dotyczy to
późniejszych zestawów, które używają rejestrów XMM, które są już oddzielne.
- Jeśli rozmiar danych nie jest wielokrotnością ilości danych przetwarzanych w
naszym kodzie, należy dodać „ogon”, który przetworzy te ostatnie dane
- Dane powinny być wyrównane w pamięci do wielokrotności rozmiaru linii cache
(16 bajtów) w celu jak najefektywniejszego działania procesora.

Zadania:
- Napisać program liczący średnią wartość elementu w macierzy 8x8 (elementy
to liczby 16-bitowe ze znakiem).
- Napisać program mnożący 2 macierze o wymiarach 8x8 (elementy są 16-bitowe
ze znakiem). Druga macierz jest przechowywana w pamięci w postaci
transponowanej.
- Zmienić program z zadania 1 tak, by używał MMX do zmniejszania głośności.
- Sprawdzić, co się stanie jak w programie sse.asm operacje MOVDQU zostaną
zastąpione MOVDQA.
- Przepisać resztę programu mmx.asm z użyciem SSE.
- Napisać program, który wczytuje plik graficzny w formacie PPM (binarnym,
8-bitowym, patrz http://netpbm.sourceforge.net/doc/ppm.html) i dla każdych
dwóch pikseli znajdujących się pod sobą, zastępuje je średnią z wartości ich
składowych RGB (efekt rozmycia w pionie). Można założyć, że obrazek ma
wielkość będącą wielokrotnością jakiejś liczby. Do konwersji obrazków w
innym formacie na PPM można użyć programu "convert".
