Assembler

W tym tutorialu przyjmujemy, że czytelnik wie jak używać oprogramowania MASM. Jeżeli nie zapoznałeś się jeszcze z MASM, pobierz z internetu win32asm.exe, i przeczytaj tekst wewnątrz pakietu zanim zaczniesz pracę z tutorialem. Dobrze. Jesteś gotów. Zaczynamy!

 

Teoria

Programy Win32 wykonywane są w trybie chronionym, który jest dostępny począwszy od 80286. Ale 80286 jest już historią. Będziemy się więc koncentrować na 80386 i jego następcach. Windows wykonuje każdy program Win32 w oddzielnej wirtualnej przestrzeni. To znaczy, każdy program Win32 będzie miał swoją własną przestrzeń adresową o wielkości 4 GB. Jednakże, to wcale nie znaczy, że każdy program win32 ma 4GB fizycznej pamięci, tylko, że może odwoływać się do każdego adresu z tego zakresu. Windows zrobi wszystko co niezbędne ażeby odwołania programu do pamięci działały prawidłowo. Oczywiście, program musi przestrzegać reguł ustanowionych przez Windows, w przeciwnym wypadku pojawi się błąd ochrony (General Protection Fault). Każdy program jest sam w swojej przestrzeni adresowej. To jest przeciwieństwo sytuacji spotykanej w Win16. W Win16 programy mogą widzieć się nawzajem. Tak nie jest w Win32. Ta cecha pomaga wyeliminować możliwość taką, że jeden program zapisuje kod lub dane innego programu.
Model pamięci jest również całkowicie odmienny od  16-bitowego świata. Pod Win32, nie musimy się już zajmować modelem pamięci i segmentami! Jest tu tylko jeden model: Model pamięci flat. Nie ma tu segmentów 64K. Pamięć jest   większa aż do 4 GB. To znaczy, że nie musisz się zajmować również segmentowymi rejestrami. Możesz użyć jakiegokolwiek punktu w przestrzeni pamięci. To jest znaczne UŁATWIENIE dla programistów. To sprawia, że programowanie w asemlerze dla Windows32 jest tak łatwe jak programowanie w C.
Kiedy programujesz pod Win32, musisz znać parę ważnych reguł. Pierwszą taką regułą jest, że, Windows używa rejestrów esi, edi, ebp i ebx i oczekuje, że wartości w tych rejestrach się nie zmienią. Więc pamiętaj: jeżeli używasz któregokolwiek z tych rejestrów w funkcji wywoływanej, nigdy nie zapomnij odtworzyć ich przed zwróceniem sterowania do Windows. Funkcją wywoływaną jest Twoja własna funkcja, która jest wywoływana przez Windows. Oczywistym przykładem jest procedura windows. To nie znaczy, że nie możesz użyć tych czterech rejestrów. Możesz, tylko upewnij się, że odtworzyłeś ich zawartość, przed przekazaniem sterowania spowrotem do Windows.

Zawartość:

Poniżej jest szkielet programu. Jeżeli nie rozumiesz części kodu, nie panikuj. Wyjaśnię wszystko później.

.386
.MODEL Flat, STDCALL
.DATA
    <Twoje dane zainicjowane>
    ......
.DATA?
   <Twoje dane niezainicjowane>
   ......
.CONST
   <Twoje stałe>
   ......
.CODE
   <etykieta>
    <Twój kod>
   .....
    i <etykieta>


To wszystko! Przeanalizujmy ten szkielet programu.

.386
To jest dyrektywa assemblera, mówiąca mu ażeby użył zbioru instrukcji 80386. Możesz również użyć instrukcji .486, .586 ale bezpieczniej będzie ograniczyć je do .386. Są tutaj aktualnie dwie prawie identyczne formy dla każdego modelu CPU. .386/.386p, .486/.486p. Te "p" wersje są konieczne tylko wtedy, gdy Twój program używa instrukcji uprzywilejowanych. Instrukcje te są instrukcjami zarezerwowanymi przez CPU/system operacyjny trybu chronionego. One mogą również być wykorzystane przez uprzywilejowany kod, taki jak sterowniki urządzeń wirtualnych. Przez większość czasu Twój program będzie pracował w trybie nie- uprzywilejowanym, więc bezpiecznie jest używać wersji nie-p.

.MODEL FLAT, STDCALL
.MODEL jest dyrektywą assemblera, która określa model pamięci twojego programu. Pod Win32, jest tylko jeden model, FLAT model.
STDCALL mówi MASM o konwencji przekazywania parametru. Konwencja przekazywania parametru określa kierunek  przekazywania parametru, od-lewej-do-prawej lub od-prawej-do-lewej, oraz kto będzie równoważył (balansował) ramkę stosu po wywołaniu funkcji.
Pod Win16 są dwa typy konwencji wywołania C i konwencja wywołania typu PASCAL
C przekazuje parametry z prawej na lewo, to znaczy, parametr najbardziej z prawej jest kładziony najpierw. Sposób wywołania jest odpowiedzialny za równowagę ramki stosu po wywołaniu. Na przykład, w celu wywołania funkcji nazwanej foo(int first_param, int second_param, int third_param) w konwencji wywołania C kod assemblera będzie wyglądał:

push  [third_param]               ; Połóż trzeci parametr
push  [second_param]            ; Następnie drugi
push  [first_param]                ; I pierwszy
call    foo
add    sp, 12                                ; Sposób wywołania równoważy ramkę stosu
PASCAL Ta konwencja wywołania jest odwrotna do konwencji C. Przekazuje parametry z lewej na prawą stronę i wywołanie jest odpowiedzialne za równoważenie stosu po wywołaniu.
Win16 przyjął
konwencję PASCAL ponieważ to produkuje mniejszą ilość kodu i jest wygodne kiedy nie wiesz jak dużo parametrów będzie przekazanych do funkcji, jak w przypadku wsprintf(). W tym przypadku funkcja nie ma sposobu określenia ile parametrów będzie położonych na stos, więc nie można zbalansować stosu.
STDCALL jest połączeniem konwencji C i PASCAL. Przekazuje parametr z prawej na lewo ale wywołanie jest odpowiedzialne za zbalansowanie stosu dopiero po wywołaniu. Platforma Win32 używa STDCALL zawsze. Za wyjątkiem jednego przypadku: wsprintf(). Musisz użyć konwencji C z wsprintf().

.DATA
.DATA?
.CONST
.CODE

Wszystkie cztery dyrektywy oznaczają sekcję. Nie ma segmentów w Win32, pamiętasz? Ale możesz dzielić przestrzeń adresową na logiczne sekcje. Początek jednej sekcji oznacza koniec poprzedniej sekcji. Są dwie grupy sekcji: sekcja danych (data) i kodu (code). Sekcje data są podzielone na 3 kategorie:

  • .DATA    Ta sekcja zawiera zainicjowane dane Twojego programu.
  • .DATA?  Ta sekcja zawiera niezainicjowane dane Twojego programu. Czasami potrzebujesz wcześniejszego przydzielenia części pamięci ale nie chcesz inicjować go. Ta sekcja jest do tego celu. Zaletą niezainicjowanych danych jest to, że nie zajmują pamięci w pliku wykonywalnym. Na przykład, jeżeli przydzielisz 10 000 bajtów w Twojej sekcji .DATA?, twój program w pamięci zwiększy się o 10 000 bajtów, pomimo, że rozmiar programu będzie taki sam. W ten sposób mówisz assemblerowi jak dużą przestrzeń potrzebujesz, podczas gdy program jest ładowany do pamięci.
  • .CONST  Ta sekcja zawiera deklaracje stałych używanych przez Twój program. Stałe w tej sekcji nie mogą być modyfikowane w programie. One są właśnie *constant*.

Nie musisz używać wszystkich czterech sekcji w programie. Zadeklaruj tylko tą sekcję (te sekcje) którą potrzebujesz.

Jest tylko jedna sekcja dla kodu: .CODE. To jest miejsce, gzie Twój kod rezyduje.

<label>
end <label>
gdzie <label> jest etykietą użytą do określenia rozszerzenia Twojego kodu. Obie etykiety muszą być identyczne.  Cały Twój kod musi znajdować się pomiędzy<label> i end <label>


[Tutorial część 2]