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
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.
-
1Dang... 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– kuroi neko2017年08月08日 22:05:01 +00:00Commented 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.Edgar Bonet– Edgar Bonet2017年08月09日 06:48:04 +00:00Commented Aug 9, 2017 at 6:48 -
Thanks a lot. That sort of cooking is pretty confidential I guess :).kuroi neko– kuroi neko2017年08月09日 09:52:38 +00:00Commented 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.emacs drives me nuts– emacs drives me nuts2022年11月07日 13:16:40 +00:00Commented Nov 7, 2022 at 13:16