Преобразование двоичного числа в 4 цифры BCD - как работает деление?

Изучаю сборку для процессора Motorola 68000. Я использую следующую книгу:
Программирование на языке ассемблера 68000, второе издание, Левенталь, Хокинс, Кейн, Крамер
и Симулятор EASy68k.

У меня есть несколько вопросов о преобразовании двоичного числа в двоично-десятичный код (двоично-десятичный код).
Исходная проблема в книге гласит: «Преобразуйте содержимое переменной NUMBER в ячейке памяти 6000 в четыре цифры BCD в переменная STRING в ячейке 6002 (старшая цифра в 6002). 16-битное число в NUMBER беззнаковое и меньше 10 000 ».

Пример:

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

Поскольку 1C52 (шестнадцатеричный) = 7250 (десятичный) (MC68k - процессор с прямым порядком байтов)

Поскольку MC68k - хороший процессор CISC с богатым арсеналом инструкций, написать решение было несложно:

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, в моем алгоритме.

А также, для двоичного кода в двоичный, я могу использовать инструкции ADD и логического сдвига для имитации умножения, но каков будет аналогичный способ для двоичного кода в двоично-десятичный? Как я могу имитировать деление и вычислить частное / остаток, используя более простые инструкции, чем DIV (например, вычитание, сложение, логические сдвиги и т. Д.)?

Я также нашел здесь интересный алгоритм преобразования двоичного в двоично-десятичный:
http://www.eng.utah.edu/~nmcdonal/Tutorials/BCDTutorial/BCDConversion.html

Но я не могу понять, почему это работает. Почему нам нужно добавить 3 (= 11 двоичных файлов) к каждому столбцу (= 4 бита), который содержит число, большее или равное 5?

Я думал о кодировании решения, использующего этот алгоритм, но:
- после 3 смен мне нужно было бы проверять, содержит ли столбец единиц число больше 4 после каждой смены - после 7 смен мне нужно было бы проверить, есть ли Столбец единиц и десятков содержит число больше 4 после каждой смены
- после 11 смен мне пришлось бы проверять, содержат ли столбцы единиц, десятков и сотен число больше 4 после каждой смены
- после 15 смен , Мне нужно будет проверять, содержат ли столбцы единиц, десятков, сотен и тысяч число больше 4 после каждой смены

похоже, что у процессора будет гораздо больше работы ...


person AltairAC    schedule 03.01.2014    source источник


Ответы (1)


По поводу «добавить три»: при достижении значения 5 или более в столбце при следующей смене значение столбца будет чем-то> = 10.

Теперь рассмотрим следующее: каждое двоичное число удваивает свой вес после сдвига влево. Но при переходе, скажем, от столбца единиц к столбцу десятков, 1 «теряет» свои предыдущие 16 значений и становится десяткой (10). Таким образом, его вес уже не 16, а 10.

Как мы это компенсируем? Просто мы добавляем три (3), что составляет половину от шести (6), чтобы в следующую смену мы потеряли шесть (3) веса, как объяснялось ранее, но в то же время восстановим его, сдвинув влево ( левый сдвиг => умножение на два) три, которые мы добавили ранее. Вес снова уравновешен.

Лучшее объяснение здесь

Надеюсь, это поможет. Кстати, я тоже изучаю M68k в университете, и ваш код был хорошим чтением, спасибо.

person znpy    schedule 04.01.2014
comment
Большое спасибо! Моя репутация недостаточно высока, чтобы проголосовать за ваш ответ (пока :)). Ваша ссылка мне очень помогла, она также содержит модифицированную версию алгоритма Binary- ›BCD, который можно использовать как метод деления на 10. Думаю, я подожду еще немного, потому что кто-то может добавить дополнительную информацию для раздела моего вопроса. - person AltairAC; 04.01.2014
comment
Вы уверены, что ваша репутация невысока? Проверьте еще раз, сейчас он кажется довольно высоким;) - person znpy; 05.01.2014