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:

Zacznijmy je po kolei omawiać (nie omówię wszystkich).

  1. instrukcje przemieszczania danych.
    Tutaj zaliczymy już wielokrotnie używane MOV oraz kilka innych: XCHG , PUSH i POP.

  2. 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:



  3. arytmetyka dziesiętna
  4. Instrukcje bitowe (logiczne).

    Instrukcja TEST działa tak samo jak AND z tym, że nie zachowuje nigdzie wyniku, tylko ustawia flagi. Pokró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	
  5. Instrukcje przesunięcia bitów.
    1. SAL, SHL - shift left - przesunięcie w lewo
      bit7 = bit6, bit6 = bit5, ... , bit1 = bit0, bit0 = 0.

    2. SHR - shift logical right - przesunięcie logiczne w prawo
      bit0 = bit1, bit1 = bit2, ... , bit6 = bit7, bit7 = 0

    3. SAR - shift arithmetic right - przesunięcie arytmetyczne (z zachowaniem znaku) w prawo
      bit0 = bit1, bit1 = bit2, ... , bit6 = bit7, bit7 = bit7 (bit znaku zachowany!)
      Najstarszy bit w rejestrze nazywa się czasem właśnie bitem znaku.

    4. ROL - rotate left - obrót w lewo
      bit7 = bit6, ... , bit1 = bit0, bit0 = stary bit7

    5. RCL - rotate through carry left - obrót przez flagę CF (flagę przepełnienia) w lewo
      CF = bit7, bit7 = bit6, ... , bit1 = bit0, bit0 = stara CF

    6. ROR - rotate right - obrót w prawo
      bit0 = bit1, ... , bit6 = bit7, bit7 = stary bit0

    7. RCR - rotate through carry right - obrót przez flagę CF w prawo
      CF = bit0, bit0 = bit1, ... , bit6 = bit7, bit7 = stara CF

    Oczywiście, te instrukcje mogą działać na wartościach większych niż bajt - bit7 jako najstarszy jest tutaj tylko dla ilustracji.

    Przy pomocy SHL można przeprowadzać szybkie mnożenie, a dzięki SHR - szybkie dzielenie. Na przykład, SHL AX,1 jest równoważne przemnożeniu AX przez 2, SHL AX,5 - przez 2^5, czyli 32. SHR BX,4 dzieli BX przez 16.

  6. Instrukcje sterujące wykonywaniem programu.

  7. Operacje na łańcuchach znaków.
    1. LODS[B/W/D/Q] - Load Byte/Word/Dword/Qword - pobierz bajt/słowo/podwójne słowo/poczwórne słowo
      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

    2. STOS[B/W/D/Q] - Store Byte/Word/Dword/Qword - zapisz bajt/słowo/podwójne słowo/poczwórne słowo
      MOV ES:[DI/EDI/RDI], AL/AX/EAX/RAX
      ADD DI,1/2/4/8 ; ADD/SUB jak wyżej

    3. MOVS[B/W/D/Q] - Move Byte/Word/Dword/Qword - przesuń (skopiuj) bajt/słowo/podwójne słowo/poczwórne słowo
      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

    4. CMPS[B/W/D/Q] - Compare Byte/Word/Dword/Qword - porównaj bajt/słowo/podwójne słowo/poczwórne słowo
      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

    5. SCAS[B/W/D/Q] - Scan Byte/Word/Dword/Qword - szukaj bajtu/słowa/podwójnego słowa/poczwórnego słowa
      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, albo REPE/REPZ albo REPNE/REPNZ co spowoduje, że będzie ona wykonywana, dopóty CX nie jest zerem i jednocześnie flaga ZF (zero flag) będzie równa odpowiednio 1 lub 0.

  8. Instrukcje wejścia/wyjścia do portów.
    Są one bardziej szczegółowo opisane w części poświęconej portom, ale podam tu skrót:

  9. Instrukcje flag
  10. Instrukcja LEA - Load Effective Address - załaduj wyliczony adres.
    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:
    1. Jak w 1 instrukcji sprawić, że EAX = EBP-12 ?
      Odpowiedź: lea eax, [ebp-12]
    2. Niech EBX wskazuje na tablicę o 20 elementach o rozmiarze 8 każdy. Jak do ECX zapisać adres jedenastego elementu, a do EDX elementu o numerze EDI?
      Odpowiedź: lea ecx, [ebx + 11*8] oraz lea edx,[ebx+edi*8]
    3. 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 (klawisz dostępu 3)
Kolejna część kursu (klawisz dostępu 4)
Spis treści off-line (klawisz dostępu 1)
Spis treści on-line (klawisz dostępu 2)
Ułatwienia dla niepełnosprawnych (klawisz dostępu 0)



Ćwiczenia

  1. Zapisz instrukcje: do rejestru AX dodaj 5, od rejestru SI odejmij 178.

  2. Nie używając cyfry jeden napisz jedną instrukcję, która zmniejszy rejestr DX o jeden.

  3. Przemnóż wartość rejestru EDI przez 2 na przynajmniej dwa różne sposoby po jednej instrukcji. Postaraj się nie używać instrukcji (I)MUL.

  4. W jednej instrukcji podziel wartość rejestru BP przez 8.

  5. Nie używając instrukcji MOV spraw, by DX miał wartość 0 (na przynajmniej 3 sposoby, każdy po jednej instrukcji).

  6. 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.

  7. W dwóch instrukcjach spraw, by EDI równał się 7*ECX. Postaraj się nie używać instrukcji (I)MUL.