WIN32汇编语言教程:第03章 使用MASM · 3.1 Win32汇编源程序的结构(2)

2. .model语句

.model语句在低版本的宏汇编中已经存在,用来定义程序工作的模式,它的使用方法是:

.model 内存模式[,语言模式][,其他模式]

内存模式的定义影响最后生成的可执行文件,可执行文件的规模从小到大,可以有很多种类型,在DOS的可执行程序中,有只用到64 KB的 .com文件,也有大大小小的 .exe文件。到了Win32环境下,又有了可以用4 GB内存的PE格式可执行文件,编写不同类型的可执行文件要用 .model语句定义不同的参数,具体如表3.1所示。

表3.1  内存模式

模式内 存 使 用 方 式
tiny

small

medium

compact

large

huge

flat

用来建立 .com文件,所有的代码、数据和堆栈都在同一个64 KB段内

建立代码和数据分别用一个64 KB段的 .exe文件

代码段可以有多个64 KB段,数据段只有一个64 KB段

代码段只有一个64 KB段,数据段可以有多个64 KB段

代码段和数据段都可以有多个64 KB段

同large,并且数据段中的一个数组也可以超过64 KB

Win32程序使用的模式,代码和数据段使用同一个4 GB段

在前面章节中已经提到过:Windows程序运行在保护模式下,系统把每一个Win32应用程序都放到分开的虚拟地址空间中去运行,也就是说,每一个应用程序都拥有其相互独立的4 GB 地址空间,对Win32程序来说,只有一种内存模式,即flat(平坦)模式,意思是内存是很“平坦”地从0延伸到 4 GB,再没有64 KB段大小限制。对比一下DOS的Hello World和Win32的Hello World开始部分的不同,DOS程序中有这样两句:

mov    ax,data
mov    ds,ax

意思是把数据段寄存器DS指向data数据段,data数据段在前面已经用 data segment 语句定义,只要DS不重新设置,那么从此以后指令中涉及的数据默认将从data数据段中取得,所以下面的语句是从data数据段取出szHello字符串的地址后再显示:

mov    ah,9
mov    dx,offset szHello
int    21h

纵观Win32汇编的源程序,没有一处可以找到ds或es等段寄存器的使用,因为所有的4 GB空间用32位的寄存器全部都能访问到了,不必在头脑中随时记着当前使用的是哪个数据段,这就是“平坦”内存模式带来的好处。

如果定义了 .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.4.2节中有详细的描述。

3. option语句

用option语句定义的选项有很多,如option language定义和option segment定义等,在Win32汇编程序中,需要的只是定义option casemap:none,这个语句定义了程序中的变量和子程序名是否对大小写敏感,由于Win32 API中的API名称是区分大小写的,所以必须指定这个选项,否则在调用API的时候会有问题。

3.1.2 段的定义

1. 段的概念

把上面的Win32的Hello World源程序中的语句归纳精简一下,再列在下面:

.386
.model flat,stdcall
option casemap:none
 <一些include语句>
.data
 <一些字符串、变量定义>
.code
 <代码>
 <开始标号>
    <其他语句>
end 开始标号

上一节讲到的选项、模式等定义并不会在编译好的可执行程序中产生什么东西,它们只是“说明”,而真正的数据和代码是定义在各个段中的,如上面的 .data段和 .code段,考虑到不同的数据类型,还可以有其他种类的数据段,下面是包含全部段的源程序结构:

.386
.model flat,stdcall
option casemap:none
 <一些include语句>
.stack [堆栈段的大小]
.data
 <一些初始化过的变量定义>
.data?
 <一些没有初始化过的变量定义>
.const
 <一些常量定义>
.code
 <代码>
 <开始标号>
    <其他语句>
end 开始标号

.stack,.data,.data?,.const和 .code是分段伪指令,Win32中实际上只有代码和数据之分,.data,.data?和 .const是数据段,.code是代码段,和DOS汇编不同,Win32汇编不必考虑堆栈,系统会为程序分配一个向下扩展的、足够大的段作为堆栈段,所以 .stack段定义常常被忽略。

  前面不是说过Win32环境下不用段了吗?是的,这些“段”实际上并不是DOS汇编中那种意义的段,而是内存的“分段”。上一个段的结束就是下一个段的开始,所有的“分段”合起来,包括系统使用的地址空间,就组成了整个可以寻址的4 GB空间。Win32环境的内存管理使用了80386处理器的分页机制,每个页(4 KB大小)可以自由指定属性,所以上一个4 KB可能是代码,属性是可执行但不可写,下一个4 KB就有可能是既可读也可写但不可执行的数据,再下面呢?有可能是可读不可写也不可执行的数据。Win32汇编源程序中“分段”的概念实际上是把不同类型的数据或代码归类,再放到不同属性的内存页(也就是不同的“分段”)中,这中间不涉及使用不同的段选择器。虽然使用和DOS汇编同样的 .code和 .data语句来定义,意思可是完全不同了!为了简单起见,在本书中还是简称“段”,读者应该注意到其中不同的含义。

上页:第03章 使用MASM · 3.1 Win32汇编源程序的结构(1) 下页:第03章 使用MASM · 3.1 Win32汇编源程序的结构(3)

第03章 使用MASM

版权所有 © 云南伯恩科技 证书:粤ICP备09170368号