Jak to zwykle w tego typu kursach bywa, część praktyczną zaczniemy od prostego programu, który wyświetla komunikat tekstowy. Tego typu komunikaty są niezwykle często stosowane w programach Windows’owych i służą najczęściej do powiadomienia użytkownika o błędzie.
Szkielet programu
Pisanie niemal każdego programu w assemblerze zaczynamy od wstawienia następującego fragmentu:
.386 .model flat, stdcall option casemap:none
include \masm32\include\windows.inc
;deklaracje bibliotek, których będziemy używać
.const
;stałe
.data
;zmienne, którym nadajemy początkowe wartości
.data?
;zmienne niezdefiniowane (podajemy tylko typ zmiennej)
.code
start:
;kod
end start
W pierwszej linii określamy zestaw instrukcji assemblera, których będziemy używać. Już procesor klasy 386 dostarcza nam tyle instrukcji, że możemy z powodzeniem stosować ten model nawet będąc zaawansowanymi koderami i nie będziemy odczuwać niedosytu. Zamiast 386 można w razie potrzeby użyć 486, 586, MMX itp. Kolejna linijka oznacza model pamięci stosowany w programie. Jedynym dopuszczalnym w Windows jest model flat. Myślę, że na tym etapie nauki nie ma potrzeby dalszego rozwodzenia się na ten temat. „Option casemap: none” mówi kompilatorowi, że ma rozróżniać wielkość liter w nazwach zmiennych i innych symboli. Natomiast plik windows.inc, który poprzez dyrektywę include dołączamy do programu, zawiera definicje wszystkich stałych używanych w API Windows.
Aby wyświetlić komunikat, musimy wiedzieć która funkcja API jest za to odpowiedzialna i w jakiej bibliotece się znajduje. Te informacje można znaleźć w „Win32 Programmer’s Reference”. Stąd wiemy, że ta funkcja to MessageBox i aby zadziałała, należy jej podać kolejno następujące parametry:
- Uchwyt okna nadrzędnego (jeżeli takowego nie mamy, podajemy 0)
- Adres w pamięci tekstu, który zostanie wyświetlony w głównej części okna
- Adres tekstu, który pojawi się na pasku tytułowym
- Styl komunikatu – w tym miejscu wybieramy przyciski i ikonę, które pojawią się w oknie
Aby rozpocząć kodowanie programu, uruchamiamy Quick Editor, znajdujący się w katalogu Macro Assemblera, tworzymy w nim nowy plik o dowolnej nazwie (z rozszerzeniem asm) i umieszczamy w nim powyższy szkielet.
Funkcje, których bedziemy używać znajdują się w bibliotekach kernel32 i user32, musimy wiec je zadeklarować w przeznaczonym do tego miejscu.
include \masm32\include\user32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib
W sekcji data definiujemy zmienne zawierające to, co chcemy wyświetlić w okienku, np.
msgTytul db "MASM32 Tutorial",0 msgTekst db "Pierwszy komunikat.",0
MsgTytul i msgTekst to nazwy zmiennych, db oznacza typ zmiennej (w tym przypadku jest to bajt, a właściwie zbiór bajtów – każda litera, każdy znak zajmuje w pamięci jeden bajt). Poza db możemy stosować jeszcze dw (word – dwa bajty, których używa się do zapisu liczb z przedziału 0..65535), dd (double word – 4 bajty), dq (quad word – 8 bajtów) oraz kilka innych typów danych, które nie będą nam potrzebne (zainteresowanych odsyłam do pomocy dołączanej do Macro Assemblera). Bajt 0 natomiast musi konczyć kazdy ciąg tekstowy.
W miejscu przeznaczonym na kod, należy wywołać funkcję wyświetlającą okienko z naszym komunikatem. MASM posiada specjalną pseudo-instrukcję służącą do wywoływania funkcji i procedur – invoke, która bardzo ułatwia pisanie kodu. Parametry dla funkcji podajemy po prostu po przecinku:
invoke MessageBox,0,addr msgTekst,addr msgTytul,MB_OK
„Addr” przed nazwami zmiennych jest potrzebny po to, aby kompilator zamienił całe wyrażenie na adres, a nie na zawartość zmiennej.
Aby program działał prawidłowo i nie wykonywał nieprawidłowej operacji, należy go zakończyć. Służy do tego funkcja ExitProcess. Dopisujemy więc do kodu jeszcze fragment:
invoke ExitProcess,0
Sekcje „.const” oraz „.data?” są puste, więc można je spokojnie usunąć. Cały kod wygląda więc tak:
.386 .model flat, stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib
.data
msgTytul db „MASM32 Tutorial”,0
msgTekst db „Pierwszy komunikat.”,0
.code
start:
invoke MessageBox,0,addr msgTekst,addr msgTytul,MB_OK
invoke ExitProcess,0
end start
Po kompilacji programik ten wyświetli okienko z komunikatem i przyciskiem OK. Jeżeli chcemy, aby zamiast OK były inne przyciski, wpisujemy odpowiednie wartości zamiast MB_OK:
- MB_ABORTRETRYIGNORE – przerwij, ponów próbę, zignoruj
- MB_OKCANCEL – ok, anuluj
- MB_RETRYCANCEL – ponów próbę, anuluj
- MB_YESNO – tak, nie
- MB_YESNOCANCEL – tak, nie, anuluj
Gdy chcemy dodatkowo wyswietlic ikonkę, dopisujemy słowo „or” oraz odpowiednią wartość z listy poniżej.
- MB_ICONEXCLAMATION
MB_ICONWARNING – wykrzyknik - MB_ICONINFORMATION
MB_ICONASTERISK – mała litera „i” - MB_ICONQUESTION – znak zapytania
- MB_ICONSTOP
MB_ICONERROR
MB_ICONHAND – krzyżyk
Przykład – wyświetlanie komunikatu z przyciskami przerwij, ponów próbę, zignoruj i znakiem zapytania:
invoke MessageBox,0,addr msgTekst,addr msgTytul,\ MB_ABORTRETRYIGNORE or MB_ICONQUESTION
Utworzyliśmy więc okienko z komunikatem. Czasem przydałoby się jednak wiedzieć który przycisk został naciśnięty przez użytkownika. Z pomocą przychodzi nam znowu „Win32 Programmer’s Reference”, w którym czytamy: „The return value is zero if there is not enough memory to create the message box. If the function succeeds, the return value is one of the following menu-item values returned by the dialog box:
Value | Meaning |
---|---|
IDABORT | Abort button was selected. |
IDCANCEL | Cancel button was selected. |
IDIGNORE | Ignore button was selected. |
IDNO | No button was selected. |
IDOK | OK button was selected. |
IDRETRY | Retry button was selected. |
IDYES | Yes button was selected.” |
Zatem funkcja zwraca np. IDYES gdy użytkownik nacisnął przycisk „Tak”. Warto wiedzieć, że funkcje API zawsze zwracają wartości w rejestrze EAX. Zatem aby sprawdzić czy „Tak” został naciśnięty, należy porównać rejestr eax ze stałą IDYES. Assembler posiada specjalną instrukcję służącą do porównywania dwóch wartości, jednak na razie możemy sobie ułatwić kodowanie, używaćjąc tzw. high-level syntax, czyli pseudo-instrukcji, które kompilator zamieni na odpowiadające im instrukcje procesora. Aby sprawdzić jaką wartość zawiera zmienna lub rejestr i odpowiednio na to zareagować, stosujemy blok instrukcji IF. Składnia wygląda następująco:
.IF warunek polecenia .ELSEIF warunek polecenia .ELSE polecenia .ENDIF
Jako warunek podajemy zwyczajne, matematycznie zapisane równanie. Możemy korzystać z następujących operatorów porównawczych: == (równy), > (większy), >= (większy lub równy), < (mniejszy), <= (mniejszy lub równy), != (różny) oraz ze spójników logicznych: || (alternatywa – lub), && (koniunkcja – i), ! (negacja).
Za wywołaniem funkcji wyświetlającej komunikat wpisujemy następujący kod:
.IF eax==IDYES invoke MessageBox, 0, addr YesTxt, addr msgTytul, MB_OK or MB_ICONEXCLAMATION .ELSE invoke MessageBox, 0, addr NoTxt, addr msgTytul, MB_OK or MB_ICONEXCLAMATION .ENDIF
Natomiast w sekcji data deklarujemy potrzebne zmienne:
YesTxt db "Wcisnąłeś 'Tak' !",0 NoTxt db "Wcisnąłeś 'Nie' !",0
Powyższe fragmenty spowodują, że gdy użytkownik wciśnie przycisk „Tak”, zostanie wyświetlony komunikat z tekstem „Wciśnąłeś 'Tak’ !”, a w innym przypadku na ekranie pojawi sie napis „Wcisnąłeś 'Nie’ !”.
Analogicznie można reagować na inne przyciski (IDABORT, IDCANCEL, IDIGNORE, IDOK, IDRETRY).
Cały program wygląda więc tak:
.386 .model flat, stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib
.data
msgTytul db „MASM32 Tutorial”,0
msgTekst db „Wciśnij tak lub nie.”,0
YesTxt db „Wcisnąłeś 'Tak’ !”,0
NoTxt db „Wcisnąłeś 'Nie’ !”,0
.code
start:
invoke MessageBox,0,addr msgTekst,addr msgTytul,MB_YESNO or MB_ICONQUESTION
.IF eax==IDYES
invoke MessageBox,0,addr YesTxt,addr msgTytul,MB_OK or MB_ICONEXCLAMATION
.ELSE
invoke MessageBox,0,addr NoTxt,addr msgTytul,MB_OK or MB_ICONEXCLAMATION
.ENDIF
invoke ExitProcess,0
end start
Przykładowy program do tej części kursu można pobrać stąd (836 bajtów). W następnym odcinku zajmiemy sie tym, co w Windows najważniejsze, czyli tworzeniem okien.
No i wiadomo kto tu jest najlepszy!
Super!!!! krok po kroku, czytelnie, tego mi bylo trzeba. Dziękuję:)
Uczę się właśnie do kolokwium z MASM’a. Ten poradnik jest genialny:) chwała za to AUTOROWI!!
Nauczylem sie wiele