the AVR 8bit core instruction set includes the more efficient 16bit versions of the "load data from memory" (LDS) and "store data to memory" (STS) instructions, as opposed to the 32bit versions. As per the AVR Instruction Set Manual, the assembler syntax for the two versions is exactly identical. Obviously the assembler is meant to find out by itself if the 16bit version can be used (i.e. the address is 7bit only, and the register is one of R16:31). Only I find the GNU assembler does not. It always emits the 32bit version.
As an example, consider the assembler code
lds r17, 0x4D
The GNU assembler emits
c4: 10 91 4d 00 lds r17, 0x004D
where it should have emitted
c4: 1D A4 lds r17, 0x4D
Maybe it is necessary to explicitly tell the assembler to use the 16bit version, perhaps by using a different mnemonic (deviating from the instruction Set manual)?
How can I make the GNU assmbler (AVR version) emit the 16bit versions of the LDS and STS instructions?
Update: The instructions are part of inline assembly in C code, hence the assembler is not directly used but called from the C compiler. The controller is a ATmega32M1, which has an AVR5 core.
The 16-bit LDS
and STS
are only available on the Reduced Tiny core, which is for devices ATtiny4, 5, 9, 10, 20, 40, 102 and 104, see -mmcu=avrtiny
in the avr-gcc online documentation.
The addresses may be in the range 0x40...0xbf
LDS
encodes as 1010 0kkk dddd kkkk
STS
encodes as 1010 1kkk dddd kkkk
where d
encodes the register R16...R31, and k
encodes the address.
Then there are the avr1
devices ATtiny11, 12, 15, 28, AT90S1200 that don't have RAM at all, and that have to use general purpose registers for static storage.
On all other cores, LDS
and STS
are 32-bit instructions that have an address range of 64 KiB from 0x0 to 0xffff.
LDS
is encoded as 1001 000d dddd 0000
STS
is encoded as 1001 001d dddd 0000
where d
specifies the register R0...R31. The code is followed by a 16-bit address.
Then there are the devices with EBI (external bus interface) avrxmega5
, avrxmega7
(ATxmega64A1, 64A1U, 128A1, 128A1U, 128A4U) that allow to connect external RAM that exceeds the 16-bit address space. This was "solved" by new RAMP special function registers that extend the address at the high end: RAMPZ
extends Z
, RAMPD
extends LDS
and STS
, etc. This is a pain and not supported by the compiler. The only support from GCC is that it saves / restores these SFRs in ISR prologue / epilogue when they may be used in the ISR.
How can I make the GNU assmbler (AVR version) emit the 16bit versions of the LDS and STS instructions?
You can't, see above.
The instructions are part of inline assembly in C code
Notice, that on Reduced Tiny, you have to make sure that the address of a variable in static storage fits the LDS
/ STS
address range. Notice for example that an ATtiny40 has RAM outside the 0x40...0xbf range. You can assert that a variable location fits that range by means of the absdata
variable attribute
As you are using ATmega32M1, no restrictions apply to the address range.
For locations in the I/O range, the in
and out
instructions can be used. On non-Xmega, non-ReducedTiny devices, this range extends from 0x20...0x5f. Notice you have to use inline asm print modifier %i
to get a suitable I/O address. In the lower half of that range, instructions sbic
, sbis
, sbi
and cbi
can be used, too.
For examples on how to use addresses in inline asm, see the AVR-LibC inline asm Cookbook.