一. Win32汇编源程序的结构
hello world程序
.386
.model flat, stdcall
option casemap: none
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
.data
szCaption db ‘A MessageBox!', 0
szText db 'Hello, World!', 0
.code
start:
invoke MessageBox, NULL, offset szText, offset szCaption, MB_OK
invoke ExitProcess, NULL
end start
1. 模式定义
程序的第一部分是模式和源程序格式的定义语句:
.386
.model flat, stdcall
option casemap: none
这些指令定义了程序使用的指令集、工作模式和格式。
1). 指定使用的指令集
.386语句是汇编语言的伪指令,它在低版本的宏汇编中就已经存在,类似的指令还有:
.8086, .186, .286, .386/.386p, .486/.486p, .586/.586p等,
用于告诉编译器在本程序中使用的指令集。
在DOS的汇编中默认使用的是8086指令集,那时候如果在源程序中写入80386所特有
的指令或使用32位寄存器就会报错,为了在DOS环境下进行保护模式编程或仅为
了使用32位寄存器,常在DOS的汇编中使用.386来定义。
Win32环境工作在80386及以上的处理器中,所以这一句.386是必不可少的。
后面带p的伪指令则表示程序中可以使用特权指令,如:
mov cr0, eax
这一类指令必须在特权级0上运行,如果只指定.386,那么使用普通的指令是可以的,
编译时到这一句就会报错,如果我们要写的程序是VxD等驱动程序,中间要用到特权指令,
那么必须定义.386p,在应用程序级别的Win32编程中,程序都是运行在优先级3上,
不会用到特权指令,只需要定义.386就够了。80486和Pentium处理器是80386处理器指令的超集,
同样道理,如果程序中要用80486处理器或者Pentium处理器的指令,
则必须定义.486或.586。另外,Intel公司的80x86系列处理器从Pentium MMX开始增加了MMX
指令集,为了使用MMX指令,除了定义.586之外,还要加上一句.mmx伪指令:
.586
.mmx
2). .model语句
.model语句在低版本的宏汇编中已经存在,用来定义程序工作的模式,它的使用方法是:
.model 内存模式[, 语言模式][, 其他模式]
内存模式的定义影响最后生成的可执行文件,可执行文件的规模从小到大,可以有很多种类型,
在DOS的可执行程序中,有只用到64KB的.com文件,也有大大小小的.exe文件。
到了Win32环境下,又有了可以用4GB内存的PE格式
可执行文件,编写不同类型的可执行文件要用.model语句定义不同的参数,具体如下所示:
tiny 用来建立 .com文件,所有的代码、数据和堆栈都在同一个64 KB段内
small 建立代码和数据分别用一个64 KB段的 .exe文件
medium 代码段可以有多个64 KB段,数据段只有一个64 KB段
compact 代码段只有一个64 KB段,数据段可以有多个64 KB段
large 代码段和数据段都可以有多个64 KB段
huge 同large,并且数据段中的一个数组也可以超过64 KB
flat Win32程序使用的模式,代码和数据段使用同一个4 GB段
Windows程序运行在保护模式下,系统把每一个Win32应用程序都放在分开的虚拟地址空间中去运行,
也就是说,每一个应用程序都拥有其相互独立的4GB地址空间,对Win32程序来说,
只有一种内存模式,即flat(平坦)模式,意思是内存是很“平坦”的从0延伸到4GB,
在没有64KB段大小限制
如果定义了.model flat,MASM自动为各种段寄存器做了如下定义:
ASSUME cs:FLAT, ds:FLAT, ss:FLAT, es:FLAT, fs:ERROR, gs:ERROR
也就是说,CS,DS,ES和SS段全部使用平坦模式,FS和GS寄存器默认不使用,
这时若在源程序中使用FS或GS,在编译时会报错。如果有必要使用它们,
只需在使用前用下面的语句声明一下就可以了:
assume fs:nothing, gs:nothing 或者 assume fs:flat, gs:flat
在Win32汇编中,.model语句中还应该指定语言模式,即子程序的调用方式,
例子中用的是stdcall,它指出了调用子程序或Win32 API时参数传递的次序和堆栈平衡的方法,
相对于stdcall,不同的语言类型还有C,SysCall,BASIC,FORTRAN和PASCAL,
虽然各种高级语言在调用子程序时都是使用堆栈来传递参数,但它们的处理方法各有不同。
要和别的语言配合,就必须指定相应的语言种类。
Windows的API调用使用的是stdcall格式,所以在Win32汇编中没有选择,
必须在 .model中加上stdcall参数。
3). option语句
用option语句定义的选项有很多,如option language定义和option segment定义等,
在Win32汇编程序中,需要的只是定义option casemap:none,
这个语句定义了程序中的变量和子程序名是否对大小写敏感,
由于Win32 API中的API名称是区分大小写的,所以必须指定这个选项,
否则在调用API的时候会有问题。