Asembler x86/Pierwszy program/NASM: Różnice pomiędzy wersjami
Usunięta treść Dodana treść
przerzucono treść z rozdziału "Narzędzia" |
→Hello World!: kolorki |
||
Linia 1:
=== Hello World! ===
Zacznijmy od dawki kodu, żeby mieć pojęcie jak w ogóle wygląda kod asemblera. 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!).
<source lang="asm">
segment dane
tekst db "Hello World!",0Ah,0Dh,"$"
Linia 21:
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.
Oznacza, że od tego miejsca w dół definiowany jest nowy segment o nazwie "dane".
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 $.
Tworzy segment stosu o nazwie ''stosik''...
<source lang="asm">resb 64</source>
... 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.
Analogicznie do pierwszej linijki w naszym programie, tworzy segment o nazwie ''kod''. Poniżej znajduje się jego definicja.
Linia 41:
Nazywa obecną pozycję słówkiem "start". Poprzedzenie ''start:'' dwoma kropkami oznacza, że chcemy aby to miejsce było początkiem naszego programu.
<source lang="asm">
mov ax, dane▼
mov ss, ax
</source>
Instrukcja [[Asembler X86/Instrukcje/Transferowe#mov|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.
<source lang="asm">
▲ mov dx, tekst
mov ah, 9
</source>
Ten fragment kodu zacznę tłumaczyć od końca. Instrukcja [[../Instrukcje/Różne#int|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!''
<source lang="asm">
</source>
Wywołuje funkcję przerwania 21 o numerze 0x4C00. Odpowiada ona za zakończenie działania programu i oddanie sterowania do systemu.
<source lang="asm">end</source>
Dyrektywa określająca koniec naszego kodu. Nie ma ona znaczenia i jest ignorowana, lecz tradycyjnie powinna stać na końcu pliku z kodem.
|