Recursive programming with Stack in ASM and LC3 - recursion

So I'm new to assembly and I'm also not pretty good at recursion, this is the code that I have so far and it is supposed to translate from :
int mult(int n, int m) {
if (n == 1)
return m;
else if (m == 1)
return n;
else
return mult(n-1,m-1)+n+m-1;
}
but for some reason I am in an infinite loop, I am also suppose to store the result in the variable PRODUCT but I'm having a hard time figuring out how to do that, I've attempted in the commented code but I and I know that I need help translating the base cases and figuring out if they are returning the correct thing/value.
; main - going to calculate the product of two nonnegative numbers.
PRODUCT .Fill 0
; input in R0 and R1
; output in R0
MULT
ADD R6 R6 -1 ; Pushed it (closer to zero)
STR R1 R6 0 ; storing register on stack
ADD R6 R6 -1
STR R2 R6 0
ADD R6 R6 -1
STR R3 R6 0
ADD R6 R6 -1
STR R4 R6 0
ADD R6 R6 -1
STR R5 R6 0
ADD R6 R6 -1
STR R7 R6 0
ADD R2 R0 -1 ; Saves the subtraction of n from R0 to R2
BRz BASECASE1 ; if (n == 1) return m
ADD R3 R1 -1 ; Saves the subtraction of m from R1 to R3
BRz BASECASE2 ; else if (m == 1) OR if (m - 1) == 0 return n
; recursive case begins
ADD R2 R0 0 ; copy n into R2
ADD R3 R1 0 ; copy m into R3
ADD R0 R0 -1 ; mult (n-1,
ADD R1 R1 -1 ; m-1)
ADD R4 R2 R3 ; storing n+m in R4
ADD R4 R4 -1 ; subtracting 1 form n + m and storing in R4
JSR MULT ; else return mult(n-1, m-1) + n+m-1;
ADD R5 R4 R1
ADD R0 R5 R0
;ST R0, PRODUCT
BASECASE1
LDR R7 R6 0 ; Restoring registers
ADD R6 R6 1
LDR R5 R6 0
ADD R6 R6 1
LDR R4 R6 0
ADD R6 R6 1
LDR R3 R6 0
ADD R6 R6 1
LDR R2 R6 0
ADD R6 R6 1
LDR R1 R6 0
ADD R6 R6 1
RET ; returns m
BASECASE2
LDR R7 R6 0
ADD R6 R6 1
LDR R5 R6 0
ADD R6 R6 1
LDR R4 R6 0
ADD R6 R6 1
LDR R3 R6 0
ADD R6 R6 1
LDR R2 R6 0
ADD R6 R6 1
LDR R1 R6 0
ADD R6 R6 1
RET ; returns n
.END

You are showing a comment for the concept of main, but it does not include any code to call the function.
You need a main that loads arguments into r0 and r1 (values to be multiplied), then calls multiply with a JSR; after the call the main should store r0 in PRODUCT, and then use the HALT to stop the program.
Your variable PRODUCT should appear after your main code depending on what simulator you're using, as some simulators will execute the first thing they find (which should thus be main code).
Other comments:
You generally have the right idea that you need to save & restore some registers, including r7 (the return address register), and that this save/restore needs to happen on the stack since this is a recursive routine.
You are preserving n and m in r2 and r3 respectively, but don't use them later (e.g. after the call where saving them would have been important), so this is unnecessary.
Your code after the call does a computation using R1, but that is an unknown value, since it is an argument register.
BASECASE1 does not do what the C code says (it should return m)

Related

ARM assembly recursive sum of 1..n

So I've had the following task that needs to be written in ARM Assembly
Please test the subroutine by passing the value of n equals 5. The result
should equal 15. If n equals 3, the result is 6.
I was given the corresponding Java code:
int sum (int n)
{
int result;
if (n == 1)
return 1;
result = Sum (n‐1) + n;
return result;
}
And I have written the following code in ARM Assembly
NAME main
PUBLIC main
SECTION .text: CODE (2)
THUMB
main
LDR R4, =0x005 ; Value n
BL SUM
STOP B STOP
SUM
MOV R1, #0x01
PUSH {R1, LR}
ADD R5, R5, R4 ; R5 = result
CMP R5, #1 ; Compare result to 1
BEQ ADD1
ADD0
SUB R4, R4, #1 ; Value n - 1
CMP R4, #0 ; Compare n to 0
BEQ ADD1
BL SUM
ADD1
POP {R4, LR}
BX LR ; Branch and exchange instruction set
END
The code is running fine but I want to know if there could be any slight improvements/shortcuts.
I am also a little unsure about the comments but I believe what I have written is correct.

Pipeline Diagram of IO2I Microarchitecture

Is my pipeline correct for the given sequence of instructions? If it is not, can you shine some light on where I went wrong? EDIT: I forgot to add the issue stage of the mul instruction.
MUL R6, R7, R8
ADD R9, R10, R11
ADD R11, R12, R13
ADD R13, R14, R15
ADD R19, R13, R10
LW R2, R3
ADD R12, R16, R19
LW R5, R2
ADD R15, R20, R21
MUL IF ID I Y0 Y1 Y2 Y3 Y4 W C
ADD IF ID I X0 W r C
ADD IF ID I X0 W r C
ADD IF ID I X0 W r C
ADD IF ID I I X0 W r C
LW IF ID ID I L0 L1 W r C
ADD IF IF ID I I X0 W r C
LW IF ID ID I L0 L1 W r C
ADD IF IF ID I I X0 W r C

How is recursion possible in AVR Assembly?

I can't seem to wrap my head around recursion in Assembly Language. I understand how it works in higher level languages, but I don't understand how it is possible in assembly when the return value cannot be passed directly to the function.
I'm trying to make a recursive factorial function in AVR, but I don't understand how the stack passes the value when factorial requires n * (n-1), requiring both n and n-1 simultaneously
I just helped another person with the small code below to calculate factorial in AVR AtMega assembly.
It produces a factorial from 1~10, resulting in decimal 3628800 (hex 0x375F00).
It uses exactly what the OP wanted, if selected 8! as number! in R2, it will move 8 to the resulting bytes, then multiply by number!-1 and so on, until it reaches 1, then it ends. The multiplication 24x8 is the trickiest I could write, saving registers and clock cycles. It doesn't use stack nor RAM, straight use of AVR registers.
; Input at R2, value 1~10, from 1! to 10!
; Result 1~3628800 (0x375F00) at: R20:R21:R22 (LSB)
; Temporary Multiplication Middle Byte: R17
ldi r16, low(RAMEND)
out SPL, r16
ldi r16, high(RAMEND)
out SPH, r16
Mov R16, R2 ; Get Value to factor
Rcall A0 ; Call Factorial
...
A0: Clr R20 ; Results = Number!
Clr R21 ;
Ldi R22, R16 ;
A1: Dec R16 ; Number! - 1
Cpi R16,1 ; If 1 then ended
Brne A2 ;
Ret
; This multiplication 24x8 is tricky, fast and save bytes
A2: Mul R22, R16 ; Mul Result LSB x Number!-1
Mov R22, R0 ; LSB Mul to Result LSB Byte
Mov R17, R1 ; MSB Mul to Temporary Middle Byte
Mul R20, R16 ; Mul Result MSB x Number!-1
Mov R20, R0 ; LSB Mul to MSB Result Byte, ignore MSB Mul, will be zero
Mul R21, R16 ; Mul Result Middle x Number!-1
Mov R21, R0 ; LSB Mul to Result Middle Byte
Add R21, R17 ; Add Temporary Middle to Result Middle Byte
Adc R20, R1 ; Add MSB Mul with Carry to Result MSB Byte
Rjmp A1
Using addition instead of multiplication
unsigned int accumulate(unsigned int n)
{
if(n) return(n+accumulate(n-1));
return(1);
}
and a different instruction set, perhaps easier to follow
00000000 <accumulate>:
0: e3500000 cmp r0, #0
4: 0a000005 beq 20 <accumulate+0x20>
8: e3a03000 mov r3, #0
c: e0833000 add r3, r3, r0
10: e2500001 subs r0, r0, #1
14: 1afffffc bne c <accumulate+0xc>
18: e2830001 add r0, r3, #1
1c: e12fff1e bx lr
20: e3a00001 mov r0, #1
24: e12fff1e bx lr
In this case the compiler didnt actually call the function, it detected what was going on and just made a loop.
Since there is nothing magic about recursion there is no difference in whether you call the same function or some other function.
unsigned int otherfun ( unsigned int );
unsigned int accumulate(unsigned int n)
{
if(n) return(n+otherfun(n-1));
return(1);
}
00000000 <accumulate>:
0: e92d4010 push {r4, lr}
4: e2504000 subs r4, r0, #0
8: 03a00001 moveq r0, #1
c: 0a000002 beq 1c <accumulate+0x1c>
10: e2440001 sub r0, r4, #1
14: ebfffffe bl 0 <otherfun>
18: e0800004 add r0, r0, r4
1c: e8bd4010 pop {r4, lr}
20: e12fff1e bx lr
so this shows how it works. Instead of using the stack to store the sum, the cheaper solution if you have the registers is to use a non-volatile register save that register to the stack then use that register during the funciton, depends on how many registers you have and how many local intermediate values you need to track. So r4 gets a copy of n coming in, then that is added (for factorial it is a multiply which depending on the instruction set and code can produce a lot more code that can confuse the understanding so I used addition instead) to the return value from the call to the next function (with recursion where the compiler didnt figure out what we were doing this would have been a call to ourselves, and we can write this asm and make it a call to ourselves to see how it works)
Then the function returns the sum.
If we assume that otherfun is really accumulate we enter this function with a 4 lets say
00000000 <accumulate>:
0: e92d4010 push {r4, lr}
4: e2504000 subs r4, r0, #0
8: 03a00001 moveq r0, #1
c: 0a000002 beq 1c <accumulate+0x1c>
10: e2440001 sub r0, r4, #1
14: ebxxxxxx bl accumulate
18: e0800004 add r0, r0, r4
1c: e8bd4010 pop {r4, lr}
20: e12fff1e bx lr
r4 and lr are saved on the stack (call this r4-4 and lr-4)
r4 = n (4)
r0 = n-1 (3)
call accumulate with n-1 (3)
r4 (4) and lr are saved on the stack (r4-3, lr-3) lr now points back into
r4 = n (3)
r0 = n-1 (2)
call accumulate with n-1 (2)
r4 (3) and lr are saved on the stack (r4-2, lr-2)
r4 = n (2)
r0 = n-1 (1)
call accumulate with n-1 (1)
r4 (2) and lr are saved on the stack (r4-1, lr-1)
r0 = n-1 (0)
call accumulate with n-1 (0)
now things change...
r0 = 1
return to lr-1 which is into accumulate after the call to accumulate
r4 gets 2 from the stack
r0 (1) = r0 (1) + r4 (2) = 3
return to lr-2 which is into accumulate r4 gets 3 from the stack
r0 (3) = r0 (3) + r4 (3) = 6
return to lr-3 which is into accumulate r4 gets 4 from the stack
r0 (6) = r0 (6) + r4 (4) = 10
return to lr-4 which is the function that called accumulate r4 is restored
to what it was before accumulate was first called, r4 is non-volatile you have to for this instruction set return r4 the way you found it (as well
as others, but we didnt modify those)
so the addition in this case multiplication in your desired case is
result = 1 + 2 + 3 + 4
How that happened is we basically pushed n on the stack then called the function with n-1. In this case we push 4, 3, 2, 1 then we start to unwind that and each return processes 1 then 2 then 3 then 4 as it returns
taking those from the stack essentially.
the bottom line is you dont have to care about recursion to support recursion simply use an abi that supports recursion, which is not hard to
do, then hand code the instructions in assembly as if you were the compiler
Maybe this makes it easier to see. n coming in is both a parameter coming in but also for the duration of the function it is a local variable, local
variables go on the stack.
unsigned int accumulate(unsigned int n)
{
unsigned int m;
m = n;
if(n) return(m+accumulate(n-1));
return(1);
}
back to this
unsigned int accumulate(unsigned int n)
{
if(n) return(n+accumulate(n-1));
return(1);
}
so independent of the instruction set
accumulate:
if(n!=0) jump over
return_reg = 1
return
over:
push n on the stack
first parameter (stack or register) = n - 1
call accumulate
pop or load n from the stack
return_reg = return_reg + n
clean stack
return
And also deal with return addresses for the instruction set if required.
The ABI may use the stack to pass parameters or registers.
If I didnt follow the arm abi I could implement
accumulate:
cmp r0,#0
bne over
mov r0,#1
bx lr
over:
push {lr}
push {r0}
sub r0,#1
bl accumulate
pop {r1}
add r0,r0,r1
pop {lr}
bx lr
for grins an instruction set that uses the stack for most things not
registers
00000000 <_accumulate>:
0: 1166 mov r5, -(sp)
2: 1185 mov sp, r5
4: 10a6 mov r2, -(sp)
6: 1d42 0004 mov 4(r5), r2
a: 0206 bne 18 <_accumulate+0x18>
c: 15c0 0001 mov $1, r0
10: 1d42 fffc mov -4(r5), r2
14: 1585 mov (sp)+, r5
16: 0087 rts pc
18: 1080 mov r2, r0
1a: 0ac0 dec r0
1c: 1026 mov r0, -(sp)
1e: 09f7 ffde jsr pc, 0 <_accumulate>
22: 6080 add r2, r0
24: 65c6 0002 add $2, sp
28: 1d42 fffc mov -4(r5), r2
2c: 1585 mov (sp)+, r5
2e: 0087 rts pc
it does a stack frame thing
gets the n parameter from the stack
saves that n parameter to the stack
compares and branches if not zero
in the if zero case we set the return value to 1
clean up the stack and return
now in the if not zero case
make the first parameter n-1
call a function (ourself)
do the addition and return

loRecursion Example in ARM Assembly

Can someone give me an example of how recursion would be done in ARM Assembly with only the instructions listed here (for visUAL)?
I am trying to do a recursive fibonacci and factorial function for class. I know recursion is a function that calls a function, but I have no idea how to simulate that in ARM.
https://salmanarif.bitbucket.io/visual/supported_instructions.html
In case the link doesn't work, I am using visUAL and these are the only instructions I can use:
MOV
MVN
ADR
LDR
ADD
ADC
SUB
SBC
RSB
RSC
AND
EOR
BIC
ORR
LSL
LSR
ASR
ROR
RRX
CMP
CMN
TST
TEQ
LDR
LDM
STM
B
BL
FILL
END
This doesn't load an older value for R4, so R4 just doubles every time the function calls itself.
;VisUAL initializess all registers to 0 except for R13/SP, which is -16777216
MOV R4, #0
MOV R5, #1
MOV r0, #4
MOV LR, #16 ;tells program to move to 4th instruction
FIB
STMDB SP!, {R4-R6, LR} ;Stores necessary values on stack (PUSH command)
LDR R4, [SP] ;Loads older value for R4 from memory
ADD R4, R4, R5 ;Adds R5 to R4
STR R4, [SP], #8 ;stores current value for R4 to memory
MOV R5, R4 ;Makes R5 = R4
CMP R4, #144 ;If R4 >= 144:
BGE POP ;Branch to POP
MOV PC, LR ;Moves to STMDB(PUSH) statement
POP
LDMIA SP!, {R4-R6, LR} ;Pops registers off stack
END ;ends program
You need to use the stack, STMDB and LDMIA instructions. On real ARM tools with "unified" notation, they also have mnemonics PUSH and POP.
Fibonnaci and factorial are not great examples as they don't "need" recursion. But let's pretend they do. I'll pick Fibonacci as you don't have a MUL instruction!? You want to do something like this:
START
MOV R0, #6
BL FIB
END ; pseudo-instruction to make your simulator terminate
FIB ; int fib(int i) {
STMDB SP!, {R4,R5,R6,LR} ; int n, tmp;
MOV R4, R0 ; n = i;
CMP R0, #2 ; if (i <= 2) {
MOV R0, #1 ; return 1;
BLE FIB_END ; }
SUB R0, R4, #2 ; i = n-2;
BL FIB ; i = fib(i);
MOV R5, R0 ; tmp = i;
SUB R0, R4, #1 ; i = n-1;
BL FIB ; i = fib(i);
ADD R0, R0, R5 ; i = i + tmp;
FIB_END ; return i;
LDMIA SP!, {R4,R5,R6,PC} ; }
It should terminate with R0 containing fib(6) == 8. Of course this code is very inefficient as it repeatedly calls FIB for the same values.
The STM is needed so you can use registers r4,r5 because another function call can change r0-r3 and LR. Pushing LR and popping PC is like B LR. If you were calling C code you should push an even number of registers to keep SP 64-bit aligned (we don't really need to do that here; ignore R6).
some other recursive function:
unsigned int so ( unsigned int x )
{
static unsigned int z=0;
z+=x;
if(x==0) return(z);
so(x-1);
return(z);
}
build/disassemble
arm-none-eabi-gcc -O2 -c Desktop/so.c -o so.o
arm-none-eabi-objdump -D so.o
00000000 <so>:
0: e92d4010 push {r4, lr}
4: e59f4034 ldr r4, [pc, #52] ; 40 <so+0x40>
8: e5943000 ldr r3, [r4]
c: e3500000 cmp r0, #0
10: e0803003 add r3, r0, r3
14: e5843000 str r3, [r4]
18: 1a000002 bne 28 <so+0x28>
1c: e1a00003 mov r0, r3
20: e8bd4010 pop {r4, lr}
24: e12fff1e bx lr
28: e2400001 sub r0, r0, #1
2c: ebfffffe bl 0 <so>
30: e5943000 ldr r3, [r4]
34: e8bd4010 pop {r4, lr}
38: e1a00003 mov r0, r3
3c: e12fff1e bx lr
40: 00000000
If you dont understand it then is it worth it. Is it cheating to let a tool do it for you?
push is a pseudo instruction for stm, pop a pseudo instruction for ldm, so you can use those.
I used a static local which I call a local global, it lands in .data not on the stack (well .bss in this case as I made it zero)
Disassembly of section .bss:
00000000 <z.4099>:
0: 00000000
the first to loads are loading this value into r3.
the calling convention says that r0 will contain the first parameter on entry into the function (there are exceptions, but it is true in this case).
so we go and get z from memory, r0 already has the parameter x so we add x to z and save it to memory
the compiler did the compare out of order for who knows performance reasons, the add and str as written dont modify flags so that is okay,
if x is not equal to zero it branches to 28 which does the so(x-1) call
reads r3 back from memory (the calling convention says that r0-r3 are volatile a function you can can modify them at will and doesnt have to preserve them so our version of z in r3 might have been destroyed but r4 is preserved by any callee, so we read z back into r3. we pop r4 and the return address off the stack, we prepare the return register r0 with z and do the return.
if x was equal to zero (bne on 18 failed we run 1c, then 20, then 24) then we copy z (r3 version) into r0 which is the register used for returning from this function per the calling convention used by this compiler (arms recommendation). and returns.
the linker is going to fill in the address of z to the offset 0x40, this is an object not a final binary...
arm-none-eabi-ld -Ttext=0x1000 -Tbss=0x2000 so.o -o so.elf
arm-none-eabi-ld: warning: cannot find entry symbol _start; defaulting to 0000000000001000
arm-none-eabi-objdump -D so.elf
so.elf: file format elf32-littlearm
Disassembly of section .text:
00001000 <so>:
1000: e92d4010 push {r4, lr}
1004: e59f4034 ldr r4, [pc, #52] ; 1040 <so+0x40>
1008: e5943000 ldr r3, [r4]
100c: e3500000 cmp r0, #0
1010: e0803003 add r3, r0, r3
1014: e5843000 str r3, [r4]
1018: 1a000002 bne 1028 <so+0x28>
101c: e1a00003 mov r0, r3
1020: e8bd4010 pop {r4, lr}
1024: e12fff1e bx lr
1028: e2400001 sub r0, r0, #1
102c: ebfffff3 bl 1000 <so>
1030: e5943000 ldr r3, [r4]
1034: e8bd4010 pop {r4, lr}
1038: e1a00003 mov r0, r3
103c: e12fff1e bx lr
1040: 00002000
Disassembly of section .bss:
00002000 <z.4099>:
2000: 00000000
the point here is not to cheat and use a compiler, the point here is there is nothing magical about a recursive function, certainly not if you follow a calling convention or whatever your favorite term is.
for example
if you have parameters r0 is first, r1 second, up to r3 (if they fit, make your code such that it does and you have four or less parameters)
the return value is in r0 if it fits
you need to push lr on the stack as you will be calling another function
r4 on up preserve if you need to modify them, if you want some local storage either use the stack by modifying the stack pointer accordingly (or doing pushes/stms). you can see that gcc instead saves what was in the register to the stack and then uses the register during the function, at least up to a few local variables worth, beyond that it would need to bang on the stack a lot, sp relative.
when you do the recursive call you do so as you would any other normal function according to the calling convention, if you need to save r0-r3 before calling then do so either in a register r4 or above or on the stack, restore after the function returns. you can see it is easier just to put the values you want to keep before and after a function call in a register r4 or above.
the compiler could have done the compare of r0 just before the branch, reads easier that way. Likewise could have done the mov to r0 of the return value before the pop
I didnt put parameters, so my build of gcc here appears to be armv4t, if I ask for something a little newer
arm-none-eabi-gcc -O2 -c -mcpu=mpcore Desktop/so.c -o so.o
arm-none-eabi-objdump -D so.o
so.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <so>:
0: e92d4010 push {r4, lr}
4: e59f402c ldr r4, [pc, #44] ; 38 <so+0x38>
8: e3500000 cmp r0, #0
c: e5943000 ldr r3, [r4]
10: e0803003 add r3, r0, r3
14: e5843000 str r3, [r4]
18: 1a000001 bne 24 <so+0x24>
1c: e1a00003 mov r0, r3
20: e8bd8010 pop {r4, pc}
24: e2400001 sub r0, r0, #1
28: ebfffffe bl 0 <so>
2c: e5943000 ldr r3, [r4]
30: e1a00003 mov r0, r3
34: e8bd8010 pop {r4, pc}
38: 00000000
You can see the returns read a little easier
although an optimization was missed it could have done an ldr r0,[r4] and saved an instruction. or leave that tail end as is and the bne could have been a beq to 30 (mov r0,r3; pop{r4,pc} and shared an exit.
a little more readable
so:
push {r4, lr}
# z += x
ldr r4, zptr
ldr r3, [r4]
add r3, r0, r3
str r3, [r4]
# if x==0 return z
cmp r0, #0
beq l30
# so(x - 1)
sub r0, r0, #1
bl so
ldr r3, [r4]
l30:
# return z
mov r0, r3
pop {r4, pc}
zptr: .word z
.section .bss
z: .word 0
arm-none-eabi-as so.s -o so.o
arm-none-eabi-objdump -D so.o
so.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <so>:
0: e92d4010 push {r4, lr} (stmdb)
4: e59f4024 ldr r4, [pc, #36] ; 30 <zptr>
8: e5943000 ldr r3, [r4]
c: e0803003 add r3, r0, r3
10: e5843000 str r3, [r4]
14: e3500000 cmp r0, #0
18: 0a000002 beq 28 <l30>
1c: e2400001 sub r0, r0, #1
20: ebfffff6 bl 0 <so>
24: e5943000 ldr r3, [r4]
00000028 <l30>:
28: e1a00003 mov r0, r3
2c: e8bd8010 pop {r4, pc} (ldmia)
00000030 <zptr>:
30: 00000000
Disassembly of section .bss:
00000000 <z>:
0: 00000000
EDIT
So lets walk through this last one.
push {r4,lr} which is a pseudo instruction for stmdb sp!,{r4,lr}
Lr is the r14 which is the return address look at the bl instruction
branch and link, so we branch to some address but lr (link register) is
set to the return address, the instruction after the bl. So when main or some other function calls so(4); lets assume so is at address 0x1000 so the program counter, r15, pc gets 0x1000, lr will get the value of the instruction after the caller so lets say that is 0x708. Lets also assume the stack pointer during this first call to so() from main is at 0x8000, and lets say that .bss is at 0x2000 so z lives at address 0x2000 (which also means the value at 0x1030, zptr is 0x2000.
We enter the function for the first time with r0 (x) = 4.
When you read the arm docs for stmdb sp!,{r4,lr} it decrements before (db) so sp on entry this time is 0x8000 so it decrements for the two items to 0x7FF8, the first item in the list is written there so
0x7FF8 = r4 from main
0x7FFC = 9x 0x708 return address to main
the ! means sp stays modified so sp-0x7ff8
then ldr r4,zptr r4 = 0x2000
ldr r3,[r4] this is an indirect load so what is at address r4 is read to
put in r3 so r3 = [0x2000] = 0x0000 at this point the z variable.
z+=x; add r3,r0,r3 r3 = r0 + r3 = 4 + 0 = 4
str r3,[r4] [r4] = r3, [0x2000] = r3 write 4 to 0x2000
cmp r0,#0 4 != 0
beq to 28 nope, not equal so no branch
sub r0,r0,#1 r0 = 4 - 1 = 3
bl so so this is so(3); pc = 0x1000 lr = 0x1024
so now we enter so for the second time with r0 = 3
stmdb sp!,{r4,lr}
0x7FF0 = r4 (saving from so(4) call but we dont care its value even though we know it)
0x7FF4 = lr from so(4) = 0x1024
sp=0x7FF0
ldr r4,zptr r4 = 0x2000
ldr r3,[r4] r3 = [0x2000] = 4
add r3,r0,r3 r3 = 3 + 4 = 7
str r3,[r4] write 7 to 0x2000
cmp r0,#0 3 != 0
beq 0x1028 not equal so dont branch
sub r0,r0,#1 r0 = 3-1 = 2
bl so pc=0x1000 lr=0x1024
so(2)
stmdb sp!,{r4,lr}
0x7FE8 = r4 from caller, just save it
0x7FEC = lr from caller, 0x1024
sp=0x7FE8
ldr r4,zprt r4=0x2000
ldr r3,[r4] r3 = read 7 from 0x2000
add r3,r0,r3 r3 = 2 + 7 = 9
str r3,[r4] write 9 to 0x2000
cmp r0,#0 2 != 0
beq 0x1028 not equal so dont branch
sub r0,r0,#1 r0 = 2 - 1 = 1
bl 0x1000 pc=0x1000 lr=0x1024
so(1)
stmdb sp!,{r4,lr}
0x7FE0 = save r4
0x7FE4 = lr = 0x1024
sp=0x7FE0
ldr r4,zptr r4=0x2000
ldr r3,[r4] r3 = read 9 from 0x2000
add r3,r0,r3 r3 = 1 + 9 = 10
str r3,[r4] write 10 to 0x2000
cmp r0,#0 1 != 0
beq 0x1028 not equal so dont branch
sub r0,r0,#1 r0 = 1 - 1 = 0
bl 0x1000 pc=0x1000 lr=0x1024
so(0)
stmdb sp!,{r4,lr}
0x7FD8 = r4
0x7FDC = lr = 0x1024
sp = 0x7FD8
ldr r4,zptr r4 = 0x2000
ldr r3,[r4] r3 = read 10 from 0x2000
add r3,r0,r3 r3 = 0 + 10 = 10
str r0,[r4] write 10 to 0x2000
cmp r0,#0 0 = 0 so it matches
beq 0x1028 it is equal so we finally take this branch
mov r0,r3 r0 = 10
ldmia sp!,{r4,pc}
increment after
r4 = [sp+0] = [0x7FD8] restore r4 from caller
pc = [sp+4] = [0x7FDC] = 0x1024
sp += 8 = 0x7FE0
(branch to 0x1024)(return from so(0) to so(1))
ldr r3,[r4] read 10 from 0x2000
mov r0,r3 r0 = 10
ldmia sp!,{r4,pc}
r4 = [sp+0] = [0x7FE0] restore r4 from caller
pc = [sp+4] = [0x7FE4] = 0x1024
sp += 8 = 0x7FE8
(branch to 0x1024)(return from so(1) to so(2))
ldr r3,[r4] read 10 from 0x2000
mov r0,r3 r0 = 10
ldmia sp!,{r4,pc}
r4 = [sp+0] = [0x7FE8] restore r4 from caller
pc = [sp+4] = [0x7FEC] = 0x1024
sp += 8 = 0x7FF0
(branch to 0x1024)(return from so(2) to so(3))
ldr r3,[r4] read 10 from 0x2000
mov r0,r3 r0 = 10
ldmia sp!,{r4,pc}
r4 = [sp+0] = [0x7FF0] restore r4 from caller
pc = [sp+4] = [0x7FF4] = 0x1024
sp += 8 = 0x7FF8
(branch to 0x1024)(return from so(3) to so(4))
ldr r3,[r4] read 10 from 0x2000
mov r0,r3 r0 = 10
ldmia sp!,{r4,pc}
r4 = [sp+0] = [0x7FF8] restore r4 from caller (main()'s r4)
pc = [sp+4] = [0x7FFC] = 0x708
sp += 8 = 0x8000
(branch to 0x708)(return from so(4) to main())
and we are done.
A stack is like a dixie cup holder which might be before your time. A cup holder where you pull a cup down and the next and rest of the cups stay in the holder, well you can shove one back up in there.
So a stack is temporary storage for the function, write one data item on the cup, then shove it up into the holder (save r4 from caller) write another item and shove it up into the holder (lr, return address from caller). we only used two items per function here, so each function I can push two cups up into the holder, each call of the function I get two NEW AND UNIQUE storage locations to store this local information. As I exit the function I pull the two cups down out of the holder and use their values (and discard them). This is to some extent the key to recursion, the stack gives you new local storage for each call, separate from prior calls to the same function, if nothing else you need a return address (although did make some even simpler recursion example that didnt when optimized was smart enough to make a loop out of it basically).
ldr rd,[rn] think of he brakets as saying the item at that address so read memory at the address in rn and save that value in rd.
str rd,[rn] the one messed up arm instruction as the rest the first parameter is the left side of the equals (add r1,r2,r3 r1 = r2 + r3, ldr r1,[r4] r1 = [r4]) this one is backward [rn] = rd store the value in rd to the memory location described by the address r4, one level of indirection.
stmdb sp!, means decrement the stack pointer before doing anything 4 bytes times the number of registers in the list, then write the first, lowest numbered register to [sp+0], then next to [sp+4] and so on the last one will be four less than the starting value of sp. The ! means the function finishes with sp being that decremented value. You can use ldm/stm for things other than stack pushes and pops. Like memcpy,but that is another story...
All of this is in the arm documentation from infocenter.arm.com which you should already have (arm architectural reference manual, armv5 is the preferred first one if you have not read one).

convert expression into general register operation Models

I would like to get some help to convert this expression into commands of 4 methods:
z=3*(x+2)-2*y
here are the methods with my tries:
by stack:
push 2
push -1
mult
push y
mult
push x
push 2
add
push 3
mult
add
pop z
by accumulator:
load y
mult -2
store temp
load x
add 2
mult 3
add temp
store z
register-memory:
add R1, x, 2
mult R1, R1, 3
mult R2, y, -2
add z, R1, R2
register-register:
load R1, x
add R1, R1, 2
mult R1, R1, 3
load R2, y
mult R2, R2, -2
add R1, R1, R2
store z, R1
is it ok? can I use negative numbers (-2...)?
thanks!
addz, R1, R2
Didn't you meant to write : add z, R1, R2 ?
Other than this the 4 methods look OK.
can I use negative numbers (-2...)?
This would largely depend on the availability of commands like neg or sub.
Observe the difference if sub were possible:
push 2 \
push y | = 2*y
mult /
push x \
push 2 |
add | = 3*(x+2)
push 3 |
mult /
sub <-- Does depend on the order of the previous pushes!
pop z
The by accumulator version would look like this using sub and positive numbers only:
load y
mult 2
store temp
load x
add 2
mult 3
sub temp
store z

Resources