ITEEDU

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]键修改,否则都会导致错误的结果。

ADDBCD.A
        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寄存器中的数据按十六进制的形式显示在屏幕上。请大家自行分析这个程序中所用的技巧。