Jak pisać programy w języku asembler pod Linuksem?

Część 3 - Podstawowe instrukcje, czyli poznajemy dialekt procesora

Poznaliśmy już rejestry, omówiliśmy pamięć. Pora zacząć na nich operować.
Zanim jednak 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.
      bit7 = bit6, bit6 = bit5, ... , bit1 = bit0, bit0 = 0.

    2. SHR - shift logical right
      bit0 = bit1, bit1 = bit2, ... , bit6 = bit7, bit7 = 0

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

    4. ROL - rotate left
      bit7 = bit6, ... , bit1 = bit0, bit0 = stary bit7

    5. RCL - rotate through carry left
      carry flag CF = bit7, bit7 = bit6, ... , bit1 = bit0, bit0 = stara CF

    6. ROR - rotate right
      bit0 = bit1, ... , bit6 = bit7, bit7 = stary bit0

    7. RCR - rotate through carry right
      CF = bit0, bit0 = bit1, ... , bit6 = bit7, bit7 = stara CF

    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
      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
      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
      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
      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
      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 (flaga zera) 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.
    Wykonanie:
    		lea	rej, [pamięć]
    jest równoważne:
    (przeskocz pseudo-kod LEA)
    		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: Intel i 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

  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.