使用 AVR 汇编器将两个 16 位数字相乘

问题描述 投票:0回答:1

我编写了一个计算三角形面积的代码,将底值和高值加载到两个寄存器(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 位的 (基数 x 高) 和 (基数 x 高) /2 运算?

avr atmega
1个回答
3
投票

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

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

AB * XY = (AB * Y) + ((AB * X) << 1d) (其中 << 1d means shifted 1 digit position left and equals to a multiplication by 10 in decimal)

两位数乘以一位数可以用与

相同的术语来表示

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 r18, r0 // 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

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

© www.soinside.com 2019 - 2024. All rights reserved.