So I have this piece of code, where N is the size of both arrays.
int i;
for (i = 0; i < N; i++)
{
if (listA[i] < listB[i])
{
listA[i] = listB[i];
}
}
I'm trying to implement this as an ARM Assembly subroutine, but I'm completely lost with how to deal with arrays. I have this so far:
sort1:
stmdb sp!, {v1-v5, lr}
ldmia sp!, {v1-v5, pc}
I assume that I have to use cmp to compare the values, but I'm not even sure what registers to use. Anyone have any guidance?
EDIT:
Okay I now have this code:
sort1:
stmdb sp!, {v1-v5, lr} @ Copy registers to stack
ldr v1, [a1], #0 @ Load a1
str v1, [a2], #0 @ Copy elements of a1 to a2
ldmia sp!, {v1-v5, pc} @ Copy stack back into registers
This copies the first four elements of a 10 element array, so I would assume if I changed the "#0" to "#4", it would cause the next four elements to change, but it doesn't. Why?
Firstly, as you've demonstrated, the load/store multiple instructions are primarily useful for stack operations (although they can also make an efficient memcpy
). Simply put, they load/store the specified registers, in order, from/to a contiguous block of memory from base address
to base address + (number of registers * 4)
.
In the example given, stmdb sp!, {v1-v5, lr}
is storing 6 registers in the "Decrement Before" addressing mode1, so the effective base address is sp-24
- it will store the contents of v1
at sp-24
, v2
at sp-20
,... up to lr
at sp-4
. Since the !
syntax for base register writeback is present, it will then subtract 24 from sp
, leaving it pointing at the stored value of v1
. The ldmia
is the complete reverse - "Increment After" means the effective base address is sp
, so it will load the registers from sp
up to sp+20
, then add 24 to sp
. Note that it loads the stacked lr
value directly into the pc
- this way you restore the registers and perform the function return in a single instruction.
As for the regular load/store instructions, they have 3 addressing modes - offset, pre-indexed and post-indexed. ldr v1, [a1], #0
is post-indexed, meaning "load v1
from the address in a1
, then add 0 to a1
", hence changing #0
to #4
doesn't affect the address used, only the value written back to the base register afterwards. If you'd got as far as implementing the loop there the effect would have become clearly visible.
It may be helpful to consider how some example C expressions map to these addressing modes:
int a; // r0
int *b; // r1
a = b[1]; // ldr r0, [r1, #4] (offset)
a = *(b+1); // similarly
a = *(++b); // ldr r0, [r1, #4]! (pre-indexed)
a = *(b++); // ldr r0, [r1], #4 (post-indexed)
Bear in mind the offset value can be a register instead of an immediate, too, so there are several possible ways to implement a loop like the one given.
For the authoritative reference, I'd recommend reading through the instruction section of the ARM Architecture Reference Manual, or for a less exhaustive but more accessible introduction, the Cortex-A Series Programmer's Guide.
[1] This implies a descending stack - corresponding "Decrement After" and "Increment Before" addressing modes exist for running an ascending stack.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With