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) {

&lt;some prologue to save SREG and all needed registers&gt;

//...

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?

kuroi neko
  • 183
  • 6

1 Answers1

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.

Edgar Bonet
  • 45,094
  • 4
  • 42
  • 81