Go Down

Topic: Embedding Assembly in C++ (Read 26669 times) previous topic - next topic


I'd like to use some assembler directly in my sketch for my ISRs (It's not that they're running slowly, it's just that I'd like to try it out), and have done some poking about the internet for assistance on this topic. I seem to have found the basics on how to do it syntactically, but there's a couple things that I can't seem to find.

Is it possible to reference global variables from the ASM? What would the syntax for this be? Can I use them as addresses, and load them into a register as if it was defined in the ASM part of the program?
Can anyone provide any tips/pointers for this? Thanks!


Apr 24, 2008, 01:19 am Last Edit: Apr 24, 2008, 02:28 am by mungbean Reason: 1
I've only just started looking into this myself today, but here are some resources I found.

(I can't really answer your question, I'm just doing a brain dump on stuff I located so far in the hope that it might help someone... it would be great to have a proper tutorial on this, or even some proper docs on how C/C++ and ASM relate to the "arduino language" and how you can switch between them once you're no longer a newbie.)

The first step is using the asm() function, which generates the code.

According to the FAQ, "All standard C and C++ constructs supported by avr-g++ should work in Arduino.", so first place to look seems to be avr-gcc docs:

avr-libc inline assembler cookbook


tutorials in assembler and interfacing (good)

more avr assembly tutorials

AVR assembler user guide (overview and includes summary
of instructions and how many clock cycles they take)

AVR data book:
copy here: http://newit.gsu.unibel.by/resources/ (scroll to AVRTOC)

instruction set: http://newit.gsu.unibel.by/resources/CPUs%5CAtmel%5CDescribe%5CDATABOOK%5CCHAP05.PDF

So as an example you could replace the setup() of the canonical blink example with this and it will be equivalent:

void setup() {
 asm volatile(
  // DDRC = 0010 0000, ie. PORTB5 is OUT
   "ldi r16,48\n"
   "out 0x27,r16\n"

Anyone who's well up on this stuff, I'd appreciate some light spreading on these two issues:

1. I have some very basic stuff working, but there seems to be disparity between the AVR assembly code I've found and the weird syntax used by avr-gcc.

2. The error messages generated during assembly appear in the IDE, but the .s files are deleted, so error msgs like those below are pretty useless since you can't work out what line they relate to in the source, or what the assembler is that was actually generated from the asm() call.  Is there a way of preventing the .s file from being deleted?

/var/tmp//ccKEd1bF.s: Assembler messages:

/var/tmp//ccKEd1bF.s:56: Error: constant value required



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.

Code: [Select]
unsigned int a = 0;

void setup() {

 a = 2;

 Serial.print("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 = ");


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:

Code: [Select]

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 http://www.nongnu.org/avr-libc/user-manual/inline_asm.html#io_ops

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.

Go Up