Largest Integer in ARM assembly - arrays

This is a homework question, but I'm stuck.
The assignment is to find the largest integer in an array. Here's the C code we're given:
#include <stdio.h>
#include <stdlib.h>
extern int mybig( int array[] ) ;
void main( char * argv[], int argc )
{
int array[] = { 5, 15, 100, 25, 50, -1 } ;
int biggest ;
biggest = mybig( array ) ;
printf( "Biggest integer in array: %d\n", biggest ) ;
}
I've made about a dozen versions of the assembly so far, but this is the closest I've gotten
.global mybig
mybig: stmfd sp!, {v1-v6, lr}
mvn v1, #0
loop: ldrb a4, [a1], #4
MOVLT a4, a1
cmp a1, v1
bne loop
ldmfd sp!, {v1-v6, pc}
.end
Every time I link it together, I hit an infinite loop, and I'm not sure why. Any help would be majorly appreciated, the professor didn't teach us anything in an introductory course, just told us to do it, and gave us a link to a toolchain to compile and assemble.
EDIT: This is where I've gotten to. Program doesn't run, just hits an infinite loop.
.global mybig
mybig: stmfd sp!, {v1-v6, lr}
mvn v1, #0
mov a3, a1
loop: ldr a4, [a1], #4
cmp a4, a1
MOVMI a3, a1
cmp a1, v1
bne loop
mov a1, a4
ldmfd sp!, {v1-v6, pc}
.end
C code hasn't changed

That would be my solution:
.global mybig
mybig:
// a1 = Highest word, defaults to 0x80000000 = −2,147,483,648
// a2 = Pointer to array
// a3 = current word
mov a2, a1
mov a1, #0x80000000
.Lloop:
ldr a3, [a2], #4 // Load word and advance pointer by 4 bytes
cmn a3, #1 // Compare with -1
bxeq lr // Return if endmarker was found
cmp a1, a3 // Compare current highest word and new word
movlt a1, a3 // Replace highest word if it was smaller
b .Lloop // Loop again
.end
While this is not the best possible code in regards of performance it should be self explaining.

Related

Bus error in ARMv7 assembly function to find maximum element of array

I'm having trouble with an ARMv7 assembly function I'm trying to write. The function is below.
.global maxF32
// float maxF32(const float x[], uint32_t count)
// returns the maximum value in the array (x) containing count entries
/* R0 = float x[], R1 = uint32_t count */
maxF32:
CMP R1, #0 # SET FLAGS OF COUNT
BEQ maxF32_end # IF COUNT == 0, GO TO END
MOV R2, R0 # COPY R0 INTO R2
MOV R0, #0 # ZERO OUT R0
MOV R3, #0 # ZERO OUT R3
MOV S0, R0 # ZERO OUT S0
MOV S1, R0 # ZERO OUT S1
MOV S2, R0 # ZERO OUT S2
B maxF32_loop # BRANCH TO LOOP
maxF32_loop:
CMP R1, #0 # CHECK IF COUNT == 0
BEQ maxF32_end # IF SO, GO TO END
VLDR.F32 S0, [R2] # LOAD CURRENT ELEMENT INTO S0
ADD R2, R2, #4 # INCREMENT VECTOR POINTER
VLDR.F32 S1, [R2] # LOAD NEXT ELEMENT INTO S1
SUB R1, R1, #1 # DECREMENT COUNT
CMP S0, S1 # SET FLAGS; CHECK S0 - S1
BMI maxF32_update # IF RESULT IS NEGATIVE (S1 > S0), GO TO UPDATE GREATEST
B maxF32_loop # REPEAT LOOP
maxF32_update:
VMOV R3, R1, S1 # MOVE GREATER INTO GREATEST REGISTER
B maxF32_loop # GO BACK TO LOOP
maxF32_end:
MOV R0, R3 # COPY GREATEST INTO R0
BX LR # RETURN
I test with the following C code:
extern float maxF32(const float x[], uint32_t count);
#define COUNT 3
float x[] = {1.2, 1.3, 1.4};
float result;
result = maxF32(x, COUNT);
printf("result is %lf\n", result);
Using gdb, right around the second to last or last loop through the array, I get a "bus error" right around the CMP S0, S1 line. I sometimes also get an "expected comma" at MOV R3, R1, S1, which confuses me as I don't know where I would need an extra comma.
Last note, I am coding on a Raspberry Pi Model 3 B+.
Thanks for any and all help.
On a count of "3" you compare s0 to s1 3 times.... meaning you look for four elements in the array. (With n elements you really should only have n-1 comparisons)
The logic...
decrement count
cmp element0 element1 ... loop
decrement count
compare element1 element2 ... loop
decrement count
compare element2 with 'bus error' -- nothing there.
Moving SUB R1, R1, #1 to the first line after maxF32_loop: probably fixes it as it puts the decrement before the check for count being 0.

Caesar decoder in ARM Assembly

As it said in the title, my objective is to write a program in C and ARM assembly that decodes a string by shifting the byte value of each character by a certain amount. The "space" character isn't shifted, instead just copied over to the resulting string. The process ends when the null-terminator is located.
Here is my C code:
#include <stdio.h>
extern void init(char * encrypt);
extern char * decrypt(char * encrypt, int shift);
int main(int argc, char * argv[])
{
char * result;
char encrypt[] = "GSRKVEXYPEXMSRW CSY LEZI JSYRH XLI WLMJX ZEPYI";
int i;
init(encrypt);
for (i = 1; i < 5; i++) {
result = decrypt(encrypt, i);
printf("Possible decrypt with shift %d: %s\n", i, result);
}
}
Here is my ARM code (this is all in one file called decrypt.s):
# init: reserves space for the decryption
.global init
.text
init: stmfd sp!, {v1-v6, lr}
mov v1, a1
bl strlen
bl malloc
bufferAddr: .fill 4, 1, 0
str a1, bufferAddr
#mov a2, v1
#bl strcpy
ldmfd sp!, {v1-v6, pc}
# decrypt: performs shifting of letters to decrypt string
.global decrypt
.text
decrypt: stmfd sp!, {v1-v6, lr}
mov v1, a1 # v1 is the pointer to encrypt (string)
ldr v2, =bufferAddr # v2 is the pointer to result (string)
loop:
ldrb v3, [v1], #1 # v3 is the current char (8-bit number)
cmp v3, #0
streqb v3, [v2], #1
beq endLabel
cmp v3, #32 # check if v3 == "space"
streqb v3, [v2], #1 # if true, store space in result, increment
beq loop # if true, proceed to next char
sub v3, v3, a2 # shift v3 by shift-value
cmp v3, #65 # check if v3 >= 65 (A)
strgeb v3, [v2], #1 # if true, store char in result, increment
bge loop
# if less than A
add v3, v3, #26 # add 26, wrap between A-Z
strb v3, [v2], #1 # if true, store char in result, increment
b loop
endLabel:
ldr a1, =bufferAddr
ldmfd sp!, {v1-v6, pc}
.end
The problem is, it only goes through the loop once, then it gets stuck. Sometimes it prints an error along the lines of "sim: unknown SWI..." or "unknown v6 isbn...". Or worst of all, it just prints nothing (infinite loop?)
I'm not sure what the problem is, my logic seems to make sense.
I read the next byte:
If it's null-terminator, I put it in the resulting string as well, and end the loop.
If it's space, I put the space in the resulting string, and continue the loop.
If it's >= 65 (which it SHOULD be, unless Space/null-terminator), I subtract the shift-value. If the resulting value is >= 65, I put it in the resulting string. If it's < 65, I add 26, then put it in the resulting string, therefore successfully wrapping to the capital letters of the alphabet.
There is (should be) no other case.
Yet I still get errors, or possibly an infinite loop. Any ideas?
str a1, bufferAddr stores the value from the register at the memory location pointed to by bufferAddr. Since this is in the middle of the init function it works first time only.
However ldr v2, =bufferAddr loads the value of bufferAddr, this results in in the result overwriting decrypt.
You should use ldr v2, bufferAddr.

STRB works, unless target address gets shifted

I'm trying to use ARM assembly to insert one string into another, however my code would always return an empty string to the C program calling the assembly program. I believe I have narrowed down my issue to the STRB instruction. Below is my code with most irrelevant code removed. The important part to look at is in the "test" block.
.global ins
ins:
stmfd sp!, {v1-v6, lr}
mov v1, a1 # save pointer to 1st string
mov v2, a2 # save pointer to 2nd string
bl strlen # find out length
mov v3, a1 # save string1 length
mov a1, v2 # recover pointer to string 2
bl strlen # length of string 2
add a1, a1, v3 # total length
add a1, a1, #1 # add one for null byte
bl malloc
add a3, a3, #1
test:
ldrb v3, [v1], #1
strb v3, [a1], #1
ldmfd sp!, {v1-v6, pc}
exit:
.end
v1 and v2 hold strings 1 and 2. When I have the test block written as:
test:
ldrb v3, [v1], #1
strb v3, [a1], #1
ldmfd sp!, {v1-v6, pc}
then the program returns an empty string. However, if I have it written as:
test:
ldrb v3, [v1], #1
strb v3, [a1]
ldmfd sp!, {v1-v6, pc}
it successfully returns the first character in string 1. Obviously, this is not sufficient to build a new string, as I'm not performing an offset on a1.
Does anyone know what is causing the string to be returned as empty? I honestly have no idea what the issue may be after several hours of experimenting and researching.
Any help is greatly appreciated!
The value in a1 is returned to the C function calling your assembler routine. You need to return the address of the start of the string, but if you increment a1 while writing the string you will return the address of the end of the string instead.
If you use another register for storing the current address that you are writing to then the start address will still be in a1 when you return. e.g:
test:
mov v4, a1 # copy address of new string to v4
ldrb v3, [v1], #1
strb v3, [v4], #1 # increment v4, the start of string
# will still be in a1
ldmfd sp!, {v1-v6, pc}

Nios II assembly code to C

I'm trying to write the code below in C but I have a real problem with doing that so if someone can help me out and explain to me in the same time I'd be grateful.
array .word ?
mystery:
add r2,r0,r0
addi r7,r0,32
loop:
movia r4, array
slli r3,r2,2
add r5,r3,r4
ldw r14, (r5)
add r15, r3, r14
stw r0, (r15)
addi r2, r2, 1
bge r7,r2,loop
ret
I don't believe that array .word ? is correct syntax. I think it is just representing that there is an array of some size at the location array. Here's the translation:
int *array[];
void mystery () {
int r2;
int *r14;
for (r2 = 0; r2 <= 32; r2++) {
r14 = array[r2];
r14[r2] = 0;
}
}

ARM assembly language: caesar cypher

This is for homework
I have to take a given string and offset as parameters and create an encrypted version of the string. Here is what I have so far
.global
cypher:
stmfd sp!, {v1-v6, lr} #std
mov v1, a1 #hold the string pointer in v1
bl strlen #get the length of the string in a1
add a1, a1, #1 #add null byte space to strlen
mov v2, a1 #hold the length of space needed in v2
bl malloc #reserve space for new string in a1
mov v3, #0 #initial index of new string
loop:ldr v4, [v1], #4 #load v4 with string pointer and increment by bytes
add v5, v4, a2 #add the offset to the current character
str v5, [a1, v3] #store the new character in the new address
add v3, v3, #4 #increment the index by a byte
cmp v2, v3
bne loop
ldmfd sp!, {v1-v6, pc} #std
.end
I'm having trouble figuring out how to actually increment the character correctly. How to I add an offset to a character?(I'm guessing the ascii characters need to be incremented?)
You are iterating the loop by four bytes.
Below is the "correct" one: (and an optimize one, too: you don't need v3 and v5)
loop:ldrb v4, [v1], #1 #load v4 with string pointer and increment by bytes
subs v2, v2, #1
add v4, v4, a2 #add the offset to the current character
strb v4, [a1], #1 #store the new character in the new address
bne loop
ldmfd sp!, {v1-v6, pc} #std

Resources