Inline ASM in Arduino

Hi there,

I want to use inline asm to create a simple if statement and run arduino code inside it.
Currently I use this code for that:

int i = 0;

asm("ldi r30, 1");
//asm("ldi r31, 1");
asm("ldi r31, 2");

asm("cpse r30, r31");
asm("jmp unequal");
asm("jmp equal");

asm("equal:");
Serial.println("Equal");
i += 10;
asm("jmp end");

asm("unequal:");
Serial.println("Unequal");
i += 10;
asm("end:");

Serial.println(i);

It's almost working. The "Serial.println();" is working as intended, but the Addition is always running
in both cases. So in the end I get i = 20, instead if i = 10.

Any ideas how to fix that?

the Addition is always running
in both cases.

It looks to me as though you have the addition in both cases as the

i += 10;

is there in each case, but I am far from up to speed on AVR ASM programming

Yea, but the result in the end should be i = 10, if only one of them is executed, like the Serial.println();
I got Serial.println("Equal"); and Serial.println("Unequal"); in there, but only one is executed, as intended.

But the Addition is executed in both branches. So the result is i = 20, instead of i = 10.

OK, I see what you mean. My fault for not looking closer

Please post your complete sketch

My complete code:

void setup() {
  int i = 0;
  Serial.begin(9600);
  
  asm("ldi r30, 1");
  //asm("ldi r31, 1");
  asm("ldi r31, 2");
  
  asm("cpse r30, r31");
  asm("jmp unequal");
  asm("jmp equal");
  
  asm("equal:");
  Serial.println("Equal");
  i += 10;
  asm("jmp end");
  
  asm("unequal:");
  Serial.println("Unequal");
  i += 10;
  asm("end:");
  
  Serial.println(i);
}

void loop() {}

Serial monitor output:

09:56:17.576 -> Unequal
09:56:17.576 -> 20

keksdose132:
I want to use inline asm to create a simple if statement and run arduino code inside it.

Just why? The issue you are experiencing is called instruction reordering.
The compiler does not have to preserve the order of instructions from the cpp file, but is free to move them around for optimization purposes.
Obiously, the compiler is not allowed to reorder the instructions, if it can't determine safely, that the reordering does not change the outcome of the computation.
You inline assembly code however, does not influence the flow of execution from the compilers point of view (that is, because it does not parse the assembly code, that is the job of the assembler (avr-as)).
It will however, parse the input, output and clobber list of the inline assembly to determine constrains regarding the surrounding code.

he compiler is not allowed to reorder the instructions, if it can't determine safely, that the reordering does not change the outcome of the computation.

So how would the asm code be written to achieve what is required ?

UKHeliBob:
So how would the asm code be written to achieve what is required ?

This really depends on what the OP wants to achieve. From the description he/she gave I would suggest to write a function in assembly and pass two callbacks, one for equal and one for unequal, to the function.
Separating the assembly function into a new file helps to keep it as clean as possible (file ending must be capital ‘S’)

asm_func.S

.global asm_func ; make it a global symbol so that the linker can find it
asm_func:
  ; unequal callback is in r24:r25
  ; equal callback is in r22:r23
  ; ASSEMBLER FUNCTION CODE HERE
  ldi r30, 1
  ldi r31, 2
  cpse r30, r31
  jmp unequal
  jmp equal

; CALLBACKS ARE INVOKED HERE
equal:
  movw r30, r22 ; copy r23:r22 to Z Register
  jmp end
unequal:
  movw r30, r24 ; copy r25:r24 to Z Register
end:
  icall
  ret
.end

The c/c++ code looks like this:

void callback_unequal() {
  Serial.println("unequal");
}

void callback_equal() {
  Serial.println("equal");
}

extern "C" void asm_func(void (*cb_unequal)(void), void (*cb_equal)(void));

void setup() {
  Serial.begin(9600);
  asm_func(callback_unequal, callback_equal);
}

void loop() {}

Of course, one could put that into inline assembly and pass the callbacks in there, but that is very tedious to read und almost impossible to maintain after a week or so.

EDIT: Simply put, I don’t think that it is possible to mix inline labels with c/c++ code. The following is possible however:

void setup() {
  Serial.begin(9600);
  int i = 0;
  asm volatile("inc %0" : "=r" (i) : "0" (i));
  Serial.println(i);
  i += 10;
  asm volatile("inc %0" : "=r" (i) : "0" (i));
  Serial.println(i);
}

That is, because through the input/output list, the compiler knows that the statement modifies i and also requires read access. This is why it can’t reorder anything and the output is indeed:

1
12

How on earth is that supposed to work? You are littering assembly code in between of other code expecting the other code to have no impact on the assembly or the registers. Try this untested code:

void setup()
{
  Serial.begin(9600);

  //Must use volatile for asm and variables to avoid any optimization!

  volatile uint8_t unequal = 0;

  asm volatile (
    "ldi r30, 1 \n" //Load 1 into r30
    "ldi r31, 2 \n" //Load 2 into r31
    "cpse r30, r31 \n" //Compare r30 to r31 and skip next instruction if equal
    "ldi %[uneq], 1 \n" //Set "unequal" to 1
    :
    [uneq] "+r" (unequal) //Declare [uneq] to be a read/write reference to "unequal"
  );

  if (unequal) Serial.println("Unequal");
  else Serial.println("Equal");

}

void loop()
{
}

Do not expect a sequence of multiple asm statements to remain perfectly consecutive after compilation. If certain instructions need to remain consecutive in the output, put them in a single multi-instruction asm statement. GCC’s optimizers can move asm statements relative to other code, including across jumps.

asm statements may not perform jumps into other asm statements. GCC does not know about these jumps, and therefore cannot take account of them when deciding how to optimize. Jumps from asm to C labels are only supported under extended asm.

See this.

Danois90:
Try this untested code

I like it (except that volatile is not necessary for the variable).

JimEli:
Do not expect a sequence of multiple asm statements to remain perfectly consecutive after compilation. If certain instructions need to remain consecutive in the output, put them in a single multi-instruction asm statement.

Or you can make the asm statement volatile, because the compiler must keep the relative order of volatile statements.