Learning ARM assembly basics

Before writing more ARM code, I learned it in Internet.

STMFD = store multiple registers full descendent = stmdb (decrement before)
LDMFD = load multiple registers full descendent = ldmia (increment after)
if ! is specified, the result will be written back.

STMFD:

pre:
r1 = 0x1
r2 = 0x2
r13 = 0x00008000

stmfd !r13, {r1, r2}

post:
r1 = 0x1
r2 = 0x2
r13 = 0x00007ff8
mem[0x00007ff8] = 0x00000001
mem[0x00008000] = 0x00000002

LDMFD:

pre:
r1 = 0x0
r2 = 0x0
r13 = 0x00008000
mem[0x00008000] = 0x00000001
mem[0x00008004] = 0x00000002

ldmfd !r13, {r1, r2}

post:
r1 = 0x00000001
r2 = 0x00000002
r13 = 0x0000800c

And some more info.
R0-R3: scratch registers, don’t need to save
R4-R12: callee saved
R13=SP (Stack Pointer) stack
R14=LR (Link Register) to store return address of function call
R15=PC (Program Counter) instruction pointer

If you want to do like this x86,

push 1
push 2
call hoge
// result in EAX

It’ll be like this in ARM.

mov r0, #1
mov r1, #2
bl hoge // set LR=next instruction pointer and branch to hoge
// result in r0

startup.s:

@ startup
  .align

.global _start
_start:
  ldr r0, =0x000000d3
  msr cpsr, r0
  ldr sp, =0x06400000
  bl main
  b .

.global _add
_add:
  add r0, r0, r1
  mov pc, lr

.global _sub
_sub:
  sub r0, r0, r1
  mov pc, lr

.global _fib
_fib:
  stmfd r13!,{r8, r9, lr} // same as stmdb (decrement before) or push, ! means write back in r13

  // return 0 if fib(0)
  mov r8, #0
  cmp r0, #0
  beq _fib_end

  // return 1 if fib(1)
  mov r8, #1
  cmp r0, #2
  ble _fib_end

  // store arg-1 and arg-2 in r0 and r9
  sub r0, r0, #1 // arg-1
  sub r9, r0, #1 // arg-2

  // call fib(arg-1)
  mov r8, #0
  bl _fib
  add r8, r0, r8

  // call fib(arg-2)
  mov r0, r9
  bl _fib
  add r8, r0, r8

_fib_end:
  mov r0, r8
  ldmfd r13!,{r8, r9, lr} // same as ldmia (increment after) or pop, ! means write back in r13
  mov pc, lr

hoge.c:

static const int kA = 3;
static const int kB = 4;

int _add(int a, int b);
int _sub(int a, int b);
int _fib(int a);

int main(int argc, char const* argv[]) {
  int a = kA;
  int b = kB;

  int result;
  result = _add(a, b);
  result = _sub(a, b);
  result = _fib(0);
  result = _fib(1);
  result = _fib(2);
  result = _fib(6);
  result = _fib(10);
}

And tested it with gdb+ARM sim.

$cat startup.gdb
target sim
file hoge
load hoge
b main
run

$arm-unknown-eabi-gdb --command=startup.gdb
...
15        result = _fib(0);
(gdb) n
16        result = _fib(1);
(gdb) p result
$1 = 0
(gdb) n
17        result = _fib(2);
(gdb) p result
$2 = 1
(gdb) n
18        result = _fib(6);
(gdb) p result
$3 = 1
(gdb) n
19        result = _fib(10);
(gdb) p result
$4 = 8
(gdb) n
20      }
(gdb) p result
$5 = 55

Leave a Reply

Your email address will not be published. Required fields are marked *