Если вы понимаете, как выполняется десятичное умножение «на бумаге», тогда будет легко заменить десятичные цифры на 8-битные числа.
Допустим, у вас есть два десятичных числа: AB и XY (где A, B, X, Y - десятичные цифры).
Вы можете выразить это как сумму двух умножений двузначного числа на однозначное число:
AB * XY = (AB * Y) + ((AB * X) << 1d) <em>(где << 1d означает смещение на 1 цифру влево и равно десятичному умножению на 10) </em>
Умножение двух цифр на одну цифру можно выразить в тех же терминах, что и
AB * Y = (B * Y) + ((A * Y) << 1d) </p>
или весь эксперимент может быть записан как:
AB * XY = (B * Y) + ((A * Y) << 1d) + ((B * X) << 1d) + ((A * X) << 2d) </p>
теперь вы можете просто перейти с десятичной системы на систему на основе 256, предполагая, что каждая цифра в приведенном выше примере представляет собой один байт.
Итак, чтобы найти умножение AB * XY, вам нужно:
- вычислить B * Y, сохранить его в 4-байтовый результат (два старших байта будут равны нулю)
- вычислить B * X, сдвинуть его влево на 1 байт, добавить к результату
- вычислить A * Y, сдвинуть его влево на 1 байт, добавить к результату
- вычислить A * X, сдвинуть его влево на 2 байта, добавить к результату
В ассмблере это может выглядеть следующим образом:
Допустим, у нас есть:
- r25: r24 - первый множитель,
- r23: r22 - второй
- r21: r20: r19: r18 - результат
Код будет выглядеть так:
clr r16 // a zero register, we'll need it in the future
mul r24, r22 // r1:r0 = r24 * r22
movw r0, r18 // move result to r19:r18
clr r20 // clear r21 and r22
clr r21
mul r24, r23 // r1:r0 = r24 * r23
add r19, r0 // add to the result starting from the second from the right byte (r19)
adc r20, r1 // add next byte with carry
adc r21, r16 // add zero with carry
mul r25, r22 // r1:r0 = r25 * r22
add r19, r0 // add to the result starting from the second from the right byte (r19)
adc r20, r1 // add next byte with carry
adc r21, r16 // add zero with carry
mul r25, r23 // r1:r0 = r25 * r23
add r20, r0 // add to the result starting from the third from the right byte (r20)
adc r21, r1 // add next byte with carry
Молодец! Теперь у вас есть четырехбайтовый результат в r21: r20: r19: r18
Чтобы разделить на 2, вы можете просто переместить результат 1 двоичной позиции вправо. Вам нужно две инструкции:
- lsr - логический сдвиг вправо. Он перемещает биты на одну позицию вправо. Самая левая позиция заполнена нулями, а выталкиваемая крайняя правая позиция хранится в флаге переноса.
- ror - повернуть вправо через носитель. Он делает то же самое: перемещает биты на одну позицию вправо и сохраняет выдвинутую крайнюю правую позицию в переносе, но крайнюю левую позицию заполняет начальное значение флага переноса
код:
lsr r21
ror r20
ror r19
ror r18
теперь четырехбайтовое значение делится на два (округление вниз)