算术指令即对二进制数进行算术运算以及对以BCD码表示的十进制数运算进行调整的指令。执行算术指令会影响以下标志位的值:CF、PF、AF、ZF、SF和OF。算术指令主要被分为六类:加法指令、减法指令、乘法指令、除法指令、符号扩展指令和十进制调整指令。
1.加法指令
加法指令用来实现算术加法,有三条指令:ADD、ADC和INC。
●ADD
格式:ADD dst,src
执行的操作:(dst)←(src)+(dst)
计算两个操作数的和并将结果存到目标操作数。
●ADC
格式:ADC dst,src
执行的操作:(dst)←(dst)+(src)+CF
计算两个操作数和CF进位标志位的值之和,并将结果存到目标操作数。
●INC
格式:INC opr
执行的操作:(opr)←(opr)+1
将指定操作数加一并存回。
在这三条指令中,ADD和ADC指令分别是加法指令和带进位加法指令,都是双操作数指令。需要注意的是,两个操作数不能同为存储器寻址方式,也就是说,除了源操作数为立即数的情况之外,ADD指令和ADC指令的两个操作数中必须有一个是寄存器寻址方式。INC指令为单操作数指令,作用是将操作数的值加一,类似C语言中的“++”操作符,其操作数可以是除了立即数之外的任何一种寻址方式。这三条指令均可做字节或字运算。
ADD和ADC指令影响条件标志位(或称条件码),INC影响除了CF标志之外的其他条件标志位。条件标志(或称条件码)位中最主要的是CF、ZF、SF、OF四位,分别表示进位、结果为零、符号和溢出的情况。加法运算对这几个标志位的设置如下。
ZF位:当加法结果为0时ZF=1,否则ZF=0。
SF位:当加法结果为负数(符号位为1)时SF=1,否则SF=0。
CF位:根据最高有效位是否有向高位的进位设置,有进位时CF=1,否则CF=0。
OF位:根据操作数的符号及其变化情况来设置,当两个操作数的符号相同,而结果符号与之相反时OF=1,否则OF=0。
在ADD或ADC指令被执行时,处理器并不区别操作数是带符号数还是无符号数,一律按照上面的规则进行条件标志位的设定,因此程序员应当清楚当时处理的是带符号数还是无符号数,再决定怎样使用这些标志位。比如当SF位被置位时,如果处理的是带符号数,说明加法的结果是负数。如果处理的是无符号数,由于最高位不是符号位而是有效的数据位,SF被置位只能说明加法的结果超过了最大无符号数的一半,而不存在任何符号的意义。在加法结果溢出的表现上,无符号数和带符号数的运算也不相同。对于带符号数的加法,OF位的置位无疑代表了无符号数加法的溢出,而当处理的操作数是无符号数时,OF就不再具有溢出标志的含义,而CF置位则代表了无符号数加法的溢出。在双字的加法运算中,CF位也作为低位相加的和是否存在向高位进位的标志。
例:
ADD DX,0F0FH
如指令执行前(DX)=4652H,则指令执行后(DX)=3742H,ZF=0,SF=0,CF=1,OF=0。
例:
下列指令序列执行两个双精度数的加法。
ADD AX,CX
ADC DX,BX
设目的操作数存放在DX和AX寄存器中,其中DX存放高位字。源操作数存放在BX和CX寄存器中,其中BX存放高位字。如指令执行前:(DX)=0002H,(AX)=0F365H,(BX)=0005H,(CX)=0E024H。
则第一条指令执行后,(AX)=0D389H,SF=1,ZF=0,CF=1,0F=0。
第二条指令执行后,(DX)=0008H,SE=0,ZF=0,CF=0,OF=0。
因此该指令序列执行完后(DX)=0008H,(AX)=D389H,正是我们所希望看到的结果。可以看出,为实现双精度加法,必须用两条指令分别完成低位字和高位字的加法,而且在高位字相加时,应该使用ADC指令以便把前一条ADD指令作低位字加法所产生的进位值加入高位字之内。另外,带符号的双精度数的溢出,应该根据ADC指令的OF位来判别,而作低位加法用的ADD指令的溢出是无意义的。
2.减法指令
减法指令用来实现算术减法,有五条指令:SUB、SBB、DEC、NEG和CMP。
●SUB
格式:SUB dst,src
执行的操作:(dst)←(dst)-(src)
计算目标操作数和源操作数的差并将结果存到目标操作数。
●SBB
格式:SBB dst,src
执行的操作:(dst)←(dst)-(src)-CF
计算目标操作数和源操作数以及CF为进位标志位之差,并将结果存到目标操作数。
●DEC
格式:DEC opr
执行的操作:(opr)←(opr)-1
将指定操作数减1并存回。
●NEG
格式:NEG opr
操作:(opr)←-(opr)
●CMP
格式:CMP opr1,opr2
执行的操作:(opr1)-(opr2)
该指令与SUB指令一样执行减法操作,但它并不保存结果,只是根据结果设置条件标志位,CMP指令后往往跟着一条条件转移指令,根据比较结果产生不同的程序分支。
以上五种指令均可作字或字节运算,而且除DEC指令不影响CF标志外,它们都影响条件标志位。
在实现上,减法指令实际上就是把减数求补再和被减数执行加法运算,所以减法运算的条件码设置规则与加法类似。CF位说明无符号数相减的溢出,同时它又是被减数的最高有效位向高位的借位标志。OF位则说明带符号数的溢出。减法的CF标志位反映无符号数运算中的借位情况,因此当作为无符号数运算时,若减数>被减数,此时有借位则CF=1,否则CF=0。或者也可以简单地用二进制减法运算中最高有效位向高位的进位情况来判别,有进位时CF=0,无进位时CF=1。减法的OF位的设置方法为:若两个数的符号相反,而结果的符号与减数相同则OF=1,除上述情况外OF=0。OF=1说明带符号数的减法溢出,结果是错误的。CF标志位还可以在双字减法运算时,作为低字相减是否向高字借位的标志。
NEG指令用于求补,即计算指定操作数的补码。由于8086微处理器采用补码形式来表示数值,所以求补实际上就是带符号数的求相反数。对于NEG指令的条件码设置,8086微处理器遵守如下规则:NEG指令的条件码按求补后的结果设置,只有当操作数为0时求补运算的结果使CF=0,其他情况则CF均为1;只有当字节运算时对-128求补以及字运算时对-32768求补的情况下OF=1,其他则CF均为0。
DEC指令为单操作数指令,作用是将操作数的值减1,类似C语言的“--”操作符,其操作数可以是除了立即数之外的任何一种寻址方式。
CMP指令和SUB指令执行相同的操作,唯一的不同在于CMP指令的减法运算结果不送入目标寄存器。CMP指令经常被用于进行两个操作数的比较,再根据操作数比较的结果执行经常跟在CMP指令后面的条件转移指令,进行条件转移,从而实现分支程序的设计。根据CMP指令设置的标志位来判断比较的结果。
1)根据ZF判断两个数是否相等。若ZF=1,则两数相等。
2)若两个数不相等,则按以下两种情况考虑:
①比较的是两个无符号数:若CF=0,则dest>src;若CF=1,则dest<src。
②比较的是两个有符号数:若OF XOR SF=0,则dest>src;若OF XOR SF=1,则dest<src。
例:
SUB[SI+14H],0136H
如指令执行前(DS)=3000H,(SI)=0040H,(30054H)=4336H,则指令执行后,(30054H)=4200H,SF=0,ZF=0,CF=0,OF=0。
例:
SUB DH,[BP+4]
如指令执行前(DH)=41H,(SS)=0000H,(BP)=00E4H,(000E8)=5AH,则指令执行后,(DH)=E7H,SF=1,ZF=0,CF=1,OF=0。
下面程序用来实现比较AL、BL、CL中带符号数的大小,将最小数放在AL中。
3.乘法指令
乘法指令用来实现算术乘法,有两条指令:MUL和IMUL。和加法、减法指令无符号数与带符号数共用相同的指令不同,无符号数和带符号数的乘法操作需要使用不同的指令来完成。
●MUL
格式:MUL src
执行的字节操作:(AX)←(AL)×(src)
执行的字操作:(DX,AX)←(AX)×(src)
执行无符号乘法操作,执行字节乘法还是字乘法由源操作数的长度而定,如果源操作数为字节,则执行字节乘法,如果源操作数是字,则执行字乘法。
●IMUL
格式:IMUL src
执行的字节操作:(AX)←(AL)×(src)
执行的字操作:(DX,AX)←(AX)×(src)
和MUL指令的操作相同,唯一差别在于,执行的乘法为带符号乘法。
MUL和IMUL指令表面上看起来是单操作数指令,实际上却是双操作数指令。在MUL和IMUL指令中只给出了源操作数,而目的操作数则隐含为累加器(AX或AL)。乘法指令的源操作数可以使用除立即数以外的任何一种寻址方式。乘法指令和前面介绍过的加法、减法指令还有一个重要的不同点,就是乘法指令的结果的长度是源操作数和目的操作数的长度的二倍,所以,乘法指令的结果寄存器被隐含地指定为AX(字节操作)或DX和AX(字操作,高位在DX,低位在AX)。
MUL指令和IMUL指令的使用条件是由数的格式决定的。很明显(11111111b)×(11111111b)当把它看作无符号数时应为255d×255d=65025d,而把它看作带符号数时则为(-1)×(-1)=1。因此必须根据所要相乘的格式来决定选用哪一种指令。
乘法指令会影响全部条件码,但只有CF和OF标志位有意义,其余条件码为无定义,即在执行了MUL或IMUL指令之后,除CF和OF标志位之外,其余标志位的状态是不确定的,也是无用的。
MUL指令的条件码设置为:当乘积的高一半(字节操作的AH或字操作的DX)为0时,CF OF=00,否则,CF OF=11。这样的条件码设置可以用来判定字节相乘的结果是8位(CF=0)还是16位(CF=1),或判定字相乘的结果是16位(CF=0)还是32位(CF=1)。
IMUL指令的条件码设置为:当乘积的高一半(字节操作的AH或字操作的DX)为低一半的符号扩展时,CF OF=00,否则,CF OF=11。这样的条件码设置可以用来判定字节相乘的结果的有效位是8位(CF=0)还是16位(CF=1),或判定字相乘的结果的有效位是16位(CF=0)还是32位(CF=1)。
例:
如(AL)=0B4H,(BL)=11H,由于(AL)=0B4H为无符号数180,带符号数-76,(BL)=11H为无符号数17,带符号数17,所以执行IMUL BL的结果为:
(AX)=OFAF4H=-1292
CF=OF=1
执行MUL的结果为:
(AX)=OBF4H=3060
CF=OF=1
乘法指令的运算比较复杂,执行需要的时间比较长,因此在可以用其他指令(比如加减法指令、后面要介绍的移位等)实现的功能最好不用乘法来实现,以提高程序的运行速度。
4.除法指令
除法指令用来实现算术除法,有两条指令:DIV和IDIV。和乘法指令一样,无符号数和带符号数的乘法操作需要使用不同的指令来完成。
●DIV
格式为:DIV src
执行的字节操作:16位被除数在AX中,8位除数为源操作数,结果的8位商在AL中,8位余数在AH中。表示为:(www.xing528.com)
(AL)←(AX)/(src)的商
(AH)←(AX)/(src)的余数
执行的字操作:32位除数在DX,AX中,其中DX为高位字,16位除数为源操作数,结果的16位商在AX中,16位余数在DX中。表示为:
(AX)←(DX,AX)/(src)的商
(DX)←(DX,AX)/(src)的余数
商和余数均为无符号数。
●IDIV
格式:IDIV src
执行的操作:与DIV相同,但操作数必须是带符号数,商和余数也均为带符号数,且余数的符号和被除数的符号相同。
除法指令的寻址方式和乘法指令相同。其目的操作数必须存放在AX或DX,AX中。源操作数可以用除立即数以外的任一种寻址方式。
除法指令对所有条件码位均无定义,在除法指令执行后,所有的条件码都处于不确定状态,所以除法指令的结果是否正确也不能通过条件码来判断。当除数为0,或者被除数的高位绝对值大于除数的绝对值造成商的绝对值超过能够表示的区间而溢出时,错误的除法将触发0型中断,进行进一步的处理。
除法指令的运算复杂,执行需要的时间比较长,因此在可以用其他指令(比如加减法指令、后面要介绍的移位等)实现的功能,最好不用除法来实现,以提高程序的运行速度。
5.符号扩展指令
由于除法指令的字节操作要求被除数为16位,字操作要求被除数为32位,当需要执行诸如字节除以字节或字除以字之类的操作时,就需要将一个字节的符号位扩展到一个字,或者将一个字的符号位扩展到双字,这时就需要使用符号扩展指令。符号扩展指令有两条:CBW和CWD。
●CBW
格式:CBW
执行的操作:将AL的内容符号扩展到AH。即如(AL)的最高有效位为0,则(AH)=00;如(AL)的最高有效位为1,则(AH)=0FFH。
●CWD
格式:CWD
执行的操作:AX的内容符号扩展到DX。即如(AX)的最高有效位为0,则(DX)=0000;如(AX)的最高有效位为1,则(DX)=0FFFFH。
CBW和CWD两条指令可以实现将字节转换为字和将字转换为双字的功能,一般用于为除法指令做准备。这两条指令都不影响条件码。
6.十进制调整指令
前面提到的所有算术运算指令都是对二进制数进行运算的指令,在基于8086微处理器的计算机系统中,在第1章中介绍过的8421-BCD码也经常被使用。针对基于8421码的算术运算,8086并没有提供一套特别的算术运算指令,而是和二进制数共享相同的一组加法和减法指令,并依靠一组BCD码调整指令来对加法、减法的结果进行调整,以实现BCD码的加法和减法。
8086微处理器提供的十进制调整指令分为两组,分别针对压缩的BCD码和非压缩的BCD码。
(1)压缩的BCD码调整指令
我们知道,机器所提供的ADD、ADC以及SUB、SBB指令只适用于二进制加、减法,对于压缩的BCD码并不适用。在使用加、减法指令对BCD码运算后必须经调整后才能得到正确的结果。加法的调整规则是:任意两个用BCD码表示的十进制数位相加的结果,如数值在1010和1111之间或者产生了向高位的进位则在其上加6就可得到正确的结果。加法的十进制调整指令为DAA。
●DAA
格式:DAA
执行的操作:(AL)←把AL中的和调整到压缩的BCD格式。
这条指令之前必须执行ADD或ADC指令,加法指令必须把两个压缩的BCD码相加,并把结果存放在AL寄存器中。本指令的调整方法是:如果AF标志(辅助进位位)为1,或者AL寄存器的低4位是十六进制的A~F,则AL寄存器内容加06H,且将AF位置1;如果CF标志为1,或者AL寄存器的高4位是十六进制的A-F,则AL寄存器内容加60H,并将CF位置1,如果以上两点都不成立,则清除标志位AF和CF。经过调整后,AL的值仍是压缩型BCD码,即:二个压缩型BCD码相加,并进行调整后,得到的结果还是压缩型BCD码。
DAA指令对OF标志无定义,并影响所有其他条件标志。
类似地,针对BCD码的减法,8086提供了减法的十进制调整指令DAS。
●DAS
格式:DAS
执行的操作:(AL)←把AL中的差调整到压缩的BCD格式。
这条指令之前必须执行SUB或SBB指令,减法指令必须把两个BCD码相减,并把结果存放在AL寄存器中。本指令的调整方法是:如果AF标志为1,或者AL寄存器的低4位是十六进制的A-F,则使AL寄存器的内容减去06H,并将AF位置1。如果CF标志为1,或者AL寄存器的高4位是十六进制的A-F,则使AL寄存器的内容减去60H,并将CF位置1,如果以上两点都不成立,则清除标志位AF和CF。
DAS指令对OF标志无定义,并影响所有其他条件标志。
例:
MOV AL,48H
MOV BI,74H
ADD AL,BL
DAA
执行ADD后,(AL)=BCH,高4位低4位均大于9,故DAA指令执行加66H调整,最后结果为:(AL)=22H,CF=1,AF=1。
(2)非压缩的BCD码调整指令
DAA和DAS指令是针对压缩BCD码的调整指令,这是一种以4位二进制数来表示一位十进制数的方法,即一个字节可以用来表示2位十进制数。例如十进制数2809可以被表示为压缩BCD码(0010)(1000)(0000)(1001)。同时,8086还支持非压缩的BCD码,一种以8位二进制数来表示一位十进制数的表示方法。非压缩BCD码8位中的低4位是以8421码表示的十进制数位,高4位则没有意义。例如2809可以被表示为:uuuu0010 uuuu1000uuuu0000 uuuu1001。(可以看出数字的ASCⅡ码是一种非压缩BCD码。因为数字的ASCⅡ的高4位值为0011而低4位是以8421码表示的二进制数位,这符合非压缩BCD码的规定)
针对非压缩BCD码,8086提供了一组调整指令,分别对非压缩BCD码的加、减、乘、除运算进行调整。这些指令是加法调整指令AAA、减法调整指令AAS、乘法调整指令AAM和除法调整指令AAD。
●AAA
格式:AAA
执行的操作:(AL)←把AL中的和调整到非压缩的BCD格式
(AH)←(AH)+调整产生的进位值
对非压缩BCD码加法预算的结果进行调整,以得到正确的非压缩BCD码格式的运算结果。这条指令执行之前必须执行ADD或ADC指令,加法指令必须把两个非压缩的BCD码相加,并把结果存放在AL寄存器中。本指令的调整步骤如下。
(1)如AL寄存器的低4位在0~9之间,且AF位为0,则跳过第(2)步,执行第(3)步;
(2)如AL寄存器的低4位在十六进制数A~F之间或AF为1,则AL寄存器的内容加6,AH寄存器的内容加1,并将AF位置1;
(3)清除AL寄存器的高4位;
(4)AF位的值送CF位。
AAA指令可以直接对非压缩BCD码(诸如ASCⅡ码)相加的结果进行调整,而不需要先将其高四位清零。
AAA指令除影响AF和CF标志外,其余标志位均无定义。
●AAS
格式:AAS
执行的操作:(AL)←把AL中的差调整到非压缩的BCD格式
(AH)←(AH)-调整产生的借位值
对非压缩BCD码减法运算的结果进行调整,以得到正确的非压缩BCD码格式的运算结果。这条指令执行之前必须执行SUB或SBB指令,减法指令必须把两个非压缩的BCD码相减,并把结果存放在AL寄存器中。本指令的调整步骤如下。
(1)如AL寄存器的低4位在0~9之间,且AF位为0,则跳过第(2)步,执行第(3)步;
(2)如AL寄存器的低4位在十六进制数A~F之间或AF位为1,则把AL寄存器的内容减去6,AH寄存器的的内容减去1,并将AF位置1;
(3)清除AL寄存器的高4位;
(4)AF位的值送CF位。
和AAA指令一样,AAS指令可以直接对非压缩BCD码(诸如ASCⅡ码)相加的结果进行调整,而不需要先将其高四位清零。
AAS指令除影响AF和CF标志外,其余标志位均无定义。
●AAM
格式:AAM
执行的操作:(AX)←把AL中的积调整到非压缩的BCD格式
对非压缩BCD码乘法运算的结果进行调整,以得到正确的非压缩BCD码格式的运算结果。这条指令执行之前必须执行MUL指令把两个非压缩的BCD码相乘(此时要求其高4位为0),结果放在AL寄存器中。本指令的调整方法是,把AL寄存器的内容除以0AH,商放在AH寄存器中,余数保存在AL。
AAM指令影响SF、ZF和PF标志位,其他标志位无定义。
●AAD
格式:AAD
操作:(AL)←10×(AH)+(AL)
(AH)←0
前面所述的加法、减法和乘法的ASCⅡ调整指令都是用加法、减法和乘法指令对两个非压缩的BCD码运算以后,再使用AAA、AAS、AAM指令来对运算结果进行十进制调整的。除法的情况却不同,它是针对以下情况而设立的。
如果被除数是存放在AX寄存器中的二位非压缩BCD数,AH中存放十位数,AL中存放个位数,而且要求AH和AL中的高4位均为0。除数是一位非压缩的BCD数,同样要求高4位为0。在把这两个数用DIV指令相除以前,必须先用AAD指令把AX中的被除数调整成二进制数,并存放在AL寄存器中。由此可见,AAD指令的执行是在DIV指令执行之前而不是之后,即先将AX中的被除数调整成二进制数并存放在AL中,再用DIV指令来执行二进制除法。
AAD指令影响SF、ZF和PF标志位,其他标志位无定义。
在使用非压缩BCD码调整指令的时候,一定要注意几种非压缩BCD码调整指令在使用上的不同。加法与减法调整指令不要求运算前的非压缩BCD码高4位为0,而乘法和除法调整指令则有这个要求。加法、减法和乘法调整指令的执行应当在加法、减法和乘法指令执行之后,而除法调整指令的执行则正好相反,应当在除法指令执行之前执行。
例:
下面程序段实现按十进制计算16-8
MOV AX,0106H
MOV BL,08H
SUB AL,BL
AAS
所得结果为(AL)=08H,(AH)=0,CF=AF=1。
下面程序段实现按十进制乘法计算7×8
MOV AL,07H
MOV CL,08H
MUL CL
AAM
所得结果为(AH)=05H,(AL)=06H,即是56的非压缩BCD码表示。
下面程序段实现按十进制除法计算55÷7
MOV AX,0505H
MOV CL,07H
AAD
DIV CL
所得结果为(AH)=6,(AL)=7,即为非压缩的BCD码(商7余6)。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。