See: Getting a label to a register
There are four ways to get a label in traditional ARM,
target:
.long 0xfeadbeef, 0xdeadfeed, 0xbaddad00
adr r0,target
adrl r0,target
ldr r0,=target
sub r0,pc,#(.+8-target)
Thumb2 adds the movw movt combo, but that doesn't work for your use case.
Item 1,2,4 are basically the same. The item 3 adds an address as a constant to the literal pool and this address is loaded to the register. Something in 'C' like.
int variable;
const int *p = &variable;
int *reg = p; /* load register from memory */
Is there some pseudo-instruction that can help? Something like ldm =array, {r0,r1,r2} (fashioned after the actual pseudo-instruction ldr).
There are no pseudo-op for this; you must directly code things.
Given the 'target' definition above with three values, you can do this with...
target:
.long 0xfeadbeef, 0xdeadfeed, 0xbaddad00
adr r0, target
ldm r0, {r0-r2} ; r0 as source/dest should work without write back
; ldm r0, {r1-r3} maybe safer.
The pseudo-ops aren't implemented as this is 'rare' and the ldm is restrictive and doesn't support offsets of PC as a source. Something like,
; I just thunk it.
ldm pc, {r0-r2,pc} ; pc is always '8' ahead, assuming arm mode.
nop
.long arg0, arg1, arg2, routine
maybe possible but is restrictive as the data has to be positioned with the code and the control flow is also restrictive. Again this is a rare use case and the pseudo-op is to make it much easier to get arbitrary constants into registers which is commonly needed. So there are only direct coding of ldm commands. It is possible that some ARM CPU/architechure doesn't support having the same register as a destination; definitely not supported with write back (rN!). It depends on the class of ARM CPUs you are targeting. Using the PC as the source is probably even more unlikely to work over a large class of ARM CPUs. I would recommend the first example (rN != r15) unless you are running out of registers and on a known SOC/specific ARM CPU.
The ldrd instruction is a restrictive double word load but the source has more flexibility. You can use it to load two values without needing a separate adr. For example,
ldr r0, r1, [pc, #offset]
But it is limited to two registers. The target register need to be consecutive and the first is rN where N % 2 is zero or N is even.