Accessing certain elements of an array in arm assembler - arrays

I have a problem which is bugging me for multiple days now...
I call a function from c which is implemented in arm assembler on a raspberry pi using the neon module. The signature looks like the following:
void doStuff(const uint32_t key[4])
I can load all the values into d-registers using VLD4.32 {d6-d9}, [r0].
The problem is that I have to use a value at a certain index of the array which is calculated at runtime. So I have to access the array at an index which I only know at runtime.
In c, the code I want to achieve would look like this:
// calculations
int i = ... // 'i' is the index of value in the array
int result = key[i];
In assembler I tried this:
VMOV r8, s22 ;# copy the calculated index into an arm register
MOV r8, r8, LSL #0x2;# multiply with 4
ADD r8, r5, r8 ;# add offset to base adress
VLDR.32 d14, [r8] ;# load from adress into d-register
I also tried multiplying with 2 and 32 instead of 4. But I always get the value 3.
I got it working with this stupid and very slow solution:
;# <--- very slow and ugly --->
VLD4.32 {d6-d9}, [r1] ;# load 4x32bit from adress *r1
VMOV r6, s22 ;# r6 now contains the offset which is either 0,1,2 or 3
CMP r6, #0x0 ;# 3 - 0 == 0 -> Z set
BEQ equal0
CMP r6, #0x1
BEQ equal1
CMP r6, #0x2
BEQ equal2
VMOV d12, d9 ;# has to be 3
B continue
equal0:
VMOV d12, d6
B continue
equal1:
VMOV d12, d7
B continue
equal2:
VMOV d12, d8
B continue
continue:
;# <--- --->
I basically have an if for every possible number and then select the corresponding register.
Thanks!
Edit:
Okay it works with VLD1.32 d14, [r8]. Do not quite unterstand why it won't work with VLDR.32, though.

Related

KEIL: The reason for working a MUL within a loop only in the 1st iteration

I have written a code in keil in which there is a loop containing a multiplication which can be done either by shift left by 1 or MUL command. However, both are executed only in the 1st iteration and in the next ones, compiler executes the code, while nothing happens.
Is there someone who knows the probable reason?
Thanks
Here is the code of the loop:
loop ADD r9, r9, #1
;MUL r8, r7, r11 ;double the X
LSL r12, r7, #1
CMP r12, r4 ;CMP the X with P
BHI G1
BLS G0
it was solved. it occured because this is a consecutive shift and it should be shifted by 1 at each iteration. the written code copies the source into destination and then, shift by 1. So it is not modified after the 1st iteration. the solution is modifying r7 by LSL r7, #1. This is shifted r7 by 1 position at all iterations.

Loading Values From An Array In Assembly Language

I am working on one of the simple codes assigned for a ARM microprocessor course I'm taking. I am having a slight issue with getting my code to load a value of an array to compare using Keil. The program is supposed to compare 5 numbers and then store values if the comparison is true. When I run my program it will not load array values that I declared. My professor isn't much help either and doesn't seem to know why it's not working properly.
Here's what I have done so far. I also think my PUSH is wrong but I can probably figure that out after I at least get the array to load. I should be pushing those values onto the stack but I am pretty sure I'm just loading values in registers instead.
AREA main, CODE, READONLY
EXPORT __main
ENTRY
__main PROC
MOVS r5, #0
LDR r0, =NUMB
loop1
LDR r1, [r0]
CMP r5, #5
BEQ stop
loop
CMP r1, #10
BLT low10
CMP r1, #100
BLT mid
CMP r1, #255
BLT high100
low10
PUSH {r2}
MOVS r2, #2
ADDS r5, #1
B loop1
mid
PUSH {r3}
MOVS r3, #0
ADDS r5, #1
B loop1
high100
PUSH {r4}
MOVS r4, #1
ADDS r5, #1
B loop1
stop B stop
ENDP
AREA myDATA, DATA, READWRITE
ALIGN
NUMB DCD 1,11,111,11,1
END
With respect to the array, your element size is not 1-byte, it's 4-bytes.
Using GNU & GDB, if we examine the address at R0 and interpret as signed words (i.e, 4 byte form), we see the expected array values.
.data
NUMB: .word 1,11,111,11,1
...
LDR r0, =NUMB
(gdb) x/8wd $r0
0x200dc: 1 11 111 11
0x200ec: 1 4929 1634033920 16804194
So you will need to change your values in the context of R5 to assume a 4-byte word. E.g.,
CMP r5, #(4*5)
ADDS r5, #4
Is is sadly very simple, just change in myData READWRITE to READONLY :)

How do I get my dot: function to loop back up properly?

first timer here.
I am attempting to code an arm assembly morse code translator on my raspberry pi 3. The program should read the string literal (x), take the corresponding morse code string (h) and scan through the string (".... ") and output a dot on the LED set up on my breadboard for every period in the string.
My original code involved actually having a whole word to be scanned character by character and translated. My LED would just light up way before a function for lighting up the LED was even called and the program would stall. So I made this side program to try and translate only a single letter [in this case "H"] and have it output correctly on my LED.
My issue here is that I cannot get the code to loop back to LED_lp: (via exit_if:) after it branches into the dot: function in order to output the dots in the ".... " string out on the LED.
I've tried numerous things, including
pushing {lr} / popping {pc} in different areas of the labels in case it had something to do with the stack
moving away from the primary registers (r0, r1, etc.) in case it was conflicting with the wiringPi commands
[which I came to the conclusion that it does conflict, even if you are not trying to load a pin number or calling pinMode, it seemed that loading registers r0, r1 with values like 0 and 1 caused premature and unwanted changes to the pin modes / LED outputs.] Please let me know if I am incorrect about that conclusion.
Even my assembly professor was unsure of what was wrong with my code or why my LED output was not behaving properly when reviewing my original.
Apologies for the wall of text, but I assumed that a little extra backstory would save some time and avoid vagueness. I hope someone is able to assist, as I need to implement this to my final project.
Thanks in advance
.equ MY_PIN, 25
.equ OUTPUT, 1
.equ INPUT, 0
.equ LOW, 0
.equ HIGH, 1
.data
x: .asciz "H"
h: .asciz "...."
.text
.global main
main:
push {lr}
bl wiringPiSetup
ldr r4, =x
mov r5, #0
lp:
mov r6, r4 // mov r4 [x] into r6
cmp r6, #72 // check if r6 = H ascii code
beq _do_H // if equal branch to _do_H
_do_H:
ldr r4, =h
bl LED
LED:
push {lr}
mov r5, #0
LED_lp:
ldrb r6, [r4, +r5] // scan for first char in r6
cmp r6, #0 // cmp , if string done branch to exit_if
beq exit_LED
cmp r6, #46 // cmp, if first char is dash, branch to dash_check
bne check_dash
bl dot // else branch to dot
check_dash:
cmp r6, #45
beq dash
exit_if:
add r5, #1 // increment index for string scan
bal LED_lp
exit_LED:
pop {pc}
dot:
push {r0, r1,r2}
mov r0, #MY_PIN
mov r1, #HIGH
bl digitalWrite
mov r0, #100
bl delay
mov r0, #MY_PIN
mov r1, #LOW
bl digitalWrite
// bl exit_if $$ having this line BEFORE the pop causes LED to stay on
// for more than 100 ms, but seems to terminate the program after it
// lights up
pop {r0, r1,r2}
// bl exit_if $$ having this line AFTER the pop causes the led to stay on
// $$ for longer than 100 ms, and gets the program stuck running? after
// $$ execution
dash: // commented out dash to work on dot, left here for reference
// push {lr}
push {r0,r1,r2}
// mov r0, #MY_PIN
// mov r1, #HIGH
// bl digitalWrite
// mov r0, #1000
// bl delay
// mov r0, #MY_PIN
// mov r1, #LOW
// bl digitalWrite
pop {r0,r1,r2}
// pop {pc}
/////////end

Printf Change values in registers, ARM Assembly

I'm new to assembly programing and I'm programing for ARM.
I'm making a program with two subroutines: one that appends a byte info on a byte vector in memory, and one that prints this vector. The first address of the vector contains the number of elements that follows, up to 255. As I debug it with GDB, I can see that the "appendbyte" subroutine works fine. But when it comes to the "printvector" one, there are some problems. First, the element loaded in register r1 is wrong (it loads 0, when it should be 7). Then, when I read the registers values with GDB after I use the "printf" function, a lot of register get other values that weren't supposed to change, since I didn't modify them, I just used "printf". Why is "printf" modyfing the values.
I was thinking something about the align. I'm not sure if i'm using the directive correctly.
Here is the full code:
.text
.global main
.equ num, 255 # Max number of elements
main:
push {lr}
mov r8, #7
bl appendbyte
mov r8, #5
bl appendbyte
mov r8, #8
bl appendbyte
bl imprime
pop {pc}
.text
.align
printvector:
push {lr}
ldr r3, =vet # stores the address of the start of the vector in r3
ldr r2, [r3], #1 # stores the number of elements in r2
.align
loop:
cmp r2, #0 #if there isn't elements to print
beq fimimprime #quit subroutine
ldr r0, =node #r0 receives the print format
ldr r1, [r3], #1 #stores in r1 the value of the element pointed by r3. Increments r3 after that.
sub r2, r2, #1 #decrements r2 (number of elements left to print)
bl printf #call printf
b loop #continue on the loop
.align
endprint:
pop {pc}
.align
appendbyte:
push {lr}
ldr r0, =vet #stores in r0 the beggining address of the vector
ldr r1, [r0], #1 #stores in r1 the number of elements and makes r0 point to the next address
add r3, r0, r1 #stores in r3 the address of the first available position
str r8, [r3] #put the value at the first available position
ldr r0, =vet #stores in r0 the beggining address of the vector
add r1, r1, #1 # increment the number of elements in the vector
str r1, [r0] # stores it in the vector
pop {pc}
.data # Read/write data follows
.align # Make sure data is aligned on 32-bit boundaries
vet: .byte 0
.skip num # Reserve num bytes
.align
node: .asciz "[%d]\n"
.end
The problems are in
ldr r1, [r3], #1
and
bl printf
I hope I was clear on the problem.
Thanks in advance!
The ARM ABI specifies that registers r0-r3 and r12 are to be considered volatile on function calls. Meaning that the callee does not have to restore their value. LR also changes if you use bl, because LR will then contain the return address for the called function.
More information can be found on ARMs Information Center entry for the ABI or in the APCS (ARM Procedure Call Standard) document.
printvector:
push {lr}
ldr r3, =vet # stores the address of the start of the vector in r3
ldr r2, [r3], #1 # stores the number of elements in r2
.align
loop:
cmp r2, #0 #if there isn't elements to print
beq fimimprime #quit subroutine
ldr r0, =node #r0 receives the print format
ldr r1, [r3], #1 #stores in r1 the value of the element pointed by r3. Increments r3 after that.
sub r2, r2, #1 #decrements r2 (number of elements left to print)
bl printf #call printf
b loop #continue on the loop
.align
endprint:
pop {pc}
that is definitely not how you use align. Align is there to...align the thing that follows on some boundary (specified in an optional argument, note this is an assembler directive, not an instruction) by padding the binary with zeros or whatever the padding is. So you dont want a .align in the code flow, between instructions. You have done that between the ldr r1, and the cmp r2 after loop. Now the align after b loop is not harmful as the branch is unconditional but at the same time not necessary as there is no reason to align there the assembler is generating an instruction flow so the bytes cant be unaligned. Where you would use .align is after some data declaration before instructions:
.byte 1,2,3,4,5,
.align
some_code_branch_dest:
In particular one where the assembler complains or the code crashes.

ARM Assembly for development board

I'm currently messing around with an LPC 2378 which has an application board attached. On the fan there are several components, 2 of which are a fan and a heater.
If bits 6 and 7 of port 4 are connected to the fan (motor controller), the following code will turn on the fan:
FanOn
STMFD r13!,{r0,r5,r14} ; Push r0, r5 and LR
LDR R5, =FIO4PIN ; Address of FIO4PIN
LDR r0, [r5] ; Read current Port4
ORR r0, r0, #0x80
STR r0, [r5] ; Output
LDMFD r13!,{r0,r5,r14} ; Pop r0, r5 and LR
mov pc, r14 ; Put link register back into PC
How can I rewrite this block of code to turn on a heater connected to bit 5 of port 4, (Setting the bit to 1 will turn it on, setting it to 0 will turn it off).
Edit after answered question:
;==============================================================================
; Turn Heater On
;==============================================================================
heaterOn
STMFD r13!,{r0,r5,r14} ; Push r0, r5 and LR
LDR R5, =FIO4PIN ; Address of FIO4PIN
LDR r0, [r5] ; Read current Port4
ORR r0, r0, #0x20
STR r0, [r5] ; Output
LDMFD r13!,{r0,r5,r14} ; Pop r0, r5 and LR
mov pc, r14 ; Put link register back into PC
;==============================================================================
; Turn The Heater Off
;==============================================================================
heaterOff
STMFD r13!,{r0,r5,r14} ; Push r0, r5 and LR
LDR R5, =FIO4PIN ; Address of FIO4PIN
LDR r0, [r5] ; Read current Port4
AND r0, r0, #0xDF
STR r0, [r5] ; Output
LDMFD r13!,{r0,r5,r14} ; Pop r0, r5 and LR
mov pc, r14 ; Put link register back into PC
As best as I understand the code, the fan is connected only to bit 7 (if bits are numerated from 0).
The following line is responsible for turning the fan-bit on:
ORR r0, r0, #0x80
You are setting all the bits that are 1 in the "mask" to 1. Since the mask is 0x80, that is 1000 0000 in binary, it only turns on bit 7.
Now, if you need to turn on the heater instead of the fan, and you have to set bit 5 instead of 7 (on the same port), you only need to change the mask in that line. New mask should be 0010 0000 binary, that is 0x20 in hexa, so the new code should be:
ORR r0, r0, #0x20
Also, if you want to turn the heater off at some point later, you do it by unsetting only bit 5, by anding with a mask that has 1s everywhere except on bit 5. If the mnemonic for bitwise and is BIC, the line would be:
BIC r0, r0, 0xDF
Now, I have not done any assembly in months but if I am not very mistaken, the code snippet you gave is actually a subroutine. You would call it from you main functionality with something like call to the FanOn address. And, to me, it seems that the subroutine is nice in a way that it preserves all the registers it uses, e.g. it pushes them to a stack in the first line and recovers them in the end.
So, to re-use the code, you could just write a new subroutine for turning the heater on, and one for turning each thing off if you want, and only change the label/subroutine name for each one, e.g. FanOff, HeaterOn...
Since all of them preserve all the registers, you can use them sequentially without worries.
The ORR instruction turns ON a bit, the #0x80 constant determines the bit(s) (in this case, only bit 7 is turned on). To turn OFF the bit, you will need an AND instruction and compute the appropriate mask (e.g., to turn OFF bit 7, you would AND with constant #0x7F). The appropriate constants for bit 5 are #0x20 and #0xDF.

Resources