首页 > 解决方案 > 使用 AVR 汇编器将两个 16 位数字相乘

问题描述

我编写了一个代码来计算三角形区域,在两个寄存器(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 位数字作为基数和高数,如何将其加载到两个寄存器中并计算 16 位的 (base x high) 和 (base x high) /2 操作?

标签: avratmega

解决方案


如果您了解“纸上”如何执行十进制乘法,那么用 8 位数字替换十进制数字将很容易。

假设您有两个十进制数字:AB 和 XY(其中 A、B、X、Y - 是十进制数字)。您可以将其表示为两位数乘以一位数的两次乘法之和:

AB * XY = (AB * Y) + ((AB * X) << 1d) (其中 << 1d 表示左移 1 位,等于十进制乘以 10)

两位数乘以一位数可以用相同的术语表示

AB * Y = (B * Y) + ((A * Y) << 1d)

或者整个表达式可以写成:

AB * XY = (B * Y) + ((A * Y) << 1d) + ((B * X) << 1d) + ((A * X) << 2d)

现在您可以从十进制系统跳转到基于 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

现在四字节值除以二(向下舍入)


推荐阅读