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

Alt*_*rAC 0 algorithm assembly 68000

我正在研究Motorola 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
Run Code Online (Sandbox Code Playgroud)

因为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
Run Code Online (Sandbox Code Playgroud)

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  
Run Code Online (Sandbox Code Playgroud)

但是,当然,我们需要调整基数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  
    
    Run Code Online (Sandbox Code Playgroud)

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

ADD D1, D2  = pseudo-code =  D2 = D2 + D1
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

我想过编码一个使用这个算法的解决方案,但是:
- 3班后,我必须检查每个班次后列中是否包含一个大于4的数字 - 在7个班次后,我将不得不检查是否有几个列每次移动
后包含一个大于4的数字- 在11个班次之后,我将不得不检查每个班次
后的数字,数十和数百列是否包含大于4的数字- 在15个班次之后,我将不得不检查是否,每个班次后,数十,数百和数千列包含一个大于4的数字

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

小智 5

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

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

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

这里有更好的解释器

希望能帮助到你.顺便说一句,我也在大学学习M68k,你的代码读起来也不错,谢谢.