AVR组件:将16位数字加载到两个8位寄存器中

时间:2018-11-16 15:30:48

标签: assembly avr

我对AVR组装完全陌生,我有一个16位的十六进制数字0x20C

我想将此常数十六进制数加载到AVR组件中的两个8位寄存器中

是否可以执行以下操作:

LDI R17:R18 0x20C

编辑:如果无法以这种方式在两个8位寄存器中加载一个16位数字,有人可以给我一个替代选择吗?

2 个答案:

答案 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_wsts_wpush_w,…,ror_w等。它们大多数是由其他宏汇编器命令生成的。例如,{p>生成的asr_wlsr_wror_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