Wszystkie systemy Windows począwszy od Windows 3.1 udostępniają tzw API. Jest to zestaw procedur dostępnych dla programisty za pomocą których może budować działające aplikacje, a trzymając się pewnych standardów także przyśpieszyć znacząco budowę swoich programów. W programie pisanym w Asemblerze pod systemem Windows wykorzystywane są one na szeroką skale.
Powstawanie typowego programu w asemblerze wiąże się z podziałem kodu programu asemblerowego na osobne bloki tj. na sekcję kodu .CODE
i sekcję danych .DATA
.
Komentarze w programie definiujemy po znaku ;
.Dane w programie to wszystkie zmienne, czyli liczby, tablice, rekordy, wszystko co wykorzystujemy w celu przeprowadzenia w programie jakiejkolwiek operacji. Jednak zdefiniowanie sekcji danych w schemacie najprostszego programu nie jest wymagane. O ile w językach wysokiego poziomu pewne schematy budowania programów są ściśle narzucone przez kompilator, o tyle w asemblerze można utworzyć plik wykonywalny od całkowitych podstaw i ma się na tę operację największy możliwy wpływ. Przykładowy schemat najprostszego programu można przedstawić następująco:
.586 ;typ instrukcji procesora
.MODEL
FLAT,STDCALL
.CODE;początek sekcji kodu
Start:
RET
END Start
Dyrektywa .586
ustala że program może wykorzystywać instrukcje procesora 80586
lub wcześniejsze. Polecenie MODEL FLAT,STDCALL
określa tryb pamięci używany w programie jako płaski (flat), a także definiuje sposób wywołania wszystkich procedur stosowanych w programie na tzw. STDCALL
. Określenie sekcji kodu dokonujemy za pomocą .CODE
. Początek programu określamy za pomocą etykiety Start:
. Program nie istnieje jeśli nie zawiera żadnej instrukcji wykonywalnej. W przypadku najprostszego programu zdefiniowanego powyżej jest to instrukcja RET
czyli powrót z procedury, dzięki której sterowanie oddawane jest z powrotem do systemu operacyjnego. Polecenie END
określa koniec programu. Etykietę Start
należy wpisać również po dyrektywie END
, ponieważ w ten sposób informujemy asembler, żeby wpisał dane punktu wejścia programu do nagłówka ładowanego modułu .
Z technicznego punktu widzenia system wywołuje program , a na stosie odkłada adres powrotu , umiejscowiony gdzieś w kodzie systemu. Schemat wywołania programu określony jest na Rys1.
Wczytanie programu do pamięci powoduje załadowanie do pamięci odpowiednio sekcji kodu i danych. Tym samym zmienne, do których program odwołuje się w sekcji kodu automatycznie są inicjowane przez system. Następuje przekazanie sterowania przez system do pierwszej instrukcji programu. W momencie wywołania funkcji ExitProcess sterowanie oddawane jest do systemu, pobierana jest wcześniej odłożona na stos wartość przez system operacyjny i tym samym program kończy działanie, poprzez "powrót z procedury" i zwolnienie zasobów programu .
Zanim jednak uruchomimy nasz program
na etapie konsolidacji następuje dołączenie
innych modułów obiektowych i bibliotek do modułu głównego który w punkcie startowym segmentu musi mieć zdefiniowaną etykietę Start:
. Za pomocą dyrektywy INCLUDE
możemy dołączyć pliki zawierające dane, czy też procedury, które zostaną dołączone do modułu głównego i wywołane razem z nim. Pliki dołączone tą dyrektywą maja rozszerzenie .inc . Przykładowo do kodu podanego powyżej możemy dołączyć standardową bibliotekę zawierającą wszystkie pliki nagłówkowe, zawierającą się w folderze include
o nazwie masm32rt.inc
:
INCLUDE \masm32\include\masm32rt.inc ;dołączona biblioteka
.586 ;typ instrukcji procesora
.MODEL
FLAT,STDCALL
.CODE;początek sekcji kodu
Start:
RET
END Start
Alternatywą dla INCLUDE
jest INCLUDELIB
, dzięki której dołącza się procedury z innych modułów obiektowych. Dyrektywa zostaje zapisana w kodzie obiektowym i jest później wykorzystywana przez program link.exe. Ogólnie wywoływanie procedur zdefiniowanych w innych modułach obiektowych można przeprowadzić na 2 sposoby:
a) Wykorzystując dyrektywę INVOKE
w następującej postaci:
INVOKE NAZWA_PROCEDURY,p4,p3,p2,p1
,gdzie p4, p3, p2, p1 to parametry procedury.
b) Zdefiniowanie procedury w module głównym w następujący sposób:
EXTERN NAZWA_PROCEDURY@16:NEAR
,gdzie EXTERN
oznacza jej deklarację,
a liczba podana za znakiem @
wyznacza ilość potrzebnych bajtów jakie należy odłożyć na stos dla parametrów przed wywołaniem procedury. W procedurze zdeklarowanej powyżej jest to 16 bajtów. NEAR
zaznacza, iż zdefiniowana procedura znajduje się w tym samym segmencie, co moduł wywołujący, ponieważ jak już wcześniej było wspomniane w systemie Windows, pamięć traktowana jest jak jeden segment - sekcja .MODEL FLAT
.
Następnie taką procedurę wywołujemy za pomocą zwykłej instrukcji CALL
, a jej parametry podajemy od góry do dołu, zaczynając od parametru najbardziej po prawej stronie. W przypadku procedury NAZWA_PROCEDURY@16
, którą przykładowo zdeklarowaliśmy i wywoływaliśmy również za pomocą funkcji INVOKE
, analogiczne wywołanie tym sposobem wyglądałoby następująco:
PUSH p1
PUSH p2
PUSH p3
PUSH p4
CALL NAZWA_PROCEDURY@16