Asembler X86/Pierwszy program/NASM

Z Wikibooks, biblioteki wolnych podręczników.

[edytuj] Hello World!

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. Poniższy program uruchomi się tylko pod Windowsem.

segment dane
	tekst db "Hello World!",0Ah,0Dh,"$"
 
segment stosik stack
	resb 64
 
segment kod
..start:
	mov ax, dane
	mov ds, ax
	mov ax, stosik
	mov ss, ax
 
	mov dx, tekst
	mov ah, 9
	int 0x21
	mov ax, 0x4C00
	int 0x21
end

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

segment dane

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

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

Ta linijka dodaje zmienną do obecnie definiowanego segmentu (w tym przypadku chodzi o segment "dane"). 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 0A0D (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 kod

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

..start:

Nazywa obecną pozycję słówkiem "start". Poprzedzenie start: dwoma kropkami oznacza, że chcemy, aby to miejsce było początkiem naszego programu.

mov ax, dane
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 dane 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 0x21

Ten fragment kodu zacznę tłumaczyć od końca. Instrukcja int wywołuje podprogram obsługi przerwania o podanym numerze (poprzedzenie go przedrostkiem 0x 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 0x21 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 niz Windows.

mov ax, 0x4C00
int 0x21

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

end

Dyrektywa określająca koniec naszego kodu. Nie ma ona znaczenia i jest ignorowana, lecz tradycyjnie powinna stać na końcu pliku z kodem.


[edytuj] Goodbye world...

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.