3.4 算术运算类指令
89C51单片机的算术运算类指令共有24条,可以完成加、减、乘、除等各种操作,全部指令都是8位数运算指令。如果需要做16位数的运算则需编写相应的程序来实现。
算术运算类指令大多数要影响到程序状态字寄存器PSW中的溢出标志OV、进位(借位)标志CY、辅助进位标志AC和奇偶标志位P。利用进位(借位)标志CY,可进行多字节无符号整数的加、减运算,利用溢出标志可对带符号数进行补码运算,辅助进位标志则用于BCD码运算的调整。
3.4.1 加法指令
ADD A,# data ;A ← (A) + data ADD A,direct ;A ← (A) + (direct) ADD A,Rn ;A ← (A) + (Rn) ADD A,@Ri ;A ← (A) + ((Ri))
这组指令的功能是把源操作数所指出的内容与累加器A的内容相加,其结果存放在A中。源操作数的寻址方式分别为立即寻址、直接寻址、寄存器寻址和寄存器间接寻址。运算结果对程序状态字PSW中的CY、AC、OV和P的影响情况如下。
进位标志CY:在加法运算中,如果D7位向上有进位,则CY=1;否则,CY=0。
半进位标志AC:在加法运算中,如果D3位向上有进位,则AC=1;否则,AC=0。
溢出标志OV:在加法运算中,如果D7、D6位只有一个向上有进位时,OV=1;如果D7、D6位同时有进位或同时无进位时,OV=0。
奇偶标志P:当A中“1”的个数为奇数时,P=1;为偶数时,P=0。
例3-8 设(A)=94H,(30H)=8DH,执行指令ADD A,30H,操作如下:
结果(A)= 21H,(CY)= 1,(AC)= 1,(OV)= 1,(P)= 0
参加运算的两个数,可以是无符号数(0~255),也可以是有符号数(-128~+1 2 7)。用户可以根据标志位CY或OV来确定运算结果或判断结果是否正确。无符号数用CY位表示进位、溢出(不考虑 OV位),有符号数用 OV位表示溢出(不考虑CY位)。
上例中,若把94H、8DH看做无符号数相加,结果中CY=1,表示运算结果发生了溢出(结果超出了8位),此时溢出的含义是向高位产生进位,所以确定结果时不能只看累加器A的内容,而应该把CY的值加到高位上,才可得到正确的结果。即结果为121H,若把94H、8DH看做有符号数(补码表示的),结果中OV=1,它表示运算结果发生了溢出,A中的值是个错误的结果。因为两个负数相加,结果却为正数,很显然是错误的。
两个正数相加或两个负数相加时,若发生溢出,将改变结果的符号位,所得结果都是错误的,OV=1正好指出了这一类错误。
无论编程人员把参加运算的两个数看做是无符号数还是有符号数,计算机在每次运算后,都会按规则自动设置标志位CY、OV、AC、P,对于编程人员来说,应能根据这些标志来了解当前运算结果所处的状态,以确定程序的走向。
3.4.2 带进位加法指令
ADDC A,# data ;A ← (A) + data + (CY) ADDC A,direct ;A ← (A) + (direct)+ (CY) ADDC A,Rn ;A ← (A) + (Rn)+ (CY) ADDC A,@Ri ;A ← (A) + ((Ri)) + (CY)
这组指令的功能是把源操作数所指出的内容与累加器A的内容相加、再加上进位标志CY的值,其结果存放在A中。源操作数的寻址方式分别为立即寻址、直接寻址、寄存器寻址和寄存器间接寻址。运算结果对PSW标志位的影响与ADD指令相同。
需要说明的是,这里所加的进位标志CY的值是在该指令执行之前已经存在的进位标志值,而不是执行该指令过程中产生的进位标志值。
例3-9 设(A)=AEH,(R1)=81H,(CY)=1,
执行指令ADDC A,R1,则操作如下:
结果(A)= 30H,(CY)= 1,(OV)= 1,(AC)= 1,(P)= 0
带进位加法指令主要用于多字节数的加法运算。因低位字节相加时可能产生进位,而在进行高位字节相加时,要考虑低位字节向高位字节的进位,因此必须使用带进位的加法指令。
例3-10 设有两个无符号16位二进制数,分别存放在30H、31H单元和40H、41H单元中(低8位先存),写出两个16位数的加法程序,将和存入50H、51H单元(设和不超过16位)。
解 由于不存在16位数的加法指令,所以只能先加低8位,后加高8位,而在加高8位时要连低8位相加的进位一起相加,编程如下:
MOV A,30H ;取一个加数的低字节送A中 ADD A,40H ;两个低字节数相加 MOV 50H,A ;结果送50H单元 MOV A,31H ;取一个加数的高字节送A中 ADDC A,41H ;高字节数相加,同时加低字节产生的进位 MOV 51H,A ;结果送51H单元
3.4.3 带借位减法指令
SUBB A,# data ;A ← (A)- data - (CY) SUBB A,direct ;A ← (A)- (direct)- (CY) SUBB A,Rn ;A ← (A)- (Rn)- (CY) SUBB A,@Ri ;A ← (A)- ((Ri))- (CY)
这组指令的功能是将累加器A中的数减去源操作数所指出的数和进位位CY,其结果存放在累加器A中。源操作数的寻址方式分别为立即寻址、直接寻址、寄存器寻址和寄存器间接寻址。运算结果对程序状态字PSW中各标志位的影响情况如下。
借位标志CY:在减法运算中,如果D7位向上需借位,则CY=1;否则,CY=0。
半借位标志AC:在减法运算中,如果D3位向上需借位,则AC=1;否则,AC=0。
溢出标志OV:在减法运算中,如果D7、D6位只有一个向上需借位时,OV=1;如果D7、D6位同时需借位或同时无借位时,OV=0。
奇偶标志P:当A中“1”的个数为奇数时,P=1;为偶数时,P=0。
减法运算只有带借位减法指令,而没有不带借位的减法指令。若要进行不带借位的减法运算,应该先用指令将CY清零,然后再执行SUBB指令。
需强调的一点是,减法运算在计算机中实际上是变成补码相加,下面举例说明。
例3-11 设(A)=DBH,(R4)=73H,(CY)=1。
执行指令SUBB A,R4 则操作如下:
结果(A)= 67H,(CY)= 0,(AC)= 0,(OV)= 1
由上述两式可见两种算法的最终结果是一样的。在此例中,若DBH和73H是两个无符号数,则结果67H是正确的;反之,若为两个带符号数,则由于产生溢出(OV=1),使得结果是错误的,因为负数减正数其结果不可能是正数,OV=1,就指出了这一错误。
3.4.4 加1指令
INC A ;A ← (A) + 1 INC direct ;direct ← (direct)+ 1 INC Rn ;Rn ← (Rn) + 1 INC @Ri ;Ri ← ((Ri)) + 1 INC DPTR ;DPTR ← (DPTR) + 1
这组指令的功能是将操作数所指定单元的内容加1。本组指令除“INC A”指令影响P标志外,其余指令均不影响PSW标志。
加1指令常用来修改操作数的地址,以便于使用间接寻址方式。
3.4.5 减1指令
DEC A ;A ← (A) - 1 DEC direct ;direct ← (direct)- 1 DEC Rn ;Rn ← (Rn) - 1 DEC @Ri ;Ri ← ((Ri)) - 1
这组指令的功能是将操作数所指定单元的内容减1。除“DEC A”指令影响P标志外,其余指令均不影响PSW标志。
3.4.6 乘、除法指令
89C51单片机有乘、除法指令各一条,它们都是一字节指令,执行时需 4 个机器周期。
1.乘法指令
MUL AB ;BA← (A)× (B)
这条指令的功能是把累加器A和寄存器B中的两个8位无符号数相乘,所得16位乘积的低8位放在A中,高8位放在B中。
乘法指令执行后会影响3 个标志:若乘积小于FFH(即B的内容为零),则OV=0,否则OV=1。CY总是被清零,奇偶标志P仍按A中1的奇偶性来确定。
例3-12 已知(A)=80H,(B)=32H,
执行指令 MUL AB
结果(A)= 00H,(B)= 19H,OV= 1,CY= 0,P= 0
2.除法指令
DIV AB ;A← (A)/(B)之商,B← (A)/(B)之余数
这条指令的功能是对两个8位无符号数进行除法运算。其中被除数存放在累加器A中,除数存放在寄存器B中。指令执行后,商存于累加器A中,余数存于寄存器B中。
除法指令执行后也影响3个标志:若除数为零(B=0)时,OV=1,表示除法没有意义;若除数不为零,则OV=0,表示除法正常进行。CY总是被清零,奇偶标志P仍按A中1的奇偶性来确定。
例3-13 已知(A)=87H(135D),(B)=0CH(12D),
执行指令 DIV AB
结果(A)= 0BH,(B)= 03H,OV= 0,CY= 0,P= 1
3.4.7 十进制调整指令
DA A
该指令的功能是对A中刚进行的两个BCD码的加法结果进行修正。该指令只影响进位标志CY。
有时希望计算机能存储十进制数,而且能进行十进制数的运算,这时就要用BCD码来表示十进制数。
所谓BCD码就是采用4位二进制编码表示的十进制数。4位二进制数共有16个编码, BCD码是取它前 10 个的编码 0000~1001 来代表十进制数的 0~9,这种编码称为8421BCD码,简称BCD码。一个字节可以存放2位BCD码(称为压缩的BCD码)。
如果两个BCD码数相加,结果也是BCD码,则该加法运算称为BCD码加法。在89C51单片机中没有专门的BCD码加法指令,要进行BCD码加法运算,也要用加法指令ADD或ADDC,然而计算机在执行ADD或ADDC指令进行加法运算时,是按照二进制规则进行的,对于4位二进制数是按逢16进位;而BCD码是逢10进位,两者存在进位差。因此用ADD或ADDC指令进行BCD码相加时,可能会出现错误。例如:
在上述3组运算中,(a)的运算结果是正确的,因为8的BCD码就是1000;(b)的运算结果是错误的,因为13的BCD码应是00010011,但运算结果却是1101,BCD码中没有这个编码;(c)的运算结果也是错误的,因为 17 的BCD码应是00010111,而运算结果是00010001。
由此可知,当运算结果大于16或在10~16之间时,都将出现错误结果,因此要对结果进行修正,这就是所谓的十进制调整问题。
使用DA A指令可修正这种错误,它能对运算结果自动进行调整。实际上,计算机在遇到十进制调整指令时,中间结果的修正是由ALU硬件中的十进制调整电路自动进行的。因此,用户不必考虑它是怎样调整的。使用时只需在上述加法指令后面紧跟一条DA A指令即可。
在执行DA A指令之后,若CY=1,则表明相加后的和已等于或大于十进制数100。
例3-14 试编写程序,实现95+59的BCD码加法,并将结果存入30H、31H单元。
MOV A,# 95H ;95的BCD码数送A中 ADD A,# 59H ;A与59的BCD码相加,结果存在A中 DA A ;对相加结果进行十进制调整 MOV 30H,A ;A中的和 (十位、个位的BCD码)存入30H MOV A,# 00H ;A清零 ADDC A,# 00H ;加进位 (百位的BCD码) DA A ;BCD码相加之后,必须使用调整指令 MOV 31H,A ;存进位
第一次执行DA A指令的结果:(A)= 54H,CY= 1
最终结果(31H)= 01H,(30H)= 54H
需要指出的是,DA A指令只能用在加法指令的后面。如果要进行BCD码减法运算,也应该进行调整,但在89C51单片机中没有十进制减法调整指令,也不像有的微处理器有加减标志,因此要用适当的方法来进行十进制减法运算。
为了进行十进制减法运算,可用加减数的补数来进行,2位十进制数是对100取补的,例如:减法60-30=30,也可以改为补数相加为
60+(100-30)=130
丢掉进位后,就得到正确的结果。
在实际运算时,不可能用9位二进制数来表示十进制数100,因为CPU是8位的。为此,可用8位二进制数10011010(9AH)来代替。因为这个二进制数经过十进制调整后就是100000000。因此,十进制无符号数的减法运算可按以下步骤进行:
(1)求减数的补数,即9AH-减数;
(2)被减数与减数的补数相加;
(3)对第二步的和进行十进制调整,就得到所求的十进制减法运算结果。
这里用“补数”而没有用“补码”,这是为了和带有符号位的补码相区别。由于现在操作数都是正数,没有必要再加符号位,故称“补数”更为合适一些。
例3-15 编写程序实现十进制减法,计算87-38。
CLR C ;减法之前,先清CY位,即CY= 0 MOV A, # 9AH ;9AH送A中 SUBB A, # 38H ;做减法,计算38的补数送A中 ADD A, # 87H ;38的补数与87做加法 DA A ;对相加结果进行调整
丢掉进位,取调整结果的低8位,即得结果为十进制数49,显然是正确的结果。