OK I'm working through this slowly while doing several other things (as usual).
So it looks like yes -- you can pass in and modify global variables.
unsigned int a = 0;
void setup() {
Serial.begin(9600);
a = 2;
Serial.print("a = ");
Serial.println(a);
asm("mov r0,%0 \n\t" // copy a to r0
"inc r0 \n\t" // increment
"inc r0 \n\t" // increment
"mov %0,r0 \n\t" // copy r0 back to a
: "+r" (a));
Serial.print("now a = ");
Serial.println(a);
}
void loop() {
}
It took me a while to understand the syntax of asm() because it's weird and it was too late at night. :-[
If all you want to do is write some assembler, with no copying between C/C++ variables and the CPU registers, you can simply give asm() one parameter that's a string of opcodes separated by newlines:
asm("cli\n nop\n nop\n nop\n sei\n");
This has been used about the place as a way of getting very short, accurate delays, as long as you know how many clock cycles the code will take to complete. (Generally one cycle per instruction.)
To pass variables back and forth you must specify a list of input and output operands separated by ":".
The operands are referenced within the assembly code string using %0, %1, %2... (like parameters in shell script programming).
So in the code above the %0 gets mapped onto the variable a.
The "+r" bit is explained (not very clearly) in the Inline Assembler cookbook, part of the AVR libc docs, at Inline Assembler Cookbook
the 'r' is a 'constraint' (in this case representing 'any register') that goes along with the opcode mov, which is where the %0 is being used as an operand. The '+' is a 'modifer' that in this case means 'read/write operand'.
Each opcode has an associated set of constraints depending on what operands it can use, e.g. some opcodes only operate on registers. There's a table listing opcodes and their constraints on the cookbook page.