7

For some reason I want to mix a bit of assembly with C++ (not C).
To allow the code to be compilable using the standard Arduino IDE, I don't want to use direct assembly source.
I don't want to use a wrapper C file either.

My problem is as follows:

I have a class with a single instance (implementing some kind of driver) that looks like this:

class cDriver {
public:
    char ISR_param;
    // ...
};

cDriver Driver;

If I want to access the ISR_param of this single instance from within an ISR, I can do something like:

ISR (TIMER2_COMPA_Vect) {
    do_something_with (Driver.ISR_param);
}

This works like a charm: the address of Driver.ISR_param beign a constant, the compiler simply generates an lds r24, &Driver.ISR_param instruction to load the proper byte as a parameter for do_something_with.

Now when I try to replicate the same mechanism with inline assembler, things get a lot more complicated.

The address of Driver.ISR_param is only known to the C++ compiler as an mangled label, and I can't find a way to pass it to the inline assembler to generate the equivalent lds r24, <&Driver.ISR_param>.

trying to remane the variable

The renaming of variables for assembly like so:

char global_var asm("my_var");

and an attempt to use it like so:

asm ("lds r24, my_var\n");

will compile, but produce a linker error (my_var is undefined).

Looking at the object symbol table, the only label for global_var is a C++-like mangled name.
However hard I tried this renaming trick, I could not bring the compiler to generate a my_var label.

trying to use asm constraints

A constraint like

asm (lds r24, %0\n" : : "I"(&global_var));

will simply not compile. I scoured the possible parameter types and could not find a way to let the assembler know about this bloody address.

a terribly awkward workaround

The only way I found to make the compiler generate this dreaded lds r24,... instruction is to perform an actual function call.

So you can do something like:

__attribute__((always_inline)) give_me_my_friggin_variable(char data)
{
    asm volatile ("cmp  %0,%0\n"::"r"(data));
}   

ISR (TIMER2_COMPA_Vect, ISR_NAKED) {

    <some prologue to save SREG and all needed registers>

    //...

    give_me_my_friggin_variable(Driver.ISR_param);

    // continue assembly code with r24 properly initialized
}

That will generate:

    lds r24, <the proper address> // added by the compiler...
    cmp r24,r24                   // ...because of the register constraint here

Well, that sort of works, but at the cost of generating a totally useless instruction.

so my question is...

Is there a simpler way to let the assembler know about addresses of C++ static variables?

1 Answer 1

4

Try this:

asm volatile("lds r24, %0" : : "i" (&Driver.ISR_param));

The "i" constraint means you provide an integer constant. It can be any integer, unlike the uppercase "I" constraint which is limited to 6-bit positive integers.

Addendum: The constraints supported by gcc are listed in the gcc documentation, section Constraints for asm Operands:

  • The "i" constraint is listed in the subsection “Simple Constraints” and is described as:

An immediate integer operand (one with constant value) is allowed. This includes symbolic constants whose values will be known only at assembly time or later.

  • The "I" constraint is in the subsection “Machine Constraints” and, for the AVR architecture, is defined as

Constant greater than -1, less than 64.

4
  • 1
    Dang... And thanks :). This "i" type was not listed in the online manuals I found. For completeness, could you add a link to an up to date documentation? Btw. I wonder why an example of such an obvious use of the inline assembler is so hard to come by.
    – kuroi neko
    Commented Aug 8, 2017 at 22:05
  • @kuroineko: The "i" constraint is indeed missing from the avr-libc's Inline Assembler Cookbook. I added a link to the gcc documentation that lists it. Commented Aug 9, 2017 at 6:48
  • Thanks a lot. That sort of cooking is pretty confidential I guess :).
    – kuroi neko
    Commented Aug 9, 2017 at 9:52
  • The "i" constraint does not mean "integer". "i" specifies an immediate operand, which is the union of "n" (integral operand known at compile-time) and "s" symbolic operand known at link time. For example, addresses of variables in static storage are known at link time, thus "i" will work for them. Commented Nov 7, 2022 at 13:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.