Asembler x86/Pierwszy program/NASM

Pierwszy program

GNU As
FASM
MASM
NASM

Hello World!

edytuj

Zacznijmy od dawki kodu, aby w ogóle mieć pojęcie, jak wygląda kod w języku Asembler. Będzie to tradycyjny już program Hello World, który można napotkać w niemal każdym podręczniku do nauki programowania w dowolnym języku (za zadanie ma po prostu wyświetlenie napisu Hello World!). Uwaga: jak już wcześniej było wspomniane, konkretny kod zapisany w Asemblerze wykonuje się na konkretnej maszynie działającej pod kontrolą danego systemu operacyjnego.

DOS/Windows

edytuj
segment .data
	tekst db "Hello World!",0Dh,0Ah,"$"

segment stosik stack
	resb 64
segment .text
mov ax, .data
mov ds, ax
mov ax, stosik
mov ss, ax
mov dx, tekst
mov ah, 9
int 21h
mov ax, 4C00h
int 21h

Program ten po uruchomieniu w konsoli wyświetla na ekranie tekst "Hello World!". Postaram się zrozumiale wyjaśnić, o co w nim chodzi.

segment .data

Oznacza, że od tego miejsca w dół definiowany jest nowy segment o nazwie ".data".

tekst db "Hello World!",0Dh,0Ah,"$"

Ta linijka dodaje zmienną do obecnie definiowanego segmentu (w tym przypadku chodzi o segment ".data"). tekst to nazwa naszej zmiennej, db to typ naszej zmiennej (db - 1 bajt), zaś wszystko, co znajduje się dalej w tej linijce to wartość początkowa dla naszej zmiennej. Jak widać, jest to ciąg znaków zakończony znakami 0D0A (określające przejście do nowej linii) oraz znakiem $ oznaczającym koniec naszego ciągu (dla migrantów z C/C++ - jest to odpowiednik znaku '\0'). Gdyby zabrakło tego znaku, instrukcje operujące na naszej zmiennej nie mogłyby określić, gdzie jest jej koniec, więc wyjechałyby poza przydzielony jej obszar pamięci dopóki nie znalazłyby znaku $.

segment stosik stack

Tworzy segment stosu (stack) o nazwie stosik i...

resb 64

... i rozmiarze 64 bajtów. Dyrektywa resb jest podobna do poznanej przed chwilą dyrektywy db, tyle że tworzy konkretną ilość zmiennych bez przydzielania im wartości początkowych.

segment .text

Analogicznie do pierwszej linijki w naszym programie, tworzy segment o nazwie .text. Poniżej znajduje się jego definicja.

mov ax, .data
mov ds, ax
mov ax, stosik
mov ss, ax

Instrukcja mov kopiuje wartość drugiego parametru do pierwszego. Jako, że po starcie programu rejestry segmentowe są niezainicjowane, musimy ręcznie przydzielić im adresy odpowiednich segmentów. W pierwszej linijce kopiujemy adres segmentu .data do rejestru ax. Następnie, z rejestru ax kopiujemy go do rejestru segmentowego ds (instrukcja mov nie pozwala na bezpośrednie przydzielanie wartości rejestrom segmentowym, dlatego musieliśmy użyć rejestru ax jako pośrednika). W następnych 2 linijkach powtarzamy operację, tyle że kopiujemy adres segmentu stosik (który jest stosem naszego programu) do rejestru ss.

mov dx, tekst
mov ah, 9
int 21h

Ten fragment kodu zacznę tłumaczyć od końca. Instrukcja int wywołuje podprogram obsługi przerwania o podanym numerze (zakończenie go przyrostkiem h oznacza liczbę w zapisie szesnastkowym). Podprogram ów wywołuje odpowiednią funkcję o numerze podanym w rejestrze ah (wcześniej nadaliśmy temu rejestrowi wartość 9, więc instrukcja int 21h wywołała funkcję numer 9 przerwania numer 21 w zapisie szesnastkowym). Wywołana w tym przypadku funkcja wyświetla w konsoli ciąg znaków, którego adres znajduje w rejestrze dx (przydzieliliśmy temu rejestrowi adres naszej zmiennej tekst). W efekcie na ekranie pojawi się więc napis Hello World!.
Uwaga: należy zaznaczyć, że przerwanie 21h oraz opisana funkcja obsługiwane są przez MS-DOS, przez co kod nie jest przenośny na inne platformy niż Windows.

mov ax, 4C00h
int 21h

Wywołuje funkcję przerwania 21 o numerze 4C00h. Odpowiada ona za zakończenie działania programu i oddanie sterowania do systemu.

Poniżej znajduje się źródło programu działającego pod systemem operacyjnym Linux.

;; hello.asm
;; Wypisuje określony ciąg 14 znaków
;;

segment .data
msg     db      "Hello World!", 0Ah    ; umieszcza w segmencie danych ciąg znaków zakończony znakiem końca linii

segment .text
        global  _start

_start:
        mov     eax, 4
        mov     ebx, 1
        mov     ecx, msg        ; adres pierwszego znaku do wyświetlenia
        mov     edx, 14         ; liczba znaków do wyświetlenia
        int     80h            ; wywołanie funkcji systemowej wyświetlającej ciąg znaków o danej długości

; wyjscie z programu
        mov     eax, 1
        xor     ebx, ebx
        int     0x80
; KONIEC PROGRAMU

Aby zainstalować kompilator NASM
w systemach z rodziny RedHat/Fedora
wystarczy jako administrator systemu
wydać polecenie yum install nasm.

Aby skompilować program należy wcześniej upewnić się, ża posiadamy zainstalowany kompilator NASM. Następnie należy wydać polecenie:

nasm -f elf hello.asm

Otrzymamy wówczas plik hello.o, który należy zlinkować do postaci wykonywalnej poleceniem:

ld hello.o -o hello

Aby uruchomić tak przygotowany program należy wprowadzić:

./hello

Goodbye world...

edytuj

Powyższy kod początkowo może trochę odstraszać, lecz jeśli masz pierwszy raz styczność z Asemblerem, prawdopodobnie spodziewałeś się czegoś znacznie gorszego. O Asemblerze krąży wiele nieprawdziwych informacji, iż jest to trudny do nauki język. Tak naprawdę nie jest trudny - jest nieczytelny. Po kilku godzinach od napisania nie rozumie się swojego własnego kodu, dlatego nawet jeśli nie przejmowałeś się komentarzami programując w innych językach programowania (bo można w nich sobie radzić bez komentarzy), staraj się pokonać nieco swoje przyzwyczajenia i dopisywać w kodzie komentarze w kluczowych miejscach.
Niestety, luksus ten dotyczy nas jedynie przy pisaniu własnych programów. Jeśli uczysz się asemblera głównie pod kątem zrozumienia zdeasemberowanych plików, tam jest to o tyle utrudnione, że rzuceni jesteśmy w tysiące linii kodu, o którego strukturze nie mamy zielonego pojęcia, dodatkowo nie mamy do pomocy żadnych komentarzy, a język Asembler jest bardzo nieczytelny. Zaczynaj od prostych programów, nie rzucaj się na głęboką wodę. Następnie przechodź do coraz większych, starając się modyfikować w nich konkretną rzecz (np. pozamieniać znaczenie przycisków itp.) - przede wszystkim, nie poddawaj się! Dzięki znajomości Asemblera Twoje możliwości będą znacznie szersze.