【问题标题】:Converting a Binary number to 4 BCD digits - how does division work?将二进制数转换为 4 个 BCD 数字 - 除法是如何工作的?
【发布时间】:2014-01-03 21:14:39
【问题描述】:

我正在研究 Motorola 68000 CPU 的组装。我用的书是:
68000 汇编语言编程,第二版,Leventhal、Hawkins、Kane、Cramer
EASy68k 模拟器。

我有几个关于将二进制数转换为 BCD(二进制编码十进制)的问题。
书中原题说:“将内存位置 6000 的变量 NUMBER 的内容转换为位置 6002 的变量 STRING 中的四个 BCD 数字(6002 中的最高有效位)。NUMBER 中的 16 位数字是无符号的且小于超过一万。”

例子:

 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  
    

产生相同的结果(原始数字 x -> 10x)。 ADD 指令的工作方式如下:

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

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

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

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

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

但我不知道为什么会这样。为什么我们需要将 3(= 11 二进制)添加到包含大于或等于 5 的数字的每一列(= 4 位)?

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

看来 CPU 要做的事情还有很多……

【问题讨论】:

    标签: algorithm assembly 68000


    【解决方案1】:

    关于“加三”的事情:当某一列的值达到 5 或更多时,在下一个班次时,该列的值将 >= 10。

    现在考虑一下:每个二进制数在左移后权重加倍。但是,当从一列到十列时,1“失去”其先前的 16 个 wight 并变为十 (10)。因此,它的重量不再是 16,而是 10。

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

    Better explainer here

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

    【讨论】:

    • 非常感谢!我的声誉还不够高,无法对您的答案进行投票(目前:))。您的链接对我帮助很大,它还包含 Binary->BCD 算法的修改版本,可用作除以 10 技术。我想我再等一会儿,因为有人可能会为我的问题的除法部分添加一些额外的信息。
    • 你确定你的声誉很低吗?再次检查,现在似乎很高;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-21
    • 1970-01-01
    • 2010-10-02
    • 1970-01-01
    • 2023-03-25
    • 2018-09-15
    相关资源
    最近更新 更多