目录
8086的算术运算指令除了常见的加、减、乘、除指令,还有符号拓展指令和十进制调整指令。同时,在讨论算术运算指令时,我们需要仔细考虑状态标志带来的影响。
进位标志CF
首先是进位标志CF,它指示字节最高位是否产生进位。注意,在字节内产生进位不影响CF。例如如下二进制加法:
00011000+01111100 = [0]10010100
由于最高位没有继续向上进位,因此CF=0,但是如果是如下情况:
10011000+01111100 = [1]00010100
最高位继续向上进位,因此CF=1。
溢出标志OF
溢出标志用于表示有符号整数加减运算的结果是否超出了范围。若超出范围,则是有溢出,设置OF=1,否则OF=0。例如如下有符号运算:
00111010+0111100 = 10110110 (58+124=182)
运算结果发生了溢出,所以OF是1,实际上表达的是负数。10101010按照负数补码+1得到绝对值86,故这个数是-86。他和二进制数124相加,结果发生了进位,但是由于是有符号整数相加,不考虑进位,因此结果表现为38.
由上可以看出,进位标志CF和溢出标志OF作用的地方不相同。其中,CF用于判断无符号整数运算结果是否超出范围,而溢出标志表示有符号整数运算结果是否超出范围。实际上,在使用汇编语言进行编程时,标志的控制方面是需要程序员注意的。
其他状态标志
1.零标志ZF反映运算结果是否为0。注意,产生进位的情况不在该标志的考虑范围内。例如如下二进制加法
11111111+00000001 = [1]00000000.
此时进位标志CF被设置为1,但同时地零标志ZF也会被设置为1,表示运算结果是0.
2.符号标志SF反映运算结构是正数还是负数。可以通过符号位来判断。
3.奇偶标志PF并非判断结果是奇数还是偶数,而是反映运算结果最低字节中‘1’的个数是偶数还是奇数,从而实现奇偶校验。若个数是偶数个,则PF=1,否则PF=0。
4.调整标志AF反映加减运算时最低半字节有无进位或者借位.当有进(借)位时,将AF设置为1,否则设置为0.
总结了以上运算时可能遇到的标志之后,接下来介绍算术运算指令。、
加法指令
8086提供了三种加法指令:
加法指令ADD
操作:将源操作数与目的操作数相加,结果送目的操作数。其基本格式如下:
ADD reg,imm/reg/mem
ADD mem,imm/reg
寻址方式:和其他指令相同,源操作数和目的操作数不可以同时是存储器的存储单元。
标志影响:对OF,SF,ZF,AF,PF,CF造成影响。
带进位加法指令ADC
操作:在执行ADD指令的同时加上进位标志CF。主要用于和ADD指令一起完成多精度数相加。其基本格式如下:
ADC reg,imm/reg/mem
ADC mem,imm/reg
寻址方式:和其他指令相同,源操作数和目的操作数不可以同时是存储器的存储单元。
标志影响:ADC用在下位进位的加法中,在运算时将会在除最低字节考虑CF的影响。例如,若操作数值分别为0001H和0002H,CF=1,那么得出结果将会是0005H。
增量指令INC
操作:只有一个操作数,对操作数加1。其基本格式如下:
INC reg/mem
寻址方式:因为为自增,不能为立即数寻址。
标志影响:不影响CF,其他情况和ADD,ADC一样。
从汇编指令可以看出,这是高级语言中运算符+和++(C语言)的雏形和底层。
减法指令
减法指令执行字或者字节的减法运算。常用的减法指令有SUB,SBB,DEC,NEG,CMP
减法指令SUB
操作:执行减法指令,结果送目的操作数,类似于ADD指令。其基本格式如下:
SUB reg,reg/mem/imm
SUB mem,reg/imm
寻址方式:和其他指令相同,源操作数和目的操作数不可以同时是存储器的存储单元。
标志影响:影响OF,SF,PF,CF,ZF,AF.
带借位减法指令SBB
操作:在执行SUB的基础上,如果CF为1,还需要再减去1。其基本格式如下:
SBB reg,imm/reg/mem
SBB mem,imm/reg
寻址方式:和其他指令相同,源操作数和目的操作数不可以同时是存储器的存储单元。该指令常用于和SUB结合来进行多精度的减法。
标志影响:用在下位借位的减法中,一般会在除最低字节外考虑CF的影响。
减量指令DEC
操作:只有一个操作数,对操作数-1.其基本格式如下:
DEC reg/mem
寻址方式:因为为自减,不能为立即数寻址。
标志影响:不影响CF,其他情况和SUB,SBB一样。
求补指令NEG
操作:对操作数按位取反后加1.当操作数是有符号数时,求补指令作用为求其相反数。其基本格式如下:
NEG reg/mem
寻址方式:因为为求反,不能为立即数寻址。
标志影响:和SUB一样。
比较指令CMP
操作:将目的操作数减去源操作数,但是结果不返回。即:只会影响标志,不会影响操作数。一般用于流程的控制,比如条件和循环判断。其基本格式如下:
CMP reg,imm/reg/mem
CMP mem,imm/reg
寻址方式:和其他指令相同,源操作数和目的操作数不可以同时是存储器的存储单元。
影响标志:和SUB一样。
下方展示了一个比较和返回的简单代码:
CMP AL,100 ;AL-100
JB below ;AL<100,跳转到below执行
SUB AL,100 ;AL<-AL-100
INC AH ;AJ<-AH+1
below:
从产生效果来看,减法指令是高级语言中 -, --(C语言),>,<的底层之一。
乘法指令
乘法指令包括无符号数乘法指令MUL和有符号数乘法指令IMUL。
无符号数乘法指令MUL
操作:MUL是单操作数指令,其隐含的目的操作数是AX。其基本格式如下:
MUL r8/m8 ;AX<-AX*r8/m8
MUL r16/m16 ;AX<-AX*r16/m16
如果是字节量相乘,则AL与r8/m8得到16位的字,存入AX中;若是16位数据相乘,则AX与r16/m16相乘,得到32位结果,其高字存入DX,低字节存入AX中。
寻址方式:寄存器寻址或者主存寻址。
标志影响:对OF和CF有影响。若乘积的高一半(比如字节乘法的AH)不含有有效数值,那么OF=CF=0。否则OF=CF=1。
不同于没有影响,乘法指令对其他标志的影响是未定义的。
下面是一个展示无符号数计算和存储的简单过程。
MOV AL,0B4H
MOV BL,11H
MUL BL ;将结果0BF4H送AX,OF=CF=1(AH不为0)
有符号乘法指令IMUL
操作:IMUL是单操作数指令,其隐含的目的操作数是AX。其基本格式如下:
IMUL r8/m8
IMUL r16/m16
寻址方式:同MUL指令。
标志影响:同MUL指令。
注意,同一操作数在不同的指令下,结果可能是不同的。如果将无符号数存储和计算过程中的MUL指令换成IMUL指令,那么计算结果将会是FAF4H(这是因为最高位被当成了符号位导致的)。
除法指令
和乘法指令相同,除法指令分为无符号二进制除法指令DIV和有符号二进制数除法指令IDIV两种。
无符号数二进制除法指令DIV
操作:DIV是单操作数指令,其隐含的源操作数是AX。注意,进行的操作是二进制除法。其基本格式如下:
DIV r8/m8 ;AX<-AX/r8(m8)
DIV r16/m16 ;AX<-AX/r16(m16)
当时字节除法,将8位商送AL,8位余数存入AH。如果是16位字除法,16位商存入AX,16位余数存入DX。余数的符号和被除数相同。
寻址方式:寄存器寻址或者主存寻址。
标志影响:DIV指令对标志的影响没有定义。但是除法却有可能产生溢出。这种情况是因为被除数远大于除数导致的。在DIV指令中,当做字节除法时,默认被除数是AX;当做字除法时,默认被除数是DX、AX组成的32位。如果存放商的寄存器AL/AX不能表达,便产生溢出。CPU此时会产生编号为0的内部终端。而操作系统只提示出错。下面是一个溢出的例子:
MOV AX FFF0H
MOV BL 02H;
DIV BL ;进行字节除法,结果得到7778H,超出字节表达范围
另外,在除数为0时,一样会产生溢出。
有符号数二进制除法指令IDIV
IDIV指令的大致操作、寻址方式和DIV指令相同,在此不做赘述。
符号扩展指令
8086处理器在进行指令时一般要求两个操作数类型一致,但是实际的数据类型不一定满足要求,例如一定要将8位和16位加减运算,这时需要先进行扩展,转换成相同位数据然后进行运算。符号扩展指令就是应对上述情况而产生的。
对无符号数据,只要在前面加0就实现了位数扩展且大小不变,这叫做0位扩展。而对于有符号数据,由于存在负数(补码)表示的问题,需要进行符号拓展。符号扩展的规则是:当是正数的情况下,和无符号数一样实行0位扩展;当是负数时,因为补码表示的缘故,为不影响数据大小,将高位全部填充为1。
假设有8位无符号数据64H(100)需要位数扩展,那么位数扩展之后的16位数据为0064H,使用了0位扩展。另有16位有符号数据FF00H(-256)需要位数扩展,那么位数扩展之后的32位数据是FFFF FF00H,使用了符号扩展。
8位扩展指令CB(byte)W(word)
操作:CBW不带其他操作数,其将AL的最高有效位D7扩展到D15。如果有效位是0,那么AH=00;否则为FF。
寻址方式:操作数固定为AL。
标志影响:无。
16位扩展指令CW(word)D(doubleword)
操作:CBW不带其他操作数,其将AX的最高有效位D15扩展到DX。
寻址方式:操作数固定为AX。
标志影响:无。
考虑以下无符号数减法:
得到的结果为FFFD F6F6H。
上面的扩展说明:当高位全是符号扩展时,实际上截断高位不影响计算结果。因此在进行乘法指令时,通过CF和OF可以判断是否可以截断高位而不影响实际结果。
十进制调整指令
上述运算指令都是二进制计算,但是如果需要实际上进行实际计算,则需要进行十进制调整指令。通过第一章我们知道,实际上计算机内十进制使用(压缩)BCD码表示。
压缩BCD码调整指令DAA、DAS
操作:对AL的二进制结果进行十进制调整。由于在不同的运算中二进制和十进制实际存在差异,使用DAA(addition)进行加和的十进制调整,使用DAS(substitution)进行减差的十进制调整。其基本格式如下:
DAA ;对AL中的加和进行十进制调整
DAS ;对AL中的减差进行十进制调整
寻址方式:操作数固定为AL。
标志影响:对OF标志无定义,并且根据操作结果影响其他所有标志。其中CF反映加减的进(借)位状态。
下面给出一个加和例子。
MOV AL,68H
MOV BL,28H
ADD AL,BL ;AL<-90H
DAA ;AL<-96H,反映68+28=96.CF=0
非压缩BCD码调整指令
非压缩BCD码用一个字节表示一个十进制位,通常低四位表示有效数字,高四位任意但是通常为0。与压缩BCD码不同,非压缩BCD码调整指令还提供了乘法、除法的十进制调整指令。
加和调整指令AAA
操作:一般跟随在AL为目的操作数的ADD或者ADC之后,对AL进行非压缩BCD码调整。
寻址方式:操作数固定为AL。
标志影响:如果产生了进位,则将进位1加到AH中,同时CF=AF=1。否则CF=AF=0。对其他标志无定义。
下面给出一个非压缩BCD码的加法运算:
MOV AX,0608H ;AX<-68
MOV BL,09H ;BX<-9
ADD AL,BL ;AX = 0611H
AAA ;AX=0707H,CF=AF=1
产生了进位时,可以看到AH的06变成了07,同时AL的高四位被清零,AL的低四位加6.
减差调整指令AAS
操作:一般跟随在AL为目的操作数的SUB或者SBB之后,对AL进行非压缩BCD码调整。
寻址方式:操作数固定为AL。
标志影响:如果产生了借位,则将借位1加到AH中,同时CF=AF=1。否则CF=AF=0。对其他标志无定义。
同时,该指令一样将AL的高四位清零。
乘积调整指令AAM
操作:AAM一般跟随在以AX为目的操作数(8位乘法)的MUL指令后,对AX进行非压缩BCD码调整。利用MUL相乘的两个非压缩BCD码的高四位必须为0。
寻址方式:操作数固定为AX.
标志影响:根据结果设置SF,ZF和PF。但是OF,AF,CF无定义。
下面给出一个非压缩BCD码的乘法运算。
MOV AX,0608H
MOV BL,09H
MUL BL
AAM ;AX=0702H,->8*9=72
注意,AX的结果只反映了AL和BL的乘积,AX的高8位被截断了。
除法调整指令AAD
操作:AAD先将存放在AX寄存器中的两位非压缩BCD码进行调整,再用DIV除以一个非压缩BCD数。即和其他指令先运算再调整相反。其中,要求AL,AH和除数的高四位为0,否则将产生错误。
寻址方式:操作数固定为AX。
标志影响:和AAM相同,根据结果设定SF,ZF和PF,但是对OF,CF,AF无定义。
下面给出一个非压缩BCD码除法的例子。
MOV AX 0608H
MOV BL,09H
AAD ;AX = 0044H,对应十进制数68
DIV BL ;AL=07H,AH=05H
;实现除法68/9 = 7 ……5
总结:
算术运算指令是汇编语言的核心指令之一,根据不同的使用情况设置了多种四则运算指令,并且设置了辅助的符号扩展指令和十进制调整指令。使用时需要不仅需要记忆寄存器和主存的结果,更要关注符号标志对运算结果的影响。在运算指令中存在迭代和流程控制的INC,DEC和CMP指令,他们的使用方法也值得关注。