1
FORMATY INSTRUKCJI 80X86
Tryb adresowania
Postać argumentu
Rejestr segmentu
Rejestrowe
rej Brak
Natychmiastowe
stała brak
Bezpośrednie
przesunięcie
etykieta
DS
DS
Pośrednie rejestrowe
[BX]
[BP]
[DI]
[SI]
DS
SS
DS
DS
Pośrednie bazowe
[BX] + przesunięcie
[BP] + przesunięcie
DS
SS
Bezpośrednie indeksowe
[DI] + przesunięcie
[SI] + przesunięcie
DS
DS
Indeksowane bazowe
[BX][SI] + przesunięcie
[BX][DI] + przesunięcie
[BP][SI] + przesunięcie
[BP][DI] + przesunięcie
DS
DS
SS
SS
Adres efektywny jest odległością, wyrażoną w bajtach, określającą położenie
argumentu od początku segmentu.
Czas potrzebny jednostce wykonawczej na obliczenie adresu efektywnego jest
jednym z głównych czynników określających czas wykonania instrukcji.
Adresowanie bezpośrednie
Adresowanie bezpośrednie dotyczy rozkazów, w których adres argumentu jest
zawarty w bajcie rozkazowym lub następuje bezpośrednio po nim.
Adresowanie bezpośrednie odnosi się zwykle do rozkazów 3-bajtowych, w
których dwa ostatnie bajty zawierają adres argumentu umieszczonego
w pamięci. W niektórych mikroprocesorach jest możliwe określenie adresu
jednym bajtem odnoszącym się do zerowej strony pamięci. Adresowanie to
jest stosowane wówczas gdy znane jest położenie (adres) argumentu.
2
Kod operacji
Adres
Argument
Pamięć
W trybie adresowania bezpośredniego adres efektywny zawarty jest w instrukcji
– tak jak przechowywane są dane o wartościach natychmiastowych w
instrukcjach.
Przykład
MOV AX, TABLICA
Uwaga – procesory x86 przechowują dane w pamięci w odwrotnym porządku
tj. bardziej znaczący bajt jest umieszczony za mniej znaczącym.
Adresowanie pośrednie dotyczy rozkazów, w których jest zawarty adres
pośredni, pod którym znajduje się adres argumentu. Adresowanie to może
być stosowane wówczas, gdy program nie ma przyporządkowanego na
stałe obszaru pamięci, w którym działa, a adres argumentu jest obliczany w
trakcie realizacji programu.
0000
0001 BB TABLICA
0002 AA
0003 TABLICA+2
0004
AA BB
AX
3
Kod operacji
Adres pośredni
Adres
Pamięć
Argument
Adresowanie pośrednie
Adresowanie pośrednie przez rejestr
W trybie adresacji pośredniej przez rejestr, adres efektywny argumentu zawarty
jest w rejestrze bazowym BX, wskaźniku bazy BP lub w rejestrze indeksowym
(SI lub DI). Rejestry będące argumentami pośrednimi obejmujemy nawiasami
kwadratowymi, aby odróżnić je od rejestrów argumentów.
Przykład
Instrukcja:
MOV AX, [BX]
ładuje zawartość komórki pamięci (określonej adresem zapisanym w rejestrze
BX) do rejestru AX.
Jednym ze sposobów umieszczania przesunięcia w rejestrze BX jest użycie
przedrostka OFFSET przed adresem pamięci. Na przykład:
MOV BX, OFFSET TABLICA
MOV AX, [BX]
powyższe instrukcje są równoważne:
MOV AX, TABLICA
Adresowanie pośrednie poprzez rejestr bazowy
W trybie adresacji pośredniej poprzez rejestr bazowy, asembler oblicza adres
efektywny przez dodanie wartości przesunięcia do zawartości rejestru BX lub
BP. Taki sposób wykorzystania rejestru jest bardzo wygodny do uzyskania
dostępu do struktur danych umieszczonych w różnych częściach pamięci, na
przykład, tablica rekordów.
4
Przykład
MOV AX, [BX] + 4
Adresowanie indeksowe polega na wyznaczeniu adresu argumentu przez
zsumowanie zawartości rejestru indeksowego i adresu zawartego wewnątrz
rozkazu. Może być stosowane w tych mikroprocesorach, które zawierają
rejestry indeksowe. Adresowanie to jest stosowane wówczas, gdy jest
konieczny dostęp do danych umieszczonych kolejno w pamięci.
Kod operacji
Adres bazowy
Przesunięcie
Pamięć
Argument
+
Rejestr indeksowy
Przesunięcie
Adresowanie indeksowe
Adresowanie indeksowane bezpośrednie
W trybie adresowania indeksowanego bezpośrednio, adres efektywny jest sumą
przesunięcia i rejestru indeksowego DI lub SI. Ten rodzaj adresowania jest
wygodny do uzyskiwania dostępu do elementów tablicy. Przesunięcie wskazuje
początek tablicy a rejestr indeksowy na jej element.
0019
001A
001B
001C
001D
001E BB
001F AA
0020
001A
BX
AA BB
AX
5
Na przykład, mamy tablicę bajtów TABLICA_B:
MOV DI, 2
MOV AL, TABLICA_B[DI]
sekwencja powyższa ładuje trzeci element tablicy do rejestru AL.
W tablicy słów TABLICA_S, elementy są oddalone od siebie o 2 bajty – należy
podwoić numer elementu, aby uzyskać wartość jego indeksu:
MOV DI, 4
MOV AX, TABLICA_S[DI]
Adresowanie indeksowane bezpośrednie poprzez rejestr bazowy
Adres efektywny jest sumą rejestru bazowego i rejestru indeksowego oraz
(opcjonalnie) przesunięcia. Ponieważ mamy tu dwa przesunięcia, ten tryb
adresowania jest wygodny do uzyskania dostępu do tablic dwuwymiarowych.
Rejestr bazowy przechowuje adres początku tablicy, podczas gdy przesunięcie i
rejestr indeksowy dostarczają przesunięcia wiersza i kolumny:
MOV AX, TABLICA_2 [BX] [DI]
Inne postacie argumentów trybu adresowania indeksowanego bezpośrednio
poprzez rejestr bazowy:
MOV AX, [BX + 2 + DI]
MOV AX, [DI + BX + 2]
MOV AX, [BX + 2] [DI]
MOV AX, [BX] [DI + 2]
Wiele mikroprocesorów posiada rejestr używany do adresowania
pośredniego z automatyczną dekrementacją, np. przed przesłaniem danej pod
wyznaczony adres i inkrementacją po pobraniu danej. Umożliwia to realizację
mechanizmu stosu, czyli pobieranie danych w odwrotnej kolejności niż zostały
zapamiętane.
Mechanizm stosu jest realizowany w pamięci o bezpośrednim dostępie z
użyciem wyżej wymienionego rejestru, zwanego wówczas wskaźnikiem stosu.
Jego zawartość stanowi adres wierzchołka stosu. Każdy zapis na stos powoduje
przesunięcie wierzchołka stosu w górę tak, że poprzednia wartość staje się
niedostępna, a dostępna jest wartość ostatnio przesłana. Po pobraniu danej z
wierzchołka stosu obniża się on i znów staje się dostępna dana zapisana przed
ostatnio pobraną. Stos jest bardzo wygodną organizacją danych stosowaną
często wówczas, gdy należy przechowywać aktualne dane podczas
wykonywania innego zadania, przerywającego realizowany ciąg operacji.
6
Tryby
adresowania
umożliwiają programiście piszącemu programy
użytkowe na łatwą implementację różnych typów danych, występujących w
językach wysokiego poziomu. Ogólnie, dane mogą być statyczne (w tym
globalne), lokalne (tworzone na stosie podczas wejścia do procedury)
i umieszczane w pamięci przydzielanej dynamicznie.
W mikroprocesorach rodziny Intel do tworzenia przemieszczenia mogą być
wykorzystywane trzy elementy: rejestr bazowy, rejestr indeksowy i stała
określająca stałe przemieszczenie. W mikroprocesorze 80286 jako rejestry
bazowe mogą być wykorzystywane rejestry BX lub BP, w mikroprocesorze
80386 i i486 jeden z ośmiu rejestrów:
EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI.
jako rejestry indeksowe w mikroprocesorze 80286 można wykorzystać rejestry
SI oraz DI, natomiast w mikroprocesorze 80386 i i486: EAX, EBX, ECX,
EDX, EBP, ESI, EDI.
Jak widzimy rejestr ESP nie może być rejestrem indeksowym. Dodatkowo w
mikroprocesorach 80386 i i486 jest kodowany współczynnik skali równy 1, 2, 4
lub 8, przez który należy pomnożyć zawartość rejestru indeksowego.
Przemieszczenie w przypadku mikroprocesora 80286 jest liczbą 8 lub 16-
bitową, w przypadku mikroprocesorów 80386 i i486 liczbą 8 lub 32-bitową.
7
INSTRUKCJE PRZESYŁANIA DANYCH
Mnemonik Format
asemblera
Znaczniki
Ogólnego przeznaczenia
MOV
MOV przeznaczenie, źródło
PUSH
PUSH źródło
PUSH
PUSH wartość natychmiastowa
PUSHA
PUSHA
POP
POP przeznaczenie
POPA
POPA
XCHG
XCHG przeznaczenie, źródło
XLAT
XLAT tablica źródłowa
Wejścia/wyjścia
IN
IN akumulator, port
OUT
OUT port, akumulator
Przenoszenia adresów
LEA
LEA rejestr-16, pamięć-16
LDS
LDS rejestr-16, pamięć-32
LES
LES rejestr-16, pamięć-16
Przenoszenie znaczników
LAHF LAHF
SAHF
SAHF
SF, ZF, AF, PF, CF
PUSHF PUSHF
POPF POPF
wszystkie
Instrukcje ogólnego przeznaczenia
MOV - kopiowanie wartości z argumentu źródłowego do docelowego.
Możliwych jest wiele kombinacji argumentów
mov reg, reg
mov reg, imm
mov reg, mem
Przykłady:
MOV AX, 1 ; AX=1
MOV ES:[BX], AX
MOV DS., AX
MOV BL, AL.
MOV CL, 25H
8
PUSH źródło
POP przeznaczenie
Instrukcja PUSH umieszcza zawartość 16-bitowego rejestru lub komórki
pamięci na szczycie stosu. Instrukcja POP działa odwrotnie, zdejmuje słowo ze
stosu i umieszcza je w komórce pamięci lub w rejestrze.
Argument
108
107
109
110
111
Wskaźnik stosu
109
Argument
108
107
109
110
111
Wskaźnik stosu
108
a)
b)
c)
d)
Argument
108
107
109
110
111
Wskaźnik stosu
108
Argument
108
107
109
110
111
Wskaźnik stosu
109
Zapis i odczyt danej ze stosu: a) stan przed zapisem, b) stan po
zapisie, c) stan przed odczytem, d) stan po odczycie.
Przykład
PUSH AX ; Zapamiętaj AX,
PUSH ES ;ES,
PUSH DI ;DI,
PUSH SI ;SI
. . . .
. . . .
POP SI
; Przywróć SI,
POP DI
;DI,
POP ES
;ES,
POP AX ; i AX
PUSH ES
POP DS.
9
PUSHA, POPA - instrukcje przesłania i pobrania wszystkich rejestrów
ogólnego przeznaczenia.
PUSHA przenosi rejestry w kolejności AX, CX, DX, BX, początkową wartość
SP, BP, SI i DI. POPA – przywraca wartości tych rejestrów w odwrotnym
porządku.
XCHG - zamiana miejscami bajtów lub słów.
XCHG AX, BX ; Zamień miejscami dwa 16-bitowe rejestry
XCHG AL., AH ;
XLAT - pobranie bajtu z tablicy do AL. Maksymalny rozmiar tablicy wynosi
256 bajtów.
Przykład
MOV AL.,
10
MOV BX,
OFFSET
TAB_ZN
XLAT TAB_ZN
Instrukcje wejścia-wyjścia
IN akumulator,
port
OUT port,
akumulator
Przykłady
IN
AL., 200
;Pobierz bajt z portu 200
IN AL.,
PORT_VAL
OUT 30H,
AX
OUT
DX, AX
; Wyślij słowo do portu określonego przez DX
Instrukcje przenoszenia adresów
LEA – załadowanie adresu efektywnego
LEA rejestr-16, pamięć-16
Przykład
LEA BX,
TABLICA[DI]
10
LDS - załadowanie adresu logicznego do wskazanego rejestru i do rejestru DS.
Instrukcja LDS – (załaduj wskaźnik używając DS) czyta z pamięci 32-bitowe
słowo i ładuje mniej znaczące 16 bitów do podanego rejestru a 16 bardziej
znaczących bitów do DS.
Przykład
TUTAJ_FAR DD
TUTAJ
przesłanie przesunięcia i adresu segmentu TUTAJ do BX i DS:
LDS BX,
TUTAJ_FAR
Instrukcje przesyłania znaczników
PUSHF – przesłanie rejestru znaczników na szczyt stosu,
POPF – pobranie znaczników ze stosu
Przykład
PUSH AX
PUSH DI
PUSH SI
PUSHF
CALL PROC
POPF
POP SI
POP DI
POP AX
11
INSTRUKCJE ARYTMETYCZNE
Mnemonik Format
asemblera
Znaczniki
Dodawanie
ADD
ADD przeznaczenie, źródło OF, SF, ZF, AF, PF, CF
ADC
ADC przeznaczenie, źródło OF, SF, ZF, AF, PF, CF
AAA AAA
DAA
DAA
SF, ZF, AF, PF, CF
INC
INC przeznaczenie
OF, SF, ZF, AF, PF
Odejmowanie
SUB przeznaczenie,
źródło
OF, SF, ZF, AF, PF, CF
SBB przeznaczenie,
źródło
OF, SF, ZF, AF, PF, CF
AAS
DAS
OF, SF, ZF, AF, PF, CF
DEC
przeznaczenie
OF, SF, ZF, AF, PF
NEG
przeznaczenie
OF, SF, ZF, AF, PF, CF
CMP przeznaczenie,
źródło
OF, SF, ZF, AF, PF, CF
Mnożenie
MUL MUL
źródło OF,
CF
IMUL IMUL
źródło OF,
CF
IMUL
IMUL rej16, natychmiastowa OF, CF
IMUL
IMUL rej16, źródło, natychm. OF, CF
AAM AAM
Dzielenie
DIV
DIV źródło
IDIV IDIV
źródło
AAD AAD
Instrukcje dodawania
ADD – dodaj, ADC – dodaj z przeniesieniem
ADD
AX, CX
; Dodaj mniej znaczące 16 bitów
ADC BX,
DX
;
następnie bardziej znaczące 16 bitów
AAA – poprawka wyniku dodawania liczb w reprezentacji ASCII
DAA - poprawka wyniku dodawania liczb w postaci kodu BCD.
Dla obu instrukcji argument powinien być umieszczony w rejestrze AL.
12
Przykład
ADD
AL., BL
; Dodaj spakowane liczby BCD z AL. i BL
AAA
;
i
przekształć wynik do liczby niespakowanej
Instrukcje odejmowania
SUB – odejmowanie
SBB – odejmowanie z pożyczką
Instrukcje SUB i SBB są podobne do swoich odpowiedników w operacji
dodawania (ADD i ADC), ale przy odejmowaniu znacznik przeniesienia (CF)
działa jako wskaźnik pożyczki
Przykład
SUB
AX, BX
;Odejmij mniej znaczące 16 bitów
SBB BX,
DX
;
następnie bardziej znaczące
AAS – poprawka ASCII po odejmowaniu
DAS – poprawka dziesiętna
CMP – porównanie wartości przeznaczenia ze źródłem, CMP odejmuje źródło
od przeznaczenia i ustawia lub zeruje znaczniki: OF, SF, ZF i CF.
Instrukcje mnożenia
MUL – mnożenie liczb bez znaku
IMUL – mnożenie liczb ze znakiem
MUL źródło
IMUL źródło
gdzie źródło jest bajtowym lub rozmiaru słowa rejestrem ogólnego
przeznaczenia lub komórką pamięci. Jako drugiego argumentu instrukcje te
używają zawartości rejestru AL. (dla operacji bajtowych) lub AX (dla operacji
na słowach). Iloczyn o podwójnym rozmiarze przekazują w następujący sposób:
- mnożenie bajtów wytwarza 16-bitowy iloczyn, przechowywany w AH i AL.
- mnożenie słów wytwarza 32-bitowy iloczyn, przechowywany w DX
(bardziej znaczące bity) i w AX (mniej znaczące).
Instrukcje dzielenia
DIV źródło
IDIV źródło
gdzie źródło jest rejestrem ogólnego przeznaczenia o rozmiarze bajtu lub słowa
albo komórką pamięci i zawiera dzielnik, dzielna jest przechowywana w AX lub
w DX i AX (dla operacji na słowie).
13
INSTRUKCJE STERUJĄCE
Mnemonik Format
asemblera
Znaczniki
Przekazanie (skok) bezwarunkowe
CALL
CALL adres
RET
RET [wartość pobierana]
JMP
JMP adres
Przekazanie (skok) warunkowe
JA/JNBE
JA skok bliski
JAE/JNB
JAE skok bliski
JB/JNAE/JC
JB skok bliski
JBE/JNA
JBE skok bliski
JCXZ
JCXZ skok bliski
JE/JZ
JE skok bliski
JG/JNLE
JG skok bliski
JGE/JNL
JGE skok bliski
JL/JNGE
JL skok bliski
JLE/JNG
JLE skok bliski
JNC
JNC skok bliski
JNE/JNZ
JNE skok bliski
JNO
JNO skok bliski
JNP/JPO
JNP skok bliski
JNS
JNS skok bliski
JO
JO skok bliski
JP/JPE
JP skok bliski
JS
JS skok bliski
Pętle
LOOP
LOOP skok bliski
LOOP/LOOPZ
LOOPE skok bliski
LOOPNE/LOOPZ LOOPNE skok bliski
W celu umożliwienia zmiany biegu programu można używać instrukcji skoków – przejście do
innego miejsca programu (warunkowo lub bezwarunkowo) bez jawnej możliwości powrotu
do miejsca wywołania (adres powrotu nie jest generowany i przechowywany), ale też można
zastosować instrukcję CALL, która poza przekazaniem sterowania do nowego miejsca kodu
podobnie jak JMP, zapamiętuje poprzez odłożenie na stosie adres następnej po CALL
instrukcji, umożliwiając powrót do miejsca wywołania. Po wykonaniu kodu procedury
wykonywana jest komplementarna do CALL instrukcja RET, zdejmująca ze stosu adres
powrotu i przekazująca do niego sterowanie. Rozróżniamy procedury bliskie (near) i dalekie
(far). Dla bliskich, mieszczących się w tym samym segmencie kodu, na stos odkładany jest
IP, dla dalekich IP oraz CS.
14
Instrukcja CALL:
CALL adresat
gdzie adresat jest nazwą wywołanej procedury.
Jeśli adresat jest typu NEAR, CAAL umieszcza na stosie przesunięcie następnej po CALL
instrukcji. Gdy adresat jest typu FAR – umieszcza na stosie najpierw zawartość rejestru CS, a
następnie przesunięcie.
Po zapamiętaniu adresu powrotu, CALL ładuje adres względny etykiety adresata do IP. Jeśli
procedura jest typu FAR, ładuje także do CS adres segmentu etykiety.
Instrukcja RET:
RET wartość
pobiera adres powrotny ze stosu.
Jeśli procedura była typu NEAR (tzn. w tym samym segmencie kodu co CALL), RET pobiera
jedno słowo i ładuje go do wskaźnika instrukcji (IP). Jeśli procedura była typu FAR
(znajdowała się w innym segmencie kodu), RET pobiera dwa słowa ze stosu: przesunięcie dla
IP, następnie adres segmentu dla CS.
Stos:
Struktura danych, stosująca metodę LIFO : last-in , first-out, zwykle zorganizowana zgodnie z
zasadą: dane na stosie są zapamiętywane od większych do mniejszych adresów. Dla
określenia miejsca, w którym będzie przechowana zawartość „odkładanego” słowa pamięci
używane są rejestry SS: adres początku segmentu stosu (stanowiący adres rzeczywistego
„końca” stosu) i SP wskaźnik na wierzchołek stosu. Do kontaktów z daną na wierzchołku
stosu służą instrukcje PUSH : umieść słowo na stosie – zawartość SP zmniejsz o 2( dla
operacji 32-bitowych o 4), a następnie argument instrukcji jest kopiowany pod adres PAO
SS:SP. Argumentem może być rejestr, argument natychmiastowy bądź inna komórka PAO.
Przeznaczeniem PUSH jest odłożenie argumentów procedury na stos przed jej wywołaniem,
może również służyć do rezerwacji miejsca na stosie dla chwilowych zmiennych programu.
PUSHF i POPF odkłada i pobiera rejestr flag jako młodsze słowo Eflags, PUSHFD i POPFD
jako 32-bitowe dwusłowo (push flags double).
Modyfikacją instrukcji PUSH jest PUSHA – czyli push all, która odkłada osiem rejestrów
ogólnych w następującej kolejności :
EAX, ECX, EDX, EBX, pocz•tkow• warto••
ESP przed po•o•eniem EAX, EBP, ESI, oraz EDI.
Instrukcja ta upraszcza sposób wywołania procedur.
Komplementarne do powyższych są instrukcje POP: zdejmij słowo ze stosu, umieść je w
argumencie a następnie SP zwiększ o dwa ( dla 32-bitowych o 4); POPA:
EDI, ESI,
EBP, ignoruje doubleword, EBX, EDX, ECX, i EAX. Rejestr ESP
jest odtwarzany przez zdejmowanie ze stosu 8 warto•ci. Je•li
argumenty s• 16-bitowe, s•owa ze stosu s• przesy•ane do
rejestrów w nast•puj•cej kolejno•ci: DI, SI, BP, ignoruje word,
BX, DX, CX, i AX.
15
Przykład wykorzystania stosu jako chwilowego bufora na dane:
push bx;
zapamiętaj BX na stosie
push cx
push dx
mov ax,
10 ; AX = 10
mov cl, 3 ; CL = 3
mov bx, 2 ; BX = 2
push ax ;
10 na stos
pop
dx
; DX = 10
shl
ax, cl ; AX = AX * ( 2 * 2 * 2 ) ! AX = 80
inc
ax
; AX = 81
add
ax, bx ; AX = 83
sub
ax, dx ; AX = 73
pop dx
pop cx
pop bx
Skoki również mogą być różnej „długości” :
krótkie short ( 1 bajt kodu + 1 bajt adresu względnego) w obszarze –128 do +127 bajtów od
miejsca wywołania,
bliskie near ( 1 bajt kodu + 2 bajty adresu bezwzględnego – tu offset) w obszarze bieżącego
segmentu kodu,
dalekie far do instrukcji w innym segmencie kodu.
Przykład wykorzystania nietypowego instrukcji RET – skok:
mov ax,
OFFSET
tutaj
push ax
ret
...
tutaj:
...
Standardowa sekwencja działań przy realizacji bliskiej CALL to: ( patrz materiały)
1. Położenie IP na stos
2. Załadowanie do IP offsetu wywoływanej procedury
3. Rozpoczęcie wykonywania wywoływanej procedury.
Przy bliskim RET są wykonywane następujące czynności:
1. Pobranie zawartości wierzchołka stosu do IP
2. Jeśli RET ma argument n, to zwiększany jest SP o liczbę n, zdejmując ze stosu n bajtów
argumentów
3. Rozpoczęcie wykonywania instrukcji, wskazywanej teraz przez CS:IP.
16
Dla dalekich Call i Ret odpowiednio najpierw CS, potem IP są odkładane na stosie, a
odwrotnie najpierw IP potem CS są zdejmowane.
Dla przekazywania parametrów przez stos użyteczny bywa rejestr BP, taktowany
jako bazowy do pobierania wartości argumentów:
push param1
push param2
call proc1
...
; procedura
proc1 proc
push
BP
mov
BP,
SP
mov AX, [BP] ; stare BP
mov AX, [BP] + 2; stare IP, miejsce powrotu
mov AX, [BP] + 4; ostatnio położony parametr czyli param2
mov AX, [BP] + 6; param1
...
pop
BP
ret
4
proc1 endp