Умножьте два 16-битных числа с помощью AVR-ассемблера - PullRequest
0 голосов
/ 23 мая 2019

Я написал код для вычисления площади треугольника с загрузкой значений base и high в двух регистрах (R16 и R18).Это 8-битные значения

 .include "./m2560def.inc"  
;------------------------------------------------------------------
; Constants
;------------------------------------------------------------------
.def    base    = r16
.def    high    = r17

.equ    base_value  = 10
.equ    high_value = 20

    .cseg
        .org    0x0000
        rjmp    reset           ; reset intr
reset:
        LDI R16, HIGH(RAMEND)
        OUT SPH, R16
        LDI R16, LOW(RAMEND)
        OUT SPL, R16

RCALL   configure_ports

start:
        LDI base, base_value
        LDI high, high_value 
        MUL base, high
        MOVW R18, R0
        LSR  R18         ;divide by 2 
        MOV  R19, R18
        OUT  PORTB, R19  ;Output result in PORTB
        OUT  PORTD, R19  ;Output result in PORTD
        RJMP    start

configure_ports:
        ;Configurare B and C ports as outputs
        LDI R16, 0XFF
        OUT DDRB, R16
        OUT DDRC, R16
        ret

Если я использую 16-битные числа для основания и старшего, как загрузить его в два регистра и вычислить операции (base x high) и (base x high) / 2 для 16бит?

1 Ответ

0 голосов
/ 23 мая 2019

Если вы понимаете, как выполняется десятичное умножение «на бумаге», тогда будет легко заменить десятичные цифры на 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, вам нужно:

  1. вычислить B * Y, сохранить его в 4-байтовый результат (два старших байта будут равны нулю)
  2. вычислить B * X, сдвинуть его влево на 1 байт, добавить к результату
  3. вычислить A * Y, сдвинуть его влево на 1 байт, добавить к результату
  4. вычислить 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

теперь четырехбайтовое значение делится на два (округление вниз)

...