Currently doing a project for school in Arm Assembly and I've run into a strange problem early on. The program takes two integers as input and is supposed to print the first integer and then, print the absolute value of that integer. The problem is that when I input a negative value, I can't find a way to identify that it is negative, as no matter what kind of branch condition I use the branch is not taken.
This is in Armv8, I've tried to compare the value in x1 to #0 and use BLT and BMI both, but it seems that in either case the branch is not taken. Interestingly the value is always printed as negative, so I'm very confused as to how this works.
.data
newline: .asciz "\n"
input_fmt: .asciz "%d %d"
output_fmt: .asciz "%d"
strfmt: .asciz "hello"
num1: .space 256
num2: .space 256
.text
.global main
main:
//take input
ldr x0, =input_fmt
ldr x1, =num1
ldr x2, =num2
bl scanf
ldr x0, =output_fmt
ldr x1, =num1
ldr x1, [x1]
bl printf
ldr x1, =num1
ldr x1, [x1]
cmp x1, #0 //<---- PROBLEM
bmi ABS //Branch is never taken, even when input is negative
ldr x0, =output_fmt
bl printf
b exit
ABS:
neg x1, x1
br x30
exit:
mov x0, #0
mov x8, #93
svc #0
Related
.equ READERROR, 0 #Used to check for scanf read error.
.global main # Have to use main because of C library uses.
main:
prompt:
# Ask the user to enter a number.
ldr r0, =strInputPrompt # Put the address of my string into the first parameter
bl printf # Call the C printf to display input prompt.
ldr r0, =numInputPattern # Setup to read in one number.
ldr r1, =intInput # load r1 with the address of where the
# input value will be stored.
bl scanf # scan the keyboard.
cmp r0, #READERROR # Check for a read error.
beq readerror # If there was a read error go handle it.
ldr r1, =intInput # Have to reload r1 because it gets wiped out.
ldr r1, [r1] # Read the contents of intInput and store in r1 so that
# it can be printed
ldr r0, =strOutputNum
bl printf
ldr r0, =strOutputEven
loop:
cmp r1, #101
beq end
cmp r1, #0
bne odd
cmp r1, #1
bne even
odd:
add r1, r1, LSL #1 /* r1 ← r1 + (r1 << 1) */
bl printf
even:
mov r1, r1, ASR #1 /* r1 ← (r1 >> 1) */
b end_loop
end_loop:
add r2, r2, #1 /* r2 ← r2 + 1 */
b loop /* branch to loop */
# Print the input out as a number.
# r1 contains the value input to keyboard.
b myexit # leave the code.
readerror:
# Got a read error from the scanf routine. Clear out the input buffer then
# branch back for the user to enter a value.
# Since an invalid entry was made we now have to clear out the input buffer by
# reading with this format %[^\n] which will read the buffer until the user
# presses the CR.
ldr r0, =strInputPattern
ldr r1, =strInputError # Put address into r1 for read.
bl scanf # scan the keyboard.
Not going to do anything with the input. This just cleans up the input buffer. The input buffer should now be clear so get another input.
b prompt
myexit:
End of my code. Force the exit and return control to OS
mov r7, #0x01 # SVC call to exit
svc 0 # Make the system call.
.data
Declare the strings and data needed
.balign 4
strInputPrompt: .asciz "Input a number between 1 and 100: \n"
.balign 4
strOutputNum: .asciz "You entered: %d \n"
.balign 4
strOutputEven: .asciz "The even numbers from 1 to %d are: \n"
Format pattern for scanf call.
.balign 4
numInputPattern: .asciz "%d" # integer format for read.
.balign 4
strInputPattern: .asciz "%[^\n]" # Used to clear the input buffer for invalid input.
.balign 4
strInputError: .skip 100*4 # User to clear the input buffer for invalid input.
.balign 4
intInput: .word 0 # Location used to store the user input.
.global printf
.global scanf
cannot get even and odd functions to work
The first problem is here:
cmp r1, #0
bne odd
cmp r1, #1
bne even
This actually compares the entire number to 0 or 1, when you only want the rightmost bit. You'll need to do something like this. I'll only list the even case, I think you can figure out the odd. I'm a bit rusty at ARM assembly and haven't tested this but something like it should work:
tst r1,#1 # sets the flags as you did "AND r1,r1,#1" but doesn't change r1
bne odd # now you will jump to label "odd" if r1 is odd, or keep
# going if it's even.
# if it isn't odd, it must be even, so your even code goes here!
push r1-r2 # technically this is a thumb mode only instruction but if your
# assembler allows unified syntax it should get assembled as the
# 32-bit instruction "STMFD sp!,{r1-r2}"
# this saves r1 and r2 on the stack, we get them back with POP.
# C functions assume the stack is aligned to 8 bytes so we have to push
# an even number of registers even though we only needed to push R1.
ldr r0,=strOutputEven # r1 still contains intInput
bl printf # prints "The even numbers from 1 to %d are,"
pop R1-R2 # unified syntax for "LDMFD sp!,{r1-r2}"
# now we'll do the loop:
ldr r0,=numInputPattern
mov r1,#0 # we don't need the input anymore
mov r2,#0 # the sum goes here
loop_even:
add r1,r1,#2
cmp r1,#101
bcc exitLoop # if R1 is greater than or equal to 101, exit.
PUSH R0-R3 # I can't remember what printf alters so better safe than sorry.
bl printf
POP R0-R3
add r2,r2,r1 # add R1 to R2 and store the result in R2.
b loop_even
exitloop:
mov r1,r2 # put the sum into r1 so we can print it
bl printf # print the string
b my_exit # we're done!
odd:
I would recommend reading up on the PUSH and POP instructions and how they work, it's a much more reliable way of temporarily preserving registers than the method you used with ldr r1,[r1]. Don't feel ashamed if you need to print out a reference list of the ARM instructions - I use one all the time.
This is a homework problem I have been struggling with all week.
The Guidelines:
" Write a program that asks the user to enter positive nonzero integer values. The user enters a -999 to signal the end of the data series. After all the numbers have been entered, the program should output the largest and smallest numbers entered.
Largest Number: XXXX
Smallest Number: XXXX
If a negative value is entered other than -999 the program should stop with an error message.
I would love to use an array, but can't find a way to use an array to store integers that a user has entered.
I realize it may be a mess, but here is the code I've adapted for a previous program:
.global main
.func main
#Starts Program
main:
sub sp, sp, #24 #stack
ldr r0, =userPrompt #calls prompt
bl printf #prints to screen
ldr r0, =format #calls format
mov r1, SP #Add Stack to register 1
bl scanf
ldr r3, [sp] #stores user input
add sp, sp, #16 #restores stack
mov r5, #0 #Moves 0 into R5
cmp r3, #0 #Compares r3 to 0
blt ex #branches to exception
// bgt ex2 #branches to exception2
storeValue:
STR r1, [r4]
add sp, sp, #24
getNum:
ldr r0, =userPrompt #calls prompt
bl printf #prints to screen
ldr r0, =format #Calls Format
mov r1, sp #Add Stack to register 1
bl scanf #Prints to Screen
ldr r3, [sp] #stores user input
add sp, sp, #16 #restores stack
mov r5, #0 #Moves 0 into R5
cmp r3, #0 #Compares r3 to decimal 0
blt exception #branches to excption if r3 = less than 0
loop: #Beginning of loop
cmp r4, r3 #Compares r3 to r4 to see if equal
bgt end #Greater than Branch
str r4,[] r4, r4, #1 #increases by 1
beq loop #branch if equal
blt loop #branch if less than
#End Program
mov r7, #1 #syscall
swi 0
ex:
ldr r0, = exception #calls exception to print(e)
bl printf #prints to screen
b getNum #branches to getNum to re-ask for a number
LrgNum:
.data
output: .asciz "\n\nLargest Number Entered %d is: %d\n\n\n" "\n\nSmallest Number Entered %d is: %d\n\n\n"
usrPrompt: .asciz "\n\nEnter positive numbers to compare. To see results enter -199 \t"
exception: .asciz "\nInvalid Entry. Enter a positive number greater than 0\n\n"
format: .asciz "%d"
input: .word 0
blanklines: .asciz "\n\n"
exitcom: .word "-199"
.end
For this very specific task, you don’t need an array at all: the minimum and maximum of a sequence can be computed “on-the-fly”, with no need for storing earlier values.
Now I won’t give you assembly code, but I think you already know how to implement all of it.
For the record: storing a sequence of unknown, arbitrarily long length would be much trickier, as you would need to allocate an initial buffer, then increase its capacity each time your buffer is full, by reallocating it with a larger capacity and copying its values. Last, but certainly not least: I am not entirely sure about ARM assembly, but I believe that, to be able to “allocate”, you would have to implement your own small memory manager (i.e., a malloc routine).
trying to learn some basic ARM and I am trying to figure how to get the length of a string using a while loop. Below is what I am trying to achieve in C and also what I am attempting to do in ARM. My issue comes from the while loop, I am unable to go through the string until I hit the null terminator and to keep track of it with a variable.
char inputString[7];
int lengthOfInput = 0;
while (inputString[lengthOfInput] != '\0')
{
lengthOfInput++;
}
.section .data
#need to include \n if you want to test the output. no idea why
input_prompt : .asciz "string: "
input_spec : .asciz "%[^\n]"
length_spec : .asciz "length: %d\n"
#8 bytes for 7 characters plus \0
input: .space 8
.section .text
.global main
main:
#print prompt
ldr x0, =input_prompt
bl printf
#get input
ldr x0, =input_spec
ldr x1, =input
bl scanf
#call get_length
ldr x9, =input
ldrsw x0, [x9]
bl get_length
#print result from get_length
#to do
#exit
b exit
get_length:
#intializing "i" as 0
add x19, xzr, xzr
get_length_loop:
#address of userString[i]
add x12,x19,x1
#if == 0, go somewhere, maybe get_length should be changed
cbz x12, get_length
add x19,x19, 1
b get_length_loop
#return result
mov x0, x19
ret
exit:
mov x0, 0
mov x8, 93
svc 0
ret
I'm designing an AArch64 application in assembly and C, using Linaro toolchain, and frequently use objdump to look at my own disassembled binary.
However, objdump does not decode part of file, and treats it as data. It always happens after a second label in source.
For example, this code:
.global _Reset
_Reset:
BL get_cpuid
CBNZ x0, inf_loop
LDR x0, =page_table_base
LDR x1, =0x0000000000000601
STR x1, [x0, #0x00]
test:
LDR x1, =0x0060000040000601
STR x1, [x0, #0x08]
...
Disassembles to
Disassembly of section .startup:
0000000000000000 <_Reset>:
0: 94000024 bl 90 <get_cpuid>
4: b50004c0 cbnz x0, 9c <inf_loop>
8: 58000880 ldr x0, 118 <TXTN+0x3>
c: 580008a1 ldr x1, 120 <TXTN+0xb>
10: f9000001 str x1, [x0]
0000000000000014 <test>:
14: 580008a1 .word 0x580008a1
18: f9000401 .word 0xf9000401
...
Why does this happen?
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.