dareks_

  • Dokumenty2 821
  • Odsłony754 023
  • Obserwuję432
  • Rozmiar dokumentów32.8 GB
  • Ilość pobrań362 067

Farbaniec D. - Asembler. Leksykon kieszonkowy

Dodano: 6 lata temu

Informacje o dokumencie

Dodano: 6 lata temu
Rozmiar :9.6 MB
Rozszerzenie:pdf

Farbaniec D. - Asembler. Leksykon kieszonkowy.pdf

dareks_ EBooki Infornatyka
Użytkownik dareks_ wgrał ten materiał 6 lata temu.

Komentarze i opinie (0)

Transkrypt ( 25 z dostępnych 206 stron)

551. £ Poznaj Asemblera i dołącz do elity programistów D ow iedz się, jak jest zbudowany i jak działa procesor Poznaj język Asem bler i narzędzia u m ożliw iające tw orzenie w nim program ów N aucz się pisać w ydajny kod działający w system ach D O S i W indow s D A W ID F A R B A N IE C

Spis treści 1. Podstawowe informacje 7 1.1. Słowem wstępu 7 1.2. Architektura x86 — podstawowe informacje 10 1.3. Budowa programu dla podsystemu DOS (16-bitowego) 13 1.4. Budowa programu dla systemu Windows (32-bitowego) 15 2. Podstawowe instrukcje procesora 18 2.1. Instrukcja kopiowania i instrukcje arytmetyczne 18 2.2. Instrukcje logiczne i przesunięć bitowych 20 2.3. Instrukcje wywołania procedury i powrotu 26 2.4. Instrukcja porównania i instrukcje skoku 29 2.5. Instrukcje do operacji na łańcuchach znaków 30 3. Makroinstrukcje 35 3.1. Makroinstrukcje kontroli przepływu 35 3.2. Makroinstrukcje do tworzenia pętli 36 4. Praca z danymi 39 4.1. Adresowanie i wskaźniki 39 4.2. Zmienne i stałe 41 4.3. Praca ze strukturami 42 5. Programowanie w systemie Windows 45 5.1. Konsola w systemie Windows 45 5.2. Proste okno dialogowe 47 5.3. Odczyt myszki i klawiatury 51 5.4. Operacje na plikach i alokacja bloków pamięci 52 5.5. Tworzenie menu 55 5.6. Kontrolka przycisku (Button) 56 5.7. Kontrolka pola tekstowego (Edit) 57 5.8. Kontrolka wielokrotnego wyboru (CheckBox) 60 3

5.9. Kontrolka pojedynczego wyboru (RadioButton) 62 5.10. Kontrolka listy rozwijanej (ComboBox) 62 5.11. Kontrolka listy (ListBox) 64 5.12. Kontrolka paska postępu (ProgressBar) 65 5.13. Kontrolka widoku drzewa (TreeView) 66 5.14. Kontrolka widoku listy (ListView) 68 5.15. Kontrolka suwaka (TrackBar) 70 5.16. Kontrolka podpowiedzi (Tooltip) 71 5.17. Kontrolka do wprowadzania adresu IP (IPAddress) 78 5.18. Kontrolka tekstu o bogatym formatowaniu (RichEdit) 83 5.19. Kontrolki w nowym stylu (pliki .manifest) 85 5.20. Podpięcia do systemu Windows (Hooks) 87 5.21. Ekran powitalny (Splashscreen) 89 5.22. Ikona w zasobniku systemowym (Tray) 91 5.23. Podmiana procedury obsługi okna 93 5.24. Programowanie aplikacji wielowątkowych 95 5.25. Tworzenie bibliotek DLL 97 5.26. Pliki odwzorowane w pamięci 99 5.27. Pobieranie adresu IP lokalnego komputera 99 6. Dodatkowe zestawy instrukcji 102 6.1. Korzystanie z instrukcji koprocesora (FPU) 102 6.2. Korzystanie z rozszerzeń MMX i SSE 110 6.3. Nowe rozszerzenie —Advanced Vector Extensions (AVX) 118 7. Asembler 64-bitowy (x86-64) 120 7.1. Wstęp do Asemblera dla architektury 64-bitowej 120 8. Opis rozkazów procesorów z rodziny 80x86 125 8.1. Instrukcje na literę A 125 8.2. Instrukcje na literę B 128 8.3. Instrukcje na literę C 132 8.4. Instrukcje na literę D 137 8.5. Instrukcje na literę E 139 8.6. Instrukcje na literę H 139 8.7. Instrukcje na literę I 140 8.8. Instrukcje na literę J 143 8.9. Instrukcje na literę L 144 8.10. Instrukcje na literę M 148 8.11. Instrukcje na literę N 151 4 | Asembler. Leksykon k eszonkowy

8.12. Instrukcje na literę O 152 8.13. Instrukcje na literę P 154 8.14. Instrukcje na literę R 156 8.15. Instrukcje na literę S 160 8.16. Instrukcje na literę T 168 8.17. Instrukcje na literę V 169 8.18. Instrukcje na literę W 169 8.19. Instrukcje na literę X 170 9. Dyrektywy asemblera MASM 172 9.1. Etykiety kodu 172 9.2. Warunkowa kontrola przepływu 173 9.3. Alokacja danych 175 9.4. Przyrównania 177 9.5. Makra 177 9.6. Procedury 178 9.7. Rodzaj zestawu instrukcji procesora 179 9.8. Bloki powtórzeń 182 9.9. Zakres 183 9.10. Segmenty 184 9.11. Uproszczone segmenty 185 9.12. Łańcuchy znaków 187 9.13. Struktury i rekordy 188 9.14. Różne 189 Dodatki 192 A. Tablica kodów ASCII 192 B. Potęgi liczby dwa 193 C. Lista opkodów procesora 80x86 196 Skorowidz 202 Sp s treśc | 5

6 j Asembler. Leksykon k eszonkowy

Rozdział 1. Podstawowe informacje 1.1. Słowem wstępu Witaj! Zapraszam do lektury leksykonu języka Asembler. Niektórzy z pewnością się zastanawiają, dlaczego warto poznać właśnie Asembler. Mówi się, że programy pisane w tym języku są znane z tego, iż są małe i szybkie. I rzeczywiście takie właśnie są, a to dlatego, że mają taki kod, jaki my sami stworzymy, a nie taki, jaki zostanie przetłu­ maczony przez kompilator. Co ważne, nie posiadają również zbędnych bibliotek. Na jednym z wykładów z programowania niskopoziomowe- go usłyszałem kiedyś istotną uwagę: programista piszący w językach wysokiego poziomu żyje w swoim własnym świecie i nie ma pojęcia, co się tak naprawdę dzieje w środku. Dzięki programowaniu w Asemblerze możemy zrozumieć, jak działa procesor czy jak jest zbudowany pro­ gram, i nauczyć się wykorzystywać jeszcze inne aspekty niskopo- ziomowego programowania, o których programista zajmujący się je­ dynie pisaniem na przykład w Javie nie ma w ogóle pojęcia. Dlatego każdy szanujący się programista powinien poznać przynajmniej pod­ stawy Asemblera. Podstawowe pojęcia Asembler (wielką literą) — niskopoziomowy język programowania. asembler (małą literą) — program przeprowadzający proces asem- blacji, po którym otrzymujemy plik *.obj. Konsolidator (ang. linker) — program, który po przeprowadzeniu wcześniejszej asemblacji dokonuje konsolidacji, czyli łączenia. W wyni­ ku jego działania otrzymujemy plik wykonywalny *.exe. Odpluskwiacz (ang. debugger) — program do wyszukiwania błędów w programach. Przedstawia kod programu w Asemblerze i pozwala wykonywać go instrukcja po instrukcji, co ułatw ia analizę aplikacji i wykrywanie błędów. 7

Potrzebne narzędzia Podczas pisania leksykonu używałem asemblera MASM, który można pobrać za darmo ze strony http://masm32.com/, oraz środowiska pro­ gramistycznego WinAsm, które również jest dostępne bezpłatnie na stronie http://winasm.net/. Do szukania błędów i analizy naszych pro­ gramów może się przydać debugger — ja polecam Olly Debugger (http://ollydbg.de/). Na początku należy zainstalować pakiet MASM32, następnie środo­ wisko WinAsm (przy czym kolejność jest tu istotna), a później można jeszcze doinstalować wymieniony wcześniej Olly Debugger. Ważne jest, aby w środowisku WinAsm odpowiednio ustawić ścieżki. Aby to zrobić, należy wybrać w górnym menu Tools/Options/Files & Paths. Przykładowe ścieżki przedstawiam na rysunku 1.1. Options General [ Files & Paths pEditor Intellisense | KeyWords Colors Miscellaneous | Binary Path V*1asm32^in Li Indude Path yiasm32\[ndude LI Library Path Wasm32\Jb i Keyword File ^WinASM\

Idąc od lewej do prawej, pierwszy przycisk dokonuje asemblacji, drugi linkowania, trzeci wykonuje obie te czynności naraz, czwarty uruchamia gotowy program, a piąty kończy działanie programu. Systemy liczbowe Do programowania w Asemblerze na pewno przyda się wiedza o dwóch dodatkowych systemach liczbowych (prócz dziesiętnego): dwójkowym (binarnym) i szesnastkowym (heksadecymalnym). Po­ niżej zamieszczam skrótowy opis: • System binarny — podstawą systemu są dwie cyfry: 0 i 1. W sys­ temie binarnym działa komputer i jest zapisywany kod pro­ gramu. Przyjęło się, że liczby są zapisywane z literą b na końcu (np. 10001011b). • System heksadecymalny — podstawą są tu cyfry od 0 do 9 i litery od A do F (w sumie szesnaście znaków). Dla człowieka jest on o wiele wygodniejszy od systemu binarnego. Liczby w tym syste­ mie przyjęło się zapisywać z literą h na końcu (np. 23E0B3h). To wszystko na temat systemów liczbowych. Warto jednak doczytać informacje na ich temat, a także nauczyć się je ręcznie przeliczać. Jednak w trakcie programowania zdecydowanie szybciej i wygodniej jest przeliczać te systemy za pomocą kalkulatora systemu Windows. Jednostki informacji pamięci komputerowej Do programowania w języku Asembler przyda się znajomość jedno­ stek informacji pamięci komputerowej, które są związane również z rozmiarem rejestrów procesora czy zmiennych w pamięci. Warto je zatem znać i umieć je przeliczać. Bit — najmniejsza ilość informacji. Przyjmuje wartość 0 lub 1. Półbajt (ang. nibble) — są to 4 bity. Warto znać to określenie (nibble), bo można je spotkać w artykułach w języku angielskim. Bajt — jest to 8 bitów. Jego maksymalna wartość to 255d. Słowo (ang. word) — 2 bajty, czyli 16 bitów. Maksymalna wartość to 0FFFFh (lub 65535d). Podwójne słowo (ang. double word) — dwa słowa, czyli 4 bajty (32 bity). Maksymalna wartość to 0FFFFFFFFh (lub 4294967295d). Rozdz ał 1. Podstawowe nformacje | 9

Poczwórne słowo (ang. quad word) — cztery słowa, czyli 8 bajtów (64 bity). Kilobajt — 1024 bajty. Megabajt — 1024 kilobajty, 1 048 576 bajtów. 1.2. Architektura x86 — podstawowe informacje Jeżeli chcesz programować w Asemblerze x86, musisz bliżej poznać tę architekturę. Poniżej zostaną opisane najważniejsze rejestry procesora. Wytłumaczę także, czym jest stos. Rejestry procesora Rejestry to komórki pamięci wewnątrz procesora służące mu do wy­ konywania różnych operacji. Warto wspomnieć, że operacje na reje­ strach są o wiele szybsze niż na zmiennych w pamięci. Istnieją cztery rejestry ogólnego przeznaczenia (rysunek 1.3): EAX, EBX, ECX i EDX. Mają one po 32 bity (4 bajty). Każdy z nich dzieli się na dwie części po 16 bitów. Są to: „starsze słowo" (HIGH-WORD) i „młodsze słowo" (LOW-WORD). Przyjrzyjmy się temu na przykładzie rejestru EAX: młodsze słowo to rejestr AX, który z kolei dzieli się na dwa rejestry 8-bitowe: AH i AL. Warto też wspomnieć, że możemy używać tych rejestrów do czego chcemy, ale każdy z nich ma swoje specjalne przeznaczenie. Bity numerujemy od zera, czyli najmłodszy jest bit zerowy. To jednak nie wszystkie rejestry. Są jeszcze dwa rejestry indeksowe: EDI (rejestr przeznaczenia) i ESI (rejestr źródła), które dzielą się na DI i SI. Używa się ich do operacji na łańcuchach (np. na tekście). ESI przechowuje źródło (ang. source), a EDI — miejsce docelowe (ang. destina­ tion). Z kolei EBP (wskaźnik bazowy) i ESP (wskaźnik stosu) to rejestry wskaźnikowe; pierwszy z nich służy do adresowania, a drugi prze­ chowuje wskaźnik wierzchołka stosu. Jeżeli modyfikujemy zawartość rejestrów ESI, EDI, EBP lub ESP, musimy je przywrócić, zanim wrócimy do systemu Windows. 10 | Asembler. Leksykon k eszonkowy

EAX (A k u m u la to r) A X AH A L I I . 1 1 1 1 1 1 1111 i i i i i 31 15 7 0 --------r-------- S ta rs z e s ło w o M ło d s z e s ło w o EBX (R e je s tr bazowy) BX BH BL i i i i i i i i i i i i i i i i i i i i i i 31 L5 7 0 S ta rs z e sło w o M łodsz e sło w o ECX (L ic z n ik ) c x CH CL i ;............... ¡ m m i i i M M I I I i i i i i i i 31 15 7 0 S ta rs z e s ło w o M ło d s z e sło w o EDX ( R e je s tr danych) DX DH ...... DL I I I I 1 1 I I 1 1 1 1 1 1 1 L i i i 1 1 1 31 , 5 7 0 1 1-------- S ta rs z e s ło w o M ło d sze s ło w o Rysunek 1.3. Rejestry ogólnego przeznaczenia Pozostałe rejestry to: • Rejestry segmentowe (16-bitowe): CS (ang. Code Segment), DS (ang. Data Segment), ES (ang. Extra Segment), SS (ang. Stack Segment), FS, GS. • Rejestr flag — EFLAGS (flagi, inaczej znaczniki, będą nas intere­ sować, gdy będziemy debugować swoje programy): • CF (ang. Carry Flag) — znacznik przeniesienia. Flaga zostaje ustawiona, gdy podczas działania nastąpiło przeniesienie z bitu najbardziej znaczącego poza dostępny zakres zapisu. W przeciwnym wypadku znacznik ma wartość zero. • PF (ang. Parity Flag) — znacznik parzystości. Jest ustawiany, gdy w wyniku wykonania działania liczba bitów o wartości 1 w mniej znaczącym bajcie jest parzysta. • AF (ang. Auxillary Flag) — znacznik przeniesienia pomocni­ czego. Jest ustawiany, gdy nastąpiło przeniesienie z bitu numer 3 na bit numer 4 lub pożyczenie z bitu numer 4. Rozdz ał 1. Podstawowe nformacje | 11

• ZF (ang. Zero Flag) — znacznik zerowy. Przyjmuje wartość 1, gdy wynikiem ostatnio wykonanego działania było zero. • SF (ang. Sign Flag) — znacznik zmiany znaku. Jest ustawiany, gdy najbardziej znaczący bit (czyli najstarszy bit, nazywany bitem znaku) w otrzymanym wyniku równa się jeden. • OF (ang. Overflow Flag) — znacznik nadmiaru/przepełnienia. Jest ustawiany, gdy w danym działaniu nastąpiło przeniesie­ nie na najstarszy bit lub pożyczenie z tego bitu. • TF (ang. Trap Flag) — jest ustawiany przy debugowaniu. Pro­ cesor wywołuje wtedy tylko jedną instrukcję, a następnie do­ chodzi do przerwania, co pozwala dołączonemu debuggerowi zbadać program. • IF (ang. Interrupt Flag) — zezwolenie na przerwanie. • DF (ang. Direction Flag) — znacznik kierunku. • IOPL (ang. I/O Privilege Level) — informuje o priorytecie dostępu do portów wejścia-wyjścia. CPL (ang. Current Priority Level) musi być większe lub równe od tego znacznika, aby program miał dostęp do portów wejścia-wyjścia. Dostępny tylko w pro­ cesorach 286 i nowszych. • NT (ang. Nested Task) — dostępny tylko w procesorach 286 i nowszych. • RF (ang. Resume Flag) — znacznik wznowienia; dostępny tylko w procesorach 386 i nowszych. • VM(ang. Virtual 8086 Mode) — włączenie trybu wirtualnego; dostępny tylko w procesorach 386 i nowszych. • AC (ang. Alignment Check) — dostępny tylko w procesorach 486SX i nowszych. • VIF (ang. Virtual Interrupt Flag) — dostępny tylko w procesorach Pentium i nowszych. • VIP (ang. Virtual Interrupt Pending) — dostępny tylko w pro­ cesorach Pentium i nowszych. • ID (ang. Identification) — odczyt rodzaju procesora; dostępny tylko w procesorach Pentium i nowszych. 12 | Asembler. Leksykon k eszonkowy

• Rejestry koprocesora: ST0...ST7 — do operacji na liczbach zmien­ noprzecinkowych. • Rejestry MMX (64-bitowe): mm0...mm7. • Rejestry SSE (128-bitowe): xmm0...xmm7. • EIP — rejestr zawierający adres aktualnie wykonywanej instrukcji. To już wszystkie rejestry, które będą nas interesowały. Umiejscowienie znaczników w rejestrze EFLAGS zostało zaprezentowane na rysunku 1.4. 3lbo|29|28|27|26|25|24|23|22|212019181716N14i:14um £■7e.u mzy . Z - _¡f ZT££ £ 3 S B Rysunek 1.4. Umiejscowienie znaczników w rejestrze EFLAGS Stos Jest to część pamięci, w której można przechowywać różne dane, aby później móc z nich skorzystać. Jak sama nazwa wskazuje, wartości są układane jak na stosie. Ostatnia odłożona wartość będzie zdjęta jako pierwsza. Możesz to sobie wyobrazić jako pudełko, do którego kładziesz kartki papieru. Ostatnia kartka jest wyciągana jako pierw­ sza. Instrukcja PUSH odkłada wartość na stos, natomiast POP zdejmuje i zapisuje do rejestru/pamięci. 1.3. Budowa programu dla podsystemu DOS (16-bitowego) W programowaniu dla podsystemu DOS korzystamy z rejestrów 16- bitowych oraz rejestrów segmentowych. Z kolei do wykonywania różnych czynności, takich jak na przykład wyświetlanie tekstu czy operacje na plikach, korzystamy z przerwań DOS oraz BIOS. Istnieją tutaj dwa rodzaje programów: programy typu COM (zajmują jeden segment, tj. 64 kB) oraz programy typu EXE. Warto również zaznaczyć, że programy 16-bitowe (DOS-owe) nie uruchomią się już na nowych, 64-bitowych systemach operacyjnych. Rozdz ał 1. Podstawowe nformacje | 13

Budowa programu dla podsystemu DOS Budowa programu typu COM jest następująca: .model tin y ; m odel pam ięci .data ; dane .code .startu p ; instrukcje .e x it end Natomiast budowa programu typu EXE wygląda tak: .model small ; model pam ięci .stack 100h ; stos .data ; dane .code s t a r t : mov a x , @data ; pobierz adres segmentu danych mov ds, ax ; umieść g o w rejestrze DS (Data Segment) ; instrukcje end s ta rt Gotowe szablony aplikacji DOS-owych zawiera środowisko WinAsm. Wystarczy wybrać z górnego menu File/New Project i przejść na za­ kładkę DOS. Przerwania Jak już wspomniałem we wstępie, programowanie dla DOS-a opiera się na przerwaniach. Ogólna zasada wywoływania przerwań polega na umieszczeniu w odpowiednim rejestrze numeru funkcji, a następnie wywołaniu odpowiedniego przerwania za pomocą instrukcji INT. Dla przykładu poniżej zamieszczam program typu EXE wyświetlający tekst na konsoli: .model small .stack 100h .data msg db "H ello W o rld !",1 3 ,1 0 ,"$ "; napis musi się kończyć znakiem $ .code s t a r t : mov ax, @data ; pobierz adres segmentu danych mov ds, ax ; i umieść g o w rejestrze DS mov dx, o ffs e t msg ; pobierz adres napisu do rejestru DX 14 | Asembler. Leksykon k eszonkowy

mov ah, 9 ; funkcja 09h w rejestrze AH oznacza ”wypisz tekst na standardowe wyjście” in t 21h ; wywołanie przerwania DOS o numerze 21h mov ax, 0C07h ;funkcja 0Ch = ”odśwież bufor i czytaj ze standardowego wejścia' in t 21h ; czeka na naciśnięcie klawisza mov ax, 4C00h ;funkcja wyjścia in t 21h ; wywołanie przerwania DOS o numerze 21h end s ta rt Obecnie mało kto pisze programy dla DOS-a w Asemblerze, ale po­ stanowiłem o tym wspomnieć, gdyż czasem może się to przydać (choćby na zajęciach na niektórych uczelniach w Polsce). 1.4. Budowa programu dla systemu Windows (32-bitowego) Przy programowaniu dla systemu DOS korzysta się z rejestrów 16-bitowych oraz przerwań, natomiast w programowaniu dla sys­ temu Windows (32-bitowego) będziemy korzystać z 32-bitowych re­ jestrów procesora oraz funkcji WinAPI. Jeżeli chodzi o pamięć, w pod­ systemie DOS były segmenty, tutaj natomiast mamy jeden typ pamięci — płaski (ang. flat). Oznacza to, że pamięć tworzy dużą, ciągłą prze­ strzeń o rozmiarze 4 GB. Budowa programu dla systemu Windows: .386 ; zestaw instrukcji .model f l a t , s td c a ll ; model pam ięci i sposób wywoływaniafunkcji option casemap:none ; rozróżnianie małych i dużych liter ; pliki nagłówkowe in cl ude windows.inc i ncl ude kernel32. inc includ e user32.inc ; biblioteki in cl u d elib u se r3 2 .lib in cl u d elib k e rn e l3 2 .lib .data ; dane zainicjowane .d a ta ? ; dane niezainicjowane .code s ta r t: ; początek etykiety głównej ; instrukcje end s ta r t ; koniec etykiety głównej Rozdz ał 1. Podstawowe nformacje | 15

Program „Witaj, świecie!" Poniżej przedstawiam kod programu wyświetlającego okno infor­ macyjne poprzez wywołanie funkcji MessageBox. .386 .model f l a t , std c a ll option casemap:none i nclude windows.i nc in cl ude kernel 32.inc includ e user32.inc in cl udelib u se r3 2 .lib in cl udelib kernel 3 2 .lib .data MsgCaption db "In fo rm a cja ",0 MsgBoxText db "W ita j, ś w ie c ie !",0 .d a ta ? .code s ta r t: invoke MessageBox, NULL, addr MsgBoxText, addr MsgCaption, MB_OK invoke Ex itPro cess, NULL end s ta rt W powyższym kodzie najpierw mamy określony zestaw instrukcji (.386), następnie model pamięci i sposób wywoływania funkcji, póź­ niej zaś dyrektywę rozkazującą asemblerowi odróżniać małe i duże litery. Dalej zostają dołączone odpowiednie pliki nagłówkowe i bibliote­ ki. Niżej w sekcji danych zostały zadeklarowane dwa ciągi znaków zakończone zerem (w DOS-ie znakiem kończącym był znak $, tutaj jest to zero). W sekcji kodu pojawiło się wywołanie funkcji MessageBox wraz z podaną listą argumentów, która wyświetla okno informacyjne (funkcja pochodzi z biblioteki user32dll). Niżej znajduje się wywołanie funkcji ExitProcess, która kończy pracę programu (funkcja pochodzi z biblioteki kernel32.dll). Zapewne zadajesz sobie teraz pytanie, skąd wziąć opisy tych wszystkich funkcji oraz informacje, z jakich bibliotek pochodzą. Dokładną doku­ mentację można znaleźć na stronach MSDN oraz w pliku WIN32.HLP (Win32 Programmer's Reference), który można znaleźć w sieci. Funkcje WinAPI zawsze zwracają wartość w rejestrze EAX. Funkcje, których wyniki są 64-bitowe, w 32-bitowym systemie zwracają w parze EDX: EAX. 16 | Asembler. Leksykon k eszonkowy

Szablon aplikacji dla systemu Windows zawiera nasze środowisko WinAsm. Wystarczy wybrać z górnego menu File/New Project, przejść na zakładkę Bare Bone i wskazać EXE. Pseudoinstrukcja INVOKE Na koniec kilka słów o pseudoinstrukcji INVOKE. Jak zapewne zauwa­ żyłeś, w powyższym kodzie wywoływaliśmy funkcję, podając jej na­ zwę i argumenty po przecinku. Standardowo w Asemblerze się tak nie robi — przeważnie odkłada się argumenty na stos przy użyciu instrukcji PUSH i wywołuje funkcję za pomocą instrukcji CALL. Jednak makroasembler MASM ma pseudoinstrukcję, która pozwala wywo­ ływać funkcję w ten prosty i czytelny sposób. Dla porównania poni­ żej przedstawię wywołanie tej samej funkcji z użyciem INVOKE i bez. Wywołanie funkcji MessageBox z użyciem pseudoinstrukcji INVOKE: invoke MessageBox, NULL, addr MsgText, addr MsgCaption, MB_OK Wywołanie tej samej funkcji bez użycia pseudoinstrukcji INVOKE: push MB_OK push o ffs e t MsgCaption push o ffs e t MsgText push NULL c a ll MessageBox Korzystanie z INVOKE jest warte polecenia, gdyż dzięki temu kod jest bardziej przejrzysty i łatwiej się ustrzec błędu przy podawaniu ar­ gumentów funkcji. Ten rozdział był małym wprowadzeniem do programowania w Asem­ blerze dla systemu Windows. Poznałeś w nim budowę prostego pro­ gramu. W dalszych rozdziałach opiszę najważniejsze instrukcje, gdyż programowanie w Asemblerze to przede wszystkim instrukcje pro­ cesora, a nie tylko wywoływanie funkcji WinAPI. Rozdz ał 1. Podstawowe nformacje | 17

Rozdział 2. Podstawowe instrukcje procesora 2.1. Instrukcja kopiowania i instrukcje arytmetyczne Instrukcja kopiowania (MOV) Zacznijmy od najczęściej spotykanej instrukcji — MOV. Składnia: MOV cel, źródło To chyba najczęściej spotykana instrukcja. Kopiuje wartość z operandu źródłowego do operandu celu, przy czym pierwsza wartość pozostaje niezmieniona. Na przykład: mov eax, 25 ; EAXprzyjmuje wartość 25 mov ecx, eax ; skopiuj do ECX wartość z EAX mov zmienna, eax ; skopiuj do zmiennej wartość z EAX Dlaczego nie jest MOV zmienna, zmienna? Ponieważ procesor nie potrafi wykonać czegoś takiego. Musimy posłużyć się rejestrem pomocniczym (najpierw skopiować wartość do rejestru, a potem do zmiennej) lub odłożyć wartość na stos (instrukcja PUSH), po czym przy użyciu instrukcji POP zdjąć ją ze stosu i umiejscowić w drugiej zmiennej. Oprócz kopiowania wartości stałej do rejestru/zmiennej czy kopio­ wania wartości między rejestrami możemy skopiować wartość z da­ nego miejsca w pamięci. Wykonujemy to w następujący sposób: mov eax, dword p tr [ebx] Rejestr EBX zawiera adres, pod którym znajduje się wartość — zosta­ nie ona skopiowana do rejestru EAX. Instrukcje arytmetyczne Instrukcje arytmetyczne służą do wykonywania podstawowych działań matematycznych, takich jak dodawanie, odejmowanie, mnożenie i dzielenie. 18 | Asembler. Leksykon k eszonkowy

ADD (dodawanie) Składnia: ADD operand1, operand2 Instrukcja ADDdodaje wartość do rejestru lub komórki pamięci. Przykładowe użycie: add eax, 124 ; dodaje 124 do wartości w rejestrze EAX add ecx, ebx ; dodaje wartość z E B X do wartości w ECX add zmienna, 25 ; dodaje 25 do wartości w zmiennej ”zmienna” SUB (odejmowanie) Składnia: SUB operand1, operand2 Odejmuje wartość drugiego operandu od pierwszego i zapisuje wynik w pierwszym. MUL (mnożenie) Składnia: MUL operand1 Mnoży rejestr EAX (lub AX, lub AL) przez podaną wartość. Mnoży liczby bez znaku. Wynik znajduje się w rejestrze EAX/AX/AL. IMUL (mnożenie ze znakiem) Składnia: IMUL wartość IMUL wynik, wartość, wartość IMUL wynik, wartość Mnoży wartość z EAX z podaną wartością (IMUL wartość) lub mnoży dwie wartości i wynik zachowuje w pierwszym operandzie (IMUL wynik, wartość, wartość), lub mnoży rejestr z wartością (IMUL wynik, wartość). DIV (dzielenie bez znaku) Składnia: DIV dzielnik Instrukcja ta dzieli wartość w rejestrze EAX przez wartość podaną jako dzielnik. Dzielona jest zawsze wartość w EAX. Także wynik zostanie umieszczony w tym rejestrze, natomiast reszta z dzielenia — w EDX. mov eax, 32 ; EAX przyjmuje wartość 32 mov ecx, 2 ; ECXprzyjmuje wartość 2 d iv ecx ; podziel EAXprzez ECX Rozdz ał 2. Podstawowe nstrukcje procesora | 19

IDIV (dzielenie ze znakiem) Składnia: IDIV dzielnik Działa podobnie jak instrukcja DIV, tyle że dzieli ze znakiem. INC (inkrementacja) Składnia: INC rejestr lub zmienna Zwiększa wartość rejestru lub zmiennej o 1. DEC (dekrementacja) Składnia: DEC rejestr lub zmienna Działa odwrotnie do instrukcji INC. Zmniejsza podaną wartość o 1 (zmi enna=zmienna-l). Przykład: dec eax ; zmniejsza wartość w EAX o 1 dec [zmienna] ; zmniejsza wartość zmiennej o 1 2.2. Instrukcje logiczne i przesunięć bitowych Instrukcje logiczne Instrukcje logiczne służą do wykonywania operacji logicznych na bitach, takich jak koniunkcja (iloczyn logiczny), alternatywa (suma logiczna), alternatywa wykluczająca i negacja (zaprzeczenie). AND (koniunkcja) Składnia: AND operand1, operand2 1 and 1 = 1 1 and 0 = 0 0 and 1 = 0 0 and 0 = 0 Dla przykładu wykonamy koniunkcję dla dwóch liczb binarnych: 11101010100 and 01010110000 = 01000010000 Przy ręcznym liczeniu najwygodniej napisać je jedna pod drugą i spraw­ dzać według tabelki poszczególne bity. 20 | Asembler. Leksykon k eszonkowy

OR (alternatywa) Składnia: OR operand1, operand2 1 or 1 = 1 1 or 0 = 1 0 or 1 = 1 0 or 0 = 0 Dla przykładu mamy dwie liczby binarne: 10100011100 or 01110010010 = 11110011110 A zatem bierzemy po kolei dwa odpowiadające sobie bity i spraw­ dzamy według tabelki, jaki będzie dla nich wynik. XOR (alternatywa wykluczająca) Składnia: XOR operand1, operand2 1 xor 1 = 0 1 xor 0 = 1 0 xor 1 = 1 0 xor 0 = 0 Będziemy często używać tej instrukcji do czyszczenia zawartości ja­ kiegoś rejestru (gdyż wynik operacji xor dla dwóch tych samych wartości daje zero). NOT (zaprzeczenie) Składnia: NOT operand1 not 1 = 0 not 0 = 1 Krótko mówiąc, odwraca wszystkie bity w podanym operandzie (zamiast 1 daje 0, zamiast 0 daje 1). Instrukcje przesunięć bitowych Instrukcje przesunięć bitowych służą do wykonywania operacji, które zmieniają miejsce bitów w rejestrze. Ujmując to prościej, bity są prze­ suwane w prawo lub lewo. SAL/SHL (przesunięcie arytmetyczne/logiczne w lewo) Składnia: SAL operand1, operand2 SHL operand1, operand2 Rozdz ał 2. Podstawowe nstrukcje procesora | 21

Przesuwa w lewo bity pierwszego operandu o podaną liczbę bitów w operandzie drugim. Starszy przesunięty bit zostaje zachowany w fladze CF (ang. Carry Flag), a młodsze bity są wypełniane zerami. Sposób przesuwania się bitów dla instrukcji SAL/SHL w 32-bitowym rejestrze prezentuje rysunek 2.1. Dla przykładu przesuniemy wartość w EAXo 1 bit w lewo: mov eax, 1 ; do EAX dajemy wartość 1 shl eax, 1 ; EA X będzie równy 2 (10 binarnie), ; gdyżjedynka się przemieściła, ; a na je j miejscu mamy zero, ; co daje wartość 10b (2h). Najmłodszy bit EAX (32 bity) jest zerowany 1 < n Carry Flag 31 15 1 0 (znacznik Starsze bity Młodsze bity przeniesienia) Rysunek 2.1. Rysunek prezentujący, jak przesuwają się bity dla instrukcji SAL/SHL SAR/SHR (przesunięcie arytmetyczne/logiczne w prawo) Składnia: SAR operand1, operand2 SHR operand1, operand2 Instrukcja SARprzesuwa w prawo bity pierwszego operandu o poda­ ną liczbę bitów w operandzie drugim. Najmłodszy przesunięty bit zostaje zachowany w fladze CF, a najstarszy bit zostaje zachowany. Instrukcja SHR działa podobnie, różni się tylko tym, że zeruje najstarszy bit (nazywany również bitem znaku). Sposób przesuwania się bitów dla instrukcji SAR został zaprezentowany na rysunku 2.2, a dla in­ strukcji SHR— na rysunku 2.3. Dla przykładu przesuniemy wartość w EAXo 1 bit w prawo: mov eax, 1 ; do EAX idzie wartość 1 shr eax, 1 ; EA X będzie równy 0, ; fla g a C F zostanie ustawiona. 22 | Asembler. Leksykon k eszonkowy

Rysunek 2.2. Rysunek prezentujący, jak przesuwają się hity dla instrukcji SAR Rysunek 2.3. Rysunek prezentujący, jak przesuwają się hity dla instrukcji SHR ROL/RCL (rotacja w lewo bez użycia flagi CF/z użyciem flagi CF) Składnia: ROL operand1, operand2 RCL operand1, operand2 Pierwsza instrukcja przesuwa logicznie w lewo pierwszy operand o liczbę bitów podaną w operandzie drugim. Nie używa flagi CF, więc najmłodszy bit przyjmuje wartość najstarszego bitu. Druga instrukcja przeprowadza tę samą czynność, ale przy użyciu flagi CF. Najmłodszy bit przyjmuje wartość, która była w CF, a znacznik CF przyjmuje wartość najstarszego bitu. Sposób obracania się bitów dla instrukcji ROL w 32- bitowym rejestrze został przedstawiony na rysunku 2.4, a dla instrukcji RCL— na rysunku 2.5. Rysunek 2.4. Rysunek prezentujący, jak obracają się hity dla instrukcji ROL Rozdz ał 2. Podstawowe nstrukcje procesora | 23

EAX (32 bity) 31 15 7 0 Starsze bity --- ------- Młodsze bity Carry Flag (znacznik przeniesienia) Rysunek 2.5. Rysunek prezentujący, jak obracają się bity dla instrukcji RCL ROR/RCR (rotacja w prawo bez użycia flagi CF/z użyciem flagi CF) Składnia: ROR operandl, operand2 RCR operandl, operand2 Pierwsza instrukcja przesuwa logicznie w prawo pierwszy operand o liczbę bitów podaną w operandzie drugim. Nie używa flagi CF, więc najstarszy bit przyjmuje wartość najmłodszego bitu. Druga in­ strukcja przeprowadza tę samą czynność, ale przy użyciu flagi CF. Najstarszy bit przyjmuje wartość, która była w CF, a znacznik CF przyjmuje wartość najmłodszego bitu. Sposób obracania się bitów w 32-bitowym rejestrze dla instrukcji ROR został zaprezentowany na rysunku 2.6, a dla instrukcji RCR— na rysunku 2.7. EAX (32 bity) 31 15 7 0 Starsze bity Młodsze bity ■<-------------------- Carry Flag (znacznik przeniesienia) Rysunek 2.6. Rysunek prezentujący, jak obracają się bity dla instrukcji ROR SHLD/SHRD (przesunięcie logiczne w lew o/w prawo podwójnego rejestru) Składnia: SHLD operandl, operand2, operand3 SHRD operandl, operand2, operand3 24 | Asembler. Leksykon k eszonkowy

EAX (32 bity) 31 15 7 0 Starsze bity Młodsze bity ------ — Carry Flag (znacznik przeniesienia) Rysunek 27. Rysunek prezentujący, jak obracają się hity dla instrukcji RCR Pierwsza instrukcja (SHLD) przesuwa w lewo bity w operandzie pierw­ szym o liczbę bitów podaną w operandzie trzecim. Najstarsze bity pierwszego operandu zostają wyrzucone (flaga CF przyjmuje wartość ostatniego wyrzuconego bitu), a najmłodsze bity przyjmują wartości najstarszych bitów z drugiego operandu. Uwaga! Bity drugiego ope- randu pozostają niezmienione. SHRD przesuwa natomiast bity pierw­ szego operandu o liczbę bitów podaną w operandzie trzecim. Naj­ młodsze bity pierwszego operandu zostają wyrzucone, a najstarsze bity przyjmują wartość najmłodszych bitów z operandu drugiego. Bity drugiego operandu pozostają niezmienione. Sposób przesuwania się bitów dla instrukcji SHLD został zaprezentowany na rysunku 2.8, a dla instrukcji SHRD— na rysunku 2.9. Przykład dla instrukcji SHLD: mov ecx, 0 ; zerujemy ECX mov eax, OFFFFFFFFh ; do EAX wartość OFFFFFFFFh shld ecx, eax, 2 ; EA X będzie równy OFFFFFFFCh, gdyż ; dwa jeg o najmłodsze bity zostały zastąpione ; przez dwa najstarsze bity rejestru ECX, ; które miały wartość zero. Przykład dla instrukcji SHRD: mov ecx, 0 ; zerujemy ECX mov eax, OFFFFFFFFh ; do EAX wartość OFFFFFFFFh shrd ecx, eax, 2 ; EA X będzie równy 3FFFFFFFh, gdyż ; dwa jeg o najstarsze bity zostały ; zastąpione dwoma najmłodszymi bitami ; z rejestru ECX, które miały wartość zero. Rozdz ał 2. Podstawowe nstrukcje procesora | 25

Rysunek 2.8. Rysunek prezentujący, jak przesuwają się bity dla instrukcji SHLD Rysunek 2.9. Rysunek prezentujący, jak przesuwają się bity dla instrukcji SHRD 2.3. Instrukcje wywołania procedury i powrotu Instrukcje związane z procedurami Procedury to wydzielone części programu, które mogą być wielokrotnie wywoływane, bez potrzeby kopiowania tego samego kodu w różne miejsca. Tworzenie takich procedur (nazywanych też podprogramami) jest możliwe przy użyciu instrukcji związanych z procedurami. CALL (wywołanie funkcji/podprogramu) Składnia: CALL nazwa_funkcji 26 | Asembler. Leksykon k eszonkowy