Volací konvence
Způsob, jakým jsou předávany argumenty a návratové hodnoty funkcí, není u procesorů x86 pevně určen a jedná se o konvence definované v době překladu. Můžeme si tedy definovat ,,vlastní konvence''. Situaci komplikuje překladač (Visual Studio), který automaticky vytváří prolog a epilog funkce a mj. přidává ladící informace. Tento problém lze ale obejít pomocí specifického modifikátoru.
__declspec(naked) int foo() { // zamezí vložení prologu a epilogu
_asm {
mov eax, 10
ret; // je potreba uvest instrukci ret
}
}
Příklady
// funkce jednoho argumentu, ktera vraci hodnotu o jedna vetsi
// pouzita konvence CDECL
__declspec(naked) int incf1(int arg) {
_asm {
push ebp; // prolog funkce
mov ebp, esp;
mov eax, [ebp + 8];
inc eax;
mov esp, ebp; // epilog funkce
pop ebp;
ret;
}
}
// funkce jednoho argumentu, ktera vraci hodnotu o jedna vetsi
// pouzita konvence FASTCALL (argument je predan pomoci registru ecx)
__declspec(naked) int incf2(int arg) {
_asm {
push ebp; // prolog funkce
mov ebp, esp;
mov eax, ecx
inc eax
mov esp, ebp; // epilog funkce
pop ebp;
ret;
}
}
// funkce jednoho argumentu, ktera vraci hodnotu o jedna vetsi
// pouzita konvence FASTCALL (argument je predan pomoci registru ecx)
// optimalizavana verze -- vsimnete si, ze v tomto pripade neni potreba prolog ani epilog
// a staci jen instrukce ret
__declspec(naked) int incf3(int arg) {
_asm {
mov eax, ecx
inc eax
ret;
}
}
// priklady volani funkci s ruznymi konvencemi
int foo() {
_asm {
// volani funkce s konvenci CDECL
push dword ptr 10
call incf1
add esp, 4
// volani funkce s konvenci FASTCALL
mov ecx, 10
call incf2 // jde pouzit i incf3
}
}
Poznámka
Použitou volací konvenci lze pro jednotlivé funkce určit pomocí specifických modifikátorů, např. __fastcall, __cdecl, atd. V průběhu tohoto cvičení je ale nepoužívejte.
Úkoly
- S použitím výše popsaného postupu napište funkci
int min1(int a, int b), která vrací nejmenší hodnotu ze dvou. K předání argumentů použijte zásobník. Tuto funkci zavolejte. - S použitím výše popsaného postupu napište funkci
int min2(int a, int b), která vrací nejmenší hodnotu ze dvou. K předání argumentů použijte registr ecx a zásobník. Tuto funkci zavolejte. - S použitím výše popsaného postupu napište funkci
int min3(int a, int b), která vrací nejmenší hodnotu ze dvou. K předání argumentů použijte registry ecx a edx. Tuto funkci zavolejte. - S použitím výše popsaného postupu napište funkce počítající rekurzivně faktoriál zadaného argumentu jednou s použitím konvence CDECL a jednou s použitím fastcall. Tyto funkce zavolejte a výsledek vypište na standardní výstup.
- S použitím výše popsaného postupu napište funkce počítající rekurzivně fibonacciho číslo zadaného argumentu jednou s použitím konvence CDECL a jednou s použitím fastcall.
Pro získání bodového hodnocení je potřeba splnit úkoly 1, 2, 3 a 4.