assemblyavrgnu-assembler

AVR instructions LDS and STS 16 bit versions with GNU assembler


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.


Solution

  • 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

    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.

    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.