[ Pobierz całość w formacie PDF ]
; sposób 2:
mov ecx, 10 ; ECX to zmienna I. i=1
petla_for:
add eax, ecx ; EAX to zmienna J. j=j+i
loop petla_for ; zmniejsz ECX o 1 i jeśli różny od
; zera, skocz do: petla_for
Pamiętajmy jednak, że instrukcja LOOP działa tylko na rejestrze (E)CX, więc jeśli chcemy mieć kilka
zagnieżdżonych pętli, to przed każdą z nich (rozpoczynającą się zmianą rejestru ECX) musimy zachować ten
rejestr (np. na stosie), a po zakończeniu pętli musimy przywrócić jego poprzednią wartość.
Sprawa z pętlami o nieznanej ilości powtórzeń nie jest o wiele trudniejsza. Pętla typu for jest całkowicie
równoważna pętli while. Właśnie z tego skorzystamy, a kod niewiele będzie się różnić budową od
poprzedniego przykładu.
Powiedzmy, że mamy taką pętlę:
(przeskocz ten przykład)
for (i=100; i+1
{
j=j+i+4;
}
Możemy ją zastąpić równoważną konstrukcją:
(przeskocz zamianę for na while)
i=100;
while (i+1
{
j=j+i+4;
i=1+2;
}
Otrzymujemy kod:
(przeskocz tłumaczenie while)
mov ecx, 100 ; ECX to zmienna I. i=100
nasza_petla:
mov ebx, ecx
add ebx, 1 ; EBX = i+1
cmp ebx, [n] ; sprawdzamy, czy i+1
ja koniec_while ; wychodzimy, gdy i+1 > n
add eax, ecx ; EAX to zmienna J. j=j+i
add eax, 4 ; j=j+i+4
add ecx, 2 ; i=i+2
jmp short nasza_petla
koniec_while:
Ostatni rodzaj pętli to pętle typu do-while (repeat...until). Taka pętla różni się tym od poprzedniczek, że
warunek jest sprawdzany po wykonaniu kodu pętli (czyli taka pętla zawsze będzie wykonana co najmniej
raz). Daje to pewne możliwości optymalizacji kodu.
Popatrzmy na taki przykład:
(przeskocz przykład do-while)
do
136 Bogdan Drozdowski
2007-11-12 Język asembler dla każdego Bogdan Drozdowski
{
i=i+1;
j=j-1;
} while ((i 1));
Warunek wyjścia to: i >= n LUB j
A teraz popatrzcie, co można z tym zrobić:
(przeskocz tłumaczenie do-while)
petla_do:
add ecx, 1 ; ECX to zmienna I. i=i+1
add edx, 1 ; EDX to zmienna J. j=j+1
cmp ecx, [n]
jae koniec ; i >= n jest jednym z warunków
; wyjścia. Drugiego nie musimy
; sprawdzać, bo wynik i tak
; będzie prawdą
cmp edx, 1
jbe koniec ; j
jmp petla_do
koniec:
Można przepisać to w lepszy sposób:
(przeskocz lepszy sposób)
petla_do:
add ecx, 1 ; ECX to zmienna I. i=i+1
add edx, 1 ; EDX to zmienna J. j=j+1
cmp ecx, [n]
jae koniec ; i >= n jest jednym z warunków
; wyjścia. Drugiego nie musimy
; sprawdzać, bo wynik i tak
; będzie prawdą
; jeśli nadal tutaj jesteśmy,
; to z pewnością i
cmp edx, 1
ja petla_do ; j
; wyjścia. Jeśli j > 1,
; to kontynuuj wykonywanie pętli.
; Jeśli j
; opuszczamy pętlę:
koniec:
Jeśli warunek kontynuacji lub wyjścia z pętli jest wyrażeniem złożonym, to:
" jeśli składa się z alternatyw (działań typu OR, ||), to na pierwszym miejscu sprawdzajcie te warunki,
które mają największą szansę być prawdziwe. Oszczędzicie w ten sposób czasu na bezsensowne
sprawdzanie reszty warunków (wynik i tak będzie prawdą).
" jeśli składa się z koniunkcji (działań typu AND, &&), to na pierwszym miejscu sprawdzajcie te
warunki, które mają największą szansę być fałszywe. Wynik całości i tak będzie fałszem.
Przykłady:
1) a == 0 || (b > 1 && c
Bogdan Drozdowski 137
Bogdan Drozdowski Język asembler dla każdego 2007-11-12
2) (b 0
W przypadku 1 najpierw sprawdzamy, czy a jest równe zero. Jeśli jest, to cały warunek jest prawdziwy. Jeśli
nie jest, sprawdzamy najpierw ten z dwóch pozostałych, który ma największą szansę bycia fałszywym (jeśli
któryś jest fałszywy, to wynik jest fałszem).
W przypadku 2 najpierw sprawdzamy, czy c jest większe od zera. Jeśli nie jest, to cały warunek jest fałszywy.
Jeśli jest, to potem sprawdzamy ten z pozostałych warunków, który ma większą szansę bycia prawdziwym
(jeśli któryś jest prawdziwy, to wynik jest prawdą).
Decyzje wielowariantowe (wyrażenia typu switch/case)
(przeskocz decyzje wielowariantowe)
Fragment kodu:
(przeskocz schemat switch/case)
switch (a)
{
case 1: .....
case 2: .....
....
default: .....
}
w prosty sposób rozkłada się na serię wyrażeń if i else if (oraz else, o ile podano sekcję default). Te zaś już
umiemy przedstawiać w asemblerze. Jest jednak jedna ciekawa sprawa: jeśli wartości poszczególnych
przypadków case są zbliżone (coś w stylu 1, 2, 3 a nie 1, 20, 45), to możemy posłużyć się tablicą skoków
(ang. jump table). W tej tablicy przechowywane są adresy fragmentów kodu, który ma zostać wykonany, gdy
zajdzie odpowiedni warunek. Brzmi to trochę pokrętnie, dlatego szybko pokażę przykład.
(przeskocz przykład switch/case)
switch (a)
{
case 1:
j=j+1;
break;
case 2:
j=j+4;
break;
case 4:
j=j+23;
break;
default:
j=j-1;
}
A teraz tłumaczenie:
(przeskocz tłumaczenie przykładu switch/case)
mov eax, [a]
cmp eax, 1 ; jeśli a 5,
; to na pewno default
jb sekcja_default
cmp eax, 5
ja sekcja_default
138 Bogdan Drozdowski
2007-11-12 Język asembler dla każdego Bogdan Drozdowski
jmp [przypadki + eax*2 - 2]
przyp1:
add dword ptr [j], 1 ; NASM/FASM: bez słowa PTR
jmp koniec
przyp2:
[ Pobierz całość w formacie PDF ]