avr - 使用 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 操作?
解决方案
如果您了解“纸上”如何执行十进制乘法,那么用 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 的乘法,您需要:
- 计算 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
现在四字节值除以二(向下舍入)