Caesar decoder in ARM Assembly - c

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.

Related

How can I get the ASCII value of characters in a string in ARM Assembly?

I have to convert the following code into assembly language:
void rotN1(char *str, int n)
{ // NB: str will be modified in-place
char *p;
for (p = str; *p != '\0'; p++)
{
int currChar = (int)*p;
if (currChar >= 'a' && currChar <= 'z')
{
currChar = currChar + n;
if (currChar > 'z')
{
currChar = currChar - 26;
}
*p = (char)currChar;
}
}
}
It's basically using a Caesar cipher on a given string (str), and n is the encryption key.
.data
array: .string %[str]
.equ len.array,.-array
.align
.text
.global main
main:
nop
ldr r2,=array // pointer
MOV r0, #0 // initialise loop index to 0
MOV r1, #len.array // number of elements
MOV r4, %[n]
Loop:
ldrb r3, [r2, r0]
mov r6, #
B check_A
B check_Z
ADD r3, r3, r4
ADD r0, r0, #1 //increment loop index
CMP r0, r1
BLE Loop
_exit:
mov r7, #1
svc 0
check_A:
CMP r3, #97
BLT _exit
check_Z:
CMP r3, #122
BGT _exit
I haven't finished the code but while traversing the array I need to get the ASCII value of that character in each iteration. Is there a way to do that? I have to do this as inline ARM in C.
"...I need to get the ASCII value of that character in each iteration."
You already have them.
ASCII characters each have an integral value as shown in the linked table. Integral values for characters ranging from A-to Z (as shown in the decimal column of the table) span from 65 to 90, while a-z spans from 97 to 122. So, in an array such as:
char msg[] = "Hello";
msg[0] has a value of 72,
msg[4] has a value of 111,
msg[5] has a value of 0.
Note, the last conversion is because msg is initialized with a string literal, and string literals in C are defined as a char array terminated with a NULL terminator. Note that not all char arrays are required be NULL terminated, in which case that array would be a simple char array, not a C string.
Also, as you display in your post, an ASCII char surrounded by single quotes represents the integral value of that character. eg in the expression
char aitch = 'H';
aitch now has the value 72. (again, shown in base 10)

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.

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}

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

Largest Integer in ARM assembly

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.

Resources