I think it’s good to have a stringifying macro in your utils header:
#define STR_IMPL_(x) #x //stringify argument
#define STR(x) STR_IMPL_(x) //indirection to expand argument macros
Then you can keep the macro numerical and stringify it on the spot:
#define LEDS 48
int x = LEDS;
void DrawFrame()
{
asm(
"ldi R27, 0x00 \n\t"
"ldi R26, 0x00 \n\t"
"ldi R18, "STR(LEDS)" \n\t"
...
}
The above preprocesses to:
int x = 48;
void DrawFrame()
{
asm(
"ldi R27, 0x00 \n\t"
"ldi R26, 0x00 \n\t"
"ldi R18, ""48"" \n\t"
...
}
which relies on the fact that adjacent string literals get concatenated.
Alternatively, you could create a STR
macro that’s tolerant of commas:
#define STR_IMPL_(...) #__VA_ARGS__ //stringify argument
#define STR(...) STR_IMPL_(__VA_ARGS__) //indirection to expand argument macros
and then stringify the whole asm
body with it as follows:
asm( STR(
ldi R27, 0x00;
ldi R26, 0x00;
ldi R18, LEDS; //LEDS gets expanded
)); //renders: "ldi R27, 0x00; ldi R26, 0x00; ldi R18, 48;"
(Personally, I write asm on clang/gcc/tinycc like this because I find it more convenient than explicit string literals). This approach has the potential downside in that the assembly will be just one line and will assemble as long as the assembler also accepts ;
as an instruction separator in addition to newlines (works at least for the x86-64 assembler on clang/gcc/tinycc).