§4.3 伪指令(指示性语句)
本节概述
在ASM 86中有多种伪指令,它们是符号定义语句、数据定义语句、段定义语句、过程定义语句和终止语句。
教学目标
掌握各种伪指令。
学习内容
符号常数定义语句
变量定义语句(数据定义语句)
存储单元的类型
分析运算符和合成运算符
段定义语句
子程序定义语句
汇编结束语句
重点难点
各种语句定义的格式。
学习方法
掌握各种语句的定义格式。
关键字
伪指令
参考资料
1、《微型计算机技术及应用》,戴梅萼等编著,第二版,清华大学出版社
2、《微型计算机原理》,季维法等编著,第一版,电子科技大学出版社
3、《微型计算机原理—常见题型解析及模拟题》,武自芳主编,西北工业大学出版社
4、《80X86/80X87汇编语言程序设计》,洪志全等编著,电子科技大学出版社
§4.3.1 符号定义语句
EQU伪指令给一个符号定义一个值,或定义一个符号,该语句有如下两种形式:
名字 EQU 表达式
新名字 EQU 老名字
该伪指令功能是给名字(符号)定义一个值或其他符号。
符号定义语句不能重新定义,即在同一个程序中,用EQU定义的符号,不能再赋以不同值。但可以用一个解除语句对某符号的定义,以便给符号重新定义。解除语句格式为:
PUREG 符号1,符号2,……,符号n
该语句本身不允许有名字。
与符号定义语句类似,还有一个等号语句,所不同的是允许对符号进行再定义。
“=”伪指令的功能类似EQU,不同之处是EQU伪指令不允许对符号重复定义,而“=”伪指令则允许对同一符号多次重复定义,即可对符号名再定义,其格式为:
符号名=表达式。
§4.3.2 变量定义语句(数据定义语句)
这类语句用于为数据分配存储单元,并可以用一个符号与这个存储单元相联系,也可以为存储单元分配初值,并为数据提供一个任选初值。其格式为:
符号 DX 表达式
其中定义符内X为字母B、W、D、Q、T之一。
DB伪指令:定义字节型存储单元(Define Byte),字节的值域对于无符号整数为0~255,对带符号的 整数为-128~+127。初值表中各项数据用逗号隔开,每项数据占一个字节单元。符号名为变量名,是初值表中第一项数据所在存储单元的符号地址,初值表中第一项数据后面个项数据的单元地址依次在该符号地址上递增1。如果初值表中的初始值为“?”。则对应字节单元将不赋初值,其内容为未定义的不确定值。
DW伪指令:定义字节存储单元(Define Word),字的值域对无符号整数为0~65535,对带符号数为-32768~+32767,它与DB不同的是初值表中各项数据占一个字单元(两个字节),并且字单元不仅可以存放整型数,还可以存放变量的偏移地址。
DD伪指令:定义双字型存储单元(Define Double Word),每个数据项占4个字节单元。双字单除了可存放双字整数外,还可以存放实数或存放一个变量的段地址和偏移地址。
由汇编语言产生的目标码、指令和存放指令的地址,在目标码产生后,指令就已经放在指令存储器中,然后就可以执行。在指令送至存储器的时候,数据项的初值也可以送至存储器中,这意味着目标码除了包含指令和它们的地址外,也可包含数据项的初值和地址,这些初值是由数据定义语句所规定的。例
在汇编时遇到问号(?),则只为数据项分配响应的存储单元,但不产生目标码来初始化这些存储单元,即不为数据项规定相应初值,用来存放指令执行时间的中间结果。
初值除直接表示数据定义语句中外,还可用一个表达式来表示。表达式的值可以由汇编时计算或由EQU语句给出。
同样,在存储单元中可以存放地址值,存放段内偏移量需一个字,存放全地址则需要两个字 ,一个字放段字址,另一个字放放段内偏移量。
在实际应用中,还经常会用到由字节、字或双字构成的表。如8086中XLAT指令可以利用一个由字节组成的表,把一个编码转换为同一个值的另一个编码;中断服务程序的入口地址表,其中每一项是一个双字指针,串操作指令对包含串元素的由字节或字组成的表进行操作等。如何在内存中建立这样的表呢?只要在数据定义语句的参数部分,因如若干个用逗号分隔的参数就可以建立一个表。
可以用数据定义语句在内存中定义一个字符串,字符串中的每一个字符用它的ASCII码表示,为一个字节,故字符串的定义必须用DB命令。有两种定义字符串的方法,一种是字符串中的每一个字符分别定义,每个字符之间用逗号分隔;另一种方法在整个字符串的前后加单引号。
§4.3.3 存储单元的类型
1、 变量的类型
变量在定义时被指定其类型,变量的类型可以是:字节(Byte,用DB定义)、字(Word,用DW定义)、双字(DWORD,用DD定义)。
变量被指定类型后,汇编程序能生成正确的机器码。
像[SI]这样的存储单元,汇编程序不知道其类型是字节、字、双字。应该在源程序中用伪指令明确指出。
2、调用和转移的类型(SHORT、NEAR、FAR)
规定一个NEAR指令单元长度为两字节,一个FAR指令单元长度为四字节。指令位置能出现在跳转或调用指令语句中,如果指令的位置类型为NEAR型,那么汇编程序就产生一条段内跳转或调用指令,若该单元类型为FAR型,汇编程序就产生一条段间跳转或调用指令。一个存储器地址加上或减去一个数字量,所得到的新存储器地址与源存储器地址具有相同的类型。
§4.3.4 分析运算符和合成运算符
分析运算符用来将存储器操作数分解为它的组成部分,这类运算符有SEG,OFFSET,TYPE,SIZE,LENGHT。合成运算符有PTR和THIS。
1、SEG、OFFSET(分析运算符)
SEG运算符返回的是存储器地址操作数的段分量,即取得存储器符号的段地址值;
OFFSET运算符返回的是段内偏移量,即取得存储器符号的地址偏移量。 这两种分量一般都是数值。
2、 TYPE、LENGTH、SIZE(分析运算符)
TYPE取得存储器操作数的类型(字节=1,字=2,双字=4),返回一个数值,它表示存储器操作数相关的类型部分。各种存储器地址操作数类型部分的值分别为:数据字节为1;数据字为2;数据双字为4;NEAR和FAR的值无实际的物理意义。
LENGTH取得存储器操作数元素的个数,即返回的数值是与存储器地址操作数相关的单元(字节、字或双字)数目。
SIZE取得存储器操作数占用的存储器字节数,即返回的数值等于分配给指定的存储器地址操作数的字节数。
注意:MASM 5.0验证(99.2.13)
X DW 5 DUP (0) ;LENGTH X=5
Y DW 0,0,0,0,0 ;LENGTH Y=1
3、 PTR、THIS(合成运算符)
PTR用于指定存储单元的类型,它能产生一个新的存储器地址操作数(一个变量或标号)。新的操作数的段地址和段内偏移量与源操作数相同,但类型不同。它与数据定义语句不同,PTR运算符不分配任何存储器,它只是给已分配了的存储单元一个新的意义。
THIS产生一个新的变量或标号,其地址等于当前地址,类型在THIS中指定。(THIS不分配存储器)。
4、$
$符号表示当前地址。
§4.3.5 段定义伪指令SEGMENT/ENDS
关心汇编语言源程序的段结构的原因是:
(1) CPU是按分段方式寻址的,即CPU对存储器的访问按分段进行,把存储器的地址分为段基地址和相对于段基地址的偏移量,用CS:IP寻址代码段;用SS:SP寻址堆栈段;用DS或ES表示数据段的基地址,用寻址方式表示数据段的偏移量。
(2) 汇编程序(MASM)知道程序的段结构后,才能产生正确的机器指令。
例如,产生正确的段内调用、段间调用、段内转移、段间转移指令机器码。
(3) 当指令对当前数据段、堆栈段访问时,产生最优指令,而对其他段访问时,在指令前要加段超越前缀或改变段寄存器的值。
在汇编语言源程序中,段按如下格式定义:
段名 SEGMENT 段属性
语句行
┇
语句行
段名 ENDS
其中,"段属性"包含"定位类型"、"组合类型"、"类别"。
1、 定位类型(Align)
定位类型选项表示该段的起始边界要求,有PAGE(页边界)、PARA(节边界)、WORD(字边界)、BYTE(字节边界)四种,缺省是PARA, 如表所示。见图4-3。
2、 组合类型(Combine)
Combine 选项指示连接程序(LINK)如何处理同名段。
可有如下选择:
(1) NONE,没有指定Combine选项,连接程序(LINK)不把同名段合并,在可执行文件中,每个模块的同名段均有自己的物理段基地址。
(2) PUBLIC,指示LINK将所有同名段合并成一个新的连续段,新段中的所有指令和数据的地址使用同一个段寄存器,所有偏移量调整为相对与新段的首地址。
(3) COMMON,指示LINK将所有同名段置为相同起始地址,段的最终长度等于所有段中的最大长度。
(4) AT address,指示LINK,段内所有标号和变量的地址,都根据地址address来确定。
(5) STACK,指示LINK将所有同名段合并成一个新的连续段,且把新段作为堆栈区域使用。堆栈指针SP被初始化为该新段的长度。堆栈段基地址寄存器SS的值被初始化为新段的首地址。
(6) MEMORY,指示LINK把本段定位在其他段之上(地址较大),如果有多个MEMORY属性段,LINK只把第一个作为MEMORY属性处理,其他作为COMMON属性处理。
3、 类别('Class')
类别可以是任何合法名称,必须用单引号括起来。当未指定类别时,该段的类别名为空。 LINK把同类别名的段连续放在一起。(不合并)。
4、 ASSUME伪指令
ASSUME伪指令告诉汇编程序(MASM),存储器寻址的缺省段名。
例:ASSUME CS:CODE,DS:DATA,ES:DATA,SS:STACK 告诉汇编程序:
段名为CODE的段是代码段,其基地址与CS相联系。
段名为DATA的段是数据段,其基地址与DS相联系。
段名为DATA的段同时也是附加段,其基地址与ES相联系。
段名为STACK的段是堆栈段,其基地址与SS相联系。
注意,ASSUME伪指令与程序运行时段寄存器的值毫无关系。
(1) 程序运行时,DS、ES寄存器的值,用指令赋给。如,
MOV AX,DATA ;DATA是段名
MOV DS,AX MOV ES,AX
(2) 程序运行时,SS和SP的值 当某一个段具有STACK组合属性(Combine)时,SS的初值被自动初始化为该段的基地址,SP的初值被自动初始化为该段的长度。
SS、SP的值也可以用指令来设定,如,
MOV AX,STACK ;STACK是段名
MOV SS,AX
MOV SP,1000H
(3) CS和IP的值
CS的初值由END伪指令指定。例,
END START ;START是一个标号
指定CS的初值等于标号START的段地址。
IP的初值一般为0000H,也可以使用ORG伪指令改变。如,
代码段中,
ORG 100H
指定IP的值等于0100H。
CS、IP的值在程序运行过程中自动变化。当指令顺序执行时,每执行一条指令,IP的值增加该指令的机器码长度;调用/返回调用、中断/返回中断、转移可以改变CS、IP的值。
5、 ORG伪指令
代码段中的ORG伪指令,指定IP的值。
数据段中的ORG伪指令,指定下一个存储单元的地址偏移量。如,
DATA SEGMENT
ORG 200H
X DW ?
DATA ENDS 指定变量X的地址偏移量为200H。
§4.3.6 子程序定义语句
子程序,又称为过程。
定义:
子程序名 PROC 属性
┇
RET
┇
RET
子程序名 ENDP
子程序用PROC/ENDP伪指令定义。定义时,可以指定子程序的属性(NEAR或FAR),若不指定,汇编程序认为是NEAR属性。
子程序用CALL指令调用,为了让汇编程序产生正确的机器指令,调用时可以指定调用类型(FAR段间调用,NEAR段内调用)。若未指定调用类型,而汇编程序又不能确认时,认为是NEAR段内调用。
CALL 子程序名
CALL NEAR PTR 子程序名
CALL FAR PTR 子程序名
子程序中,必须有一个RET指令被执行,允许子程序中出现过个RET指令。RET指令也可以带参数。
RET
RET n ;n为0~65535间的偶数。
§4.3.7 汇编结束伪指令END
这是一条源程序结束指令。它告诉汇编程序,对源程序进行汇编时,遇到该语句,说明源程序到此结束,以后的内容不属于本程序了。终止语句格式为:
END [表达式]
这里的表达式通常就是程序的第一条指令前面的标号,这样,程序在汇编、连接后,得到的目标程序在执行时会自动从第一条指令开始。当然,这个表达式也可以省略。
源程序结束伪指令,汇编程序遇到该伪指令时,结束翻译源程序。如果END伪指令带标号,指定程序运行时CS的初值等于该标号的段地址。