Noah Caldwell
Posts: 7
Joined: Wed Jan 11, 2012 8:59 pm

ARM Assembly: ldr versus mov

Wed Sep 05, 2012 4:08 pm ... /ok01.html

I've been reading the Baking Pi course (see link above), and I've come to the 4th 'part': Enabling Output. I am confused as to the difference between 'ldr' and 'mov'. Even the definitions given are identical:

mov reg,#val puts the number val into the register named reg.
ldr reg,=val puts the number val into the register named reg.

Besides the slightly different syntax, what is the difference? (And yes, I RTFM (or at least his explanation) several times. I didn't get it.)

User avatar
Posts: 876
Joined: Wed May 16, 2012 6:32 pm

Re: ARM Assembly: ldr versus mov

Wed Sep 05, 2012 4:16 pm

6. Question on differences between LDR and MOV

What is the difference between MOV and LDR, they seem to have the same function of
saving information into registers?
Answer part 1: How different are they?
Note: “#” for mov, “=” for ldr. To define an immediate value
o MOV can only move an 8-bit value (0x00->0xff=255) into a register
while LDR can move a 32-bit value into a register. The immediate value is
prefixed by different characters in mov and ldr: “#” for mov, “=” for ldr.
Mov r1,#255 ; ok, 255 is the biggest number you can mov
Mov r1,255 ; is wrong , missing #
Mov r1,#256 ; is wrong, the number is bigger than 255
Mov r1,#0x12340000 ; is wrong, the number is bigger than 255

Ldr r1,=255; you can do this,
Ldr r1,=256; you can do this,
Ldr r1,=0x12340000; you can do this,
o MOV can run faster than LDR.
o LDR can move a data from a memory address to a register, MOV can only
i) move data between two registers or ii) save a 8-bit immediate value to a
register. e.g.

value1 DCD 0; this define an integer variable “value1” with address “=value1”
;A standard pair of statements for moving a data into a register
Ldr r0,=value1 ; 1) save the address of the variable value1 in r0
Ldr r1,[r0]
;2)use r0 as the address (pointer) to get value1 to r1

Note: Mov cannot be used to achieve the same result, because mov r1,[r0] is not allowed
Answer part 2 : How similar are they?.
MOV is a real instruction (a 32-bit instruction) , LDR is a pseudo instruction (the
assembler will use multiple 32-bit instructions to achieve the goal). For data move, if
the immediate value is small, the assembler will use “mov” to implement it, so mov and
ldr are exactly the same if the immediate value less or equal to 255. For example, for
ldr r0,=14; the immediate value is 14 and is <255, so it will be implemented using mov
r0,#14 (see the use of # and = ).

However, for a large immediate value (>255) “mov” does NOT work, e.g. ldr
r0,=0x55555555; it is not ok to use mov r0,#0x55555555 because it is not allowed. Then,
the assembler will generate some code to place the constant 0x55555555 in a nearby
table in the code area. Then it uses an instruction to load a data from that table pointed by
the program counter and an offset to fill up r0. The reason is because there is no way to
fit a 32-bit data into a 32-instruction (an instruction must have the instruction-code-part
and the data-part, if the data-part is 32-bit long, there is no room to store the instruction-
code-part). Details can be found at


see also the directive “LTORG” for how to setup the table. Usually it is placed at the near
by code area. You don’t need to worry too much because everything is automatic; you
only need to place “LTORG” at the end of the code area as shown in the example at
Keil. ... 5ajfQm39GQ
Last edited by DexOS on Wed Sep 05, 2012 4:20 pm, edited 1 time in total.
Batteries not included, Some assembly required.

User avatar
Posts: 64
Joined: Thu Aug 30, 2012 12:50 pm
Location: Cambridge, UK

Re: ARM Assembly: ldr versus mov

Wed Sep 05, 2012 4:19 pm

Yes, there is a difference, I believe I mention it in the text but I'll elabourate here.

ldr is capable of loading any constant, but at the cost of speed and space. If you open kernel.list, you'll see the ldr instruction actually get's converted to something like ldr rn,[pc,#x] and then later down the listing you'll see the constant. What the assembler has done is store the constant near the instruction in memory, and then write an instruction that loads this value from the memory.

mov is more restricted, but faster and smaller. If you look in kernel.list, you'll see the mov instruction is unchanged. It is just one instruction, and doesn't access memory. This means it will be faster, and takes less space. Unfortunately for us, mov can only load certain numbers; numbers who's binary representation is 8 1s or 0s, followed by any number of 0s, for example 1024 is valid (10000000000 in binary) but 1025 is not (10000000001 in binary).

Hope this clarifies.

Noah Caldwell
Posts: 7
Joined: Wed Jan 11, 2012 8:59 pm

Re: ARM Assembly: ldr versus mov

Wed Sep 05, 2012 4:25 pm

So mov is faster, but can only be used for 0-255. (Small numbers.)
Ldr can move anything, but is slower.

Ok, I get it. Thanks for replying so promptly!

Posts: 1006
Joined: Sat May 26, 2012 5:32 pm

Re: ARM Assembly: ldr versus mov

Wed Sep 05, 2012 5:36 pm

That is incorrect about mov only handling 0 to 255. Go to the ARM ARM and look at the instruction encoding, assuming the traditional 32 bit arm instructions (Thumb2 has a whole new way to look at what constants you can use) . The lower 12 bits of the instruction are the shifter operand.

Of those 12 bits 8 bits are the immediate and 4 bits are how much to shift that immediate.

From the arm arm

Legitimate immediates
Not all 32-bit immediates are legitimate. Only those that can be formed by rotating an 8-bit
immediate right by an even amount are valid 32-bit immediates for this format.

So you can do any of these:

mov r0,#0x00000089
mov r0,#0x00008900
mov r9,#0x89000000

you are not limited to 0-255 you are limited to 0-255 shifted by an even amount.

The mov instruction is fast, and you will often see folks do something like this:

mov r0,#0x12000000
orr r0,r0,#0x00340000

to say put a base address 0x12340000 into a register, say you were trying to address 0x12340056 you would

mov r0,#0x12000000
orr r0,r0,#0x00340000
ldr r1,[r0,#0x56]

You can have a 12 bit offset, the above three instructions give you up 28 non-zero bits of immediate for an address.

ldr r0,=0x12345678

is simply a short cut for typing this:

ldr r0,myconst
myconst: .word 0x12345678

The two problems with this trick is first it causes a data cycle, the second problem is that you need to be aware of the range of the pc relative ldr instruction, and you need to leave holes in your code for the assembler to find places for adding this constant. Even with binutils there are times where it is smart and times where it is not and despite an unconditional branch it doesnt realize it could have put the immediate there so you will eventually if you do this enough need to use .pool or some other directive depending on the assembler and version to tell the assembler "this is a good place to put constants".

You will also see the assembler take this:

ldr r0,=0x12000000

and actually encode this:

mov r0,#0x12000000

because it knows that constant will fit.

if I know the constant will fit I use a mov rd,#immed, otherwise I decide how I feel about it that day whether or not to have a mov plus up to for orrs or do the ldr rd,=immediate thing or create the memory location in a controlled space and load the value that way. If for example I am executing from a slow spi flash, I prefer the mov plus a few orrs to a load, as I know the memory/i/o cost cost for each solution. If it is a case of the code is in ram, the constant is in the same ram, do I fetch a few more instructions from ram or cause an individual ram read. sometimes the tradeoff is negligible. The instruction fetches from the (newer) arms are bursts 8 words at a time, that kind of thing, so the extra instruction is likely already fetched an in the pipeline, no stalls no speed problems. but the individual read that takes some waiting for, even in the same ram the system can/will stall waiting. if you have an mmu and a cache on that ram it gets much much worse, the three or four instructions can be much faster, if the fetch is close enough you might be cached or maybe are going to cache that soon anyway so it may not be a problem. on the other hand if it is close enough you might have to put a jump over the pool...

I have not been through the whole Baking Pi but from the start there are a number of places where this particular topic could have been addressed, two instructions are used where only one was needed and would have been easier to explain the one instruction than the two. then later another approach is used with the ldr = instead of the two instruction approach that had been used above. I dont know if that was intentional or accidental.

When learning any assembly language you should first look for the designers manuals instruction set reference including the binary encoding for the instructions. The answers of why some immediates are valid and some not, why some offsets are valid and some not, and why some branches can only branch so many words or bytes in any particular direction, etc. These are all explained by looking at the reference material and in particular the machine encoding, how many bits, unsigned or sign extended, etc. The ARM version of this is called the ARM ARM, ARM Architectural Reference Manual. available at, you can get the ARMv5 or whatever the oldest one is for these kinds of basic questions, the difference between the ARMv5 and the core on the raspberry pi is not too many 32 bit instructions, the huge difference would be in the thumb/thumb2 area and just overall performance which is not visible to the programmer anyway (As far as the instruction set goes).


Posts: 1006
Joined: Sat May 26, 2012 5:32 pm

Re: ARM Assembly: ldr versus mov

Wed Sep 05, 2012 6:10 pm

Note that once you open the door to thumb and thumb2 ldr vs mov has more rules. And arm and the gnu tools have this unified syntax thing where you can try to write code that assembles both to 32 bit arm instructions or to thumb/thumb2 instructions. Trying to make load immediates that conform to everything will impose more limits on the immediates or registers or distance an ldr = can go, etc. (This is also in the ARM ARM. Thumb2 you need something newer than the oldest ARM ARM, go for the one that matches the core in use, note that ARMv6 thumb2 is next to nothing, basically just do thumb on an armv6, but armv7 there are over 100 new thumb2 instructions parroting arm 32 bit instructions to some extent).


Posts: 12
Joined: Sat Aug 11, 2012 5:28 pm

Re: ARM Assembly: ldr versus mov

Sat Sep 15, 2012 10:08 am

ldr is a load register operation
mov is a memory access operation
The two are similar, but they use different operands, it takes some getting used to ;) .
A good introduction can be found at:

Posts: 1006
Joined: Sat May 26, 2012 5:32 pm

Re: ARM Assembly: ldr versus mov

Sat Sep 15, 2012 2:36 pm

Except for when the assembler replaces the ldr rd,=immed with mov rd,#immed.

Code: Select all

ldr r1,=0
ldr r2,=0x1200
ldr r3,=0x50000
ldr r4,=0x1234
mov r1,#0

Code: Select all

00000000 <.text>:
   0:	e3a01000 	mov	r1, #0
   4:	e3a02c12 	mov	r2, #4608	; 0x1200
   8:	e3a03805 	mov	r3, #327680	; 0x50000
   c:	e51f4000 	ldr	r4, [pc, #-0]	; 14 <.text+0x14>
  10:	e3a01000 	mov	r1, #0
  14:	00001234 	andeq	r1, r0, r4, lsr r2
After it is assembled, yes, absolutely the ldr is a memory access operation and mov is a register operation.

If you really want the ldr to be a memory operation and not a shortcut to a mov immediate then

Code: Select all

ldr r1,something

something: .word 0

Code: Select all

00000000 <something-0x4>:
   0:	e51f1004 	ldr	r1, [pc, #-4]	; 4 <something>

00000004 <something>:
   4:	00000000 	andeq	r0, r0, r0
No, none of that code is usable, the assembler has placed data in the path of the execution and you would have problems with that normally. Realistically you would have some sort of a branch or something:

Code: Select all

ldr r1,something
b somewhere
something: .word 0

Posts: 1456
Joined: Sun Sep 11, 2011 2:32 pm

Re: ARM Assembly: ldr versus mov

Sat Sep 15, 2012 3:15 pm

ldr= is a pseudo-instruction, and will generate either:

mov if the constant can be encoded as a 12 bit arm modified value
mvn if the complement of the constant can be encoded as a 12 bit arm modified value
ldr with a pc-relative offset to a literal pool otherwise.


Posts: 151
Joined: Wed Nov 23, 2011 1:29 pm

Re: ARM Assembly: ldr versus mov

Sat Sep 15, 2012 5:57 pm

So, if the assembler will generate the optimal instruction(s) for ldr, is there a reason to bother with the other ones (mov, mvn)?

Posts: 1456
Joined: Sun Sep 11, 2011 2:32 pm

Re: ARM Assembly: ldr versus mov

Sat Sep 15, 2012 6:51 pm

Narishma wrote:So, if the assembler will generate the optimal instruction(s) for ldr, is there a reason to bother with the other ones (mov, mvn)?
For the "mov rd, #imm" and "mvn rd, #imm" cases, no, unless you're writing an assembler or compiler.

"mov rd, rn" and "mvn rd, rn" are still useful.

The shifted register versions are synonyms for the various shift and rotate instructions, so you probably won't use them.

Return to “Bare metal, Assembly language”