我們先假設您已知道了如何使用MASM。如果您還不知道的話,請下載 win32asm.exe ,並請仔細研讀其中所附帶的文檔資料。好,如果您已准備就緒,我們這就開始吧!
理論:
WIN32 程序運行在保護模式下的,保護模式的歷史可以追溯到 80286。而今 80286 已成為了歷史。所以我們將只把精力集中於 80386 及後續的X86 系列 CPU。Windows 把每一個 Win32 應用程序放到分開的虛擬地址空間中去運行,也就是說每一個應用程序都擁有其相互獨立的 4GB 地址空間,當然這倒不是說它們都擁有 4GB 的物理地址空間,而只是說能夠在 4GB 的范圍內尋址。操作系統將會在應用程序運行時完成 4GB 的虛擬地址和物理內存地址間的轉換。這就要求編寫應用程序時必須格守 Windows 的規范,否則極易引起內存的保護模式錯誤。而過去的 Win16 內存模式下,所有的應用程序都運行於同一個 4GB 地址空間,它們可以彼此"看"到別的程序的內容,這極易導致一個應用程序破壞另一個應用程序甚至是操作系統的數據或代碼。
和 16 位 Windows 下的把代碼分成 DATA,CODE 等段的內存模式不同,WIN32 只有一種內存模式,即 FLAT 模式,意思是"平坦"的內存模式,再沒有 64K 的段大小限制,所有的 WIN32 的應用程序運行在一個連續、平坦、巨大的 4GB 的空間中。這同時也意味著您無須和段寄存器打交道,您可以用任意的段寄存器尋址任意的地址空間,這對於程序員來說是非常方便的,這也使得用32位匯編語言和用C語言一樣方便。 在Win32下編程,有許多重要的規則需要遵守。有一條很重要的是:Windows 在內部頻繁使用 ESI,EDI,EBP,EBX 寄存器,而且並不去檢測這些寄存器的值是否被更改,這樣當您要使用這些寄存器時必須先保存它們的值,待用完後再恢復它們,一個最顯著的應用例子就是 Windows 的 CallBack 函數中。
內容:
下面的程序段是一個框架, 若您現在還不知道這些指令的確切意義的話,沒關系, 隨後我就會給大家詳細解釋。
.386
.MODEL Flat, STDCALL
.DATA
<Your initialized data>
......
.DATA?
<Your uninitialized data>
......
.CONST
<Your constants>
......
.CODE
<label>
<Your code>
.....
end <label>
框架就這麼簡單,好,我現在就給您解釋:
.386
這是一個匯編語言偽指令,他告訴編譯器我們的程序是使用80386指令集編寫的。您還可以使用 .486、.586, 但最安全的還是使用.386。對於每一種CPU有兩套幾乎功能相同偽指令: .386/.386P、 486/.486P、 586/.586P。 帶P的指令標明您的程序中可以用特權級指令。特權級指令是保留給操作系統的,如虛擬設備驅動程序。在大多數時間,您的程序都無須運行在RING0層,故用不帶後綴P的偽指令已足夠了。
.MODEL FLAT,STDCALL
.MODEL 是用來指定內存模式的偽指令,在Win32下,只有一種內存模型,那就是FLAT。 STDCALL 告訴編譯器參數的傳遞約定。參數的傳遞約定是指參數傳達時的順序(從左到右或從右到左)和由誰恢復堆棧指針(調用者或被調用者)。在Win16下有兩種約定:C 和 PASCAL。C 約定規定參數傳遞順序是從右到左,即最右邊的參數最先壓棧,由調用者恢復堆棧指針。
例如:為調用函數 foo ( int first_param, int second_param, int third_param ); 按C約定的匯編代碼應該是這樣的:
push [third_param]
push [second_param]
push [first_param]
call foo
add esp, 3 * 4 ;調用者自己恢復堆棧指針
PASCAL約定和C約定正好相反,它規定參數是從左向右傳遞,由被調用者恢復堆棧。Win16采用了PASCAL約定, 因為PASCAL約定產生的代碼量要小。當不知道參數的個數時,C約定特別有用。如在函數wsprintf () 中, wsprintf預先並不知道要傳遞幾個參數,所以它不知道如何恢復堆棧。STDCALL是C約定和PASCAL約定的混合體,它規定參數的傳遞是從右到左,恢復堆棧的工作交由被調用者。Win32只用STDCALL約定,但除了一個特例,即:wsprintf。
.DATA .DATA? .CONST .CODE
上面的四個偽指令是"分段"(SECTION)偽指令。我們上面剛講過Win32下沒有"段"(SEGMENT)的概念,但是您可以把您的程序分成不同的"分段", 一個"分段"的開始即是上一個"分段"的結束。WIN32中只有兩種性質的"分段":DATA和CODE。
其中DATA"分段"又分為三種:
.DATA 其中包括已初始化的數據。
.DATA? 其中包括未初始化的數據。比如有時您僅想預先分配一些內存但並不想指定初始值。使用未初始化的數據的優點是它不占據可執行文件的大小,如:若您要在 .DATA? 段中分配10,000字節的空間,您的可執行文件的大小無須增加10,000字節,而僅僅是要告訴編譯器在裝載可執行文件時分配所需字節。
.CONST 其中包括常量定義。這些常量在程序運行過程中是不能更改的。 應用程序並不需要以上所有的三個"分段", 可以根據需要進行定義。
.CODE 這是代碼"分段"。
<譯者注:實際上,分段並不是象在 Dos 下一樣,為不同的段分別指出不同的段寄存器,因為 Windows 下只有一個 4GB 的段,Windows 程序中的分段表現在當程序裝載時,賦予不同的分段不同的屬性,比如說當你的程序加載時,對於 Ring3 程序來說,.code 段是不可寫的,而 .data 段是可寫的,如果你嘗試象在 Dos 下一樣寫自己的代碼部分,你會得到一個藍屏錯誤>
<label>
end <label>
是用來唯一標識您的代碼范圍的標簽, 兩個標簽必須相同,應用程序的所有可執行代碼必修在兩個標簽之間。