Programy pod Win32 uruchamiają
się w trybie chronionym, który jest dostępny od 80286. Ale 80286 to już historia.
Więc musimy się tylko przejmować 80386 i jego następcami. Windows uruchamia
każdy program w oddzielnej przestrzeni wirtualnej. Oznacza to, że każdy prog pod
windows ma swoje własne 4GB adresowalnej pamięci. Jednak nie ozancza to, że
każdy winows'owy program ma 4GB fizycznej pamięci, tylko że program może zaadresować
każdy adres w tym przedziale. Windows zrobi wszystko co konieczne, by referencje
programu do pamięci były prawidłowe. Oczywiście program musi przestrzegać
zasad ustanowionych przez Windows, inaczej spowoduje piękny General Protection
Fault. Każdy program jest sam w swojej przestrzeni adresowej. Jest to kontrast
do sytuacji z Win16. Wszystkie programy Win16 mogą *widzieć* siebie nawzajem.
Pod Win32 jest inaczej. Ta cecha pomaga zredukować szanse, że jeden program
zapisze coś do kodu/danych innego programu.
Model pamięci jest także drastycznie inny niż w starych czasach "16-bitowego
świata". Pod Win32 nie musimy się już przejmować modelem pamięci ani segmentami !
Jest tylko jeden model pamięci: Model pamięci Flat (płaski). Nie ma już 64K
segmentów. Pamięć jest wielką 4-gigową przesrzenią. Oznacza to też, że już
nie będziesz musial się bawić rejestrami segmentów. Możesz użyć każdego rejestru
segmentu, aby zaadresować jakikolwiek punkt w pamięci. To BARDZO pomaga programistą.
Czyni to programowanie Win32asm tak proste jak C.
Kiedy programujesz pod Win32, musisz znać kilka podstawowych reguł. Jedna z tych
reguł mówi, że Windows używa esi, edi, ebp i ebx wewnętrznie i nie oczekuje
zmiany wartości w tych rejestrach. Więc pamiętaj tą pierwszą regułę: jeżeli
użyjesz jakiegokolwiek z tych czterech rejestrów w swojej funkcji,
nie zapomnij ich przywrócić do poprzedniego stanu zanim oddasz kontrolę w ręce Windows.
Funkcja ta jest twoją własną funkcją wywoływaną przez Windows. Oczywistym
przykładem jest procedura Windows. Nie oznacza to, że nie możesz używać tych czterech
rejestrów, możesz. Ale pamiętaj by je przywrócić zanim wyjdziesz do Windows.
To jest szkielet programu. Jeżeli czegoś nie rozumiesz, nie panikuj. Wszystko wytłumaczę później.
.386
.MODEL Flat, STDCALL
.DATA
<Twoje zainicjowane dane>
......
.DATA?
<Twoje niezainicjowane dane>
......
.CONST
<Twoje stałe>
......
.CODE
<Etykieta>
<Twój kod>
.....
end <Etykieta>
To wszystko! Przeanalizujmy ten szkielet :)
.386
To jest dyrektywa assemblera, mówiąca aby
użył zestawu instrukcji 80386. Możesz też użyć .486, .586 ale najbezpieczniej
jest się trzymać .386. Aktualnie występują dwie prawie identyczne formy
dla każedgo modelu CPU. .386/.386p, .486/.486p. Te wersje "p" są
konieczne tylko wtedy, gdy program używa instrukcji uprzywilejowanych.
Instrukcje uprzywilejowane są to instrukcje zarezerwowane przez CPU/system
operacyjny w trybie chronionym. Mogą zostać użyte tylko przez uprzywilejowany
kod, taki jak sterownik urządzenia wirtualnego (VxD). Przez większość czasu
twój program będzie pracował w trybie nieuprzywilejowanm więc bezpieczniej
jest używać wersji bez "p".
.MODEL FLAT, STDCALL
.MODEL
- w assemblerze dyrektywa ta specyfikuje model pamięci twojego programu. Pod Win32
jest tylko jeden model pamięci, model FLAT
.
STDCALL mówi MASM'owi o
sposobie przechodzenia parametrów. Sposób przechodzenia parametrów określa
kolejność przechodzenia parametrów - z lewej do prawej albo z prawej do lewej,
a także kto zbalansuje budowę stosu po wywołaniu funkcji.
Pod Win16, są dwa sposoby wywoływania -
C i PASCAL
Wywoływanie C
czyta parametry od prawej do lewej , czyli parametr najbardziej na prawo jest
ładowany pierwszy. Wywołujący jest odpowiedzialny za zbalansowanie budowy stosu
po wywołaniu. Na przykład, aby wywołać funkcję o nazwie foo (int pierwszy_parametr,
int drugi_parametr, int trzeci parametr) sposobem C kod asm będzie wyglądał tak:
push [third_param] ; Daj na stos trzeci parametrWywoływanie PASCAL jest odwrotnością sposobu C. Czyta parametry od lewej do prawej, a wywołujący jest odpowiedzialny za zbalansowanie budowy stosu po wywołaniu.
push [second_param] ; Po czym drugi
push [first_param] ; i pierwszy
call foo
add sp, 12 ; Wywołujący balansuje budowę stosu
.DATA
.DATA?
.CONST
.CODE
Te cztery dyrektywy są czymś co nazywa sie sekcją.
Pamiętasz, że w Win32 nie ma segmentów ? Ale możesz podzielić całą twoją pamięć adresową
na logiczne sekcje. Początek jakiejś sekcji określa koniec poprzedniej. Są dwie grupy sekcji:
data i code. Sekcje data są podzielone na 3 kategorie:
Nie musisz używać wszystkich trzech sekcji w swoim programie.
Zadeklaruj tylko te sekcje, które chcesz użyć.
Jest tylko jedna sekcja dla kodu: .CODE.
To tutaj znajduje się twój kod.
<Etykieta>
end <Etykieta>
gdzie <Etykieta> jest dowolną etykietą używaną do
określenia obszaru twojego kodu. Obie etykiety muszą być identyczne.
Cały twój kod musi znajdować się pomiędzy <Etykieta>
i end <Etykieta>