I am completely new to AVR assembly and I have a hex number 0x20C
which is 16 bit.
I want to load this constant hex number in two 8 bit registers in AVR assembly
Is it possible to do something like:
LDI R17:R18 0x20C
EDIT: if it is not possible to load a 16 bit number in two 8 bit registers this way, may someone could give me an alternative option?
There are a few instructions to do 16-bit addition or increment with pairs of registers, but not loads from memory and certainly not immediates. You need to load each byte separately, using one ld
/ldi
/ldd
/lds
/whatever instruction for each byte / each destination register.
(There is an instruction to copy a pair of registers to another pair, upported on many modern AVR CPUs, (see @ReAl's answer), but not load or load-immediate. You can make a macro for 2 instructions as ReAl shows, but it won't improve performance or code-size, just human readability.
AVR is a RISC instruction-set where most (almost all?) whole instructions are 16-bit. There isn't room for a 16-bit immediate, only 8. You can always split an immediate into its two 8-bit halves, like ldi r17, 0x0c
for the low half and ldi r18, 0x2
for the high half.
I checked the AVR instruction set and didn't see any multi-byte loads or immediates (https://onlinedocs.microchip.com/pr/GUID-0B644D8F-67E7-49E6-82C9-1B2B9ABE6A0D-en-US-1/index.html - update; the online ISA ref is much less nicely formatted than it used to be).
Then I compiled this C with AVR gcc on the Godbolt compiler explorer to see if maybe there was something I was missing.
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
So unless AVR gcc has a missed optimization, or there's some reason it's avoiding a word load, you can't do it.
Update: gcc is targeting baseline AVR, so it can't use movw
to copy a pair of registers.
Compiling with -mmcu=avr6
makes add(int*, int*)
more efficient:
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
But as we can see, there's still no instruction for doing anything else to a pair of registers, so it all has to be done separately. Still, copying a pair of registers with one machine instruction is quite nice because it's not rare to need to do that.