内存变量定义的一般形式
定义数据变量语句是在程序中经常使用的伪指令语句,其一般格式如下:
[变量名] 数据定义符 表达式1[, 表达式2, …, 表达式n] ;注释
该定义格式的主要解释如下:
、
变量名必须是一个合法的标识符,它可以写,也可以不写;
、
数据定义符用于确定内存单元的数据类型,常用的定义符有:DB、DW和DD等;
、
表达式是定义内存单元时的初值表达式,一个定义语句可以有多个初值表达式,各表达式之间必须用逗号‘,’分开;如果某个存储单元没有初值表达式,则必须用一个问号‘?’来表示;
、
在定义语句的后面可以书写注释内容,也可以不写。
调整偏移量伪指令
调整偏移量伪指令是在内存变量定义时用来调整内存变量起始偏移量的,它们是在把源程序汇编成目标文件时起作用。常用的调整偏移量伪指令有:EVEN、ALIGN和ORG,它们的主要目的是:为了更有效地读取内存单元的内容。
1.偶对齐伪指令EVEN
偶对齐伪指令格式:
EVEN
伪指令的作用是:告诉汇编程序(Assember),本伪指令下面的内存变量从下一个偶地址单元开始分配。
如果下一个偏移量是偶地址,那么,该伪指令不起作用,否则,汇编程序将空出一个字节,从下一偶地址开始为其后变量分配内存单元。
ps:80X86CPU是以偶地址访问形式访问存储器,一次读取一个字,如果要访问以奇地址开始的一个字,则先要访问高字节的偶地址,取里面的偶地址的8位数据作为高字节存入高地址,然后再访问低字节的偶地址取里面的奇地址的8位数据作为低字节存入低地址,这样形成一个16位的数据.
2.对齐伪指令ALIGN
对齐伪指令格式:
ALIGN Num
其中:Num必须是2的幂,如:2、4、8和16等。
伪指令的作用是:告诉汇编程序,本伪指令下面的内存变量必须从下一个能被Num整除的地址开始分配。
如果下一个地址正好能被Num整除,那么,该伪指令不起作用,否则,汇编程序将空出若干个字节,直到下一个地址能被Num整除为止。
试比较下面二组变量定义,它们的对齐效果一致吗?
B1
DB 12H
B1
DB 12H
EVEN
ALIGN 2
W1
DW 4567H
W1
DW 4567H
从上面的对比,我们不难看出:伪指令ALIGN的说明功能要比伪指令EVEN强。
3.调整偏移量伪指令ORG
调整偏移量伪指令格式:
ORG 数值表达式
伪指令的作用是:告诉汇编程序,本伪指令下面的内存变量从该“数值表达式”所指定的地址开始分配。
在以上三个伪指令EVEN、ALIGN和ORG中,伪指令EVEN的使用频率较高。
偏移量计数器的值
前面,我们介绍了几种改变偏移量计数器之值的方法,但在程序中还无法引用其值。汇编语言提供了一个特殊的符号“$”来引用偏移量计数器的值。
例如:
W1
DW
$, $
ORG
$+3 ;从当前地址开始空3个字节
B1
DB
43h
假设:在给变量W1分配内存单元时,当前偏移量计数器的值为2。
于是,变量W1后面第一个“$”代表数值2,第一个字分配后,此时偏移量计数器$的值就为4,所以,第二个“$”就代表数值4。
在分配完二个字之后,偏移量计数器的值变为6,$+3的值为9,所以,伪指令“ORG $+3”就表示下一个变量从偏移量为9的单元地址开始分配。
重复说明符DUP
从前面的内容里,我们知道了定义少量内存变量的定义形式,但如果在程序中要说明50个、100个、200个甚至更多的、同类型的内存变量时,若采用前面所学的方法,对它们一一加以说明显然是不可行的。为此,汇编语言提供了变量的重复说明符DUP,其说明的一般形式如下:
count DUP (表达式, 表达式, …, 表达式)
解释:count是重复次数,(表达式, 表达式, …, 表达式)是被重复的部分,“表达式”可以是存储单元的初值,也可以是含义另一个DUP的式子。如果在表达式的括号中有多个表达式,那么,它们之间要用逗号','分开。
例如:
BUFFER
DB
100 DUP(?)
STRING
DB
120 DUP('ABCDE'), 0
DATA1
DW
50 DUP(10H, 20 DUP(1,2,3), 20H)
POINTS
DD
12, 30 DUP(0)
从上面的例子可看出:用DUP说明内存变量相当于在高级语言中定义数组。
结构类型的定义
用STRUC和ENDS可以把一系列数据定义语句括起来作为一种新的、用户定义的结构类型。其一般说明格式如下:
结构名 STRUC [Alignment][, NONUNIQUE]
数据定义语句序列
结构名 ENDS
解释:结构名是一个合法的标识符,且具有唯一性。结构名代表整个结构类型,前后两个结构名必须一致。结构内被定义的变量为结构字段,变量名即为字段名。
一个结构中允许含有任意多个字段,各字段的类型和所占字节数也都可任意。如果字段有字段名,则字段名必须唯一。每个字段可独立存取。
、对齐方式(Alignment):可用1、2或4来指定结构中字段的字节边界(Byte boundary),其缺省值为1。
、NONUNIQUE:要求结构中的字段必须用全名才能访问,见本小节中的“结构类型字段的引用”。
结构中的字段可以有字段名,也可以没有字段名。有字段名的字段可直接用该字段名来访问它,没有字段名的字段可以用该字段在结构中的偏移量来访问。
结构类型变量的定义
在定义某个结构类型后,程序员就可以说明该结构类型的内存变量。它的说明形式与前面介绍的简单数据类型的变量说明基本上一致。其定义格式如下:
[变量名] 结构名
解释:1)、
变量名即为该结构类型的变量名,它可省缺。如果省缺,则不能用符号名来访问该内存单元;
2)、
字段值表是给字段赋初值,中间用逗号','分开,其字段值的排列顺序及类型应与该结构说明时各字段相一致;
3)、
如果结构变量中某字段用其说明时的缺省值,那么,可用逗号来表示,如果所有字段都如此,则可省去字段值表,但必须保留一对尖括号"< >"。
定义了结构类型的变量后,若要访问其结构中的某个字段,则可采用如下形式:
结构变量名.字段名
联合类型的定义
联合数据类型是一种特殊的数据类型。它可以实现:以一种数据类型存储数据,以另一种数据类型来读取数据。程序员可以根据不同的需要,以不同的数据类型来读取联合类型中的数据。也就是说,在一些情况下,以一种数据类型来读取联合类型中的数据,而在另一些情况下,又以另一种数据类型来读取其数据。
1、联合类型的说明
联合数据类型其说明格式如下:
[联合类型名] UNION [Alignment] [,NONUNIQUE]
数据定义语句序列
[联合类型名] ENDS
联合类型中的各字段相互覆盖,即同样的存储单元被多个不同的字段所对应,并且其每个字段的偏移量都为0。
联合类型所占的字节数是其所有字段所占字节数的最大值。
、对齐方式(Alignment):可用1、2或4来指定结构字节的边界,其缺省值为1。它还用可伪指令ALIGN或EVEN来重新定界,也可用命令行选项/Zp来定界;
、NONUNIQUE:要求联合类型中的字段必须用全名才能访问,引用联合类型字段的方法见下面的“联合类型字段的引用”。
例如:
DATATYPE
UNION
BB DB ?
;定义一个字节类型的字段
WW DW ?
;定义一个字类型的字段
DD DD ?
;定义一个双字类型的字段
DATATYPE
ENDS
联合类型DATATYPE的字段分布如图4.8所示。
在联合类型的最外层定义中,在伪指令UNION和ENDS的前面一定要书写该联合类型名,而在其嵌套定义的内层,伪指令UNION和ENDS之前一定不能写联合类型名。
图4.8 DATATYPE的字段分布示意图
例如:
UNION1
UNION
BB
DB ?
WW
DW ?
UNION
;联合类型的嵌套定义形式
W1
DW ?
B1
DB ?
ENDS
UNION1
ENDS
联合数据类型的变量只能用第一个字段的数据类型来进行初始化。
定义了联合类型的变量后,就可根据需要,以不同的数据类型或字段名来存取该联合类型中的数据。
记录类型的说明
汇编语言的记录类型与高级语言的记录类型不同,它是为按二进制位存取数据提供方便的。记录类型的说明要用到另一个保留字RECORD,其说明格式如下:
记录名 RECORD字段 [, 字段, ……]
其中“字段”代表:字段名:宽度[=初值表达式]
解释:
1、记录名代表该记录类型;
2、记录类型可以由多个字段组成,每个字段之间要用逗号','分开;
3、字段的属性包括字段名、宽度和初值;
4、字段的“宽度”表示该字段所占的二进制位数,它必须是一个常数,并且所有字段的宽度之和不能大于16;如果记录的总宽度大于8,则系统为该记录类型分配二个字节,否则,只分配一个字节;
记录的最后一个字段排在所分配空间的最低位,然后对记录中的字段依次“从右向左”分配二进制位,左边没有分完的二进制位补0;
5、初值表达式给出的是该字段的缺省值。如果初值超过了该字段的表示范围,那么,在汇编时将产生错误提示信息;如果某字段没有初值表达式,则其初值为0。
例如:
COLOR
RECORD BLINK:1, BACK:3=0, INTENSE:1=1, FORE:3
FLOAT
RECORD DSIGN:1, DATA:8, ESIGN:1, EXP:4
[变量名] 记录名
解释:
1、变量名即为该记录类型的变量名,它可省缺。如果省缺,则不能用符号名来访问该内存单元;
2、字段值表是给字段赋初值,中间用逗号','分开,其字段值的排列顺序及大小应与该记录说明时各字段相一致;
3、如果记录变量的某字段用其说明时的缺省值,那么,可用逗号来表示,如果所有字段都如此,则可省去字段值表,但必须保留一对尖括号"< >"。
记录的专用操作符
操作符WIDTH和MASK是作用于记录类型的两个专用保留字,利用它们可得到记录类型的不同属性。
操作符WIDTH
操作符WIDTH返回记录或其字段的二进制位数,即其宽度。其一般书写格式如下:
WIDTH 记录名 或 WIDTH 记录字段名
假设有前面定义的记录类型COLOR,那么,WIDTH COLOR的值为8,WIDTH BACK的值为3,WIDTH BLINK的值为1。
操作符MASK
操作符MASK返回一个8位或16位二进制数。在该二进制数中,被指定记录或字段使用的对应位的值为1,否则,其值为0。其一般书写格式如下:
MASK 记录名 或 MASK 记录字段名
假设有前面定义的记录类型FLOAT,那么,MASK EXP的值为000FH,MASK DATA的值为1FE0H,WIDTH DSIGN的值为2000H。
记录字段
记录字段名是一个特殊的操作符,它本身也是操作数,其返回值是该字段移到所在记录的最低位所需要的位数,即该字段最低位在记录中的位置。
假设有前面定义的记录类型FLOAT,那么,有:
MOV CL, EXP
相当于
MOV CL, 0
MOV CL, DATA
相当于
MOV CL, 5
数据类型的自定义
在有了一些数据类型后,程序员还可定义这些数据类型的别名或指针类型。表达这种定义的伪指令是TYPEDEF,其定义形式如下:
新数据类型名 TYPEDEF [位距] [PTR] 数据类型
其中:“位距”是NEAR、FAR或PROC等。
例如:
CHAR
TYPEDEF BYTE
;给BYTE定义另一个别名CHAR
PCHAR
TYPEDEF PTR CHAR
;定义一个字符指针数据类型PCHAR
有了上述定义之后,下面的变量说明就是合法的。
CH1
CHAR 'ABCDEF'
;定义一个字符串常量
PCH1
PCHAR CH1
;定义一个指向字符串常量CH1的变量