首先讲一下51单片机为什么不能直接进行16位的二进制数运算?
在第一章第一节51单片机内部结构概述中提到,51单片机是一个8位的CPU,所以一次操作最大的无符号8位二进制数是1111 1111(B),化成16进制表示就是0FF(H),也就是十进制中的255(D)。所以对一个16位的二进制数,它是采用把16位的二进制数分成两个8位的二进制数去存储,计算后的结果也是以两个8位的二进制存储到两个相邻的存储单元。
课堂练习:编写16位二进制数的减法运算(参考16位加法程序的地址分配)。
要求:最终结果放入寄存器R1,R0中,并记录有借位16进制减法和无借位16进制减法运算的实验过程。
测试用例:
case1:0412H - 0203H
case2:0403H - 0212H
参考程序: 16位二进制的加法运算
ORG 30H
MOV 30H,#01H ;第一个数的低8位存入30H
MOV 31H,#02H ;第一个数的高8位存入31H
MOV 32H,#03H ;第二个数的低8位存入32H(老师给的实验报告上这里打印错误)
MOV 33H,#04H ;第二个数的高8位存入33H(老师给的实验报告上这里打印错误)
MOV A,30H ;读30H数据到累加器A
ADD A,32H ;A与32H数据进行加法运算
MOV R0,A ;低8位加法结果存入R0
MOV A,31H ;读31H数据到累加寄存器A
ADDC A,33H ;带进位的加法运算
MOV R1,A ;高8位进位加法结果存入R1
END
程序分析:
MOV A,30H ;读30H数据到累加器A
ADD A,32H ;A与32H数据进行加法运算
MOV R0,A ;低8位加法结果存入R0
对于这一段程序的作用呢,其实就可以理解为我们在做十进制中两位数加法的时候,先把个位两个数相加的和写下来,如果个位数和大于10(在单片机ADD指令中等同于加完之后A中的数大于0FF(H),也就是溢出),就写下个位数和的个位数部分,再做个记号(在单片机中体现为将CY位置1),等十位上的相加的时候再加个一(单片机中ADDC带进位相加就是这个作用)。
MOV A,31H ;读31H数据到累加寄存器A
ADDC A,33H ;带进位的加法运算
MOV R1,A ;高8位进位加法结果存入R1
这一段程序 也是就在做十进制中两位数加法的时候,将十位数相加的作用。当数据不够大的时候,也就是上一段程序A中结果不出现溢出的时候,这里用ADD替换ADDC作用也是相同的。但是当上一段程序A中结果出现溢出的时候,这里就必须使用ADDC指令了,不然结果就会出错。
接下来我们开始写减法运算的程序。
在十进制中加法和减法的差别也就是一个符号的差别,51单片机的16位二进制减法运算也可以参考加法写下来,只需将ADDC指令换成SUBB指令即可,但是单片机中没有对应ADD指令的不带进位的减法指令,所以对于替换ADD指令,我们仍然采用SUBB指令,只不过在这条指令前面加一条
CLR C
指令。这条指令的作用就是用来清零CY位,防止CY位对SUBB指令造成的影响。
所以16位二进制数的减法运算如下:
ORG 30H
MOV 30H,#01H
MOV 31H,#02H
MOV 32H,#03H
MOV 33H,#04H
MOV A,30H
CLR C
SUBB A,32H
MOV R0,A
MOV A,31H
SUBB A,33H
MOV R1,A
END
老师给的两个数据测试,第一个是不会产生溢出的,CY位一直是0,也是就PSW程序状态字中第一位一直是0。如果你实验时使用第一组数据进行单步调试时发现PSW的第一位变为了一,建议好好检查。
第二组数据中程序运行到第一个SUBB指令,PSW中的第一位会变为1,一直到第二个SUBB指令结束,PSW中的第一位才会重新变为0。
至于程序每一步运行后,寄存器中A和PSW的状态我就不一一显示了,看懂程序后自己一步步的读可以写出来。
运行结果:
case1: R0 = 0F R1 = 02
case2: R0 = F1 R1 = 01
还有上次实验时误导了几个同学写了两个程序,在第二个SUBB之前也加了CLR C命令,这样出现的问题是不能计算会产生溢出的数据。
在此表示歉意!