ARM - Infinite Loop While Searching String - string

Can anybody point out why? I can't see the problem.
String to search: "aassaas"
String to search with: "as"
SEARCHSTRING:
STMFD SP!, {R4-R7, LR}
MOV R6, #0 #Matches found
MOV R3, #0 #Placeholder
LOOP: LDRB R4, [R0] #R4 = String to search
LDRB R5, [R1] #R5 = String to search with
CMP R4, R5 #Do they match?
ADDEQ R3, R3, #1 #If yes, increase placeholder
LDREQB R4, [R0, #1]! #Get next char
LDREQB R5, [R1, #1]! #Get next char
BLNE RESET #If not, reset placeholder and strings.
#R0 is nevertheless initial pos+1
CMP R5, #0 #Is string to search with at the end?
ADDEQ R6, R6, #1 #If so, add +1 to matches
BLEQ RESET #Reset placeholder and strings.
CMP R4, #0 #Is the string to search finished?
BNE LOOP #If not, start over.
MOV R0, R6 #If so, move answer into R0.
LDMFD SP!, {R4-R7, PC} #Jump back.
RESET:
STMFD SP!, {LR}
CMP R3, #0 #Is the placeholder at 0? (initial position)
SUBNE R0, R0, R3 #If not, subtract from String to search pos
SUBNE R1, R1, R3 #And string to be searched pos
ADDNE R0, R0, #1 #Increment string to search+1 so we don't start at the same spot
MOVNE R3, #0 #Empty the placeholder
LDMFD SP!, {PC} #Jump back

I don't understand why a) you're writing this in assembler instead of C, and b) why you're not using some routine based on strstr. The most likely scenario is that this is a homework problem, or some other form of learning exercise, so I don't want to give too much away. In any event, there are a couple of problems that I noticed. The first bit I notice is in the RESET routine:
RESET:
STMFD SP!, {LR}
CMP R3, #0 #Is the placeholder at 0? (initial position)
SUBNE R0, R0, R3 #If not, subtract from String to search pos
SUBNE R1, R1, R3 #And string to be searched pos
ADDNE R0, R0, #1 #Increment string to search+1 so we don't start at the same spot
MOVNE R3, #0 #Empty the placeholder
LDMFD SP!, {PC} #Jump back
The CMP is unnecessary - consider what the effect of the SUBNE calls will be if R3 is 0, and you'll see that you can perform the subtractions unconditionally. You want to run ADD R0, R0, #1 unconditionally - in fact, this is a big part of the reason you have an infinite loop. If you get to the RESET subroutine, and R3 is 0, then it doesn't change any state. I also notice that the STMFD / LDMFD pair is really not necessary - LR won't be modified in this subroutine, so it doesn't need to go on the stack.
Next, I notice that you're not careful enough about when to terminate your loop. Consider what happens if you give two empty strings as arguments to SEARCHSTRING. Call it with two empty strings as arguments, and single-step through your assembly code to see the problem. The general form of a for loop, when compiled to assembly, will be something like:
for(initial; comparison; increment) {
body;
}
INITIAL:
MOV R0, #0 #initialize variables
B CONDITION #jump to condition check
BODY:
LDR R1, [R0]
INCREMENT: #really, part of the for-loop body.
ADD R0, R0, #1
CONDITION:
CMP BLAH, BLAH #test-condition
BLT BODY #restart loop if condition indicates we should do so.
hopefully this will help you to reorganize the code in a more straightforward way.

Related

Armv7 Assembly - Extra console prompt "pi#raspberrypi:~ $ " when I press enter?

I get this output from my program which takes a user input, and replaces the linefeed with a null terminating and prints it back out to the console
pi#raspberrypi:~ $ ./tester
Please enter 4 different numbers between 1-5 together without space or special characters.
1234
1234
pi#raspberrypi:~ $
pi#raspberrypi:~ $
But when I type 123 I only get a single line prompt which is what I'm looking for when I enter 1234.
pi#raspberrypi:~ $ ./tester
Please enter 4 different numbers between 1-5 together without space or special characters.
123
123
pi#raspberrypi:~ $
This is the code I'm executing, it's as minimum as I could get it for minimum functional requirements.
.global _start
_start:
LDR r1, =prompt
BL _sPrint
LDR r1, =userInput # point to the space allocated for input
MOV r2, #4 # set the limit of character to read in
BL _sInput
LDR r1, =userInput
BL _sPrint
Ldr r1, =newline
BL _sPrint
B _exit
#_sPrint prints out a string based on it's variable length determined by _strlen
#strlen, and findEnd are both needed for _sPrint.
_sPrint:
MOV r7, #4 #sets r7 to console STDOUT
MOV r0, #1 #set WRITE destination to STDOUT (terminal)
PUSH {r0, r1, lr}
BL _strLen #gets the stringlength and the end
POP {r0, r1, lr}
SWI 0
mov pc, lr
_strLen:
mov r2, #0
#find end of strlen finds the end of the string and stores the length in r2 for console output
findEnd:
LDRB r0, [r1], #1
ADD r2, r2, #1
CMP r0, #0
BNE findEnd
SUB r2, r2, #1
MOV pc, lr
_sInput:
PUSH {R1-R8, lr}
MOV r7, #3 #register r7 being set to 3 to indicate message being read in (read syscall)
MOV r0, #0 #Set READ device to the STDIN (keyboard)
SWI 0
POP {R1-R8, lr}
#String fix takes a string value at r1's address and changes the line feed to be null termianted.
strfx:
LDRB r0, [r1],#1 #loads a single byte from r1 (r1 is dereferenced), which is the _sInput to r0
CMP r0, #10 #is r0 our newline?
BNE strfx
MOV r0, #0 #set r0 to null
STRB r0, [r1, #-1] #store r0's value back into r1's current address location. The final address
MOV PC, LR #location of r1 newline to be the NULL in r1.
_exit:
MOV r7, #1
SWI #0
.data
prompt: .asciz "\nPlease enter 4 different numbers between 1-5 together without space or special characters. \n \n"
newline: .asciz "\n"
userInput: .space 6
You're reading four characters. When the user enters "1234\n" (five characters), the newline is left in the input buffer to be read by the shell. When the user enters "123\n", the newline is actually read by you. Since you have newline-handling code, the solution is simple: you need to read five characters, not four.

ARM Assembly loading string produces weird terminal output

I am trying to space out my string by replacing each character with a space string combined with an underscore string to create _ _ _ _.
However, I get a weird output:
If I load 1 character e.g ldr r1, = '_'
it works however I get _____ and I am trying to get _ _ _ _ _. What is the best way to do this?
NOTE: I do not know C and I am new to ARM.
My function:
sub r3, r0, #1 #has the length
ldr r0, = buffer #has the word
mov r5, #0 #start of increment
mov r6, r0 #copies word to r6
loop:
ldr r1, =spaceChar
strb r1, [r6, r5]
add r5, r5, #1
cmp r3, r5
bne loop
mov r1,r6
ldr r0, = HiddenWord
bl printf
pop {r4,lr}
bx lr
.data
HiddenWord:
.asciz "Word: %s"
spaceChar:
.asciz " _"
buffer:
.space 100
Since you already statically allocate the buffer the simplest solution is to preset it with " _" using your assembler and just put the terminating zero in the right place. I don't think you mentioned which assembler you use, the following works in gnu assembler:
ldr r0, =buffer-1
mov r1, #0
strb r1, [r0, r3, lsl #1]
ldr r0, =HiddenWord
ldr r1, =buffer
bl printf
pop {r4,lr}
bx lr
.data
HiddenWord:
.asciz "Word: %s\n"
buffer:
.rept 50
.ascii "_ "
.endr
If you want to fill the buffer programmatically, that could look like:
ldr r0, =buffer #has the word
ldr r1, =0x205f # underscore + space
loop:
strh r1, [r0], #2
subs r3, r3, #1
bne loop
mov r1, #0 # replace final
strb r1, [r0, #-1] # space with zero

Print Newline for Armv7 assembly program

I'm working on this armv7 assembly program that finds the greatest common divisor(gcd) of two integers. Everything is working fine except for the newline function. When i assemble and run the program, it doesn't print any newlines, just the integers in one line. Any suggestions on how i can fix that?
.global _start
_start:
mov r2, #24 #first set of integers
mov r4, #18
bl mysub1
bl mysub2
bl mysub3
mov r2, #78 #second set of integers
mov r4, #34
bl mysub1
bl mysub2
bl mysub3
mov r2, #99 #third set of integers
mov r4, #36
bl mysub1
bl mysub2
bl mysub3
_exit:
mov r7, #1
swi 0
mysub1: #subroutine to find gcd
cmp r2, r4
beq done
bgt greater
blt less
greater:
sub r2, r2, r4
bal mysub1
less:
sub r4, r4, r2
bal mysub1
done:
bx lr
mysub2: #subroutine to convert gcd result to ascii value
add r4, #48
ldr r9, =store
str r4, [r9]
mov r7, #4 #print out a newline
mov r0, #1
mov r2, #1
ldr r1, =newline
swi 0
bx lr
mysub3: #subroutine to print out the ascii value
mov r7, #4
mov r0, #1
mov r2, #2
ldr r1, =store
swi 0
bx lr
.data
store:
.space 2
newline:
.ascii "\n"
This is the culprint:
add r4, #48
ldr r9, =store
str r4, [r9]
This code has two bugs:
it only works for numbers between 0 and 9
str r4, [r9] stores four bytes to store, overwriting the newline right after the two-byte buffer.
To fix the first issue, you need to do a division with rest to separate the number in r4 into two digits. To fix the second issue, use strb or strh to store a byte or halfword instead as to not overrun the buffer.

How to print a number in ARM assembly?

I am trying to print a number that I have stored. I'm not sure if I am close or way off. Any help would be appreciated though. Here is my code:
.data
.balign 4
a: .word 4
.text
.global main
main:
ldr r0, addr_of_a
mov r1, #8
str r1, [r0]
write:
mov r0, #1
ldr r1, addr_of_a
mov r2, #4
mov r7, #4
swi #0
bx lr
addr_of_a: .word a
It compiles and runs, but I don't see anything printed. From what I understand, I need the address of where to start printing in r1, how many bytes in r2, the file descriptor in r0, and r7 specifies the write call if it is set to #4. I am simply trying to store #8, then print the stored number.
The syscall write takes on the second argument (r1) as a pointer to the string you want to print. You are passing it a pointer to an integer, which is why it's not printing anything, because there are no ASCII characters on the memory region you are passing to it.
Below you'll find a "Hello World" program using the syscall write.
.text
.global main
main:
push {r7, lr}
mov r0, #1
ldr r1, =string
mov r2, #12
mov r7, #4
svc #0
pop {r7, pc}
.data
string: .asciz "Hello World\n"
If you want to print a number you can use the printf function from the C library. Like this:
.text
.global main
.extern printf
main:
push {ip, lr}
ldr r0, =string
mov r1, #1024
bl printf
pop {ip, pc}
.data
string: .asciz "The number is: %d\n"
Finally, if you want to print the number with the syscall write you can also implement a itoa function (one that converts an integer to a string).
Hi I appreciate that this is a pretty old thread but I've scratched my head over this for a while and would like to share my solution. Maybe it'll help someone along the way!
I was aiming to print to digit without recourse to using C++ in any way, though I realise that simply decompiling a tostring() - or whatever equivalent exists in C++ - and seeing what that came up with would have been a far quicker route.
Basically I ended up with creating a pointer to an empty .ascii string in the section .data and added the digit that I wanted to print + 48 to it before printing off that digit.
The +48 of course is to refer to the specific digit's ascii index number.
.global _start
_start:
MOV R8, #8
ADD R8, R8, #48
LDR R9, =num
STR R8, [R9]
MOV R0, #1
LDR R1, =num
MOV R2, #1
MOV R7, #4
SWI 0
.data
num:
.ascii: " "
The biggest drawback of this approach is that it doesn't handle any number more than one digit long of course.
My solution for that was much, much uglier and beyond the scope of this answer here but if you've a strong stomach you can see it here:

An issue with an ARM assembly function?

I have a simple function written in ARM assembler. The first time it's ran, everything works as desired (it prints BOOT\n). However, the second time the function is executed, nothing is printed.
.globl __printTest
.text
.align 2
__printTest:
sub sp, #64 /* yes, I know this is too much */
mov r0, #66
str r0, [sp]
mov r0, #79
str r0, [sp, #1]
mov r0, #79
str r0, [sp, #2]
mov r0, #84
str r0, [sp, #3]
mov r0, #10
str r0, [sp, #4]
mov r0, #0
mov r1, sp
mov r2, #5
bl _write
add sp, #64
bx lr
What could be the issue? I suspect that this somehow screws up the buffer that it no longer works. Write is a function that calls the write syscall on Linux using the svc instruction.
The problem is that you're not saving lr.
bl _write
add sp, #64
bx lr
bl _write will overwrite lr which then points to add sp, #64, so your bx lr will just result in an endless loop on the last two instructions.
It should work if you modify your code like this:
__printTest:
push {lr}
sub sp, #64 /* yes, I know this is too much */
....
bl _write
add sp, #64
pop {pc}
As already stated in another answer, you should also use strb instead of str for byte-stores.
This function is pushing 32-bit values into unaligned stack pointer addresses. It should be using strb to write single bytes. For unaligned str, the ARM Architecture Reference Manual says:
if UnalignedSupport() || address<1:0> == ‘00’ then
MemU[address,4] = R[t];
else // Can only occur before ARMv7
MemU[address,4] = bits(32) UNKNOWN;
So depending on your configuration, you might be getting junk in your stack if you're hitting the UNKNOWN case.

Resources