I've had a couple of courses that touched on ARMv8 assembly, but both teachers described LDUR/STUR instructions a different way and now I've become pretty lost. Can someone help to clarify?
If I had the instruction:
LDUR R3, [R1, #8]
I'll be putting the answer in R3, but what am I taking from R1 and how does the offset operate? Is it like a logical shift? The ARM manual describes it as "byte offset" but then doesn't describe how that offset functions on R1. Do I shift the value stored in R1 (say R1 has value 50 in it) or is there a memory address outside of the R1 that I need to be thinking about? Other sources say I need to think of R1 as an array somehow?
LDUR is Load (unscaled) Register. It loads a value (32-bits or 64-bits) from an address plus an offset to a register. unscaled
means that in the machine-code, the offset will not be encoded with a scaled offset like ldr
uses, i.e. no shift will be applied to the immediate offset bits. The offset (simm signed immediate) will be added to the base register Xn|SP.
Thus it's possible to use displacements that aren't a multiple of 4 or 8 with ldur
, unlike with ldr
These are the prototypes for LDUR:
-- loads a 32-bit value
LDUR <Wt>, [<Xn|SP>{, #<simm>}]
-- loads a 64-bit value
LDUR <Xt>, [<Xn|SP>{, #<simm>}]
STUR is Store (unscaled) Register and works in the same way but it stores the value in a register to memory.
These are the prototypes for STUR:
-- stores a 32-bit register
STUR <Wt>, [<Xn|SP>{, #<simm>}]
-- stores a 64-bit register
STUR <Xt>, [<Xn|SP>{, #<simm>}]
LDUR/STUR allow accessing 32/64-bit values when they are not aligned to the size of the operand. For example, a 32-bit value stored at address 0x52.
In your example,
LDUR R3, [R1, #8]
this instruction will load to R3
the value pointed by R1
plus 8
bytes. This is what the ARM Reference Manual means by byte offset
.
So if R1
holds the value 0x50
, this will load the value stored at address 0x58
. The value of R1
will not be modified.
The instruction LDR R3, [R1, #8]
(LDR (immediate) the Unsigned offset variant) produces the same operation, however, the prototype is different:
-- loads a 32-bit value
LDR <Wt>, [<Xn|SP>{, #<pimm>}]
-- loads a 64-bit value
LDR <Xt>, [<Xn|SP>{, #<pimm>}]
The immediate offset pimm is different, LDUR uses a simm. This means that the offset is interpreted in a different way. The first (pimm) is a positive offset and its range is different for the 32-bit variant and the 64-bit variant.
In the 32 bit version:
In the 64 bit version:
This means that some of the offsets combinations of LDUR and LDR (immediate) are going to produce the same operation.