Invalid pointer error in insert sort in ARM assembly
I am trying to translate a simple insert-to-assembly sorting algorithm, but something about this particular configuration is throwing a program error invalid pointer
.
Here's the C version 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 the C structure that is used:
typedef struct {
int *list;
int size;
int maxSize;
} list;
Here is my build 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 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 that
str r8, [r0, r10] @ array[d-1] = t
which triggers a trip at some point.
Edit: I found that the r8 numbers during this command are incorrect, since immediately using something like
mov r8, #4
before the store will prevent the error (but of course makes the results incorrect).
When checking the contents of r0, it happens that the update is out of range because other members of the structure are changed in the process. Array index 0 is +16.
source to share
You found a problem in translation to assembly. Note, however, the following issues:
-
The outer loop should run before
c < n
insteadc < n - 1
. As coded, the last element of the array is never moved. -
it would be more readable to use 2 nested loops
for
: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; } }
source to share
Everyone has a different approach to coding. Mine is different from yours, but I would like to share my ideas. I would start with as simple work as possible in order to work something and build from there. Here is some sample code for 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
Build and wire the code and run it and check the completion status.
as -o forloop.o forloop.s
gcc -o forloop forloop.o
./forloop; echo $?
This works for me on Raspberry Pi. I don't know much about gdb
, but it might help, as Jetter suggested. (See the Commands section of http://cs107e.github.io/guides/gdb/ for more details .)
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 case cpsr
. The number on the left is the flags 8=Negative, 6=Zero&Carry, 4=Zero, 2=Carry, 1=oVerflow
.
Another approach to debugging a build program on hand is to use the linux printf command. Here's 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 is to put your code together C
and use gdb
to see how the C code is in assembly. These were interesting projects, I was not aware of insert sort.
source to share