0

I'm currently using the following inline ASM for the Cortex-M3 to branch to a specific address in flash.

__asm("LDR R0, =0x8000"); //  Load the branch address
__asm("LDR R1, [R0]");    //  Get the branch address
__asm("ORR R1, #1");      //  Make sure the Thumb State bit is set.
__asm("BX R1");           //  Branch execution
  • However, I want to replace the hard-coded value 0x8014 with a C variable that will be computed based on some other conditions.
  • The largest possible value this variable can take is 0x20000, so I'd planned on using a uint32_t to store it.
  • The compiler being used is arm-none-eabi-gcc v4.9.3

I attempted to modify my inline ASM as follows:

uint32_t destination_address = 0x8000;
__asm( "LDR R0, =%[dest]" : : [dest]"r"(destination_address) );

However, this generates the compiler error:

undefined reference to `r3'

I am fairly new to inline ASM in general. I've tried researching this issue for two days or so, but I've been confused by conflicting answers owing to the diversity of compilers out there and the fact I am using Thumb instructions for the Cortex-M3.

I think my problem is that I need to find the correct constraint for the variable destination_address (range 0x0 - 0x20000), but I'm not sure.

artless noise
  • 21,212
  • 6
  • 68
  • 105
msolters
  • 225
  • 2
  • 8
  • 1
    The good news is that the error is easy to fix (https://godbolt.org/g/3B9YqC). If you are going to be using inline asm, you should get used to using `-S` (which is what godbolt is using here). The down side is that gcc doesn't like it when you do jumps within the asm. This can really mess up optimization, symbol refs, etc. – David Wohlferd May 23 '16 at 02:12
  • @DavidWohlferd I'm not quite sure I follow. (i) Rewriting =%[dest] as [%[dest]] does allow the code to compile, as per your linked example! (ii) But I don't understand why? (iii) What does `-S` mean and more importantly where does it manifest in your example? (iv) You should post this as an answer! (v) Regarding jumping from the ASM, in this use case I am leaving the binary and never coming back. – msolters May 23 '16 at 03:28
  • 1
    ii) As you can see from the output, 0x8000 is being moved into the r3 register (what you did with `LDR R0, =0x8000`). Your second line (`LDR R1, [R0]`) assumes that the value is in r0, but when using the "r" constraint, gcc can pick any available register. Rather than guess or assume, we substitute s/R0/%[dest]. A more direct answer would be that doing `=%[dest]` is going to expand to `=r3`, which I don't believe is valid assembler. iii) -S is doc'ed here (https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html#index-S-83). – David Wohlferd May 23 '16 at 04:43
  • iv) I am not a cortex programmer, which means I was guessing. v) Hmm, might be safe then. If you will read any data written by the binary, add the "memory" clobber. – David Wohlferd May 23 '16 at 04:44
  • Ok, now I understand; -S is just a useful tool for diagnosing the ASM produced by the compiler. Has nothing to do with the actual mechanism at play here. That being said, I still don't grok why wrapping that %[dest] in an additional pair of brackets is equivalent to the substitution with R0? Indeed, even in the godbolt example it seems r3 is still being used to store the 0x8000 value, not r0. – msolters May 23 '16 at 05:18
  • r3 **is** being used to store the 0x8000. When you use "r", gcc is allowed to pick any register it wants. In this particular case, it has chosen r3, but that could change due to any number of things. Rather than try to guess which register gcc might choose, you use %[dest], which will get substituted out with the name of the register gcc picked. It needs to be wrapped in brackets because we want `LDR R1, [R3]` not `LDR R1, R3`. 1 last point: I should have added R1 as a clobber (probably doesn't matter if since you are exiting, but still). – David Wohlferd May 23 '16 at 05:36
  • 1
    Note that unless it's absolutely crucial that you `bx` without touching the link register, you may as well just [do the whole lot with plain C code](http://stackoverflow.com/q/12715556/3156750). – Notlikethat May 23 '16 at 08:11

1 Answers1

0

why are you using inline assembly?

extern void HOP ( unsigned int );
...
unsigned int some_address;
..
some_address = some_math;
HOP(some_address);

and a few lines of real asm which you can use the c compiler if you really feel you have to to make an object from to link.

.globl HOP
HOP:
   bx r0

the added benefit is it is a branch link basically if you want to be.

the compiler has already computed the address it sounds like so you "simply" need to get it into a register and bx it. Inline assembly is extremely compiler specific so you need to start by talking about what assembler, version, etc you are using.

another thing you can do is if you have this

unsigned int some_address;
..
some_address = some_math;

you can use this assembly somewhere in the project.

ldr r0,=some_address;
ldr r0,[r0]
bx r0

and the linker will resolve the address to the C variable. so can use real assembler or inline for something like that. (if the inline doesnt support something like mov %0,some_address; bx %0 and do the work for you)

old_timer
  • 69,149
  • 8
  • 89
  • 168