程序结构伪操作为程序提供了基本的程序框架,要实现程序的功能,我们还必须在这个框架内填入一系列的指令和数据。其中数据的定义就要依靠数据定义伪操作。数据定义伪操作可以定义各种数据类型并实现存储单元分配等功能。
在X86系列汇编语言程序中,使用数据定义伪操作定义的变量除了有确定的偏移地址之外,还具有一个类型属性。当汇编语言指令使用这些变量的时候,一定要遵循它的类型。比如,字节指令就不能引用字变量,否则汇编程序就会给出出错信息“invalid instruction operands”。
1.地址计数器、ORG伪指令和对准伪指令
在变量定义的过程中,变量的偏移地址是和定义的次序相关的,总的来讲就是根据变量定义的次序从低地址端向高地址端分配。在汇编语言中,‘$’符号作为汇编地址计数器用来记录当前正在汇编的指令的地址,当‘$’用在伪指令的参数字段时,则表示当前待分配存储空间的偏移地址,在未分配任何空间之前,‘$’的值为0。汇编地址计数器也可以用ORG伪操作来设置。
ORG伪操作用来修改汇编地址计数器的值,其格式如下:
ORG偏移地址
在ORG伪操作之后,汇编地址计数器‘$’的值就被设为给定的偏移地址,无论数据段中的数据定义还是代码段中的汇编代码都将被定位在该偏移地址。需要注意的是,ORG所指定的偏移地址只能比汇编地址计数器‘$’的当前值更大。下面是一个例子。
这段程序定义了两个变量STR和NUM,程序第二行的ORG伪操作将‘$’的值设为100H,这样变量STR的偏移地址就是100H。而后,在第四行的ORG伪操作被处理时,‘$’的值为104H,这个ORG伪操作就将‘$’的值设为了104H+10=10EH,于是变量NUM的偏移地址就变成了10EH。
除了ORG伪指令之外,还有一些其他的指令会作用于汇编地址计数器,这些指令有:
●EVEN伪指令
EVEN伪指令使下一个变量或指令开始于偶数字节地址。例如:
一个字的地址最好从偶地址开始,所以对于字数组为了保证它从偶地址开始,可以在DW定义之前用EVEN伪指令来达到这一目的。
●ALIGN伪指令
ALIGN伪指令使它后面的数据或指令从2的整数倍地址开始。其格式为:
ALIGN 2n(n为任意整数)
例如:
ALIGN 4
ARRAY DD 100 DUP(?)
ALIGN伪指令保证了双字数组ARRAY地址边界从4的倍数开始。
ALIGN伪指令是将当前偏移地址指针指向2的乘方的整数倍地址,如果源地址指针以指向2的乘方的整数倍地址,则不作调整。否则将指针加一个数,使地址指针指向下一个2的乘方的整数倍地址。
当然,ALIGN 2和EVEN是等价的。
汇编语言允许用户直接用‘$’来引用地址计数器的值,例如指令:
JMP $+6
它的转向地址是JMP指令的首地址加上6。当‘$’用在指令中时,它表示本条指令的第一个字节的地址。在这里,$+6必须是另一条指令的首地址。否则,汇编程序将指示出错信息。
当‘$’用在伪指令的参数字段时,则和它用在指令中的情况不同,它所表示的是地址计数器的当前值。例如指令:
ARRAY DWl,2,$+4,3,4,$+4
假设汇编时ARRAY分配的偏移地址为0074H,则汇编后,$+4所在的两个字单元为:
(ARRAY+4)=0078+4=007CH
(ARRAY+OA)=007E+4=0082H
应当注意,ARRAY数组中的两个$+4得到的结果是不同的,这是由于‘$’的值是在不断变化的。当在指令中用到‘$’时,它只代表该指令的首地址,而与‘$’本身所在的字节无关。
2.变量定义伪操作D*(Define…)
变量定义伪操作都以字母D加上一个用来表示变量类型的字母组成,它们用来定义各种变量,在X86汇编中可用的定义伪操作有DB、DW、DD、DQ和DT。
●DB(define byte)
DB伪指令用来定义字节,其后的每个数据都存储在1字节中。DB能定义十进制数、二进制数、十六进制数和ASCⅡ字符,二进制数和十六进制数要分别用"B"和"H"表示,ASCⅡ字符用单引号′(′)括起来。DB还是唯一能定义字符串的伪操作,串中的每个字符占用1字节。
●DW(define word)
DW伪指令用来定义字,对其后的每个数据分配2字节(1个字),数据的低8位存储在低字节地址中,高8位存储在高字节地址中,如下例中的变量DATA8的数据存储在0070字地址中,其中0070字节存储0BAH,0071字节存储03H。DW还可存储变量或标号的偏移地址。
●DD(define double-word)
DD伪指令用来定义双字,对其后的每个数据分配4字节(2个字)。该伪指令同样将数据转换为十六进制,并根据低地址存储低字节,高地址存储高字节的规则来存放数据。如下例DATA15的存储情况是:00A8:0F2H,00A9H:57H,00AAH:2AH,00ABH:5CH。
用DD存入地址时,第一个字为偏移地址,第二个字为段地址。
●DQ(define quad-word)
DQ伪指令用来定义4字,即64位字长的数据,DQ之后的每个数据占用8字节(4个字)。
●DT(define ten bytes)(www.xing528.com)
DT伪指令用来为压缩的BCD数据分配存储单元,它虽然可以分配10字节(5个字),但最多只能输入18个数字,要注意的是,数据后面不需要加"H"。
请看下面数据定义的例子,注意DB定义的每个数据的存储情况,左边第一列是汇编程序为数据分配的字节地址,第二列是相应地址中存储的数据或ASCⅡ字符(均用十六进制表示)。变量DATA7定义了3个数据和一个字符串,每个数据或字符串用“,”分开,它们分别存储在偏移地址002E开始的6字节单元中。
;DB例子的列表文件
;DW伪指令例子的列表文件
;DD例子的列表文件
;DQ、DT例子的列表文件
对数据定义伪指令前面的变量还要注意它的类型属性问题。变量表示该伪指令中的第一个数据项的偏移地址,此外,它还具有一个类型属性,用来表示该语句中的每个数据项的长度(以字节为单位表示),因此DB伪指令的类型属性为1,DW为2,DD为4,DQ为8,DT为10。变量表达式的属性和变量是相同的。汇编程序可以用这种隐含的类型属性来确定某些指令是字指令还是字节指令。
下例中变量OPER1为字节类型属性,OPER2为字类型属性,所以第一条MOV指令应为字节指令,第二条MOV指令应为字指令。而第三条指令的变量表达式OPER1+1为字节类型属性,AX为字寄存器,第四条指令的OPER2为字类型属性,AL为字节寄存器,因此,汇编程序将指示这两条MOV指令出错:“类型不匹配”。
3.LABEL伪指令与PTR伪操作符
如果我们需要执行将OPER+1地址的字传送到AX和把OPER2地址的字节传送到AL这两个操作,该怎么办呢?PTR伪操作符可以帮助我们达到这个目的。
PTR指定操作数的类型属性,它优先于隐含的类型属性。其格式为:
类型PTR变量[±常数表达式]
其中类型可以是BYTE、WORD、DWORD、FWORD、QWORD或TBYTE,这样变量的类型就可以指定了。下例中的两条MOV指令把OPER1+1的类型属性指定为字,把OPER2的类型属性指定为字节,这样指令中两个操作数的属性就一致了,汇编时就不会出错了。
类似地,LABEL可以使同一个变量具有不同的类型属性。其格式为:
其中变量的数据类型可以是BYTE,WORD,DWORD,标号的代码类型可以是NEAR或FAR。例如:
BYTE_ARRAY LABEL BYTE
WORD_ARRAY DW 50 DUP(?)
在50个字数组中的第一个字节的地址赋予两个不同类型的变量名:字节类型的变量BYTE_ARRAY和字类型变量WORD_ARRAY。在程序中访问数组单元时,要按指令类型来选择变量,如下面两条指令:
4.重复定义DUP(Duplicate)
DUP伪指令可以按照给定的次数来复制某个(某些)操作数,它可以避免多次输入同样一个数据。例如,把6个FFH存入相继字节中,可以用下面两种方法。显然用DUP的方法更简便些。
DATA20 DB OFFH OFFH OFFH OFFH OFFH OFFH
DATA21 DB 6 DUP(OFFH)
DUP操作一般用来保留数据区,如用数据定义伪指令“DB 64 DUP(?)”可为堆栈段保留64字节单元。DUP还可以嵌套,其用法见如下例子。
;DUP例子的列表文件
5.表达式赋值伪操作EQU
EQU是一个赋值伪操作(伪指令),它给一个数据标号赋予一个常数值,但这个常数不占用存储单元。当这个数据标号出现在程序中时,汇编程序即用它的常数值代替数据标号。EQU可以在数据段之外使用,甚至可用在代码段中间。赋值伪操作“=”的作用与EQU类似。它们之间的区别是,EQU伪操作中的标号名是不允许重复定义的,而“=”伪操作是允许重复定义的。例如,
TMP EQU 5
TMP EQU TMP+1
是错误语句,因为TMP已赋值为5,就不能再把它定义为其他数值。而
TMiP=5
TMP=TMP+1
是允许使用的。因为“=”伪操作允许重复定义。第一个语句TMP的值为5,第二个语句TMP的值就为6了。
使用EQU操作的优点可从下面的例子中看出:
假定在数据段和代码段中要多次使用一个数据(如25),那么在编程时凡是用到25的地方都可用数据标号COUNT来表示。如果程序想修改这个数据,那么只需修改EQU的赋值,而无须修改程序中其他部分,如COUNTER和MOV语句就不必修改。EQU还可给表达式赋予一个名字,举例如下:
注意:在EQU语句的表达式中,如果有变量或标号的表达式,则在该语句前应该先给出它们的定义。如上例中的ALPHA必须在BETA之前定义,否则汇编程序将指示出错。
6.基数控制伪指令(.RADIX)
基数控制伪指令.RADIX可以把默认的基数改变为2~16范围内的任何基数。其格式如下:
.RADIX基数值
其中基数值用十进制数来表示。例如:
应当注意,在用.RADIX 16把基数定为十六进制后,十进制数后面都应跟字母D。在这种情况下,如果某个十六进制数的末字符为D,则应在其后跟字母H,以免与十进制数发生混淆。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。