Wprowadzenie do Architektury komputerów Asembler procesorów rodziny x86
Rozkazy mikroprocesora Rozkazy mikroprocesora 8086 można podzielić na siedem funkcjonalnych grup: 1. Rozkazy przesłania danych w pamięci. 2. Arytmetyczne. 3. Logiczne. 4. Operacje blokowe. 5. Skoki. 6. Operacje kontroli procesora. 7. Rozkazy wejścia/wyjścia.
Rozkazy przesyłania danych Rozkazy przesyłania danych pozwalają działać na danych bajtowych i 16-bitowych (słowa). Specjalnym ich przypadkiem są rozkazy przekazywania danych 32-bitowych, które służą do inicjalizacji rejestru (np. adresowego) i rejestru segmentu pełnym adresem 20-bitowym. Rozkazy przesyłania można podzielić na: uniwersalne, przesyłania adresów, przesyłania rejestru stanu.
MOV MOV - przeznaczony do przesyłania słów lub bajtów w operacjach typu rejestr-pamięć z (do) dowolnych rejestrów. Rozkaz ten może też przesyłać do pamięci dane określone w trybie prostym bez pośrednictwa rejestru. MOV reg, reg - pomiędzy rejestrami MOV mem,reg - z rejestru do pamięci MOV reg,mem - z pamięci do rejestru MOV reg,stała - stałej do rejstru MOV mem,stała - stałej do pamięci MOV reg,seg - z rejestru segmentowego do rejestru 16-bitowego MOV mem,seg - z rejestru segmentowego do pamięci MOV seg,mem - z pamięci do rejestru segmentowego
PUSH, POP PUSH - przesłanie słowa na szczyt stosu. PUSH reg/mem PUSH stała - prześlij 16-bitową wartość z rejestru lub pamięci na stos - prześlij stałą na stos POP - pobranie słowa ze szczytu stosu, a następnie modyfikacja wskaźnika stosu. POP reg,mem - pobierz daną ze stosu do rejestru lub do pamięci
XCHG XCHG - służy do wymiany zawartości rejestrów i pamięci. Argumentem rozkazu nie mogą być rejestry segmentowe. XCHG reg/mem,mem/reg
LEA LEA - rozkaz przesyłania do rejestru 16-bitowego adresu efektywnego określonego w trybie odwołania do pamięci. Rejestrem wynikowym operacji może być rejestr uniwersalny, wskaźnik lub rejestr indeksowy. Rozkaz jest przeznaczony do pobierania adresu obiektu z pamięci w celu rutynowych działań na adresach. LEA reg,pamięć - zmienna pamięć jest komórką pamięci, której adres efektywny będzie wyliczony i przesłany do rejestru reg.
LAHF, SAHF LAHF - rozkaz pobrania do rejestru AH młodszego bajtu rejestru znaczników LAHF SAHF - rozkaz przesłania do rejestru znaczników rejestru AH. SAHF
POPF, PUSHF Ponadto ze stosem współpracują instrukcje POPF i PUSHF: POPF - pobranie do rejestru znaczników słowa ze stosu. POPF PUSHF - odłożenie rejestru znaczników na stos. PUSHF
Tryby adresowania 1. Domyślny Przykłady: cbw ;domyślne operandy al i ax mul bx ;domyślny operand ax mul bl ;domyślny operand al 2. Natychmiastowy mov al, 5 mov al, A num EQU 5 imd= num-2 mov al, num mov [si],imd
Tryby adresowania 3. Rejestrowy mov ds, ax mov al, bl mov ebx, eax
Tryby adresowania 4. Bezpośredni Liczba DW 50... mov ax, Liczba mov ax, [Liczba] mov ax, [1234] mov ax, es:[12h] 5. Bezpośredni z przemieszczeniem Liczba DW 50 DW 40... mov ax, [Liczba+2] ;ładuje 40 do ax
Tryby adresowania 6. Bazowy mov ax, [bx] Tablica DB 5, 10, 15, 20... mov bx, OFFSET Tablica lea bx Tablica add bx, 3 mov al, [bx] ;ładuje 20 do al. lea bx, Tablica[3] mov al, [bx] ;ładuje 20 do al. Operator OFFSET: OFFSET wyrażenie
Tryby adresowania 7. Bazowy z przemieszczeniem Tablica DB 5, 10, 15, 20... mov al, [bx+3] ; ładuje 20 do al mov al, [bx]+3 ; ładuje 20 do al mov al, 3[bx] ; ładuje 20 do al. add bx, 3 mov al, [bx+tablica] ; ładuje 20 do al mov al, Tablica[bx] ; ładuje 20 do al
Tryby adresowania 8. Indeksowy mov ax, [si] Tablica DW 5, 10, 15, 20 mov si, OFFSET Tablica add si, 3 mov al, [si] 9. Indeksowy z przemieszczeniem mov mov ax, [si+4] ax, [di]+4 add si, 3 mov al, [si+tablica] mov al, Tablica[si]
Tryby adresowania 10. Bazowy indeksowany (z przemieszczeniem) mov ax, [bx+di] mov ax, [bx][si] mov ax, [di][bp] ;operand w segmencie stosu mov ax, [bx+si+3] mov ax, [bx][si+3] mov al, [bx+si+tablica] mov al, Tablica[bx][di] mov ax, [di+bp-2] ;operand w segmencie stosu
Operacje na flagach CLC - Ustawia flagę CF na 0 przykład: clc CLD: Ustawia flagę DF na 0 przykład: cld CLI: Ustawia flagę IF na 0 przykład: cli CMC : Neguje zawartość flagi CF przykład: cmc STC: Ustawia flagę CF na 1 przykład: stc STD: Ustawia flagę DF na 1 przykład: std STI : Ustawia flagę IF na 1 przykład: sti CF (carry flag) - znacznik przeniesienia IF (interrupt flag) - znacznik przerwań DF (direction flag) - znacznik kierunku, wskazuje, czy zawartości rejestrów SI i DI mają być zwiększane (0) lub zmniejszane (1) o jeden w czasie wykonywania operacji łańcuchowych
Rozkazy arytmetyczne Rozkazy arytmetyczne mikroprocesora 8086 mogą wykonywać cztery podstawowe działania arytmetyczne: dodawanie, odejmowanie, mnożenie i dzielenie, operując na danych 8- i 16-bitowych. Każde z działań może operować na liczbach przedstawionych na trzy sposoby: niekodowanych - zwykła reprezentacja dwójkowa liczb (np. 10000000b), na liczbach kodowanych dziesiętnie (np. 128), liczbach zapisywanych w postaci HEX (np. 80h).
ADD, SUB ADD - dodawanie arytmetyczne dwóch liczb. ADD reg,reg - dodawanie zawartości dwóch rejestrów ADD reg,mem - dodawanie do zawartości rejestru danej z pamięci ADD mem,reg - dodawanie do danej w pamięci zawartości rejestru ADD reg,stała - dodawanie danej natychmiastowej do rejestru ADD mem,stała - dodawanie danej natychmiastowej do danej w pamięci SUB - odejmowanie arytmetyczne dwóch liczb. SUB reg,reg - odejmowanie zawartości dwóch rejestrów SUB reg,mem - odejmowanie od zawartości rejestru danej z pamięci SUB mem,reg - odejmowanie od danej w pamięci zawartości rejestru SUB reg,stała - odejmowanie danej natychmiastowej od rejestru SUB mem,stała - odejmowanie danej natychmiastowej od danej w pamięci
INC, DEC INC - zwiększenie bajtu lub słowa o 1 (inkrementacja). INC reg - inkrementacja rejestru INC mem - inkrementacja komórki w pamięci DEC - zmniejszenie bajtu lub słowa o 1 (dekremantacja). DEC reg - dekrementacja rejestru DEC mem - dekrementacja komórki w pamięci
AAA, AAS AAA - instrukcja AAA używana jest do korekty wyniku po dodaniu dwóch liczb w rozpakowanym kodzie BCD (generalnie 16- bitowych). AAA Przykład: mov bx,0207h mov ax,0405h add ax,bx aaa - korekcja ;teraz ax=060ch ;teraz ax=0702h AAS - instrukcja AAS używana jest do korekty wyniku po odjęciu dwóch liczb w rozpakowanym kodzie BCD (generalnie 16- bitowych).
MUL, IMUL MUL - mnożenie liczb bez znaku. Operacja 8-bitowa pobiera pierwszy argument z AL, natomiast operacja 16-bitowa pobiera argument z AX. Wynik jest przekazywany w pierwszym przypadku do AX, natomiast w drugim przypadku do pary rejestrów AX i DX. MUL reg MUL mem - mnożenie przez rejestr - mnożenie przez zawartość komórki pamięci IMUL - jak wyżej dla liczb ze znakiem. IMUL reg - mnożenie przez rejestr IMUL mem - mnożenie przez zawartość komórki pamięci
DIV, IDIV DIV - dzielenie liczb bez znaku. Operacja dzielenia przez dzielnik 8-bitowy pobiera pierwszy argument z AX, natomiast operacja dzielenia przez dzielnik 16-bitowy pobiera argument z pary rejestrów AX i DX (DX jest starszym słowem wyniku). DIV reg DIV mem - dzielenie przez daną z rejestru - dzielenie przez daną z komórki pamięci IDIV - jak wyżej dla liczb ze znakiem.
Rozkazy logiczne, przesunięcia, obroty Rozkazy logiczne obejmują standardowy zbiór operacji: negacji, sumy, iloczynu i różnicy symetrycznej. Przesunięcia i obroty występują w ośmiu wariantach. Mogą być jedno- i wielokrokowe. Wyniki przekazują w rejestrze lub pamięci. Wariant krokowy odwołuje się do rejestru CL, jako źródła licznika przesunięcia. NOT - negacja każdego bitu liczby. NOT reg - negacja danej w rejestrze NOT mem - negacja danej w pamięci
AND, TEST AND - Iloczyn logiczny argumentów. AND reg,reg - iloczyn logiczny zawartości dwóch rejestrów AND reg,mem - iloczyn logiczny zawartości rejestru i danej w pamięci AND mem,reg - iloczyn logiczny danej w pamięci i zawartości rejestru AND reg,stala - iloczyn logiczny zawartości rejestru i stałej AND mem,stała - iloczyn logiczny danej w pamięci i zawartości rejestru TEST - testowanie wybranych bitów argumentów. Instrukcja ta wykonuje logiczną operację AND na swoich argumentach. Wynik nie jest nigdzie zapamiętywany, ale na jego podstawie ustawiane są znaczniki. TEST reg,reg - testowanie zawartości dwóch rejestrów TEST reg,mem - testowanie zawartości rejestru z daną w pamięci TEST mem,reg - testowanie danej w pamięci z zawartością rejsetru TEST reg,stała - testowanie zawartości rejestru z daną natychmiastową TEST mem,stała - testowanie danej w pamięci z daną natychmiastową
OR, XOR OR - suma logiczna argumentów. OR reg,reg - suma logiczna zawartości dwóch rejestrów OR reg,mem - suma logiczna zawartości rejestru i danej w pamięci OR mem,reg - suma logiczna danej w pamięci zawartością rejestru OR reg,stała - suma logiczna zawartości rejestru i danej natychmiastowej OR mem,stała - suma logiczna danej w pamięci z daną natychmiastową XOR - różnica symetryczna argumentów (suma modulo 2). Składnia jak dla instrukcji OR.
SHL, SHR SHL - przesunięcie logiczne w lewo. SHL mem,1 - przesunięcie logiczne komórki pamięci o 1 w lewo SHL reg,1 - przesunięcie logiczne zawartości rejestru o 1 w lewo SHL mem,cl - przesunięcie logiczne komórki pamięci o zawartość CL w lewo SHL reg,cl - przesunięcie logiczne komórki rejestru o zawartość CL w lewo SHR - przesunięcie logiczne w prawo. Składnia i argumenty jak dla SHL.
SAL, SAR SAL - przesunięcie arytmetyczne w lewo. Operacja równoważna z mnożeniem przez 2. SAL mem,1 - przesunięcie arytm. komórki pamięci o 1 w lewo SAL reg,1 - przesunięcie arytm. zawartości rejestru o 1 w lewo SAL mem,cl - przesunięcie arytm. komórki pamięci o zawartość CL w lewo SAL reg,cl - przesunięcie arytm. komórki rejestru o zawartość CL w lewo SAR - przesunięcie arytmetyczne w prawo. Operacja równoważna z dzieleniem przez 2. (Zachowywany jest znak w przypadku U2) SAR mem/reg,1 - przesunięcie arytm. komórki pamięci/rejestru o 1 w prawo SAR mem/reg,cl - przesunięcie arytm. komórki pamięci/rejestru o zawartość CL w prawo
Obroty ROL - obrót w lewo. Składnia jak dla rozkazów SHL, SHR, SAL, SHR. ROR - obrót w prawo. Składnia jak dla rozkazów SHL, SHR, SAL, SHR. RCL - obrót w lewo z uwzględnieniem bitu Carry. Składnia jak dla rozkazów SHL, SHR, SAL, SHR. RCR - obrót w prawo z uwzględnieniem bitu Carry. Składnia jak dla rozkazów SHL, SHR, SAL, SHR. ROL RCL
Skoki Rozkazy skoków można podzielić na: skoki zwykłe i warunkowe, wywołania podprogramów, powroty z procedur, rozkazy tworzenia iteracji, przerwania programowe.
A i B to dwie dowolne wartości, które porównujemy. Wykonanie instrukcji CMP powoduje ustawienie odpowiednich flag procesora (OF, SF, ZF), a to właśnie na tej podstawie są wykonywane instrukcje skoku Skoki warunkowe JE/JZ JL/JNGE JLE/JNG JB/JNAE JBE/JNA JNE/JNZ JNL/JGE JNLE/JG JNB/JAE JNBE/JA Skok, gdy równy/zero Skok, gdy mniejszy/nie równy lub nie większy Skok, gdy mniejszy lub równy/nie większy Skok, gdy poniżej/nie powyżej lub nie równo Skok, gdy poniżej lub równo/nie powyżej Skok, gdy nie równy/nie zero Skok, gdy nie mniejszy/większy lub równy Skok, gdy nie mniejszy lub równy/większy Skok, gdy nie poniżej/powyżej lub równo Skok, gdy nie poniżej lub równo/powyżej Po rozkazie skoku należy podać miejsce w programie gdzie należy skoczyć (najczęściej jest to ETYKIETA) Powyższe rozkazy najczęściej występują po instrukcji porównania CMP CMP A, B (A rejestr/adres, B rejestr/adres/stała)
Inne skoki CALL JMP RET IRET Skok do podprogramu (wywołanie procedury) Skok bezwarunkowy Powrót z podprogramu (procedury) Powrót z procedury obsługi przerwania Procedury <nazwa_procedury> PROC ;wszystkie rejestry wykorzystywane w procedurze - na stos (push) ; TUTAJ KOD PROCEDURY ;wszystkie rejestry wykorzystywane w procedurze - zdjąć ze stosu (pop) RET <nazwa_procedury> ENDP WYWOŁANIE PROCEDURY: CALL <nazwa_procedury>
UWAGA: Rozkazy kontroli iteracji wykorzystują rejestr CX jako licznik iteracji. Należy zatem pamiętać, aby w pętlach (bo do tego głównie będą wykorzystywane tego rodzaju skoki) uważać z modyfikacją rejestru CX. Poza tym zasięg tych skoków to -128 do +127 bajtów. Etykieta, do której wykonywany jest skok powinna się znajdować na ogół powyżej instrukcji kontroli iteracji. Rozkazy kontroli iteracji LOOP - zmniejsza CX o 1 i wykonuje skok, gdy CX!= 0. LOOP nazwa_etykiety - skok do etykiety, gdy CX!= 0 LOOPZ - jak LOOP, tylko może wykonać dodatkowo skok, gdy bit zera jest ustawiony. LOOPNZ - jak LOOP, tylko może wykonać dodatkowo skok, gdy bit zera nie jest ustawiony. JCXZ - rozkaz skoku warunkowego, który jest wykonywany, gdy CX jest równy zero.
Rozkazy przerwań programowych INT - przerwanie programowe. INT numer_przerwania ;wywołanie przerwania o podanym numerze RET - powrót z podprogramu. RET IRET - powrót z podprogramu obsługi przerwania. IRET
Rozkazy wejścia/wyjścia OUT - wysłanie danej do portu, numer portu można podać bezpośrednio, (liczba 8 bitowa), lub skorzystać z rejestru dx, dane w akumulatorze (AX,AL) Przykład: out dx,al out 70h,al - wysłanie bajtu z al to portu dx - wysłanie zawartości al do portu 70h IN - odebranie danej z portu, numer portu można podać bezpośrednio, (liczba 8 bitowa), lub skorzystać z rejestru dx, dane trafiają do akumulatora Przykład: in al,71h in al,dx - odczytanie bajtu z portu 71h i umieszczenie go w al - odczytanie bajtu z portu dx i umieszczenie go w al
Konwencje adresowania danych wielobajtowych Istnieją dwa sposoby zapisu danej wielobajtowej w pamięci, zwane konwencjami adresowania danych. Nazwy konwencji pochodzą z powieści Podróże Guliwera, w której odnosiły się one do dwóch społeczności, różniących się zasadami jedzenia gotowanych jaj.
Little-Endian i Big-Endian
Little-Endian W konwencji Little-Endian adres bajtu odzwierciedla wagę bajtu w liczbie. Jest to konwencja naturalna dla komputera, chociaż dla człowieka na pierwszy rzut oka wydaje się dziwna jesteśmy przyzwyczajeni do zapisywania liczb począwszy od najbardziej znaczących cyfr. Istotną cechą tej konwencji jest to, że jeżeli zapiszemy w pamięci daną całkowitoliczbową w długim formacie (np. 64 bity), to przy dostępach do jej mniej znaczącej części o mniejszej długości (np. jednego, dwóch lub czterech bajtów) adres danej będzie w każdym przypadku ten sam. Jest to wygodne przy częstym rzutowaniu typów całkowitoliczbowych, co ma miejsce w programach pisanych w języku C.
Big-Endian Zapis Big-Endian, naturalny dla człowieka, jest zwykle mniej wygodny dla komputera. Dostęp do danej całkowitoliczbowej w pamięci wymaga zmiany wartości adresu w zależności od długości danej. Jeżeli liczba 32-bitowa zostanie zapisana pod adresem A, to jej wartość w postaci jednobajtowej ma adres A+3. Istotną zaletą konwencji Big-Endian jest możliwość szybkiego porównywania łańcuchów tekstowych przy użyciu instrukcji operujących na liczbach całkowitych o długości 32 lub 64 bitów. Wektor znaków (bajtów) interpretowany jako liczba ma w najbardziej znaczącym bajcie pierwszy bajt łańcucha. Zamiast więc prowadzić operację porównywania łańcuchów znak po znaku, można ją wykonać posługując się grupami znaków o długości słowa procesora
Big-Endian Zapis Big-Endian, naturalny dla człowieka, jest zwykle mniej wygodny dla komputera. Dostęp do danej całkowitoliczbowej w pamięci wymaga zmiany wartości adresu w zależności od długości danej. Jeżeli liczba 32-bitowa zostanie zapisana pod adresem A, to jej wartość w postaci jednobajtowej ma adres A+3. Istotną zaletą konwencji Big-Endian jest możliwość szybkiego porównywania łańcuchów tekstowych przy użyciu instrukcji operujących na liczbach całkowitych o długości 32 lub 64 bitów. Wektor znaków (bajtów) interpretowany jako liczba ma w najbardziej znaczącym bajcie pierwszy bajt łańcucha. Zamiast więc prowadzić operację porównywania łańcuchów znak po znaku, można ją wykonać posługując się grupami znaków o długości słowa procesora
Dyrektywa ptr Dyrektywa ptr mówi procesorowi o tym jak ma traktować wskazaną komórkę pamięci byte ptr jako bajt 8 bit word ptr jako słowo 16 bit dword ptr jako podwójne słowo 32 bit Przykłady: Pamiętać o little endian! Młodszy bajt liczby jest umieszczony pod adresem o mniejszej wartości add al, byte ptr [ds:bx] mov byte ptr [si], 3 mov ax, word ptr [es:bx]