Podstawy języka assembler, cz.3
Jak pisać programy w języku assembler?
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 wszyskich 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 (shift i rotate) i bajtach (np. bt, setcc)
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.
Są to: "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 - z_czym", ale nie zachowuje
wyniku, tylko ustawia flagi.
Wynikiem może być ustawienie 1 lub więcej flag - zaznaczenie wystąpienia jednego
z warunków. Główne warunki to:
A - above. (bez znaku)co > z_czym
cmp al,bl
ja al_wieksze_od_bl ; ja - jump if above
B - below. (bez znaku)co < z_czym
G - greater. (ze znakiem) co > z_czym
L - lower. (ze znakiem) co < z_czym
O - overflow. przepełnienie ostatniej operacji. niekoniecznie używane przy cmp.
C - carry. przepełnienie bez znaku
add al,bl
jc blad_przepelnienia ; jc - jump if carry
E lub Z - equal (równy) lub zero.
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
NB - not below
NC - no carry
AE/BE - above or equal, below or equal
NO - no overflow
arytmetyka dziesiętna
NEG - zmienia znak.
MUL, IMUL - mnożenie, mnożenie ze znakiem (tj. uwzględnia liczby ujemne)
mul cl ; AX := AL*CL
mul bx ; DX:AX := AX*BX
mul esi ; EDX:EAX := EAX*ESI
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.
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)
Instrukcje logiczne. AND, OR, XOR, NOT, TEST. TEST działa tak samo jak AND z tym, że nie
zachowuje nigdzie wyniku, tylko ustawia flagi. Po krótce wytłumaczę te instrukcje:
0 AND 0 = 0 0 OR 0 = 0 0 XOR 0 = 0 NOT 0 = 1
0 AND 1 = 0 0 OR 1 = 1 0 XOR 1 = 1 NOT 1 = 0
1 AND 0 = 0 1 OR 0 = 1 1 XOR 0 = 1
1 AND 1 = 1 1 OR 1 = 1 1 XOR 1 = 0
Przykłady zastosowania:
and ax,1 ; wyzeruje wszystkie bity z wyjątkiem bitu nr 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.
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 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: 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 (jeśli overflow, to INT4), BOUND (int 5)
Instrukcje pętli: LOOP. Składnia: "LOOP gdzies". Jeśli CX jest różny od 0, to skacz "gdzies".
Operacje na łańcuchach znaków.
LODS[B/W/D] - Load Byte/Word/Dword
MOV AL/AX/EAX , DS:[SI]
ADD SI,1/2/4 ; ADD, gdy flaga kierunku DF = 0, SUB gdy DF = 1
STOS[B/W/D] - Store Byte/Word/Dword
MOV ES:[DI], AL/AX/EAX
ADD DI,1/2/4 ; ADD/SUB jw.
MOVS[B/W/D] - Move Byte/Word/Dword
MOV ES:[DI], DS:[SI] ; to nie jest instrukcja!
ADD DI,1/2/4 ; ADD/SUB jw.
ADD SI,1/2/4
CMPS[B/W/D] - Compare Byte/Word/Dword
CMP DS:[SI], ES:[DI] ; to nie jest instrukcja!
ADD SI,1/2/4 ; ADD/SUB jw.
ADD DI,1/2/4
SCAS[B/W/D] - Scan Byte/Word/Dword
skanuje łańcuch bajtów/słów/podwójnych słów pod ES:[DI] w poszukiwaniu, czy jest tam
wartość wskazana przez AL/AX/EAX
Do każdej z tych instrukcji można z przodu dodać predrostek "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 gdy ZF (zero flag) =1 lub =0,
odpowiednio.
Instrukcje wejścia/wyjścia do portów.
IN
IN AL, port/DX.
Pobierz z portu bajt i włóż do AL. Jeśli numer portu < 255, można podać bezpośrednio.
Jeśli większy - trzeba użyć DX.
IN AX, port/DX
Jest równoważne:
IN AL, port/DX
IN AH, port+1/DX+1
IN EAX, port/DX - pobierz 4 bajty - 1 z "port" do AL, 2 z "port+1" do AH, 3 z "port+2" do
bitów 23-16 EAX, 4 z "port+3" do bitów 31-24 EAX.
OUT
OUT port/DX, AL/AX/EAX.
Uwagi jak przy "IN".
Instrukcje flag
STC/CLC - set carry / clear carry. CF := 1 i CF := 0, odpowiednio.
STD/CLD. 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" - umieść flagi na stosie (16 i 32 bity flag, odpowiednio)
"popf/popfd" - zdejmij flagi ze stosu (16/32bity 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:
mov rej, offset pamięć ; TASM
mov rej, pamięć ; NASM
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 ?
Tak: 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?
Tak: lea ecx, [ebx + 11*8 ] oraz lea edx,[ebx+edi*8]
Jak w 1 instrukcji sprawić, że ESI = EAX*9?
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:
Intel:
http://developer.intel.com/design/Pentium4/documentation.htm
AMD nie jest gorsze:
http://www.amd.com/us-en/Processors/DevelopWithAMD/0,,30_2252_739_7044,00.html
"Byle głupiec potrafi napisać kod, który zrozumie komputer. Dobry programista pisze taki kod,
który zrozumie człowiek."
Ćwiczenia:
Zapisz instrukcje: do rejestru AX dodaj 5, o rejestru SI odejmij 178.
Nie używając cyfry "1" napisz jedną instrukcję, która zmniejszy rejestr DX o 1.
Przemnóż wartość rejestru EDI przez 2 na przynajmniej dwa różne sposoby po 1 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 1 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 KURS03A KURS03kurs0304więcej podobnych podstron