W przypadku AL / AX / EAX należy używać kodowania skróconego specjalnego przypadku oraz innych krótkich formularzy i instrukcji jednobajtowych
Przykłady zakładają tryb 32/64-bitowy, w którym domyślny rozmiar operandu to 32 bity. Prefiks wielkości argumentu zmienia instrukcję na AX zamiast EAX (lub odwrotnie w trybie 16-bitowym).
inc/dec
rejestr (inny niż 8-bitowy): inc eax
/ dec ebp
. (Nie x86-64: 0x4x
bajty opcode zostały zmienione na prefiksy REX, więc inc r/m32
jest to jedyne kodowanie).
8-bitowy inc bl
jest 2 bajty, z użyciem inc r/m8
kodu operacji / M + Modr argumentu operacji kodowania . Więc używać inc ebx
do przyrostu bl
, czy jest to bezpieczne. (np. jeśli nie potrzebujesz wyniku ZF w przypadkach, gdy górne bajty mogą być niezerowe).
scasd
: e/rdi+=4
, wymaga, aby rejestr wskazywał na czytelną pamięć. Czasami przydatne, nawet jeśli nie obchodzi cię wynik FLAGI (jak cmp eax,[rdi]
/ rdi+=4
). W trybie 64-bitowym scasb
może działać jako 1-bajtowyinc rdi
, jeśli lodsb lub stosb nie są przydatne.
xchg eax, r32
: To gdzie 0x90 NOP pochodzi z: xchg eax,eax
. Przykład: ponownie ułóż 3 rejestry z dwiema xchg
instrukcjami w pętli cdq
/ dla GCD w 8 bajtach, gdzie większość instrukcji jest jednobajtowa, w tym nadużycie / zamiast /idiv
inc ecx
loop
test ecx,ecx
jnz
cdq
: znak rozszerza EAX do EDX: EAX, tzn. kopiuje wysoki bit EAX do wszystkich bitów EDX. Aby utworzyć zero ze znanymi nieujemnymi lub uzyskać 0 / -1, aby dodać / sub lub maskować. Lekcja historii x86: cltq
vs.movslq
oraz mnemoniki AT&T vs. Intel dla tego i pokrewnych cdqe
.
lodsb / d : like mov eax, [rsi]
/ rsi += 4
without clobbering flags. (Zakładając, że DF jest jasne, jakie standardowe konwencje wywoływania wymagają przy wprowadzaniu funkcji.) Również stosb / d, czasami scas, a rzadziej movs / cmps.
push
/ pop reg
. np. w trybie 64-bitowym push rsp
/ pop rdi
ma 2 bajty, ale mov rdi, rsp
potrzebuje prefiksu REX i ma 3 bajty.
xlatb
istnieje, ale rzadko jest użyteczny. Dużej tabeli odnośników należy unikać. Nigdy też nie znalazłem zastosowania dla instrukcji AAA / DAA lub innych instrukcji BCD lub 2-ASCII.
1 bajt lahf
/ sahf
rzadko są przydatne. Ty mógł lahf
/ and ah, 1
jako alternatywa setc ah
, ale nie jest to zwykle użyteczne.
A konkretnie w przypadku CF sbb eax,eax
jest 0 / -1, a nawet nieudokumentowany, ale powszechnie obsługiwany 1-bajtowy salc
(zestaw AL z Carry), który skutecznie działa sbb al,al
bez wpływu na flagi. (Usunięte w x86-64). Użyłem SALC w Wyzwaniu uznania użytkownika nr 1: Dennis ♦ .
1-bajtowy cmc
/ clc
/ stc
(odwrócenie („uzupełnienie”), wyczyszczenie lub zestaw CF) są rzadko przydatne, chociaż znalazłem zastosowaniecmc
w dodawaniu o rozszerzonej precyzji z podstawowymi fragmentami 10 ^ 9. Aby bezwarunkowo ustawić / wyczyścić CF, zwykle należy to zrobić jako część innej instrukcji, np. xor eax,eax
Czyści CF, a także EAX. Nie ma równoważnych instrukcji dla innych flag stanu, tylko DF (kierunek ciągu) i IF (przerwania). Flaga przenoszenia jest specjalna dla wielu instrukcji; shift ustawia to, adc al, 0
może dodać go do AL w 2 bajtach, a wspomniałem wcześniej o nieudokumentowanej SALC.
std
/ cld
rzadko wydaje się tego warte . Zwłaszcza w kodzie 32-bitowym lepiej jest po prostu użyć dec
wskaźnika i mov
operandu źródła pamięci w instrukcji ALU zamiast ustawiać DF so lodsb
/ stosb
go w dół zamiast w górę. Zazwyczaj jeśli trzeba w dół w ogóle, trzeba jeszcze inny wskaźnik idzie w górę, tak że trzeba więcej niż jeden std
, a cld
w całej funkcji do wykorzystania lods
/ stos
dla obu stron. Zamiast tego po prostu użyj instrukcji strunowych dla kierunku w górę. (Standardowe konwencje wywoływania gwarantują DF = 0 przy wprowadzaniu funkcji, więc można założyć, że za darmo bez użycia cld
.)
Historia 8086: dlaczego te kodowania istnieją
W oryginalnym 8086, AX był wyjątkowy: instrukcje jak lodsb
/ stosb
, cbw
, mul
/ div
i inni używają go w sposób dorozumiany. Oczywiście nadal tak jest; obecny x86 nie upuścił żadnego z kodów 8086 (przynajmniej żadnego z oficjalnie udokumentowanych). Ale później procesory dodały nowe instrukcje, które dały lepsze / bardziej wydajne sposoby robienia rzeczy bez uprzedniego kopiowania lub zamiany ich na AX. (Lub do EAX w trybie 32-bitowym.)
np. 8086 brakowało później dodatków takich jak movsx
/ movzx
aby załadować lub przenieść + przedłużyć znak lub 2 i 3 operand imul cx, bx, 1234
, które nie dają wyniku w połowie i nie mają żadnych ukrytych argumentów.
Ponadto głównym wąskim gardłem 8086 było pobieranie instrukcji, więc optymalizacja pod kątem rozmiaru kodu była wtedy ważna dla wydajności . Projektant ISA z 8086 (Stephen Morse) poświęcił dużo miejsca na kodowanie opcodu na specjalne przypadki dla AX / AL, w tym specjalne (E) AX / AL-docelowe kody dla wszystkich podstawowych instrukcji ALU natychmiast-src , po prostu opcode + natychmiast bez bajtu ModR / M. 2 bajty add/sub/and/or/xor/cmp/test/... AL,imm8
lub AX,imm16
lub (w trybie 32-bitowym) EAX,imm32
.
Ale nie ma specjalnego przypadku EAX,imm8
, więc zwykłe kodowanie ModR / M add eax,4
jest krótsze.
Zakładamy, że jeśli będziesz pracować nad niektórymi danymi, będziesz chciał mieć je w AX / AL, więc zamiana rejestru na AX była czymś, co możesz chcieć zrobić, może nawet częściej niż kopiowanie rejestru do AX za pomocą mov
.
Wszystko w kodowaniu instrukcji 8086 obsługuje ten paradygmat, od instrukcji takich jak lodsb/w
do wszystkich kodowań specjalnych przypadków dla bezpośrednich znaków w EAX po ich niejawne użycie nawet do mnożenia / dzielenia.
Nie daj się ponieść emocjom; nie jest automatycznie wygraną zamiana wszystkiego na EAX, szczególnie jeśli potrzebujesz natychmiastowego dostępu do rejestrów 32-bitowych zamiast 8-bitowych. Lub jeśli potrzebujesz przeplatać operacje na wielu zmiennych w rejestrach jednocześnie. Lub jeśli korzystasz z instrukcji z 2 rejestrami, w ogóle nie następuje to natychmiast.
Ale zawsze należy pamiętać: czy robię coś, co byłoby krótsze w EAX / AL? Czy mogę zmienić układ, aby mieć to w AL, lub czy obecnie lepiej wykorzystuję AL z tym, do czego już go używam.
Swobodnie miksuj operacje 8-bitowe i 32-bitowe, aby czerpać korzyści, gdy tylko jest to bezpieczne (nie musisz przeprowadzać operacji w pełnym rejestrze ani nic takiego).
push 200; pop edx
- 3 bajtów do inicjalizacji.