我对AVR组装完全陌生,我有一个16位的十六进制数字0x20C
。
我想将此常数十六进制数加载到AVR组件中的两个8位寄存器中
是否可以执行以下操作:
LDI R17:R18 0x20C
编辑:如果无法以这种方式在两个8位寄存器中加载一个16位数字,有人可以给我一个替代选择吗?
答案 0 :(得分:2)
有一些指令可对成对的寄存器进行16位加法或递增,但不会从内存中加载,当然也不是立即数。 您需要分别使用一个ld
/ ldi
/ ldd
/ lds
/每个字节/每个目标寄存器的任何指令来分别加载每个字节
(有一条指令可将一对寄存器复制到另一对寄存器上,并在许多现代AVR CPU上受支持(请参见@ReAl's answer),但没有加载或立即加载。您可以为2条指令制作宏如ReAl所示,但这不会提高性能或代码大小,只会提高人类可读性。
AVR是RISC指令集,其中大多数(几乎所有?)全部指令均为16位。 16位立即数没有空间,只有8位。您始终可以将立即数分成两个8位半部分,例如ldi r17, 0x0c
代表下半部分,ldi r18, 0x2
代表中间部分。高一半。
我检查了AVR指令集,但没有看到任何多字节加载或立即数(https://www.microchip.com/webdoc/avrassembler/avrassembler.wb_instruction_list.html),然后我用AVR gcc on the Godbolt compiler explorer编译了C,以查看是否存在某些问题。丢失了。
int return_immediate(void) { return 0x20c; }
ldi r24,lo8(524) # ldi r24, 0x0c
ldi r25,hi8(524) # ldi r25, 0x02
ret
int glob;
int return_global(void) {
return glob;
}
lds r24,glob
lds r25,glob+1
ret
int add(int *a, int *b) {
return *a + *b;
}
mov r30,r24
mov r31,r25 Z = a
ld r24,Z
ldd r25,Z+1 retval = *a
mov r30,r22
mov r31,r23 Z = b
ld r18,Z
ldd r19,Z+1 tmp = *b
add r24,r18
adc r25,r19 retval += tmp
ret
因此,除非AVR gcc错过了优化,或者出于某种原因避免了单词加载,否则您将无法做到。
更新:gcc的目标是基准AVR,因此它不能使用movw
复制一对寄存器。
使用-mmcu=avr6
进行编译可提高add(int*, int*)
的效率:
add:
movw r30,r24 # Z = a
ld r24,Z
ldd r25,Z+1
movw r30,r22 # Z = b
ld r18,Z
ldd r19,Z+1
add r24,r18
adc r25,r19
ret
但是,正如我们所看到的,仍然没有指令对一对寄存器执行任何其他操作,因此必须分别完成所有操作。尽管如此,用一条机器指令复制一对寄存器还是很不错的,因为这样做并非罕见。
答案 1 :(得分:1)
如Peter Cordes答案中所述,AVR指令集没有用于将16位值加载到寄存器对中的指令。但是...
如果无法在两个8位寄存器中加载16位数字 这样,有人可以给我一个替代选择吗?
为方便起见,汇编器通常具有创建宏的能力(如果您打算在汇编中进行大量编程,请注意此机会)。
宏语法取决于工具链。对于gnu as
,我使用以下宏将16位立即数加载到给定和下一个寄存器中
.macro ldi_w reg:req, val:req
ldi \reg, lo8(\val)
ldi \reg + 1, hi8(\val)
.endm
所以
ldi_w r16, 0xBEEF
将0xEF
加载到r16
中,将0xBE
加载到r17
中。
重要说明:gnu as
允许使用寄存器编号(ldi 16, 0xC3
)和寄存器名称(ldi r16, 0xC3
)。上面的宏需要寄存器 numbers ,因此我的所有汇编源都包括avr_reg_numbers.h
,其中包含类似
#ifdef r0
#undef r0
#endif
#define r0 0
使用此样式宏,可以将Peter Cordes answr中的示例重写为
mov_w r30, r24 ; r30 ← r24, r31 ← r25
ld_w r24, Z
mov_w r30, r22
ld_w r18, Z
add_w r24, r18
p.s.0我的avr_as_macro.h
包含很多宏,例如lds_w
,sts_w
,push_w
,…,ror_w
等。它们大多数是由其他宏汇编器命令生成的。例如,{p>生成的asr_w
,lsr_w
和ror_w
//---------------------------- right word shifts
// asr_w, lsr_w, ror_w
.irp cmd, asr, lsr, ror
.macro \cmd\()_w reg:req
\cmd \reg+1 $ ror \reg
.endm
.endr
p.s.1某些AVR芯片具有汇编指令movw
,该指令在一个cpu周期内移动寄存器对,而其中一些(旧的或非常简单的)则没有这样的命令。因此,我使用宏mov_w
来调用movw
或根据当前AVR拱门级别生成一对命令。
//---------------------------------- word move
// movw exist in all new AVRs except __AVR_TINY__ (tiny4..10/20/40)
// Not exist in tiny26
// odd register numbers and overlapping are not supported
#ifdef __AVR_HAVE_MOVW__
.macro mov_w dst:req, src:req
movw \dst, \src
.endm
#else
.macro mov_w dst:req, src:req
mov \dst, \src $ mov \dst+1, \src+1
.endm
#endif