Typ
operandów
Instrukcje
mogą przyjmować zero, jeden, dwa lub trzy operandy.
Wyróżnia
się trzy podstawowe typy operandów
- Wartości stałe – wartość ustalona przed tłumaczeniem programu
- Rejestr – zawartość jednego z rejestrów procesora
- Adres – odwołuje się do jakiegoś miejsca w pamięci
Instrukcja MOV
Instrukcja
MOV kopiuje dane ze źródła do miejsca docelowego. Format tej instrukcji wygląda
następująco:
MOV
cel, źródło
W
językach wysokiego poziomu wyglądałoby to w taki sposób:
Cel
= źródło
Instrukcja
MOV stawia pewne wymagania
Oba
operandy muszą być tego samego rodzaju
Oba
operandy nie mogą być jednocześnie adresem w pamięci
Rejestr
wskaźnika instrukcji nie może być rejestrem docelowym
Dozwolone
kombinacje można zapisać w następujący sposób:
MOV rejestr, rejestr
MOV pamięć, rejestr
MOV rejestr, pamięć
MOV pamięć, stała
MOV rejestr, stała
|
Pojedyncza
instrukcja mov nie może zostać użyta do przeniesienia danych spod jednego
adresu do drugiego. Kod, który wykona to działanie wygląda tak:
.data
zmienna1 WORD ?
zmienna2 WORD ?
.code
main PROC
mov ax, zmienna1
mov zmienna2, ax
|
Kopiowanie mniejszej wartości do większej
Instrukcja
MOV nie może bezpośrednio skopiować mniejszego operandu do większeg, jednak
istnieje możliwość obejścia tego. Stwórzmy zmienna licznik(bez znaku, 16-bitowa), która musi zostać przeniesiona do
rejestru ECX (32-bitowy). Możemy
przenieść do ECX zero, w ten sposób
kasując jego zawartość i wtedy skopiować do rejestru CX zmienną licznik.
.data
licznik WORD 1
.code
mov ecx, 0
mov cx, count
|
A
co się stanie gdy w podobny sposób spróbujemy skopiować liczbe ze znakiem?
.data
zeZnakiem SWORD -16 ; FFF0h(-16)
.code
mov ecx, 0
mov cx, zeZnakiem ; ECX = 0000FFF0h (+65520)
|
Jak
widać wartość zmiennej została skopiowana w dosłowny sposób, bez zachowania
kodowania U2, przez co w rejestrze ECX znajduje się liczba dodatnia.
Instrukcja MOVZX
MOVZX
(move with zero-extend) kopiuje zawartość operanda źródłowego do operanda
docelowego I wypełnia zerami rejestr do wartości 16 lub 32 bitowej. Tej
instrukcji używa się tylko podczas kopiowania liczb bez znaku.
Format
instrukcji
MOVZX reg32, reg/mem8
MOVZX reg32, reg/mem16
MOVZX reg16, reg/mem8
|
Przykład:
.data
bajt BYTE 10001111b
.code
movzx ax, bajt
|
Przed
wywołaniem instrukcji skopiowania do rejestru, może w nim znajdować się dowolna
liczba.
AX
|
|
10110011
|
11101111
|
Skopiowanie
z wypełnieniem zerami, kopiuje mniejszą wartość do dolnej części rejestru a
górną wypełnia zerami.
AX
|
|
00000000
|
10001111
|
Instrukcja MOVSX
MOVSX
(move with sign-extend) kopiuje zawartość operandu źródłowego do miejsca
docelowego i zachowuje odpowiednie kodowanie dla wypełnianych bitów. Tej
instrukcji używa się tylko podczas kopiowania liczb ze znakiem.
Format
instrukcji
MOVSX reg32, reg/mem8
MOVSX reg32, reg/mem16
MOVSX reg16, reg/mem8
|
Przykład:
.data
bajt BYTE 10001111b
.code
movsx ax, bajt
|
Przed
wywołaniem instrukcji skopiowania do rejestru, może w nim znajdować się dowolna
liczba.
AX
|
|
10110011
|
11101111
|
MOVSX
zachowuje odpowiednie kodowanie liczb ujemnych podczas kopiowania.
AX
|
|
11111111
|
10001111
|
Instrukcje LAHF I SAHF
LAHF
(Load status flags into AH) kopiuje dolny bajt rejestru EFLAGS do rejestru AH.
Kopiowane są następujące flagi: Sign, Zero, Auxilliary Carry, Parity i Carry.
Poniższy
kod kopiuje dolny bajt rejestru EFLAGS do rejestru AH a następnie kopiuje
zawartość rejestru AH do zmiennej kopiaFlag.
.data
kopiaFlag BYTE ?
.code
lahf
mov kopiaFlag, ah
|
SAHF
(store AH into status flags) kopiuje zawartość rejestru AH do dolnego bajtu
rejestru EFLAGS.
mov ah, kopiaFlag
sahf
|
Wyświetlenie rejestru flag podczas debugowania
Żeby
w Visual Studio podczas debugowania programu wyświetlić zawartość rejestru flag
należy w oknie Registers kliknąc prawym przyciskiem myszy i wybrać opcję flags.
Instrukcja XCHG
XCHG
(exchange data) zamienia zawartość dwóch operandów. Można użyć jej w trzech
wariantach:
XCHG reg, reg
XCHG reg, mem
XCHG mem, reg
|
Zasady
dotyczące używania instrukcji XCHG jest taka sama jak w przypadku instrukcji
MOV.
Operator przesunięcia
Jak
już wcześniej pisałem nazwa jakiejś zmiennej reprezentuje jej adres w pamięci.
W przypadku tablicy nazwa jest adresem pierwszego elementu, a kolejne elementy
ułozone są po kolei za pierwszym. W związku z tym można się do nich dostać
korzystając z adresu pierwszego elementu dodając do niego przesunięcie będące
wielokrotnością rozmiaru elementu.
Kod
|
Wynik
|
.386
.model flat, stdcall
.stack 4096
include kernel32.inc
includelib kernel32.lib
.data
tablicaB BYTE
10h, 20h, 30h, 40h, 50h
.code
main PROC
mov eax, 0
mov al, tablicaB
mov al, [tablicaB+1]
mov al, [tablicaB+2]
mov al, [tablicaB+3]
mov al, [tablicaB+4]
invoke ExitProcess, 0
main ENDP
END main
|
EAX
= 0
EAX
= 10h
EAX
= 20h
EAX
= 30h
EAX
= 40h
|
Wyrażenie
[tablicaB+1] dodaje do adresu tablicaB jeden bajt, a umieszczenie tego w
nawiasach [ ] mówi asemblerowi, żeby pobrał wartość elementu, który znajduje
się pod tym adresem.
MASM
nie sprawdza czy nie wyszliśmy poza zakres tablicy, więc zapisując
[tablicaB+20] odczytam wartość znajdującą się 20 bajtów poza pierwszym adresem
w tej tablicy.
W
przypadku gdyby tablica przechowywała elementy typu WORD należy przesuwać jej
indeks o 2 bajty, ponieważ jest to rozmiar każdego z elementów.
.data
tablicaW WORD
10h, 20h, 30h, 40h, 50h
.code
main PROC
mov eax, 0
mov ax, tablicaW
mov ax, [tablicaW+2]
mov ax, [tablicaW+4]
mov ax, [tablicaW+6]
mov ax, [tablicaW+8]
|
Gdyby
tablica przechowywała dane typu DWORD należałoby przesuwać indeks o 4 bajty,
jest to rozmiar każdego z elementów.
Przykładowy program
Kod
przykładowego programu pokazującego działanie ostatnio wspomnianych instrukcji
.386
.model flat, stdcall
.stack 4096
include kernel32.inc
includelib kernel32.lib
.data
zmienna1 WORD
1000h
zmienna2 WORD
2000h
tablicaB BYTE
10h, 20h, 30h, 40h, 50h
tablicaW WORD
100h, 200h, 300h
tablicaD DWORD
10000h, 20000h
.code
main PROC
mov eax, 0
mov ebx, 0
mov ecx, 0
mov edx, 0
mov bx, 0A69Bh
movzx eax, bx
movzx edx, bl
movzx cx, bl
mov bx, 0A69Bh
movsx eax, bx
movsx edx, bl
mov bl, 07Bh
movsx cx, bl
mov ax, zmienna1
xchg ax, zmienna2
mov zmienna1, ax
mov al, tablicaB
mov al, [tablicaB+1]
mov al, [tablicaB+2]
mov ax, tablicaW
mov ax, [tablicaW+2]
mov eax, tablicaD
mov eax, [tablicaD+4]
invoke ExitProcess, 0
main ENDP
END main
|
Programowanie w Assemblerze jak dla mnie jest całkowicie ciemną magią. Wiem, że jest to język programowania niskiego poziomu i za jego pomocą możemy np. sterować pracą procesora. Mimo wszystko preferuję kodowanie jak robią to ludzie z https://craftware.pl czyli programowanie przydatnych aplikacji webowych lub stacjonarnych .
OdpowiedzUsuńFaktycznie takie rzeczy to jest już programowanie i ja muszę przyznać, że raczej nie znam się na tym. Jednak bardzo chwalę sobie korzystanie z systemów informatycznych od https://www.connecto.pl/ i muszę powiedzieć, że to dzięki nim wiem jak dobrze prowadzić firmę.
OdpowiedzUsuń