0

Probably a common beginner question, and similar questions have been asked on this site, but I can't find any help on a couple points. If you have a 32-bit symbol defined with .word, and you want to load it into register r1, what's the conventional way to do it?

.syntax unified
.cpu cortex-m4
.thumb
/* ... */
_allones:  .word 0xffffffff
test:
    /* This seems to not work as expected */
    ldr    r1,_allones   @ Value is corrupted/misaligned?

    /* Two-line solution */
    ldr    r1,=_allones  @ Loads the address of _allones into r1, not the value itself
    ldr    r1,[r1]       @ Get value proper

My confusion is when you see tutorials like this one where only the ldr r1,=_allones is used to load the value of _allones (see "Initialization Code", first listing and explanation), and the answer I linked above which makes it seem like the first option in my example should work. Target is Cortex-M4, in Thumb mode, with unified syntax.

FWIW The ldr r1,_allones line instead loads the last two bytes of _allones and then the first two bytes of its address in memory, or something in that vain, I haven't looked that closely at the value.

  • 1
    In the linked example, in lines like `LDR R1, =GPIOD_MODER`, the symbol `GPIOD_MODER` is a constant, not a label. It was defined with `GPIOD_MODER EQU 0x40020C00`, not `GPIOD_MODER: .word 0x40020C00`. So what you would want is `ldr r1, =0xffffffff`, or else `allones equ 0xffffffff ; ldr r1, =allones`. – Nate Eldredge May 15 '22 at 21:12
  • 1
    Of course, for this particular constant you should instead just `mov r1, #0xffffffff` because it's encodable as an immediate. – Nate Eldredge May 15 '22 at 21:13
  • If `allones` really is a label, with the desired value actually being in memory, then you have no choice but to load the address into a register first, as in your "two-line solution". But that is not the situation for the code you linked. – Nate Eldredge May 15 '22 at 21:15
  • @NateEldredge Thanks for clearing it up for me. Put it in an answer and I would happily accept – Sam Gallagher May 17 '22 at 12:30

1 Answers1

1

In general, ldr r1, =sym will load the value of the symbol sym into r1. (It does so by assembling the value of sym into memory at a location called a literal pool which is fairly near to the instruction, so that it can be reached with the pc-relative literal form of the load instruction.)

But there are two different ways to define a symbol. It can be defined as a label, like sym:, in which case the value of the symbol is the address that it labels (which may not actually be known until the linking stage of the build). That's the case in your code. But a symbol can also be defined as a numerical constant, like sym EQU 12345, in which case its value is simply the number 12345. That's what is happening in the tutorial.

So if you want to load 0xffffffff into r1, the two possibilities would look as follows:

allones:
    .word 0xffffffff
...
    ldr r1, =allones @ now r1 contains the address where 0xffffffff is stored
    ldr r1, [r1]     @ load from that address

or

allones EQU 0xffffffff
...
    ldr r1, =allones

Of course, if the value actually is 0xffffffff, then it would be best to just write mov r1, 0xffffffff, or allones equ 0xffffffff ; mov r1, allones. (The # is optional in some assemblers.) The mov immediate instruction has a limited set of values that can be used as the immediate, but 0xffffffff is one of them. Some assemblers might optimize ldr r1, =const into mov r1, const when the constant has an appropriate value.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82