Intel x86 Assembly Language & Microarchitecture
Kontrola przepływu
Szukaj…
Bezwarunkowe skoki
jmp a_label ;Jump to a_label
jmp bx ;Jump to address in BX
jmp WORD [aPointer] ;Jump to address in aPointer
jmp 7c0h:0000h ;Jump to segment 7c0h and offset 0000h
jmp FAR WORD [aFarPointer] ;Jump to segment:offset in aFarPointer
Względne skoki w pobliżu
jmp a_label to:
- Blisko
Określa tylko przesuniętą część logicznego adresu docelowego. Zakłada się, że segment toCS. - krewny
Semantyka instrukcji to skok rel bajtów do przodu o 1 od następnego adresu instrukcji lubIP = IP + rel.
Instrukcja jest kodowana jako EB <rel8> lub EB <rel16/32> , asembler wybiera najbardziej odpowiednią formę, zwykle preferując krótszą.
Możliwe jest jmp SHORT a_label dla asemblera, na przykład w NASM jmp SHORT a_label , jmp WORD a_label i jmp DWORD a_label generują trzy możliwe formy.
Bezwzględne pośrednie skoki bliskie
jmp bx i jmp WORD [aPointer] to:
- Blisko
Określają tylko część przesunięcia logicznego adresu miejsca docelowego. Zakłada się, że segment toCS. - absolutnie pośredni
Semantą instrukcji jest skok do adresu w reg lub mem lubIP = reg,IP = mem.
Instrukcja jest kodowana jako FF /4 , dla pamięci pośredniej wielkość argumentu określana jest jak dla każdego innego dostępu do pamięci.
Absolutne dalekie skoki
jmp 7c0h:0000h to:
daleko
Określa obie części adresu logicznego : segment i przesunięcie.bezwzględny Semantą instrukcji jest skok do segmentu adresu : offset lub
CS = segment, IP = offset.
Instrukcja jest kodowana jako EA <imm32/48> zależności od rozmiaru kodu.
Możliwe jest wybranie jednej z dwóch form w pewnym asemblerze, na przykład w NASM jmp 7c0h: WORD 0000h i jmp 7c0h: DWORD 0000h generuje pierwszą i drugą formę.
Absolutne pośrednie dalekie skoki
jmp FAR WORD [aFarPointer] to:
daleko Określa obie części adresu logicznego : segment i przesunięcie.
Absolutnie pośredni Semantą instrukcji jest skok do segmentu: przesunięcie zapisane w mem 2 lub
CS = mem[23:16/32], IP = [15/31:0].
Instrukcja jest zakodowana jako FF /5 , rozmiar argumentu może być kontrolerem ze specyfikatorami wielkości.
W NASM, trochę nie intuicyjnie, są one jmp FAR WORD [aFarPointer] dla operandu 16:16 i jmp FAR DWORD [aFarPointer] dla operandu 16:32 .
Brakujące skoki
prawie absolutne
Może być emulowany niemal pośrednim skokiem.mov bx, target ;BX = absolute address of target jmp bxdaleki krewny
I tak nie ma sensu ani zbyt wąskiego zastosowania.
1 Dwa uzupełnienia służą do określenia przesunięcia ze znakiem, a tym samym przeskoczenia do tyłu.
2 Który może być seg16: off16 lub seg16: off32 , o rozmiarach 16:16 i 16:32 .
Warunki testowe
Aby użyć skoku warunkowego, należy przetestować warunek. Testowanie tutaj warunku odnosi się tylko do sprawdzania flag, rzeczywiste skoki opisano w części Skoki warunkowe .
x86 sprawdza warunki, opierając się na rejestrze EFLAGS, który zawiera zestaw flag, które potencjalnie może ustawić każda instrukcja.
Instrukcje arytmetyczne, takie jak sub lub add , oraz instrukcje logiczne, takie jak xor lub and oczywiście „ustaw flagi”. Oznacza to, że flagi CF , OF , SF , ZF , AF , PF są modyfikowane przez te instrukcje. Dowolna instrukcja może modyfikować flagi, na przykład cmpxchg modyfikuje ZF .
Zawsze sprawdź odniesienie instrukcji, aby wiedzieć, które flagi są modyfikowane przez określoną instrukcję.
x86 ma zestaw skoków warunkowych , o których mowa wcześniej, które skaczą wtedy i tylko wtedy, gdy niektóre flagi są ustawione lub niektóre są czyste lub oba.
Flagi
Operacje arytmetyczne i logiczne są bardzo przydatne w ustawianiu flag. Na przykład po sub eax, ebx , który obecnie przechowuje wartości bez znaku , mamy:
| Flaga | Po ustawieniu | Kiedy będzie jasne |
|---|---|---|
| ZF | Gdy wynik wynosi zero. EAX - EBX = 0 ⇒ EAX = EBX | Gdy wynik nie jest równy zero. EAX - EBX ≠ 0 ⇒ EAX ≠ EBX |
| CF | Kiedy wynik musiał przeprowadzić dla MSb. EAX - EBX <0 ⇒ EAX <EBX | Kiedy wynik nie musiał nosić dla MSb. EAX - EBX ≮ 0 ⇒ EAX ≮ EBX |
| SF | Po ustawieniu wyniku MSb. | Gdy wynik MSb nie jest ustawiony. |
| Z | Kiedy wystąpił podpisany przepełnienie. | Gdy podpisane przepełnienie nie wystąpiło. |
| PF | Gdy liczba bitów ustawiona w najmniej znaczącym bajcie wyniku jest parzysta. | Gdy liczba bitów ustawiona w najmniej znaczącym bajcie wyniku jest nieparzysta. |
| AF | Gdy dolna cyfra BCD wygenerowała przeniesienie. Jest to bit 4 carry. | Gdy dolna cyfra BCD nie wygenerowała przeniesienia. Jest to bit 4 carry. |
Testy nieniszczące
sub i and instrukcje zmieniać ich przeznaczenia operandu i wymaga dwóch dodatkowych kopii (Zapisywanie i przywracanie), aby utrzymać cel niezmodyfikowanej.
Aby wykonać test nieniszczący, należy wykonać instrukcje cmp i test . Są identyczne z ich niszczycielskim odpowiednikiem, z wyjątkiem tego, że wynik operacji jest odrzucany, a tylko flagi są zapisywane .
| Destrukcyjny | Nieniszczące |
|---|---|
sub | cmp |
and | test |
test eax, eax ;and eax, eax
;ZF = 1 iff EAX is zero
test eax, 03h ;and eax, 03h
;ZF = 1 if both bit[1:0] are clear
;ZF = 0 if at least one of bit[1:0] is set
cmp eax, 241d ;sub eax, 241d
;ZF = 1 iff EAX is 241
;CF = 1 iff EAX < 241
Testy podpisane i niepodpisane
CPU nie nadaje specjalnego znaczenia rejestrowaniu wartości 1 , znak jest konstrukcją programistyczną. Nie ma różnicy podczas testowania wartości podpisanych i niepodpisanych. Procesor oblicza wystarczającą liczbę flag, aby przetestować zwykłe relacje arytmetyczne (równe, mniejsze niż, większe niż itp.), Zarówno jeśli argumenty mają być uważane za podpisane i niepodpisane.
1 Chociaż zawiera pewne instrukcje, które mają sens tylko w przypadku określonych formatów, takich jak uzupełnienie dwóch. Ma to na celu zwiększenie wydajności kodu, ponieważ wdrożenie algorytmu w oprogramowaniu wymagałoby dużo kodu.
Skoki warunkowe
W zależności od stanu flag procesor może wykonać lub zignorować skok. Instrukcja wykonująca skok na podstawie flag znajduje się pod ogólną nazwą Jcc - Jump on Condition Code 1 .
Synonimy i terminologia
Aby poprawić czytelność kodu asemblera, Intel zdefiniował kilka synonimów dla tego samego kodu stanu. Na przykład jae , jnb i jnc mają ten sam kod warunku CF = 0 .
Chociaż nazwa instrukcji może dać bardzo silną wskazówkę, kiedy jej użyć, czy nie, jedynym sensownym podejściem jest rozpoznanie flag, które należy przetestować, a następnie odpowiedni wybór instrukcji.
Intel podał jednak nazwy instrukcji, które mają idealny sens, gdy są używane po instrukcji cmp . Na potrzeby tej dyskusji zakłada się, że cmp ustawił flagi przed skokiem warunkowym.
Równość
Argumenty są równe, jeśli ustawiono ZF , różnią się inaczej. Aby sprawdzić równość, potrzebujemy ZF = 1 .
je a_label ;Jump if operands are equal
jz a_label ;Jump if zero (Synonym)
jne a_label ;Jump if operands are NOT equal
jnz a_label ;Jump if not zero (Synonym)
| Instrukcja | Flagi |
|---|---|
je , jz | ZF = 1 |
jne , jnz | ZF = 0 |
Lepszy niż
W przypadku operandów bez znaku miejsce docelowe jest większe niż źródło, jeśli przeniesienie nie było potrzebne, to znaczy, jeśli CF = 0 . Gdy CF = 0 , możliwe jest, że operandy były równe, testowanie ZF będzie jednoznaczne.
jae a_label ;Jump if above or equal (>=)
jnc a_label ;Jump if not carry (Synonym)
jnb a_label ;Jump if not below (Synonym)
ja a_label ;Jump if above (>)
jnbe a_label ;Jump if not below and not equal (Synonym)
| Instrukcja | Flagi |
|---|---|
jae , jnc , jnb | CF = 0 |
ja , jnbe | CF = 0, ZF = 0 |
W przypadku podpisanych argumentów musimy sprawdzić, czy SF = 0 , chyba że wystąpił przepełnienie ze znakiem, w którym to przypadku wynikowa SF jest odwracana. Ponieważ OF = 0, jeśli nie wystąpiło podpisane przepełnienie, a 1 w przeciwnym razie, musimy sprawdzić, czy SF = OF .
ZF może być wykorzystany do wdrożenia testu ścisłego / nie ścisłego.
jge a_label ;Jump if greater or equal (>=)
jnl a_label ;Jump if not less (Synonym)
jg a_label ;Jump if greater (>)
jnle a_label ;Jump if not less and not equal (Synonym)
| Instrukcja | Flagi |
|---|---|
jge , jnl | SF = OF |
jg , jnle | SF = OF, ZF = 0 |
Mniej niż
Wykorzystują one powyższe warunki odwrócone.
jbe a_label ;Jump if below or equal (<=)
jna a_label ;Jump if not above (Synonym)
jb a_label ;Jump if below (<)
jc a_label ;Jump if carry (Synonym)
jnae a_label ;Jump if not above and not equal (Synonym)
;SIGNED
jle a_label ;Jump if less or equal (<=)
jng a_label ;Jump if not greater (Synonym)
jl a_label ;Jump if less (<)
jnge a_label ;Jump if not greater and not equal (Synonym)
| Instrukcja | Flagi |
|---|---|
jbe , jna | CF = 1 lub ZF = 1 |
jb , jc , jnae | CF = 1 |
jle , jng | SF! = OF lub ZF = 1 |
jl , jnge | SF! = OF |
Określone flagi
Każdą flagę można przetestować osobno za pomocą j<flag_name> gdzie nazwa flagi nie zawiera końcowego F (na przykład CF → C , PF → P ).
Pozostałe kody, które nie zostały wcześniej uwzględnione, to:
| Instrukcja | Flaga |
|---|---|
js | SF = 1 |
jns | SF = 0 |
jo | OF = 1 |
jno | OF = 0 |
jp , jpe (e = parzysty) | PF = 1 |
jnp , jpo (o = nieparzysty) | PF = 0 |
Jeszcze jeden skok warunkowy (dodatkowy)
Jeden specjalny skok warunkowy x86 nie testuje flagi. Zamiast tego sprawdza wartość rejestru cx lub ecx (w oparciu o aktualny tryb adresu procesora wynoszący 16 lub 32 bity), a skok jest wykonywany, gdy rejestr zawiera zero.
Ta instrukcja została zaprojektowana do sprawdzania poprawności rejestru licznika ( cx/ecx ) przed instrukcjami podobnymi do rep lub przed loop pętli.
jcxz a_label ; jump if cx (16b mode) or ecx (32b mode) is zero
jecxz a_label ; synonym of jcxz (recommended in source code for 32b target)
| Instrukcja | Zarejestruj się (bez flagi) |
|---|---|
jcxz , jecxz | cx = 0 (tryb 16b) |
jcxz , jecxz | ecx = 0 (tryb 32b) |
1 Lub coś takiego.
Sprawdź relacje arytmetyczne
Nieoznaczone liczby całkowite
Lepszy niż
cmp eax, ebx
ja a_label
Większy bądź równy
cmp eax, ebx
jae a_label
Mniej niż
cmp eax, ebx
jb a_label
Mniejszy lub równy
cmp eax, ebx
jbe a_label
Równy
cmp eax, ebx
je a_label
Nie równe
cmp eax, ebx
jne a_label
Podpisane liczby całkowite
Lepszy niż
cmp eax, ebx
jg a_label
Większy bądź równy
cmp eax, ebx
jge a_label
Mniej niż
cmp eax, ebx
jl a_label
Mniejszy lub równy
cmp eax, ebx
jle a_label
Równy
cmp eax, ebx
je a_label
Nie równe
cmp eax, ebx
jne a_label
a_label
W powyższych przykładach a_label jest miejscem docelowym dla procesora, gdy testowany warunek jest „prawdziwy”. Gdy testowany warunek jest „fałszywy”, procesor będzie kontynuował następną instrukcję po skoku warunkowym.
Synonimy
Istnieją synonimy instrukcji, które można wykorzystać do poprawy czytelności kodu.
Na przykład ja i jnbe (Skok nie niższy ani równy) są tą samą instrukcją.
Podpisano niepodpisane kody towarzyszące
| Operacja | Bez podpisu | Podpisany |
|---|---|---|
| > | ja | jg |
| > = | jae | jge |
| < | jb | jl |
| <= | jbe | jle |
| = | je | je |
| ≠,! =, <> | jne | jne |