将二进制数转换为4个BCD数字 - 除法如何工作?

时间:2014-01-03 21:14:40

标签: algorithm assembly 68000

我正在研究摩托罗拉68000 CPU的组装。我使用的书是:
68000汇编语言程序设计,第二版,Leventhal,Hawkins,Kane,Cramer
EASy68k模拟器。

关于将二进制数转换为BCD(二进制编码的十进制数),我有几个问题 书中的原始问题是:“将内存位置6000处的变量NUMBER的内容转换为位置6002处的变量STRING中的四个BCD数字(6002中的最高有效位).NUMBER中的16位数字是无符号的并且更少超过10,000。“

示例:

 input:  NUMBER - (6000) = 1C52
 output: STRING - (6002) = 07
                  (6003) = 02
                  (6004) = 05
                  (6005) = 00

因为1C52(十六进制)= 7250(十进制)(MC68k是一个大端CPU)

由于MC68k是一个不错的CISC CPU,具有丰富的指令,因此编写解决方案并不困难:

DATA:       EQU $6000
PROGRAM:    EQU $4000

        ORG DATA

NUMBER:     DS.W 1
STRING:     DS.L 1

        ORG PROGRAM

MAIN:       
        CLR.L D0                ; Clear D0               
        MOVE.W NUMBER, D0       ; Store our number (2 bytes) to D0      
        MOVEA.L #STRING+4, A0   ; We'll go backwards -> so we store the address of the last byte + 1 of the variable STRING to A0 (+1 because we use pre-decrement addressing)
        MOVEQ #1, D2            ; A counter which will cause (DBRA) two iterations of the LOOP part of the program        
        MOVE.L #$FFFF,D3        ; D3 is a mask used to clear the 2 most significant bytes of D0 in each iteration of LOOP       

LOOP:   DIVU.W #10, D0          ; Divide D0 by 10 (the result will be saved in the first 2 bytes od D0, and the remainder (our BCD digit) in the second two (more significant) two bytes of D0         
        MOVE.L D0, D1           ; Make a copy of D0         
        SWAP D1                 ; swap the first 16 bits of D0 with the second 16 bits of D0            
        MOVE.B D1,-(A0)         ; Now the first 16 bits of D1 contain the remainder (our BCD digit) which we will save to address -(A0)          
        AND.L D3, D0            ; Use the mask to clear the second half (16 bits) of D0 so that the next DIVU instruction doesn't by mistake take the remainder as a part of the number which needs to be divided      
        DBRA D2, LOOP           ; Decrement our counter D2 by 1 and go back to LOOP if D2 is not equal to -1

        DIVU #10, D0            ; This (last) division by 10 will cause our most significant BCD decimal to be at the lower 16 bits of D0 while the second most significant BCD decimal will be the remainder of the DIVU instruction and therefore stored at the higher 16 bits of D0          
        MOVE.B D0, -2(A0)       ; Save the most significant BCD digit       
        SWAP D0                 ; swap lower and higher 16 bits of D0         
        MOVE.B D0, -(A0)        ; Save second most significant BCD digit

        MOVE.B #9, D0
        TRAP #15

        END MAIN

DIVU = DIVision无符号

我很满意这个解决方案,但我想知道/了解MC68k如何更详细地执行此划分(计算结果和余数),让我解释一下。例如,如果我们想要做相反的事情,即将BCD编号转换为二进制数,我们可以使用以下算法:让我们采用以下序列:'7','2','5','0' BCD数字,其中'7'是最高有效数字,'0'是最低有效数字。如果我们想要得到这些数字的十进制数,我们就可以这样做(伪代码):

number = 0;
number = number * 10 + 7   = 0 * 10 + 7 = 0 + 7 = 7 
number = number * 10 + 2   = 7 * 10 + 2 = 70 + 2 = 72 
number = number * 10 + 5   = 72 * 10 + 5 = 720 + 5 = 725  
number = number * 10 + 0   = 725 * 10 + 0 = 7250 + 0 = 7250  

但是,当然,我们需要调整用基数2写的数字的乘法.MC68k提供或多或少的2种方法:

  1. 乘法助记符,如“MULU#10,D1”,它只会产生一个乘以10的数字
  2. 或由简单说明组成的集合:

    ADD.W D1, D1            ; D1 = D1 + D1 = 2x  
    MOVE.W D1, D3  
    LSL.W #2, D3            ; D3 = 8x = (2x) * 4 
    ADD.W D3, D1            ; D1 = 10x = 2x + 8x  
    
  3. 产生相同的结果(原始数字x - > 10x)。 ADD指令的工作方式如下:

    ADD D1, D2  = pseudo-code =  D2 = D2 + D1
    

    LSL指令是逻辑左移指令。并且我们知道将数字向左逻辑移位1位的结果与将其乘以2并将其向左移2位相同与将数字乘以4的结果相同。

    因此,对于BCD到二进制转换,我可以在我的算法中使用像MULU这样的乘法指令,而对于Binary到BCD,我可以在我的算法中使用像DIVU这样的除法指令。

    而且,对于BCD到Binary,我可以使用ADD和逻辑移位指令来模拟乘法,但是Binary到BCD的类似方法是什么?如何通过使用比DIV更简单的指令(如减法,加法,逻辑移位......)来模拟除法并计算商/余数?

    我还在这里找到了一个有趣的Binary到BCD转换算法:
    http://www.eng.utah.edu/~nmcdonal/Tutorials/BCDTutorial/BCDConversion.html

    但我无法弄清楚为什么会这样。为什么我们需要为每列(= 4位)添加3(= 11二进制),其中包含大于或等于5的数字?

    我考虑过使用这种算法编写解决方案但是:
    - 在3班之后,我必须检查每个班次后列中是否包含大于4的数字 - 在7班之后,我必须检查每班和每列之后是否包含大于4的数字 - 在11班后,我必须检查每班,数十和数百列是否包含每班后大于4的数字 - 15班后,我必须检查每班后数,数十,数百和数千列是否包含大于4的数字

    看起来像CPU还有很多工作要做......

1 个答案:

答案 0 :(得分:5)

关于'添加三'的事情:当列中的值达到5或更多时,在下一个班次中,列的值将是> = 10.

现在考虑一下:每次二进制数一旦左移,它的重量就会加倍。但是,当从一列到十列时,1'会失去它之前的16个特征而变成十分之一(10)。因此,它的重量不再是16,而是10。

我们如何弥补这一点?很简单,我们添加三(3),这是六(6)的一半,所以在下一个班次我们将失去六(3)的重量,如前所述,但同时通过左移(()同时重新获得它( left shift =>乘以2)我们之前添加的三个。重量再次平衡。

Better explainer here

希望它有所帮助。顺便说一句,我也在大学学习M68k,你的代码读起来也不错,谢谢。