汇编语言运算符

运算符用来构成表达式。汇编语言指令语句的操作数和伪指令语句中的参数常以表达式的形式出现。表达式是由常数、变量、标号等通过运算符连接而成的。任一表达式的值是在程序汇编过程中进行计算确定的,而不是在程序运行时求得的。

8086/8088 宏汇编语言运算符分为:算术运算符、逻辑运算符、关系运算符、属性值返回运算符和属性修改运算符。

一、算术运算符#

表 2 列出了算术运算符。表中所列出的前两个运算符,是属于单项运算符,仅表示数的正负。+、-、*、/ 运算是最常用的运算符,参加运算的数和运算的结果均是整数。除法运算的结果只取商的整数部分。而 MOD 运算符是进行整数除法,运算结果只取它的余数部分。

例如:

汇编语言算术运算符

算术运算符

表 2 算术运算符

上面的例子中 CONT 是两个变量的偏移量相减,其结果正是以 DATA1 为首地址占用的 6 个字节单元数。而 DATA3 是 DATA2 偏移量加 2,形成一个新的偏移量 38H,正好指向存放字符串“EF”的首址。

表 2 中后面两个运算符是进行逻辑移位。SHR 进行右移时,最左边以 0 填之;SHL 进行左移时,最右边以 0 填之。移位的位数由运算符 SHR/SHL 右边的次数确定。如次数大于等于 16,则结果恒为零。移位运算符与移位指令是完全不相同的两回事。移位运算符是对某一个具体的数(常数)在汇编时完成移位的,而移位指令是对一个寄存器或存储单元内容,在程序运行时执行移位的。根据它们在一条语句中出现的位置可以判断出它是移位运算符还是移位指令。例如:

移位运算符还是移位指令

上述 3 条指令与下面3条指令一一对应等效:

移位运算符还是移位指令

表 2 最后一个是下标运算符,它对存取数组元素是很有用的。[ ] 表示加法,把表达式 1 和表达式 2 相加后形成一个存储器操作数地址。如下面两语句是等价的:

下标运算符

即源操作数是一个存储器操作数,其地址是 DA 的偏移地址加 20H。再例如下面 5 个语句是相互等价的:

下标运算符

但是两个存储器操作数(如两个变量名)不能相加。下面几个语句都是错误的:

两个存储器操作数不能相加

二、逻辑运算符#

表 3 列出了逻辑运算符,参加运算的数和运算的结果均是整数,逻辑运算是按位进行的。与移位运算符一样,逻辑运算符与逻辑运算指令是完全不同的两回事。例如:

逻辑运算符

上述指令与下面指令一一对应等效:

逻辑运算符

逻辑运算符

表 3 逻辑运算符

三、关系运算符#

表 4 列出了关系运算符。这些运算符是用于比较两个表达式的,表达式一定是常数或同段内的变量。若是常数,按无符号数比较。若是变量,则比较它们的偏移量。比较结果以真(全 1)、假(全 0)的形式给出。例如:

关系运算符

与下面两条指令完全一一对应等效:

关系运算符

设某数据段有:

关系运算符

在程序汇编时,若 NUM<0ABH,则变量 VAR 的内容为 0FFFFH,否则它的内容为 0。

关系运算符

表 4 关系运算符

四、属性值返回运算符#

这种运算符共有 5 个,如表 5 所示。它们加在变量名或标号前,通过运算后得到(返回)一个属性值。

属性值返回运算符

表 5 属性值返回运算符

  1. SEG 运算符

SEG 为段值返回运算符。当 SEG 加在一个变量名或标号的前面时,得到的运算结果是这个变量或标号所在段的段基值。例如:

SEG 运算符

如果变量 X1 所在段的段基值为 0915H,变量 ARRAY 所在段的段基值为 0947H,那么上面两条指令汇编后分别为:

SEG 运算符

由于任意一个段的段基值是 16 位二进制数,所以 SEG 运算符返回的数值也是 16 位二进制数。

  1. OFFSET 运算符

OFFSET 为偏移值返回运算符。当该运算符加在一个变量名或标号前面时,将得到这个变量或标号在它段内的偏移量。例如:

OFFSET 运算符

设 KZ 在它段内的偏移量是 15H,那么这个指令汇编后就是:

OFFSET 运算符

这个运算符十分有用。例如,现有以 ARRAY 为首址的字节数组,为了逐个字节进行某种操作。可以使用下面的程序段:

OFFSET 运算符

在这段程序中,首先把数组变量的首字节偏移量送给 SI,把寄存器 SI 作为数组的地址指针。这样,在数组的逐个字节处理(即在 LOP 循环)中,使用寄存器间接寻址方式,每处理完一个字节,很方便地对地址指针 SI 进行修改,使它指向下一个字节。

当用 DW 或 DD 伪指令设置了某变量的地址指针后,这时程序设计人员为了获得变量的偏移量,既可以用 OFFSET 运算符,也可以直接取出地址指针。请分析下面的语句中 4 条 MOV 指令各自的含义和指令执行的结果。

OFFSET 运算符

  1. TYPE 运算符

TYPE 为类型属性值返回运算符,是取变量或标号的类型属性值(变量和标号的类型属性值如前所述)。其中变量的类型值正好表示它们每个数据所占有的存储单元字节数。而标号的类型值没有什么物理意义。例如:

TYPE 运算符

上述 3 条指令汇编后分别为:

TYPE 运算符

  1. LENGTH 运算符

LENGTH 为变量的元素总和运算符。仅加在变量的前面,返回值表示数组变量的元素个数。如果变量是用重复数据操作符 DUP 说明的,则返回外层 DUP 给定的值;如果没有 DUP 说明,则返回的值总是 1。例如:

LENGTH 运算符

  1. SIZE 运算符

SIZE 为变量字节总和运算符。这个运算符仅加在变量的前面,表示数组变量所占的总字节数。返回值等于 LENGTH 和 TYPE 两个运算符返回值的乘积。例如,对于前面例子中 K1,K2,K3,K4 变量,下面指令就表示出 SIZE 运算符的返回值:

SIZE 运算符

3 个运算符 TYPE,LENGTH,SIZE 对处理数组类型变量是很有用的。例如,为了实现某数组各元素的累加,而且从最后一个元素开始累加。可以编制如下程序段:

SIZE 运算符

在上述程序中,地址指针 SI 的设置是用 SIZE 和 TYPE 运算符计算确定的,使它指向数组的最后一个元素。CX 存放数组元素个数,供循环计数用。

注意:

LENGTH 和 SIZE 运算符仅加在变量前面,且这个变量必须是用 DUP 形式定义的;否则,它们仅返回一项数据的情况。

五、属性修改运算符#

这种运算符是对变量、标号(过程名)、直接或寄存器间接寻址的存储器操作数的类型进行修改、指定。

(一)PTR 运算符#

PTR 是类型属性修改运算符。

格式:类型  PTR  操作数

功能:将操作数强制修改为 PTR 左边给出的类型。这种修改是临时性的,仅在有 PTR 运算符的语句内有效。

说明:操作数是指要修改类型属性的标号、过程名、变量、直接或寄存器间接寻址的存储器操作数。类型可以是 BYTE、WORD、DWORD(操作数是变量、直接或寄存器间接寻址的存储器操作数时)或 NEAR、FAR 等(操作数是标号、过程名时)。

有两种情况需要使用 PTR 运算符。

(1)由于编程需要,临时修改变量的类型属性。

例如:

PTR 运算符

(2)当指令语句中的操作数类型不明确时,用以明确操作类型。

例如,语句“INC [BX]”是错误的,因为在对该指令语句进行汇编时,不能确定 [BX] 指的是字节单元还是字单元。

再如,“SUB [SI],30H”这条语句也不能让汇编程序确定是进行字节减法还是字减法运算,因为常数 30H 是没有类型的,可以看成是八位数 30H,也可以看成是十六位数 0030H。

因此,这两条语句必须用 PTR 运算符对类型加以明确,否则在汇编源程序时将会产生语法错误。上述两条语句可修改如下:

PTR 运算符

从上述例子可以看出,常数、直接或寄存器间接寻址的存储器操作数类型都是不明确的。在一条指令语句中如果只有一个操作数,其类型必须明确;如果有两个操作数,只要有一个类型是明确的就可以,否则,必须使用 PTR 运算符明确类型。

(二)HIGH/LOW 运算符#

这两个运算符叫字节分离运算符,用于分离运算对象的高字节和低字节部分。

格式:HIGH 表达式

LOW 表达式

表达式必须具有常量值,即一个常数或在汇编源程序时能确定的段/偏移量值的地址表达式,HIGH/LOW 运算符用于分离出段/偏移量的高字节或低字节。例如:

HIGH/LOW 运算符

设 DATA 段的段基值是 0926H,那么上述几条指令汇编后分别为:

HIGH/LOW 运算符

但是 HIGH/LOW 运算符不能用来分离某一个寄存器或存储器操作数内容的高字节/低字节。例如,下面的几条指令语句是错误的:

HIGH/LOW 运算符

六、运算符的优先级#

当一个表达式中同时有几个运算符时,按运算符优先级顺序执行。运算符的优先级别如表 6 所示。其中,WIDTH、MASK 是记录中用的运算符。汇编源程序时按照以下规则计算表达式的值。

(1)先执行优先级别高的运算。

(2)优先级别相同的操作,从左至右顺序进行。

(3)可以用圆括号改变运算的顺序。

例如下面两个表达式:

运算符的优先级

它们的值分别是:

运算符的优先级

运算符的优先级

表 6 运算符的优先级

(完)

comments powered by Disqus