Related
I want to print in ARM assembly language a given number in decimal in hexadecimal. I'm doing the function that does the conversion and the printing. So far the conversion works but the printing not at all.
It does only print a char at a time and it's not at all what I want, I want a special format of output such that I have 0x and 8 digits.
I wrote a function printf using the given function I had, called _writec that is working but only printing a char at a time. So I wrote a loop until I get the end of string function but here it seems that it doesn't care.
I've followed the execution step-by-step using gdb and it suddenly crash for no appearing reason. When r0 contain 0 it should go to .end according to my beq but it does not.
ARM Code:
.global _print_hex
_print_hex:
push {lr}
#According to .c algorithm : r0 = dec; r1 = quotient;
# r2 = temp; r3 = i ; r4 = j
mov fp, sp
sub sp, sp, #100 # 100 times size of char
mov r1, r0
mov r3, #0
_while:
cmp r1, #0
bne _computing
ldr r0, =.hex_0x
bl _printf
mov r4, #8
_for:
cmp r4, #0
bge _printing
ldr r0, =.endline
bl _printf
mov sp, fp
pop {pc}
_computing:
and r2, r1, #0xF
cmp r2, #10
blt .temp_less_10
add r2, #7
.temp_less_10:
add r2, #48
strb r2, [sp, r3]
add r3, #1
lsr r1, #4
b _while
_printing:
ldrb r0, [sp,r4]
bl _writec
sub r4, #1
b _for
_printf:
push {r0, r1, r2, r3, lr}
mov r1, r0
mov r2, #0
.loop:
ldrb r0, [r1,r2]
cmp r0, #0
beq .end
bl _writec
add r2, #1
b .loop
.end:
pop {r0, r1, r2, r3, lr}
bx lr
.hex_0x:
.asciz "0x"
.align 4
.endline:
.asciz "\n"
.align 4
C code (that I tried to translate):
void dec_to_hex(int dec){
int quotient, i, temp;
char hex[100];
quotient = dec;
i = 0;
while (quotient != 0){
temp = quotient % 16;
if (temp < 10){
temp += 48; // it goes in the ascii table between 48 and 57 that correspond to [0..9]
} else {
temp += 55; //it goes in the first cap letters from 65 to 70 [A..F]
}
hex[i]=(char)temp;
i++;
quotient /= 16;
}
printf("0x");
for(int j=i; j>=0; j--){
printf("%c", hex[j]);
}
printf("\n");
}
Here is the code of _writec :
/*
* Sends a character to the terminal through UART0
* The character is given in r0.
* IF the TX FIFO is full, this function awaits
* until there is room to send the given character.
*/
.align 2
.global _writec
.type _writec,%function
.func _writec,_writec
_writec:
push {r0,r1,r2,r3,lr}
mov r1, r0
mov r3, #1
lsl r3, #5 // TXFF = (1<<5)
ldr r0,[pc]
b .TXWAIT
.word UART0
.TXWAIT:
ldr r2, [r0,#0x18] // flags at offset 0x18
and r2, r2, r3 // TX FIFO Full set, so wait
cmp r2,#0
bne .TXWAIT
strb r1, [r0,#0x00] // TX at offset 0x00
pop {r0,r1,r2,r3,pc}
.size _writec, .-_writec
.endfunc
So in ARM when debugging it crashed at my first call of _printf and when I comment all the call to _printf it does print the result but not as the desired format. I only got the hex value.
I'm trying to translate a simple insertion sort algorithm to assembly, but something about this particular configuration is causing the program to get an invalid pointer error.
Here's the C version that I'm using:
int n, array[100], c, d, t;
for (c = 1; c < n - 1; c++) {
d = c;
while (d > 0 && array[d] < array[d - 1]) {
t = array[d];
array[d] = array[d - 1];
array[d - 1] = t;
d--;
}
}
This is a C struct that is being used:
typedef struct {
int *list;
int size;
int maxSize;
} list;
Here is my assembly file:
.syntax unified
.text
.align 8
.global insert_ARM
.func insert_ARM, insert_ARM
.type insert_ARM, %function
insert_ARM:
push {r4-r11, ip, lr}
# setup
ldr r4, [r0, #4]
sub r4, r4, 1 # r4 = n-1
mov r5, #1 # c=1
mov r6, #16 # d=0, which starts at #16
mov r7, #0 # t=0
for:
# d = c ; needs these lines to do the assembly equivalent, which is * 4.
mov r6, r5 # d = c
LSL r6, #2 # uses logical shift left: multiplies r6 by 4 to get the correct index
add r6, r6, 16 # add 16 because that's where the array starts
while:
# condition 1: d > 0
cmp r6, #0 # if d <= 0, get out of there
ble forLoopStatements
# condition 2: array[d] < array[d-1]
# first, I need to define array[d] and array[d-1]
# r8 = array[d] and r9 = array[d-1]
sub r10, r6, #4 # r10 = d-1
ldr r9, [r0, r10] # r9 = array[d-1]
ldr r8, [r0, r6] # r8 = array[d]
cmp r9, r8 # comparing array[d-1] with array[d]
bge forLoopStatements # if array[d] >= array[d-1], get out of there
# while effects
# note that r8 should still be array[d] here.
str r9, [r0, r6] # array[d] = array[d-1]
str r8, [r0, r10] # array[d-1] = t # BUG HERE.
sub r6, r6, #4 # d--; // does -4 for ARM
bal while # repeat loop
forLoopStatements:
# (c<n-1; c++)
add r5, r5, #1 # c++
cmp r5, r4 # compares c with n-1
blt for # if c < n-1, loop again
end:
mov r0, r10
pop {r4-r11, ip, lr}
BX lr
.endfunc
.end
It seems to be
str r8, [r0, r10] # array[d-1] = t
that causes a trip at some point.
Edit: I found out that r8's numbers during this instruction are somehow incorrect, since immediately using something like
mov r8, #4
before the store prevents the error (but of course makes the results incorrect).
Upon examining the contents of r0, it happens that the update is going off range because other members of the struct are being modified in the process. Array index 0 is at +16.
You found the problem in the translation to assembly. Note however the following problems:
The outer loop should run all the way to c < n instead of c < n - 1. As coded, the last element of the array is never moved.
it would be more readable to use 2 nested for loops:
int n, array[100], c, d, t;
for (c = 1; c < n; c++) {
for (d = c; d > 0 && array[d] < array[d - 1]; d--) {
t = array[d];
array[d] = array[d - 1];
array[d - 1] = t;
}
}
Every one has a different approach to writting code. Mine is different from your, but I would like to share my ideas. I would start with as simple as possible to get somthing working and build from there. Here is a sample code for a forloop.
/* forloop.s */
/* int n, array[100], c, d, t;
for (c=1; c<n-1; c++)
address of array = r0 = .word ( Raspbian Jessie = 32 bits )
n = r4 = array size
c = r5 = 1word = 4memory_bytes = index into array
d = r6 = c = address in array
array[d] = r10 = data
*/
.data
.balign 4
array:
.word 6, 3, 7, 8, 5, 2, 1, 9, 4
size:
.word (size - array)
.text
.global main
main:
push {r4-r12, lr} # save registers for OS
ldr r0, =array # load address of array in r0
ldr r4, =size # load address of size in r4
ldr r4, [r4] # load size in r4
sub r4, #4 # substract 1 word from r4 (n=n-1)
mov r5, #4 # move 4 in r5 (c=1word=4memory_bytes)
for: # (c=1; c<n-1; c++)
add r6, r0, r5 # d (r6) = array address (r0) + (c=4)
# while: # while loop would go here
ldr r10, [r6], #-4 # r10 = array[d], d=d-4
ldr r11, [r6] # r11 = array[d-1]
#... # while code
cmp r0, r6 # is d > 0 ...
#... #continue while loop code
# back to forloop code
cmp r5, r4 # compare (subtract) r5 (c) from r4 (n)
add r5, #4 # add 1 word to r5 (c++)
blt for # end of for loop (c<n-1)
end:
mov r0, #0 # set exit code
pop {r4-r12, lr} # restore enviroment for return to OS
bx lr # return to OS
Assemble and link the code and the run it and check ending status.
as -o forloop.o forloop.s
gcc -o forloop forloop.o
./forloop; echo $?
It works for me on the Raspberry Pi. I don't know much about gdb, but this may help as suggested by Jester. (See middle section "Commands" at http://cs107e.github.io/guides/gdb/ for more information.)
pi#RPi0:~/pgm/Asm $ gdb -tui forloop # Text User Interface
---Type <return> to continue, or q <return> to quit--- [Enter]
(gdb) layout asm
(gdb) start # start is required
(gdb) layout reg
(gdb) Ctl-x o # Selects registers as Up & Down arrow to see all
(gdb) si # single step
(gdb) [Enter] # repeat single step
(gdb) run # run program to end
(gdb) q # quit gdb
Move the down arrow to see the cpsr register. The left most number is the flags 8=Negative, 6=Zero&Carry, 4=Zero, 2=Carry, 1=oVerflow.
Another approach to debugging assembly program on arm is to use the linux printf command. Here is myprint.s.
/* myprint.s */
.data
.balign 4
format:
.asciz " %2d %2d %2d %2d %2d %2d %2d %2d %2d\n"
.balign 4
array:
.word 6, 3, 7, 8, 5, 2, 1, 9, 4
size:
.word (size - array)
.text
.global main
print: # --- a printf function to print the value in the array ---
push {r0-r12, lr} # save registers for OS
mrs r10, cpsr # save flag settings
ldr r11, =array # To print the array[0-8], the array
ldm r11, {r1-r9} # address is loaded in r11 and stored
push {r4-r10} # in reg r1-r9, printf gets args# from
ldr r0, =format # format, 3 print from r1-r3, rest from
bl printf # stack.
pop {r4-r10} # adjust stack, restore r10 (flags)
msr cpsr_f, r10 # restore saved flags
pop {r0-r12, pc} # restore reg and return
main:
push {r4-r12, lr} # save registers for OS
bl print # --- can be placed anywhere in code ---
ldr r0, =array # load address of array in r0
ldr r4, =size # load address of size in r4
ldr r4, [r4] # load size in r4
sub r4, #4 # substract 1word from r4 (n=n-1)
mov r5, #4 # move 4 in r5 (c=1word=4memory_bytes)
for: # (c=1; c<n-1; c++)
add r6, r0, r5 # d=r6 = array address (r0) + (c=4)
while: # while loop would go here
ldr r10, [r6], #-4 # r10 = array[d], d=d-4
ldr r11, [r6] # r11 = array[d-1]
cmp r10, r11 # is array[d] < array[d-1]
bge forloop_code # if not, continue forloop code
mov r7, r11 # move array[d-1] into t (r7)
str r10, [r6], #4 # store array[d] into array[d-1], (d-1)+4=d
str r7, [r6], #-4 # store t-array[d-1] into array[d], d-4=(d-1)
cmp r6, r0 # is d>0 (addr(array[d-1]) > addr(array[0]))?
bgt while # yes, check if array[d-1] < array[d-2]
forloop_code: # back to forloop code
bl print # --- can be placed anywhere in code ---
cmp r5, r4 # compare (subtract) r5 (c) from r4 (n)
add r5, #4 # add 1 word to r5 (c++)
blt for # end of for loop (c<n-1)
end:
pop {r4-r12, lr} # restore registers for OS
mov r0, #0 # set exit code
bx lr # return to OS
as -o myprint.o myprint.s
gcc -o myprint myprint.o
./myprint; echo $?
6 3 7 8 5 2 1 9 4
3 6 7 8 5 2 1 9 4
3 6 7 8 5 2 1 9 4
3 6 7 8 5 2 1 9 4
3 5 6 7 8 2 1 9 4
2 3 5 6 7 8 1 9 4
1 2 3 5 6 7 8 9 4
1 2 3 5 6 7 8 9 4
1 2 3 4 5 6 7 8 9
0
Another thought would be to assemble your C code and use gdb to see how C code in assembly. This was an interesting projects, I did not know about insertion sort.
I figured it out. Aside from cleaning up my code, I just needed to translate
while ( d > 0 )
as
cmp r6, #16 # if d <= 0, get out of there
ble forLoopStatements
instead of
cmp r6, #0 # if d <= 0, get out of there
ble forLoopStatements
to keep the minimum index at 0.
I am trying to figure out how arrays work in ARM assembly, but I am just overwhelmed. I want to initialize an array of size 20 to 0, 1, 2 and so on.
A[0] = 0
A[1] = 1
I can't even figure out how to print what I have to see if I did it correctly. This is what I have so far:
.data
.balign 4 # Memory location divisible by 4
string: .asciz "a[%d] = %d\n"
a: .skip 80 # allocates 20
.text
.global main
.extern printf
main:
push {ip, lr} # return address + dummy register
ldr r1, =a # set r1 to index point of array
mov r2, #0 # index r2 = 0
loop:
cmp r2, #20 # 20 elements?
beq end # Leave loop if 20 elements
add r3, r1, r2, LSL #2 # r3 = r1 + (r2*4)
str r2, [r3] # r3 = r2
add r2, r2, #1 # r2 = r2 + 1
b loop # branch to next loop iteration
print:
push {lr} # store return address
ldr r0, =string # format
bl printf # c printf
pop {pc} # return address
ARM confuses me enough as it is, I don't know what i'm doing wrong. If anyone could help me better understand how this works that would be much appreciated.
This might help down the line for others who want to know about how to allocate memory for array in arm assembly language
here is a simple example to add corresponding array elements and store in the third array.
.global _start
_start:
MOV R0, #5
LDR R1,=first_array # loading the address of first_array[0]
LDR R2,=second_array # loading the address of second_array[0]
LDR R7,=final_array # loading the address of final_array[0]
MOV R3,#5 # len of array
MOV R4,#0 # to store sum
check:
cmp R3,#1 # like condition in for loop for i>1
BNE loop # if R3 is not equal to 1 jump to the loop label
B _exit # else exit
loop:
LDR R5,[R1],#4 # loading the values and storing in registers and base register gets updated automatically R1 = R1 + 4
LDR R6,[R2],#4 # similarly
add R4,R5,R6
STR R4,[R7],#4 # storing the values back to the final array
SUB R3,R3,#1 # decrment value just like i-- in for loop
B check
_exit:
LDR R7,=final_array # before exiting checking the values stored
LDR R1, [R7] # R1 = 60
LDR R2, [R7,#4] # R2 = 80
LDR R3, [R7,#8] # R3 = 100
LDR R4, [R7,#12] # R4 = 120
MOV R7, #1 # terminate syscall, 1
SWI 0 # execute syscall
.data
first_array: .word 10,20,30,40
second_array: .word 50,60,70,80
final_array: .word 0,0,0,0,0
as mentioned your printf has problems, you can use the toolchain itself to see what the calling convention is, and then conform to that.
#include <stdio.h>
unsigned int a,b;
void notmain ( void )
{
printf("a[%d] = %d\n",a,b);
}
giving
00001008 <notmain>:
1008: e59f2010 ldr r2, [pc, #16] ; 1020 <notmain+0x18>
100c: e59f3010 ldr r3, [pc, #16] ; 1024 <notmain+0x1c>
1010: e5921000 ldr r1, [r2]
1014: e59f000c ldr r0, [pc, #12] ; 1028 <notmain+0x20>
1018: e5932000 ldr r2, [r3]
101c: eafffff8 b 1004 <printf>
1020: 0000903c andeq r9, r0, ip, lsr r0
1024: 00009038 andeq r9, r0, r8, lsr r0
1028: 0000102c andeq r1, r0, ip, lsr #32
Disassembly of section .rodata:
0000102c <.rodata>:
102c: 64255b61 strtvs r5, [r5], #-2913 ; 0xb61
1030: 203d205d eorscs r2, sp, sp, asr r0
1034: 000a6425 andeq r6, sl, r5, lsr #8
Disassembly of section .bss:
00009038 <b>:
9038: 00000000 andeq r0, r0, r0
0000903c <a>:
903c:
the calling convention is generally first parameter in r0, second in r1, third in r2 up to r3 then use the stack. There are many exceptions to this, but we can see here that the compiler which normally works fine with a printf call, wants the address of the format string in r0. the value of a then the value of b in r1 and r2 respectively.
Your printf has the string in r0, but a printf call with that format string needs three parameters.
The code above used a tail optimization and branch to printf rather than called it and returned from. The arm convention these days prefers the stack to be aligned on 64 bit boundaries, so you can put some register, you dont necessarily care to preserve on the push/pop in order to keep that alignment
push {r3,lr}
...
pop {r3,pc}
It certainly wont hurt you to do this, it may or may not hurt to not do it depending on what downstream assumes.
Your setup and loop should function just fine assuming that r1 (label a) is a word aligned address. Which it may or may not be if you mess with your string, should put a first then the string or put another alignment statement before a to insure the array is aligned. There are instruction set features that can simply the code, but it appears functional as is.
Enter grid size (1-9):3
*
**
***
Here's the code i have so far:
.ORIG x3000
LEA R0, PRINT
LEA R1, MEMSPAC
PUTS
GETC
PUTC
M STR R0, R1, #0
N .FILL M
LD R1, N
NOT R1, R1
ADD R1, R1, #1 ; R1 = -N
AND R2, R2, #0 ; R2 = holds number of *'s to be printed
LOOPA LEA R0, NEWLN
PUTS
ADD R3, R2, R1 ; while (R2 < N)
BRzp LOOPB
ADD R5, R5, #1 ;
ADD R4, R4, #1
LOOPC LD R0, STAR ; R0 = *
OUT ; Write *
ADD R5, R5, #-1
BRp LOOPC
ADD R5, R4, #0
ADD R2, R2, #1 ;
BRnzp LOOPA
LOOPB
LEA R0, NEWLN
PUTS
STOP HALT
STAR .FILL x2A
RIP TRAP x25
PRINT .STRINGZ "Enter grid size (1-9):"
NEWLN .STRINGZ "\n"
MEMSPAC .blkw 100
.END
This code works and it prints the triangle. However it goes into an infinite loop rather than printing grid size of 3. If i change the code "N .FILL M" to "N .FILL 3" it will print out grid size of 3. Any help would be greatly appreciated. Thanks
I am trying to make a program on ARM that finds the symmetric difference between two sets and stores it in a separate set and I am not sure what I am doing wrong, can somebody help?
Here is what I have:
start
LDR R0, = ASize ;load number of elements in A
LDR R0, [R0]
LDR R1, = BSize ;load number of elements in B
LDR R1, [R1]
LDR R2, = CSize ;load number of elements in C
LDR R2, [R2]
LDR R3, = AElems ;load elements in A
LDR R4, = BElems ;load elements in B
LDR R5, = CElems ;load elements in C
LDR R8, = '?'
while
CMP R0,#0
BEQ endwh
while2
CMP R1,#0
BEQ endwh
LDR R6, [R3]
LDR R7, [R4]
CMP R6,R7
BEQ endwh
STR R7, [R5]
ADD R2,R2,#1
ADD R4,R4,#4
STR R8, [R4]
SUB R1,R1,#1
B while
STR R6, [R5]
ADD R2,R2,#1
STR R8, [R3]
SUB R0,R0,#1
ADD R3,R3,#4
B while2
endwh
stop B stop
I managed to figure it out thanks for the help!
here is the solution I came up with
start
LDR R0, = ASize
LDR R0, [R0]
LDR R1, = BSize
LDR R1, [R1]
LDR R2, = CSize
LDR R2, [R2]
LDR R3, = AElems
LDR R4, = BElems
LDR R5, = CElems
LDR R8, = '?'
while
CMP R0,#0
BEQ endwh1
LDR R6, [R3]
LDR R7, [R4]
CMP R6,R7
BNE endwh2
STR R8, [R3]
STR R8, [R4]
ADD R3,R3,#4
SUB R0,R0,#1
B while
endwh2
CMP R1,#0
BEQ endwh3
ADD R4,R4,#4
SUB R1,R1,#1
B while
endwh3
STR R6, [R5]
ADD R5,R5,#4
ADD R2,R2,#1
ADD R3,R3,#4
SUB R0,R0,#1
LDR R4, = BElems
LDR R1, = BSize
LDR R1, [R1]
B while
endwh1
while2
CMP R1,#0
BEQ endwh
LDR R7, [R4]
CMP R7,#'?'
BEQ endwh4
STR R7, [R5]
ADD R2,R2,#1
ADD R5,R5,#4
ADD R4,R4,#4
SUB R1,R1,#1
B while2
endwh4
ADD R4,R4,#4
SUB R1,R1,#1
B while2
endwh
stop B stop