Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can someone help me understand stmdb, ldmia, and how I can go about implementing this C++ code in arm assembly language?

Tags:

c++

assembly

arm

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?

like image 288
Ian Avatar asked Apr 13 '14 17:04

Ian


1 Answers

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.

like image 114
Notlikethat Avatar answered Oct 06 '22 23:10

Notlikethat