BCD码与二进制数形式一样,含义不同。给出一个二进制数00111000,它究竟是用压缩BCD码表示的38呢?还是十进制数56呢?为了区别它们,在书写时应注意BCD码的末尾应加上"BCD"后缀。如同十六进制数后加"H",二进制数后加"B"一样。不过这种写法只是为便于我们加以区分,不能用在源程序中。例如在源程序中BCD码00111000仍然要写成00111000B,不能写00111000BCD。这其中的原因很简单,那就是编译程序并不需要区分二进制数与BCD码,00111000BCD最终的编译结果仍然是38H,和00111000B一样。
至于BCD码的运算,8086/88没有使用新的指令,仍然采用ADD、SUB这些二进制运算指令。这些指令只用于二进制数运算,它们不能处理BCD码。举例来说,如果计算56BCD+34BCD的话,使用ADD指令所得到的结果是8AH,而不是应该得到的90BCD。所以BCD码在进行了运算之后要使用一些指令对结果进行必要的调整才行。
压缩BCD码与非压缩BCD码的调整指令不一样,而且不同的运算之后采用的调整指令也不相同。我们举几个压缩的BCD码运算实例,看看调整指令应该遵循的一些规则。
(1)23BCD+36BCD
很明显这两个数相加之后所得的结果59H是不需要进行调整的。
(2)23BCD+37BCD
这两个数相加之后将得结果5AH,而我们需要的结果应该是60H,可见此结果需要调整。调整方法很简单,将5AH的低4位加上一个6就能得出正确的结果。
(3)63BCD+51BCD
这两个数相加应得的结果应该是114H,实际运算结果是0B4H,此结果也要调整。调整方法是将此结果加上60H。0B4H+60H=114H。
由(2)、(3)两例可以得出调整指令遵循的第一个规律:当运算结果的高4位或低4位出现了A-F中的数,则结果需要调整。调整方法是在出现了A-F的4位上加6。
(4)29BCD+57BCD
这两个数相加应得结果是86H,实际运算结果是80H,故此结果需要调整。调整方法与例(2)相同,低4位加6即可。
(5)74BCD+83BCD
这两个数相加应得结果是157H,实际结果是0F7H,需要调整。调整方法与例(3)相同,高4位加6。
通过例(4)、(5)可以看出即使运算结果中没有出现值为A-F的数位,但是如果出现了低4位到高4位的进位(标志位AF=1)或高4位的进位(标志位CF=1),则结果应要调整。调整方法仍然不变。
减法运算结果的调整方法与加法相反,不是加6而是减6。但是遵循的条件是一样的。具体的指令共有两条:
·DAA(Decimal Adjust for Addition)加法的十进制调整指令
·DAS(Decimal Adjust for Subtraction)减法的十进制调整指令
关于这两条指令还有几点要简单说明一下:
(1)调整指令与运算指令是不分家的,调整指令之前必须要有运算指令,运算指令之后必须紧跟调整指令。
(2)运算指令可以是ADD、SUB,还可以是带进位、借位的加减法(后文叙述),但不能是INC、DEC这两条指令,因为这两条指令不影响CF标志。
(3)运算结果必须在AL寄存器中,不能在其它寄存器内。这也可以算是累加器的一个特定用途吧。
下面的这个程序演示了压缩BCD码运算及调整指令的应用方法,从数据输入到运算结果输出过程很清楚。不过这个程序只能处理两位BCD码,不能处理更多的数位。用户必须每次输入一个两位数,不能输入一位数,也不能用[Backspace]键修改,否则都会导致错误的结果。
data segment assume ds:data msg1 db 0dh,0ah,'First:',24h ;输入第一个数 msg2 db 0dh,0ah,'Next:',24h ;输入第二个数 msg3 db 0dh,0ah,'Resault:',24h ;计算结果 data ends code segment assume cs:code main proc far push ds ;初始化堆栈 xor ax,ax push ax mov ax,data ;初始化DS寄存器 mov ds,ax mov dx,offset msg1 ;显示MSG1:"输入第一个数:" mov ah,9 int 21h call get_num ;调用GET_NUM子过程接收数据 mov bl,al ;将第一个数送入BL寄存器 mov dx,offset msg2 ;显示MSG2:"输入第二个数:" mov ah,9 int 21h call get_num ;调用GET_NUM子过程接收数据 add al,bl ;将两个数相加 daa ;对计算结果进行调整 push ax ;将调整后的计算结果送入堆栈 mov dx,offset msg3 ;显示"Resault:" mov ah,9 int 21h pop ax ;取回计算结果 call out_num ;调用OUT_NUM输出计算结果 ret ;结束进程 main endp get_num proc near ;接收数据子过程 push bx ;保存寄存器 mov ah,1 ;等待键盘输入 int 21h mov cl,4 ;将输入的字符左移4位 shl al,cl mov bl,al ;处理后的输入数据送入BL寄存器 mov ah,1 ;再次接收一个字符 int 21h and al,0fh ;保存字符的低4位 add al,bl ;将两个字符合成为一个压缩的BCD码 pop bx ;恢复寄存器 ret ;返回主过程 get_num endp out_num proc near ;输出数据子过程 mov bl,al ;待输出的数据送入BL寄存器 mov cl,4 ;保留待输出的数据高4位 shr al,cl ;并将这4位数据移至低4位 call out_chr ;调用OUT_CHR将高4位以字符形式输出 mov al,bl ;取回待输出的数据 and al,0fh ;保留其低4位 out_chr label near ;定义一个标号 or al,30h ;将4位数据转换成ASCII码 mov ah,0eh ;利用10H中断的0EH功能将ASCII码输出 int 10h ret ;返回主过程 out_num endp code ends end main
BCD运算与调整有时也可以应用到其它方面,比如下面给出的这个程序例就是应用了BCD运算将AX寄存器中的数据按十六进制的形式显示在屏幕上。请大家自行分析这个程序中所用的技巧。