Inline ASM - pass variable address as an input operator?

Go To Last Post
5 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Freaks,

Been wrestling with the GCC inline assembler today, trying to convert over a few functions to save space and execution time. However, I've hit upon a roadblock. I've searched with Google and read the Inline ASM section of the AVR-GCC manual.

I'm trying to load in the address of a global 32-bit variable (only 24 bits used), so I can increment the lower 24 bits and then store it back to SRAM. I have so far:

	asm volatile ("LDI	r26, %0              \n\t"
	              "LDI	r27, %1              \n\t"
	              "LD	r24, X+              \n\t"
	              "LD	r25, X+              \n\t"
	              "LD	r23, X+              \n\t"
	              "ADIW	r24, 0x01            \n\t"
	              "ADC	r23, r1              \n\t"
	              "ST	 -X, r23             \n\t"
	              "ST	 -X, r25             \n\t"
	              "ST	 -X, r24             \n\t" :: "M" (&CurrAddress >> 8), "M" (&CurrAddress & 0xFF) : "r23", "r24", "r25", "r26", "r27");

However I get the error "error: invalid operands to binary >>" and "error: invalid operands to binary &" when I try to compile it. The code works when the two address bytes are passed as constants in the code, but obviously I want to avoid that.

How can I pass the upper and lower bytes of the SRAM variable's address so I can use it in my inline ASM code?

Cheers!
- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

abcminiuser wrote:
How can I pass the upper and lower bytes of the SRAM variable's address so I can use it in my inline ASM code?

I believe that the code below does what you want. Note also that I removed the uses of some specific registers. This allows the optimizer more latitude.

uint32_t CurAddress;

void
foo(void)
{
  uint16_t tmp;

  __asm__ __volatile__
  (
    "ld  %A0, %a1+                   \n\t" 
    "ld  %B0, %a1+                   \n\t" 
    "ld  __tmp_reg__, %a1+           \n\t" 
    "adiw  %A0, 1                    \n\t" 
    "adc   __tmp_reg__, __zero_reg__ \n\t" 
    "st  -%a1, __tmp_reg__           \n\t" 
    "st  -%a1, %B0                   \n\t" 
    "st  -%a1, %A0                   \n\t"
    : "=&w" (tmp)
    : "e" (&CurAddress)
    : "r0"
  );
}

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Don, will test that tonight when I get home from Uni.

I notice you've omitted the loading of the X pointer. The Inline ASM documentation gives the constraint "e" as the pointer pairs - does this mean it will implicitly load the variable's address into X/Y/Z for me?

I must say that the GCC Inline ASM is FAR from intuitive. I know it's not the AVRLibC maintainer's fault, but rather that of the compiler team itself. I think from now on I'll switch to using .S files for all my assembly routines rather than inline ASM.

Again, thanks!

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

> I must say that the GCC Inline ASM is FAR from intuitive.

Sure. I believe the main purpose of inline assembly is to provide the
library maintainers with a good means of wrapping up certain stuff.
By no means it can be thought as a replacement to plain old assembly
source files.

I always thought GCC's inline assembler is terrible. -- Till the day
I've seen IAR's inline assembler. It doesn't offer you about *any*
feature. You just have to make assumptions about the register
allocation performed by the compiler, assumptions that you aren't
getting into the compiler's way, assumptions about almost everything.
Since that day, I simply realized *how* comfortable GCC's inline
assembler is, despite of its arcane syntax and semantics.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

abcminiuser wrote:
I notice you've omitted the loading of the X pointer. The Inline ASM documentation gives the constraint "e" as the pointer pairs - does this mean it will implicitly load the variable's address into X/Y/Z for me?

Indeed it does and that illustrates the power of the technique. You don't know, and need not know, which of the three registers the compiler will choose. In contrast, if you insist on using a specific register that may prevent the compiler from making a particular set of optimizations that it might otherwise have preferred to use.

Likewise, using the temporary variable (tmp in my example) allows the compiler to choose the actual registers to use consistent with its optimization strategy. I could have done the same with the third byte (for which I used r0) but I chose not to for illustrative purposes.

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net