Asembler: DOS, część 3 - Instrukcje
Jak pisać programy w języku asembler?
Część 3 - Podstawowe instrukcje, czyli poznajemy dialekt procesora.
Poznaliśmy już rejestry, omówiliśmy pamięć. Pora zacząć na nich operować. Zanim zaczniemy,
proszę Was o to, abyście tej listy też NIE uczyli się na pamięć.
Instrukcji jest dużo, a próba zrozumienia ich wszystkich na raz może spowodować niezły chaos. Co najwyżej
przejrzyjcie tą listę kilka razy, aby wiedzieć mniej-więcej, co każda instrukcja robi.
Instrukcje procesora można podzielić na kilka grup:
instrukcje przemieszczania danych
instrukcje arytmetyki binarnej
instrukcje arytmetyki dziesiętnej
instrukcje logiczne
operacje na bitach i bajtach
instrukcje przekazujące kontrolę innej części programu (sterujące wykonywaniem programu)
instrukcje operujące na łańcuchach znaków
instrukcje kontroli flag
instrukcje rejestrów segmentowych
inne
Zacznijmy je po kolei omawiać (nie omówię wszystkich).
instrukcje przemieszczania danych.
Tutaj zaliczymy już wielokrotnie używane MOV oraz kilka innych:
XCHG , PUSH i POP.
arytmetyka binarna.
add do_czego,co - dodaj
sub od_czego,co - odejmij
inc coś / dec coś - zwiększ/zmniejsz coś o 1
cmp co, z_czym - porównaj. Wykonuje działanie odejmowania co minus z_czym,
ale nie zachowuje wyniku, tylko ustawia flagi.
Wynikiem może być ustawienie lub wyzerowanie jednej lub więcej flag - zaznaczenie
wystąpienia jednego z warunków. Główne warunki to:
A - above - ponad (dla liczb traktowanych jako liczby bez
znaku): co > z_czym
(przeskocz przykład użycia warunku A)
cmp al,bl
ja al_wieksze_od_bl ; ja - jump if above
B - below - poniżej (bez znaku): co < z_czym
G - greater - więcej niż (ze znakiem): co > z_czym
L - lower - mniej niż (ze znakiem): co < z_czym
O - overflow - przepełnienie (ze znakiem, np. przebicie 32767 w górę)
ostatniej operacji. Niekoniecznie używane przy cmp.
C - carry - przepełnienie (bez znaku, czyli przebicie np. 65535 w górę)
(przeskocz przykład użycia warunku C)
add al,bl
jc blad_przepelnienia ; jc - jump if carry
E lub Z - equal (równy) lub zero. Te 2 warunki są równoważne.
(przeskocz przykłady użycia warunków równości)
cmp ax,cx
je ax_rowne_cx
...
sub bx,dx
jz bx_rowne_dx
NE/NZ - przeciwieństwo poprzedniego: not equal/not zero.
NA - not above, czyli nie ponad - mniejsze lub równe (ale dla liczb bez znaku)
NB - not below, czyli nie poniżej - większe lub równe (dla liczb bez znaku)
NG - not greater, czyli nie więcej - mniejsze lub równe (ale dla liczb ze znakiem)
NL - not lower, czyli nie mniej - większe lub równe (dla liczb ze znakiem)
NC - no carry
AE/BE - above or equal (ponad lub równe),
below or equal (poniżej lub równe)
NO - no overflow
arytmetyka dziesiętna
NEG - zmienia znak.
MUL, IMUL - mnożenie, mnożenie ze znakiem (czyli uwzględnia liczby ujemne)
(przeskocz przykłady instrukcji mnożenia)
mul cl ; AX = AL*CL
mul bx ; DX:AX = AX*BX
mul esi ; EDX:EAX = EAX*ESI
mul rdi ; RDX:RAX = RAX*RDI
imul eax ; EDX:EAX = EAX*EAX
imul ebx,ecx,2 ; EBX = ECX*2
imul ebx,ecx ; EBX = EBX*ECX
imul si,5 ; SI = SI*5
DIV, IDIV - dzielenie, dzielenie ze znakiem.
(przeskocz przykłady instrukcji dzielenia)
div cl ; AL = (AX div CL), AH = (AX mod CL)
div bx ; AX = (DX:AX div BX),
; DX = (DX:AX mod BX)
div edi ; EAX = (EDX:EAX div EDI),
; EDX = (EDX:EAX mod EDI)
div rsi ; RAX = (RDX:RAX div RSI),
; RDX = (RDX:RAX mod RSI)
Instrukcje bitowe (logiczne).
AND, OR, XOR, NOT, TEST.
Instrukcja TEST działa tak samo jak AND z tym, że nie
zachowuje nigdzie wyniku, tylko ustawia flagi. Po krótce wytłumaczę te instrukcje:
(przeskocz działanie instrukcji logicznych)
0 AND 0 = 0 0 OR 0 = 0 0 XOR 0 = 0
0 AND 1 = 0 0 OR 1 = 1 0 XOR 1 = 1
1 AND 0 = 0 1 OR 0 = 1 1 XOR 0 = 1
1 AND 1 = 1 1 OR 1 = 1 1 XOR 1 = 0
NOT 0 = 1
NOT 1 = 0
Przykłady zastosowania:
(przeskocz przykłady instrukcji logicznych)
and ax,1 ; wyzeruje wszystkie bity z
; wyjątkiem bitu numer 0.
or ebx,1111b ; ustawia (włącza) 4 dolne bity.
; Reszta bez zmian.
xor cx,cx ; CX = 0
not dh ; DH ma 0 tam, gdzie miał 1
; i na odwrót
Instrukcje przesunięcia bitów.
SAL, SHL - shift left.
bit7 = bit6, bit6 = bit5, ... , bit1 = bit0, bit0 = 0.
SHR - shift logical right
bit0 = bit1, bit1 = bit2, ... , bit6 = bit7, bit7 = 0
SAR - shift arithmetic right
bit0 = bit1, bit1 = bit2, ... , bit6 = bit7, bit7 = bit7
(bit znaku zachowany!) Najstarszy bit w rejestrze nazywa się czasem
właśnie bitem znaku.
ROL - rotate left
bit7 = bit6, ... , bit1 = bit0, bit0 = stary bit7
RCL - rotate through carry left
carry flag CF = bit7, bit7 = bit6, ... , bit1 = bit0, bit0 = stara CF
ROR - rotate right
bit0 = bit1, ... , bit6 = bit7, bit7 = stary bit0
RCR - rotate through carry right
CF = bit0, bit0 = bit1, ... , bit6 = bit7, bit7 = stara CF
Przy użyciu SHL można przeprowadzać szybkie mnożenie, a dzięki SHR - szybkie dzielenie.
Np. SHL AX,1 jest równoważne przemnożeniu AX przez 2, SHL AX,5 - przez 2^5
= 32. SHR BX,4 dzieli bx przez 16.
Instrukcje sterujące wykonywaniem programu.
Skoki warunkowe (patrz: warunki powyżej): JA=JNBE, JAE=JNB, JNA=JBE, JNAE=JB , JG=JNLE
(jump if greater - dla liczb
ze znakiem) = jump if not lower or equal, JNG=JLE, JGE=JNL, JNGE=JL,
JO, JNO, JC, JNC,
JS (jump if sign czyli bit7 wyniku jest równy 1), JNS, JP=JPE
(jump if parity equal =
liczba bitów równych jeden jest parzysta), JNP=JPO.
Skoki bezwarunkowe: JMP, JMP SHORT, JMP FAR
Uruchomienia procedur: CALL [NEAR/FAR]
Powrót z procedury: RET/RETF.
Przerwania: INT, INTO (wywołuje przerwanie INT4 w razie przepełnienia), BOUND (int 5)
Instrukcje pętli: LOOP. Składnia: LOOP gdzieś.
Jeśli CX jest różny od 0, to skacz do gdzieś.
Operacje na łańcuchach znaków.
LODS[B/W/D/Q] - Load Byte/Word/Dword/Qword
MOV AL/AX/EAX/RAX , DS:[SI/ESI/RSI]
ADD SI,1/2/4/8 ; ADD, gdy flaga kierunku DF = 0, SUB gdy DF = 1
STOS[B/W/D/Q] - Store Byte/Word/Dword/Qword
MOV ES:[DI/EDI/RDI], AL/AX/EAX/RAX
ADD DI,1/2/4/8 ; ADD/SUB jak wyżej
MOVS[B/W/D/Q] - Move Byte/Word/Dword/Qword
MOV ES:[DI/EDI/RDI], DS:[SI/ESI/RSI] ; to nie jest instrukcja!
ADD DI,1/2/4/8 ; ADD/SUB jak wyżej
ADD SI,1/2/4/8
CMPS[B/W/D/Q] - Compare Byte/Word/Dword/Qword
CMP DS:[SI/ESI/RSI], ES:[DI/EDI/RDI] ; to nie jest instrukcja!
ADD SI,1/2/4/8 ; ADD/SUB jak wyżej
ADD DI,1/2/4/8
SCAS[B/W/D/Q] - Scan Byte/Word/Dword/Qword
skanuje łańcuch bajtów/słów/podwójnych słów/poczwórnych słów
pod ES:[DI/EDI/RDI] w poszukiwaniu, czy jest tam
wartość wskazana przez AL/AX/EAX/RAX.
Do każdej z powyższych instrukcji można z przodu dodać przedrostek REP
(repeat), co spowoduje,
że będzie ona wykonywana, aż CX stanie się zerem, lub REPE/REPZ lub REPNE/REPNZ co spowoduje,
że będzie ona wykonywana, dopóty CX nie jest zerem i jednocześnie ZF
(zero flag) =1 lub =0, odpowiednio.
Instrukcje wejścia/wyjścia do portów.
IN
IN AL/AX/EAX, port/DX.
Pobierz z portu 1/2/4 bajty i włóż do AL/AX/EAX (od najmłodszego). Jeśli numer portu jest
mniejszy lub równy 255, można go
podać bezpośrednio. Jeśli większy - trzeba użyć DX.
OUT
OUT port/DX, AL/AX/EAX.
Uwagi jak przy instrukcji IN.
Instrukcje flag
STC/CLC - set carry / clear carry. Do flagi CF wstaw 1 lub 0, odpowiednio.
STD/CLD. Ustaw DF = 1, DF = 0, odpowiednio.
STI/CLI. Interrupt Flag IF := 1, IF := 0, odpowiednio.
Gdy IF=0, przerwania sprzętowe są blokowane.
Przenoszenie flag
PUSHF / PUSHFD / PUSHFQ
- umieść flagi na stosie (16, 32 i 64 bity flag, odpowiednio)
POPF / POPFD / POPFQ - zdejmij flagi ze stosu (16/32/64 bity flag)
SAHF / LAHF - zapisz AH w pierwszych 8 bitach flag / zapisz pierwsze 8 bitów flag w AH.
Instrukcja LEA - Load Effective Address.
Wykonanie:
lea rej, [pamięć]
jest równoważne:
(przeskocz pseudo-kod LEA)
mov rej, offset pamięć ; TASM
mov rej, pamięć ; NASM/FASM
Po co więc osobna instrukcja? Otóż, LEA przydaje sie w wielu
sytuacjach do obliczania złożonych adresów. Kilka przykładów:
Jak w 1 instrukcji sprawić, że EAX = EBP-12 ?
Odpowiedź: lea eax, [ebp-12]
Niech EBX wskazuje na tablicę o 20 elementach o rozmiarze 8 każdy. Jak do ECX
zapisać adres 11-tego elementu, a do EDX elementu o numerze EDI?
Odpowiedź: lea ecx, [ebx + 11*8 ] oraz lea edx,[ebx+edi*8]
Jak w 1 instrukcji sprawić, że ESI = EAX*9?
Odpowiedź: lea esi, [eax + eax*8 ]
Pominąłem mniej ważne instrukcje operujące na rejestrach segmentowych i klika innych instrukcji.
Te, które tu podałem, wystarczają absolutnie na napisanie większości programów, które można
zrobić.
Wszystkie informacje przedstawione w tej części pochodzą z tego samego źródła:
podręczniki Intela i
podręczniki AMD
Byle głupiec potrafi napisać kod, który zrozumie komputer. Dobry programista pisze taki kod,
który zrozumie człowiek.
Poprzednia część kursu (Alt+3)
Kolejna część kursu (Alt+4)
Spis treści off-line (Alt+1)
Spis treści on-line (Alt+2)
Ułatwienia dla niepełnosprawnych (Alt+0)
Ćwiczenia:
Zapisz instrukcje: do rejestru AX dodaj 5, o rejestru SI odejmij 178.
Nie używając cyfry jeden napisz jedną instrukcję, która zmniejszy rejestr DX o jeden.
Przemnóż wartość rejestru EDI przez 2 na przynajmniej dwa różne sposoby po jednej instrukcji.
Postaraj się nie używać instrukcji (I)MUL.
W jednej instrukcji podziel wartość rejestru BP przez 8.
Nie używając instrukcji MOV spraw, by DX miał wartość 0 (na przynajmniej 3 sposoby, każdy
po jednej instrukcji).
Nie używając instrukcji przesuwania bitów SH* ani mnożenia *MUL przemnóż EBX przez 8. Możesz
użyć więcej niż 1 instrukcji.
W dwóch instrukcjach spraw, by EDI równał się 7*ECX. Postaraj się nie używać instrukcji (I)MUL.
Wyszukiwarka
Podobne podstrony:
a kurs03DOS A KURS03KURS03A KURS03kurs0304A KURS03więcej podobnych podstron