Niektóre programy napisane w językach wysokiego poziomu wymagają maksymalnej możliwej prędkości wykonywania. Instrukcje języków wysokiego poziomu wykonują się znacznie wolniej niż instrukcje asmeblera, dlatego zamiana niektórych fragmentów kodu w języku wysokopoziomowym na kod języka asmeblera może znacznie przyspieszyć działanie danego programu. Stosowanie kodu asemblerowego połączonego z instrukcjami wysokopoziomowego języka programowania w dużych projektach może być najbardziej optymalnym rozwiązaniem danego problemu programistycznego, gdyż pisanie takich projektów jedynie w kodzie asmblera jest zadaniem bardzo skomplikowanym i czasochłonnym, gdzie programista narażony jest na popełnienie szeregu niezamierzonych błędów.
Zamieniamy na kod asemblera część kodu programu napisanego w języku C/C++. Programem wykorzystywanym do kompilacji kodu C/C++ w kursie jest program DEV C++. W zakładce narzędzia E-kursu można pobrać jego plik instalacyjny ze wskazanego adresu internetowego. Program ma za zadanie wyszukać maksymalną wartość w ciągu podanym przez użytkownika. Jego pełny kod można pobrać tutaj. Na kod asemblera zamieniamy fragment procedury znajdz , umieszczony między komentarzami:
void znajdz(int *wskaznik, int n)
{
int pom=0;
int i;
//-----zamieniamy na kod asemblera-----
pom=*(wskaznik+1);
for(i=1; i<=n; i++)
{
if(pom<*(wskaznik+i))
{
pom=*(wskaznik+i);
}
}
//------zamieniamy na kod asemblera-----
cout<< "Najwieksza wartosc w tym ciagu to:" <<pom<<endl;
cout<<",jej adres to:"<<&pom<<endl;
}
Aby "wpleść" kod asemblera w język C/C++ korzystamy z funkcji asm() . Pojedyncze instrukcje umieszczamy w cudzysłowie i zakańczamy znakiem końca linii: "\n" . W pierwszej linii funkcji asm() określamy typ składni dla instrukcji procesora. Podany fragment języka C/C++, zamienimy korzystając ze składni typu Intel. Aby uzyskać dostęp do odpowiednich parametrów i zmiennych lokalnych procedury korzystamy z rejestru EBP. Dodatnie przesunięcie względem adresu przechowywanego w rejestrze EBP pozwala nam na dostęp do adresów parametrów naszej procedury. Natomiast ujemne przesunięcie, to dostęp do adresów zmiennych lokalnych procedury. W pierwszym kroku zajmujemy się zamianą instrukcji pom=*(wskaznik+1). Jest to pierwszy parametr funkcji. Adres pierwszego parametru funkcji znajduje się pod adresem [EBP+8]. Kolejne adresy parametrów funkcji znajdują się pod adresami zgodnie z daną regułą: [EBP+4+N*4], gdzie N to numer parametru w funkcji. Pobieramy wartość wskaznik+1, tak więc początek naszego kodu będzie wyglądał następująco:
asm(".intel_syntax \n" //rodzaj składni
"MOV ESI,[EBP+8]\n" //wskaznik
"ADD ESI,4 \n" //wskaznik+1
"MOV EAX,[ESI] \n" //*(wskaznik+1)
Następnie wartość wskaznik+1 przypisujemy zmiennej pom, zmienna pom, będzie miała adres [EBP-4], gdyż jest to wartość 4-ro bajtowa - int, zadeklarowana jako pierwsza zmienna lokalna w funkcji:
"MOV [EBP-4],EAX\n" //pom=*(wskaznik+1)
Ustawiamy zmienną inkrementującą pętli for - i , ponieważ jest to zmienna lokalna, zadekalorowana jako druga po zmiennej pom i również typu int, jej adres będzie wynosił [EBP-8]:
"MOV EAX,4 \n" //i=1 (ładujemy 4 - bo przesunięcie będzie o 4 bajty)
"MOV [EBP-8],EAX \n"
Rozpoczynamy pętle for etykietą Petla:
"PETLA:\n" //początek pętli for
"MOV EBX,[EBP+12]\n" //ładujemy zmienną n do rejestru EBX
Zmienną n pobraną jako 2 parametr funkcji i skopiowaną do rejestru EBX, mnożymy razy 4, ponieważ jest to zmienna typu int. W pętli for, będziemy inkrementować również zmienną typu int - i, która będzie zwiększana o 4 bajty:
"MOV EAX,EBX \n" //początek pętli for
"MOV ECX,4\n"
"MUL ECX \n" //mnożymy razy 4 wartość n
Po pomnożeniu zmiennej n przez wartość rejestru ECX, kopiujemy ją do rejestru EBX i porównujemy ze zmienną i. Jeżeli wartość zmiennej i jest większa od wartości zmiennej n, kończymy wykonanie pętli for:
"MOV EBX,EAX \n"
"MOV ECX,[EBP-8]\n" //i
"CMP ECX,EBX \n"
"JA KONCZ \n" //koniec pętli for
Następnie zamieniamy na kod asemblera fragment kodu, w którym znajdujemy maksimum ciągu:
if(pom<*(wskaznik+i))
{
pom=*(wskaznik+i);
}
Do rejestrów kopiujemy odpowiednie wartości zmiennych lokalnych i parametrów:
"MOV EBX,[EBP-4]\n" //pom
"MOV EDX,[EBP+8]\n" //wskaznik
"MOV ECX,[EBP-8]\n" //i
Porównujemy aktualną wartość zmiennej pom z wartością *(wskaznik+i) i sprawdzamy czy pom nie jest mniejsze od wartości odczytanej ze zmiennej *(wskaznik+i), jeśli tak wartość *(wskaznik+i) staje się nową wartością maksymalną znalezioną w naszym ciągu:
"ADD EDX,ECX \n" //wskaznik+i
"MOV EDX,[EDX] \n" //*(wskaznik+i)
"CMP EBX,EDX \n" //pom >=*(wskaznik+i) ?
"JAE OMIN \n"
"MOV [EBP-4],EDX\n" // pom=*(wskaznik+i);
"OMIN: \n"
Na końcu naszej wstawki, inkrementujemy wartość zmiennej i o kolejne 4 bajty i wykonujemy skok na początek pętli:
"MOV ECX,[EBP-8]\n" //i
"ADD ECX,4 \n" //zwiekszam o 4 wartość i
"MOV [EBP-8],ECX\n"
"JMP PETLA\n" // pom=*(wskaznik+i);
"KONCZ:\n"
".att_syntax \n");
Cały kod wstawki przedstawiony jest na poniższym listingu, w załączniku pod listingiem umieszczony jest pełny kod programu:
asm(".intel_syntax \n" //rodzaj składni
"MOV ESI,[EBP+8]\n" //wskaznik
"ADD ESI,4 \n" //wskaznik+1
"MOV EAX,[ESI] \n" //*(wskaznik+1)
"MOV [EBP-4],EAX\n" //pom=*(wskaznik+1)
"MOV EAX,4 \n" //i=1 (ładujemy 4 - bo przesunięcie będzie o 4 bajty)
"MOV [EBP-8],EAX \n"
"PETLA:\n" //początek pętli for
"MOV EBX,[EBP+12]\n" //ładujemy zmienną n do rejestru EBX
"MOV EAX,EBX \n" //początek pętli for
"MOV ECX,4\n"
"MUL ECX \n" //mnożymy razy 4 wartość n
"MOV EBX,EAX \n"
"MOV ECX,[EBP-8]\n" //i
"CMP ECX,EBX \n"
"JA KONCZ \n" //koniec pętli for
"MOV EBX,[EBP-4]\n" //pom
"MOV EDX,[EBP+8]\n" //wskaznik
"MOV ECX,[EBP-8]\n" //i
"ADD EDX,ECX \n" //wskaznik+i
"MOV EDX,[EDX] \n" //*(wskaznik+i)
"CMP EBX,EDX \n" //pom >=*(wskaznik+i) ?
"JAE OMIN \n"
"MOV [EBP-4],EDX\n" // pom=*(wskaznik+i);
"OMIN: \n"
"MOV ECX,[EBP-8]\n" //i
"ADD ECX,4 \n" //zwiekszam o 4 wartość i
"MOV [EBP-8],ECX\n"
"JMP PETLA\n" // pom=*(wskaznik+i);
"KONCZ:\n"
".att_syntax \n");
Uruchomiony program przedstawiony jest na Rys.1:
Przekształcić na kod asemblera, fragment kodu napisanego w języku C/C++. Program ma za zadanie posortować liczby metodą bąbelkową. Kod programu, który należy przekształcić to fragment procedury sortowanie:
//-----zamieniamy na kod w asemblerze-----
for (i=0; i<99; ++i)
{
zamiana=0;
for (j=0; j<99-i; j++)
if (t[j+1] < t[j])
{
pom = t[j];
t[j] = t[j+1];
t[j+1] = pom;
zamiana=1;
}
if(!zamiana) break;
}
//------zamieniamy na kod w asemblerze-----
Kod programu w języku C/C++, można pobrać z załącznika poniżej:
Przykładowe wywołanie programu przedstawione jest na Rys.2: