Изучаю сборку для процессора 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 подхода:
- Мнемоника умножения, такая как "MULU # 10, D1", которая просто дает число, умноженное на 10.
Или набор, состоящий из простых инструкций:
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 после каждой смены
похоже, что у процессора будет гораздо больше работы ...