友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
八万小说网 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

windows环境下32位汇编语言程序设计-第1部分

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!



  


第3章 使用MASM


3。1 Win32汇编源程序的结构(1)

     

 
 
经过第2章的准备工作,相信大家都已经建好了Win32汇编的工作环境,并已经可以编译程序了,现在让我们来编译所带光盘的Chapter03HelloWorld目录下的Hello World程序。这是一个相当小的程序,就和DOS时代下经典的Hello World程序一样,并没有涉及系统中很多的东西,甚至连Windows系统中基本的消息驱动机制也没有看到,它只是简单地弹出一个消息框,在上面显示了一句“Hello,World!”,并在文字的下面显示了一个“确定”按钮,就停在那里了,当用户按下“确定”按钮,程序就退出了,同时消息框消失。这个程序运行的结果如图3。1所示。



但这样一个小程序从结构来看,却“麻雀虽小,五脏俱全”,用它来举例说明Win32汇编源程序的框架是最合适不过的了,本章将从这个程序出发,探讨MASM在Win32汇编中的用法,由于篇幅所限,内容只涉及MASM在Win32编程中常用的部分。

3。1  Win32汇编源程序的结构

任何种类的语言,总是有基本的源程序结构规范,在讨论C语言的书中,大家都会记得这个非常经典的Hello World程序:

#include 

main()



         printf(〃Hello; worldn〃);



像这样一个程序,就说明了C语言中最基本的格式,main()中的括号和下面的花括号说明了一个函数的定义方法,printf语句说明了一个函数的调用方法,调用函数语句后面的分号也是基本的格式。C是一种高级语言,在C源程序中,不必为堆栈段、数据段和代码段的定义而担心,编译器会把程序中的字符串和语句代码分别放到它们该去的地方,程序开始执行的时候也会自己找到main()函数。而汇编是低级语言,必须为所有的东西找到它们该去的地方,所以在DOS的汇编中,Hello World又长成了这样一副模样:

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

; 堆栈段

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

stack              segment stack

                   db        100 dup (?)

stack              ends

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

; 数据段

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

data               segment

szHello            db        'Hello; world';0dh;0ah;''

data               ends

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

; 代码段

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

code               segment

                   assume    cs:code;ds:data;ss:stack

start:

                   mov       ax;data

                   mov       ds;ax

 

                   mov       ah;9

                   mov       dx;offset szHello

                   int       21h

 

                   mov       ah;4ch

                   int       21h

code               ends

                   end       start

在这个源程序中,stack段为堆栈找了个家,hello world字符串则跑到数据段中去了,代码则放在代码段中,程序的开始语句必须由最后一句end start来说明应该从start这个标号开始执行,整个程序在使用过DOS汇编的程序员眼里是非常的熟悉。

到了Win32汇编的时候,程序的基本结构还是如此,先来看一看这个看起来很新鲜的Win32的Hello world程序:

                。386

                。model flat;stdcall

                option casemap:none

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

; Include 文件定义

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

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

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

; 代码段

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

                de

start:

                invoke  MessageBox;NULL;offset szText;offset szCaption;MB_OK

                invoke  ExitProcess;NULL

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

                end     start

怎么样,看来和上面的C以及DOS汇编又不同了吧!但从include,。data和de等语句“顾名思义”也能看出一点苗头来,include应该就是包含别的文件,。data想必是数据段,de应该就是代码段了吧!接下来通过这个例了程序逐段介绍Win32汇编程序的结构。

3。1。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



 
来源:电子工业出版社 作者:罗云彬 上一页         回书目         下一页          
上一页         回书目         下一页          
  


第3章 使用MASM


3。1 Win32汇编源程序的结构(2)

    
2。 。model语句

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

。model 内存模式',语言模式'',其他模式'

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

表3。1  内存模式

模    式
 内 存 使 用 方 式
 
tiny

small

medium

pact

large

huge

flat
 用来建立 文件,所有的代码、数据和堆栈都在同一个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

  

。data

  

de

  

  

     

end 开始标号

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

。386

。model flat;stdcall

option casemap:none

  

。stack '堆栈段的大小'

。data

  

。data?

  

nst

  

de

  

  

     

end 开始标号

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

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



 
来源:电子工业出版社 作者:罗云彬 上一页         回书目         下一页          
上一页         回书目         下一页          
  


第3章 使用MASM


3。1 Win32汇编源程序的结构(3)

    
2。 数据段

。data,。data?和 nst定义的是数据段,分别对应不同方式的数据定义,在最后生成的可执行文件中也分别放在不同的节区(Section)中。程序中的数据定义一般可以归纳为3类:

第一类是可读可写的已定义变量。这些数据在源程序中已经被定义了初始值,而且在程序的执行中有可能被更改,如一些标志等,这些数据必须定义在 。data段中,。data段是已初始化数据段,其中定义的数据是可读可写的,在程序装入完成的时候,这些值就已经在内存中了,。data段存放在可执行文件的_DATA节区内。

第二类是可读可写的未定义变量。这些变量一般是当做缓冲区或者在程序执行后才开始使用的,这些数据可以定义在 。data段中,也可以定义在 。data?段中,但一般把它放到 。data?段中。虽然定义在这两种段中都可以正常使用,但定义在 。data?段中不会增大 。exe文件的大小。举例说明,如果要用到一个100 KB的缓冲区,可以在数据段中定义:

szBuffer            db      100 * 1024 dup (?)

如果放在 。data段中,编译器认为这些数据在程序装入时就必须有效,所以它在生成可执行文件的时候保留了所有的100 KB的内容,即使它们是全零!如果程序其他部分的大小是50 KB,那么最后的 。exe文件就会是150 KB大小,如果缓冲区定义为1 MB,那么 。exe文件会增大到1 050 KB。。data?段则不同,其中的内容编译器会认为程序在开始执行后才会用到,所以在生成可执行文件的时候只保留了大小信息,不会为它浪费磁盘空间。和上面同样的情况下,即使缓冲区定义为1 MB,可执行文件同样只有50 KB!总之,。data?段是未初始化数据段,其中的数据也是可读可写的,但在可执行文件中不占空间,。data?段在可执行文件中存放在_BSS节区中。

第三类数据是一些常量。如一些要显示的字符串信息,它们在程序装入的时候也已经有效,但在整个执行过程中不需要修改,这些数据可以放在 nst段中,nst段是常量段,它是可读不可写的。一般为了方便起见,在小程序中常常把常量一起定义到 。data段中,而不另外定义一个 nst段。在程序中如果不小心写了对 nst段中的数据做写操作的指令,会引起保护错误,Windows会显示一个如图3。2所示的提示
返回目录 下一页 回到顶部 7 6
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!