八喜电子书 > 经管其他电子书 > windows环境下32位汇编语言程序设计 >

第4部分

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

小说: windows环境下32位汇编语言程序设计 字数: 每页4000字

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



肺募腥フ叶杂Φ氖担儆τ玫交惚嘣闯绦蛑校空庋嵊�80%以上的时间花在做无用功上(最后还是要骂Microsoft为什么不提供汇编格式的头文件,毕竟MASM32工具包不是Microsoft出的)。

有时候由于版本的原因,当使用最新的API手册时,会发现有些参数使用的常量在Windows。inc中并没有定义,这下惨了,谁都不知道类似于MB_XXXYYY的东西代表什么数值,Microsoft的《Microsoft Programmer's Reference》手册中从来就不会把参数对应的数值写进去。遇到这种情况,只有拿出最原始的办法了,就是到最新的Visual C++或SDK的include目录中去,在C语言格式的 。h头文件中把定义找出来,然后自行增补到Windows。inc中去。如果这样也找不到定义值的话,那只好放弃使用这个API了。



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


第3章 使用MASM


3。3 标号、变量和数据结构(1)

    
当程序中要跳转到另一位置时,需要有一个标识来指示新的位置,这就是标号,通过在目的地址的前面放上一个标号,可以在指令中使用标号来代替直接使用地址。

使用变量是任何编程语言都要遇到的工作,Win32汇编也不例外,在MASM中使用变量也有需要注意的几个问题,错误地使用变量定义或用错误的方法初始化变量会带来难以定位的错误。变量是计算机内存中已命名的存储位置,在C语言中有很多种类的变量,如整数型、浮点型和字符串等,不同的变量有不同的用途和尺寸,比如说虽然长整数和单精度浮点数都是32位长,但它们的用途不同。

顾名思义,变量的值在程序运行中是需要改变的,所以它必须定义在可写的段内,如 。data和 。data?,或者在堆栈内。按照定义的位置不同,MASM中的变量也分为全局变量和局部变量两种。

在MASM中标号和变量的命名规范是相同的,它们是:

1。 可以用字母、数字、下划线及符号@、和?。

2。 第一个符号不能是数字。

3。 长度不能超过240个字符。

4。 不能使用指令名等关键字。

5。 在作用域内必须是惟一的。

3。3。1  标号

1。 标号的定义

当在程序中使用一条跳转指令的时候,可以用标号来表示跳转的目的地,编译器在编译的时候会把它替换成地址,标号既可以定义在目的指令同一行的头部,也可以在目的指令前一行单独用一行定义,标号定义的格式是:

标号名:         目的指令

标号的作用域是当前的子程序,在单个子程序中的标号不能同名,否则编译器不知该用哪个地址,但在不同的子程序中可以有相同名称的标号,这意味着不能从一个子程序中用跳转指令跳到另一个子程序中。

在低版本的MASM中,标号在整个程序中是惟一的,子程序中的标号也可以从整个程序的任何地方转入。但Win32汇编使用的高版本MASM中不允许这样,这是为了提供对局部变量和参数的支持,由于在子程序入口处有对堆栈的初始化指令,所以一个子程序不允许有多个入口,其结果就是标号的作用域变成了单个子程序范围。

2。 MASM中的@@

在DOS时代,为标号起名是个麻烦的事情,因为汇编指令用到跳转指令特别多,任何比较和测试等都要涉及跳转,所以在程序中会有很多标号,在整个程序范围内起个不重名的标号要费一番功夫,结果常常用addr1和addr2之类的标号一直延续下去,如果后来要在中间插一个标号,那么就常常出现addr1_1和loop10_5之类奇怪的标号。

实际上,很多标号只会使用一到两次,而且不一定非要起个有意义的名称,如汇编程序中下列代码结构很多:

          mov   cx;1234h

          cmp   flag;1

          jz    loc1

          mov   cx;1000h

loc1:

          …

          loop  loc1

loc1在别的地方就再也用不到了,对于这种情况,高版本的MASM用@@标号去代替它:

       mov   cx;1234h

       cmp   flag;1

       jz    @F

       mov   cx;1000h

@@:

       …

       loop  @B

当用@@做标号时,可以用@F和@B来引用它,@F表示本条指令后的第一个@@标号,@B表示本条指令前的第一个@@标号,程序中可以有多个@@标号,@B和@F只寻找匹配最近的一个。

 不要在间隔太远的代码中使用@@标号,因为在以后的修改中@@和@B,@F中间可能会被无意中插入一个新的@@,这样一来,@B或@F就会引用到错误的地方去,源程序中@@标号和跳转指令之间的距离最好限制在编辑器能够显示的同一屏幕的范围内。

3。3。2  全局变量

1。 全局变量的定义

全局变量的作用域是整个程序,Win32汇编的全局变量定义在 。data或 。data?段内,可以同时定义变量的类型和长度,格式是:

变量名      类型    初始值1;初始值2,……

变量名      类型    重复数量 dup (初始值1;初始值2,……)

MASM中可以定义的变量类型相当多,具体如表3。2所示。

表3。2  变量的类型

名    称                              表示方式               缩    写         长度(字节)
 
字节
 byte
 db
 1
 
字
 word
 dw
 2
 
双字(doubleword)
 dword
 dd
 4
 
三字(farword)
 fword
 df
 6
 
四字(quadword)
 qword
 dq
 8
 
十字节BCD码(tenbyte)
 tbyte
 dt
 10
 
有符号字节(signbyte)
 sbyte
  
 1
 
有符号字(signword)
 sword
  
 2
 
有符号双字(signdword)
 sdword
  
 4
 
单精度浮点数
 real4
  
 4
 
双精度浮点数
 real8
  
 8
 
10字节浮点数
 real10
  
 10
 

所有使用到变量类型的情况中,只有定义全局变量的时候类型才可以用缩写,现在先来看全局变量定义的几个例子:

。data

wHour           dw      ?                ;例1

wMinute         dw      10               ;例2

_hWnd           dd      ?                ;例3

word_Buffer     dw      100 dup (1;2)    ;例4

szBuffer        byte    1024 dup (?)     ;例5

szText          db      'Hello;world!'   ;例6

●   例1定义了一个未初始化的word类型变量,名称为wHour。

●   例2定义了一个名为wMinute的word类型变量。

●   例3定义了一个双字类型的变量_hWnd。

●   例4定义了一组字,以0001,0002,0001,0002,…的顺序在内存中重复100遍,一共是200个字。

●   例5定义了一个1 024字节的缓冲区。

●   例6定义了一个字符串,总共占用了12字节。两头的单引号是定界的符号,并不属于字符串中真正的内容。

在byte类型变量的定义中,可以用引号定义字符串和数值定义的方法混用,假设要定义两个字符串“Hello;World! ”和“Hello again”,每个字符串后面跟回车和换行符,最后以一个0字符结尾,可以定义如下:

szText          db      'Hello;world!';0dh;0ah;'Hello again';0dh;0ah;0

2。 全局变量的初始化值

全局变量在定义中既可以指定初值,也可以只用问号预留空间,在 。data?段中,只能用问号预留空间,因为 。data?不能指定初始值,这里就有一个问题:既然可以用问号预留空间,那么在实际运行的时候,这个未初始化的值是随机的还是确定的呢?在全局变量中,这个值就是0,所以用问号指定的全局变量如果要以0为初始值的话,在程序中可以不必为它赋值。



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


第3章 使用MASM


3。3 标号、变量和数据结构(2)

    
3。3。3  局部变量

局部变量这个名称最早源于高级语言,主要是为了定义一些仅在单个函数里面有用的变量而提出的,使用局部变量能带来一些额外的好处,它使程序的模块化封装变得可能,试想一下,如果要用到的变量必须定义在程序的数据段里面,假设在一个子程序中要用到一些变量,当把这个子程序移植到别的程序时,除了把代码移过去以外,还必须把变量定义移过去。而即使把变量定义移过去了,由于这些变量定义在大家都可以用的数据段中,就无法对别的代码保持透明,别的代码有可能有意无意地修改它们。还有,在一个大的工程项目中,存在很多的子程序,所有的子程序要用到的变量全部定义到数据段中,会使数据段变得很大,混在一起的变量也使维护变得非常不方便。

局部变量这个概念出现以后,两个以上子程序都要用到的数据才被定义为全局变量统一放在数据段中,仅在子程序内部使用的变量则放在堆栈中,这样子程序可以编成“黑匣子”的模样,使程序的模块结构更加分明。

局部变量的作用域是单个子程序,在进入子程序的时候,通过修改堆栈指针esp来预留出需要的空间,在用ret指令返回主程序之前,同样通过恢复esp丢弃这些空间,这些变量就随之无效了。它的缺点就是因为空间是临时分配的,所以无法定义含有初始化值的变量,对局部变量的初始化一般在子程序中由指令完成。

在DOS时代,低版本的宏汇编本来无所谓全局变量和局部变量,所有的变量都是定义在数据段里面的,能让被所有的子程序或主程序存取,就相当于现在所说的全局变量,用汇编语言在堆栈中定义局部变量是很麻烦的一件事情。要和高级语言做混合编程的时候,程序员往往很痛苦地在边上准备一张表,表上的内容是局部变量名和ebp指针的位置关系。

1。 局部变量的定义

MASM用local伪指令提供了对局部变量的支持。定义的格式是:

local       变量名1''重复数量''':类型';变量名2''重复数量''':类型'……

local伪指令必须紧接在子程序定义的伪指令proc后、其他指令开始前,这是因为局部变量的数目必须在子程序开始的时候就确定下来,在一个local语句定义不下的时候,可以有多个local语句,语法中的数据类型不能用表3。2中的缩写,如果要定义数据结构,可以用数据结构的名称当做类型。Win32汇编默认的类型是dword,如果定义dword类型的局部变量,则类型可以省略。当定义数组的时候,可以 '' 括号括起来。不能使用定义全局变量的dup伪指令。局部变量不能和已定义的全局变量同名。局部变量的作用域是当前的子程序,所以在不同的子程序中可以有同名的局部变量。

这里有几个定义局部变量的例子:

local      loc1'1024':byte        ;例1

local      loc2                   ;例2

local      loc3:WNDCLASS          ;例3

●   例1定义了一个1 024字节长的局部变量loc1。

●   例2定义了一个名为loc2的局部变量,类型是默认值dword。

●   例3定义了一个WNDCLASS数据结构,名为loc3。

下面是局部变量使用的一个典型的例子:

TestProc         proc

                 local  @loc1:dword;@loc2:word

                 local  @loc3:byte

 

                 mov    eax;@loc1

                 mov    ax;@loc2

                 mov    al;@loc3

                 ret

 

TestProc         endp

这是一个名为TestProc的子程序,用local语句定义了3个变量,@loc1是dword类型,@loc2是word类型,@loc3是byte类型,在程序中分别有3句存取3个局部变量的指令,然后就返回了,编译成可执行文件后,再把它反汇编就得到了以下指令:

:00401000 55                        push ebp

:00401001 8BEC                  mov ebp; esp

:00401003 83C4F8                    add esp; FFFFFFF8

:00401006 8B45FC                mov eax; dword ptr 'ebp…04'

:00401009 668B45FA              mov ax; word ptr 'ebp…06'

:0040100D 8A45F9                mov al; byte ptr 'ebp…07'

:00401010 C9                    leave

:00401011 C3                    ret

可以看到,反汇编后的指令比源程序多了前后两段指令,它们是:

:00401000 55                        push ebp

:00401001 8BEC                  mov ebp; esp

:00401003 83C4F8                add esp; FFFFFFF8

            …

:00401010 C9                        leave

这些就是使用局部变量所必需的指令,分别用于局部变量的准备工作和扫尾工作。执行了call指令后,CPU把返回的地址压入堆栈,再转移到子程序执行,esp在程序的执行过程中可能随时用到,不可能用esp来随时存取局部变量,大家一定有印象,在介绍寄存器的时候提到过ebp寄存器也是以堆栈段为默认数据段的,所以,可以用ebp做指针,于是,在初始化前,先用一句push ebp指令把原来的ebp保存起来,然后把esp的值放到ebp中,供存取局部变量做指针用,再后面就是在堆栈中预留空间了,由于堆栈是向下增长的,所以要在esp中加一个负值,FFFFFFF8就是–8。慢着!一个dword加一个word加一个字节不是7吗,为什么是8呢?这是因为在80386处理器中,以dword为界对齐时存取内存速度最快,所以MASM宁可�

返回目录 上一页 下一页 回到顶部 赞(1) 踩(1)

你可能喜欢的