A.D.Danilecki, Poznan, Polska
Politechnika Poznanska, Wydzial Informatyki i Zarzadzania
W tej chwili adanilecki _malpa_ cs.put.poznan.pl
Z wykorzystaniem wielu listow z uwagami od wielu autorow
Krótki wstęp do programowania z wykorzystaniem inline assemblera x86


6. Wykorzystanie instrukcji specyficznych dla MMX i Pentium


W punkcie tym opisane są instrukcje nie tylko mmx-owe, ale także charakterystyczne dla pentiumów wszelkiego rodzaju.
Instrukcje mmx w ogólności zaliczamy do instrukcji typu SIMD (Simple instruction multiple data) to znaczy że ta sama instrukcja wykonuje się na większej ilości danych. Zgodnie z głosami na pl.comp.os.linux instrukcje te są nieprzydatne i niewykorzystywane. Pozwolę sobie z tym nie zgodzić. Jeżeli w programie nie operujesz na liczbach zmiennoprzecinkowych, tzn. nie korzystasz z instrukcji koprocesora, wtedy z powodzeniem możesz użyć instrukcji mmx-owych i zwiększyć prędkość programu.
Spotkałem się na sieci z biblioteką umożliwiającą wykorzystywanie instrukcji mmx w programach w C, ale użycie tej biblioteki znacząco spowalnia program. Łatwiej więc samemu umieścić odpowiedni kod w swoim programie.
Przy instrukcjach mmx pojawia się pojęcie "saturated" czyli "nasycenia" liczby. Po polsku lepiej brzmi "zaokrąglanie" wyników i to dobrze oddaje o co chodzi. Po prostu wyniki niektórych operacji są automatycznie zaokrąglane do najbliższej liczby, tzn. 0xff+0x01 będzie równe dalej 0xff a nie 0x00 (zakładając że rozmair typu wynosi właśnie 0xff).
Instrukcje mmx operują na liczbach 64-bitowych! Przy czym liczby te można traktować albo jako zwykłe integery o bardzo dużym zakresie, albo jako dwie liczby 32-bitowe, cztery 16-bitowe lub osiem 8-bitowych. I tak np. instrukcja paddb spowoduje równoczesne dodanie ośmiu liczb. Imponujące nieprawdaż? Idealnie nadaje się to do operacji graficznych. 8-bitowa liczba, to nic innego jak kolor punktu w trybie 256 kolorów. Dodanie do tabeli 8-bitowych liczb można zinterpretować jako operację graficzną na kolorach. Osiem razy, teoretycznie szybszą, niż bez instrukcji mmx.
Aby wykryć, czy nasz procesor potrafi wykorzystać programy z instrukcjami mmx możemy wykorzystać instrukcję cpuid, która pojawiła się wraz z pentium, tak jak to widać w poniższym przykłądzie:

Przykład wykorzystania polecenia CPUID

unsigned int a=0;
unsigned int b=0;
void main()
{
asm volatile (
"movl $1,%eax\n\t" // chcemy informacji na temat wsparcia dla różnych isntrukcji
"cpuid\n\t" // %%edx zawiera informacja o procesorze
"bt $15,%edx\n\t" // sprawdzamy, czy bit 15 w %edx jest ustawiony
// jeśli tak, oznacza to że można używać cmovcc
"jb p_war\n\t" // bt ustawia CF na 1, gdy bit 15 w %edx był równy jeden
"jmp dalej\n\t" // czyli gdy CF == 1, skocz do war, w przeciwnym razie skocz do etykiety dalej
"p_war:\n\t"
"movl $1,b\n\t" // przeniesienie warunkowe (cmov) wsparte
"dalej:\n\t"
"bt $23,%edx\n\t" // czy można używać MMX?
"jb mmx\n\t"
"jmp kon2\n\t"
"mmx:\n\t"
"movl $1,a\n\t" //można używać instrukcji MMX
"kon2:\n\t"

);
if (a==1)
{
printf(" Można używać instrukcji mmx\n");
instrukcje_mmx();
}
if (b==1)
{
printf(" Można używać instrukcji cmov\n");
instrukcje_cmov_i_fcmov();
}
}
Instrukcje mmx spisane są tutaj .
Instrukcja cmov opisana jest tutaj .
Niestety nie mogłem sprawdzić tej instrukcji, gdyż K6 MMX jej nie posiada, a taki komputer posiadam ja.

Wykorzystanie paddxx

#include <stdio.h>

int main()
{ char b[8]={0,1,2,3,4,5,6,7};
char a[8]={10,11,12,13,14,15,16,17};
int i;

asm volatile (

// w tym miejscu teoretycznie powininienem zapamiętać stan
// stosu koprocesora. Ponieważ go nie użuwam więc nie zawracam sobie głowy. "
movq %0,%%mm0
paddb %1,%%mm0
movq %%mm0,%1
"


// tutaj powinienem dodać instrukcję emms i przywrócić stan koprocesora,
// ale w tak krótkim programiku nie zawracam sobie głowy
// EMMS , FSAVE : "=m" (a), "=m" (b)

);
for (i=0;i<8;i++)
printf("a[%d]=%d,b[%d]=%d\n",i,a[i],i,b[i]);
return 0;
}
Efekt : Na ekranie pojawia się napis:
 
a[0]=10,b[0]=10
a[1]=11,b[1]=12
a[2]=12,b[2]=14
a[3]=13,b[3]=16
a[4]=14,b[4]=18
a[5]=15,b[5]=20
a[6]=16,b[6]=22
a[7]=17,b[7]=24