Bito
Bito

Reputation: 105

arm program stops after STR with post-index offset

I am finishing an assignment in which I need to receive an array by input and return it in reverse. I have asked questions about this assignment before and this time I'm close to finishing it. My issue now is that when I store one value of the array on memory my program stops immediately.

Edit: I pasted the full code now. The str that is failing is highlighted with the annotation HERE IT CRASHES. For this assignment I was given this code without a main code or the variables primero, segundo and tercero, which are strings to be printed as the program goes. Prints and fgets are functions to print to screen and read from stdin.

@ Las siguientes funciones fueron obtenidas del archivo ejemplo "UsefulFunctions.s" presente
@en la pagina oficial de descarga del software ARMSIM#
@(Agradecimientos al desarrollador original de estas funciones: R.N. Horspool)

.global prints, fprints, fgets


@ PROGRAMA
.text
.global main
main:
    ldr r0, =primero
    bl prints
    ldr r0, adr_num                     @where n will be stored
    mov r1, #4                          @buffer for the first number
    mov r2, #0                          @indicates fgets must read from stdin
    bl fgets
    bl atoi                             @turns to int so as to evaluate without caring about a "\n"
    mov r5, r0                          @store de received int
    ldr r6, =arreglo
firstLoop:
    cmp r5, #0
    beq msg
    ldr r0, =segundo
    bl prints 
    ldr r0, adr_num                     @where n will be stored
    mov r1, #4                          @buffer for the first number
    mov r2, #0                          @indicates fgets must read from stdin
    bl fgets
    bl atoi                             @turns to int so as to evaluate without caring about a "\n"
    str r0, [r6], #4                    @--HERE IT CRASHES--store de recived int in register 
    sub r5, r5, #1                      
    b firstLoop
msg:
    ldr r0, =tercero
    bl prints
    ldr r0, [r6], #-4
secondLoop:
    cmp r8, #0
    beq exit
    ldr r0, [r6], #-4                           @move result to register r0 so as to turn int to string
    ldr r1, adr_str                     @enough space is saved so as to store the result of transformation
    bl itoa
    bl prints
    sub r8, r8, #1
    b secondLoop                            


@Memory data address in DATOS

adr_num: .word number       @Here the space for an int is defined (see end of document, section .DATA)
adr_str: .word string           @Here the space for a string is defined (see end of document, section .DATA)

@reading of the first character (n):
ldr r0, adr_num                     @where n will be stored
mov r1, #4                          @buffer for the first number
mov r2, #0                          @indicates fgets must read from stdin
bl fgets
bl atoi                             @turns to int so as to evaluate without caring about a "\n"
mov r5, r0                          @store de received int

mov r0, r5                          @move result to register r0 so as to turn int to string
ldr r1, adr_str                     @enough space is saved so as to store the result of transformation
bl itoa
bl prints

b exit

exit:
mov r0, #0x18
mov r1, #0
swi 0x123456

@ prints: Returns an ASCII string ending in null to stdout
@
@ Abstract use:
@    prints(r0)
@ Inputs:
@    r0: memory address to ASCII string ending in null
@ Resultado:
@    N/A, but string is written to stdout (console)
prints:
    stmfd   sp!, {r0,r1,lr}
    ldr r1, =operands
    str r0, [r1,#4]
    bl  strlen
    str r0, [r1,#8]
    mov r0, #0x0
    str r0, [r1]
    mov r0, #0x05
    swi 0x123456
    ldmfd   sp!, {r0,r1,pc}



@ fgets: read a line of ASCII text from a stream of inputs (open text file or stdin)
@
@ Abstract use:
@    r0 = fgets(r0, r1, r2)
@ Inputs:
@    r0: memory address of a buffer where first line will be stored
@    r1: buffer size (must accommodate an ending character)
@    r2: name of a file to open for input or "0" to read from stdin
@ Resultado:
@    r0: buffer memory address if characters where able to be read or = if    @        characters weren't read by an EOF error.
@        One text line including a terminating linefeed character
@        is read into the buffer, if the buffer is large enough.
@        Otherwise the buffer holds size-1 characters and a null byte.
@        Note: the line stored in the buffer will have only a linefeed
@        (\n) line ending, even if the input source has a DOS line
@        ending (a \r\n pair).
fgets:  stmfd   sp!, {r1-r4,lr}
    ldr r3, =operands
    str r2, [r3]    @ specify input stream
    mov r2, r0
    mov r4, r1
    mov r0, #1
    str r0, [r3,#8] @ to read one character
    mov r1, r3
    mov r3, r2
1:  subs    r4, r4, #1
    ble 3f      @ jump if buffer has been filled
    str r3, [r1,#4]
2:  mov r0, #0x06   @ read operation
    swi 0x123456
    cmp r0, #0
    bne 4f      @ branch if read failed
    ldrb    r0, [r3]
    cmp r0, #'\r'   @ ignore \r char (result is a Unix line)
    beq 2b
    add r3, r3, #1
    cmp r0, #'\n'
    bne 1b
3:  mov r0, #0
    strb    r0, [r3]
    mov r0, r2      @ set success result
    ldmfd   sp!, {r1-r4,pc}
4:  cmp r4, r2
    bne 3b      @ some chars were read, so return them
    mov r0, #0      @ set failure code
    strb    r0, [r2]    @ put empty string in the buffer
    ldmfd   sp!, {r1-r4,pc}

@ strlen: computes the length of a string made form ASCII characters ending in null
@
@ Abstract use:
@    r0 = strlen(r0)
@ Inputs:
@    r0: memory address of an ASCII string ending in null.
@ Resultado:
@    r0: string length (excluding ending byte)
strlen:
    stmfd   sp!, {r1-r3,lr}
    mov r1, #0
    mov r3, r0
1:  ldrb    r2, [r3], #1
    cmp r2, #0
    bne 1b
    sub r0, r3, r0
    ldmfd   sp!, {r1-r3,pc}


@ atoi: turns an ASCII string ending in null to it's int equivalent
@
@ Abstract use:
@    r0 = atoi(r0)
@ Inputs:
@    r0: memory address of an ASCII string ending in null.
@ Resultado:
@    r0: value of te converted int
atoi:
    stmfd   sp!, {r1-r4,lr}
    mov r2, #0      @ holds result
    mov r3, #0      @ set to 1 if a negative number
    mov r4, #10
1:  ldrb    r1, [r0], #1    @ get next char
    cmp r1, #0
    beq 4f
    cmp r1, #' '
    beq 1b
    cmp r1, #'\n' @se añadio la regla para que no procese los '\n'
    beq 1b
    cmp r1, #'-'
    moveq   r3, #1
    ldreqb  r1, [r0], #1
    b   3f
2:  cmp r1, #9
    bgt 4f
    mul r2, r4, r2
    add r2, r2, r1
    ldrb    r1, [r0], #1
3:  subs    r1, r1, #'0'
    bge 2b
4:  cmp r3, #0
    moveq   r0, r2
    mvnne   r0, r2
    ldmfd   sp!, {r1-r4,pc}


    @ itoa: int to ASCII
    @
    @ Abstract use:
    @    r0 = itoa(r0, r1)
    @ Exit parameters:
    @    r0: signed integer
    @    r1: buffer address that is large enough to keep the functions result,   @   @    which will be an ASCII string ending in NULL characterla direccion   @   @    de un buffer suficientemente grande para mantener el
    @ Resultado:
    @    r0: buffers address
    itoa:
        stmfd   sp!, {r1-r7,lr}
        mov r7, r1      @ remember buffer address
        cmp r0, #0      @ check if negative and if zero
        movlt   r2, #'-'
        moveq   r2, #'0'
        strleb  r2, [r1],#1 @ store a '-' symbol or a '0' digit
        beq 3f
        mvnlt   r0, r0
        ldr r3, =4f     @ R3: multiple pointer
        mov r6, #0      @ R6: write zero digits? (no, for leading zeros)
    1:  ldr r4, [r3],#4 @ R4: current power of ten
        cmp r4, #1      @ stop when power of ten < 1
        blt 3f
        mov r5, #0      @ R5: multiples count
    2:  subs    r0, r0, r4  @ subtract multiple from value
        addpl   r5, r5, #1  @ increment the multiples count
        bpl 2b
        add r0, r0, r4  @ correct overshoot
        cmp r5, #0      @ if digit is '0' and ...
        cmpeq   r6, #0      @    if it's a leading zero
        beq 1b      @ then skip it
        mov r6, #1
        add r2, r5, #'0'    @ ASCII code for the digit
        strb    r2, [r1],#1 @ store it
        b   1b
    3:  mov r0, #0
        strb    r0, [r1]
        mov r0, r7
        ldmfd   sp!, {r1-r7,pc}
    4:  .word   1000000000, 100000000, 10000000, 1000000
        .word   100000, 10000, 1000, 100, 10, 1, 0


@ DATOS
    .data
operands: .word 0, 0, 0
string: .space 32                           @buffer for a 32 character string

number: .space 4                            @buffer for a number (n y then k) + ending character
list: .space 4000                           @buffer for a 1000 numbers max list (only 999 will be used as true max)
found: .ascii "Number is in the list."
space: .space 2
unfound: .ascii "Number is not on the list."
primero: .asciz "Enter array length: "
segundo: .asciz "Enter next number of the array: "
tercero: .asciz "Inverted array is: "
arreglo: .word 0, 0, 0                  

If anyone could tell me what am I doing wrong I'd be much obliged. So far I have used the debugger integrated on ARMSim to pin point the line that gives me issues, i tried adding a memory address to r6 so it isn't in 00000000 but it didn't help.

--------EDIT--------- I tried simply storing a number into a register in another file without any other functions and my problem persist, seems I don't know how to use the STR instruction, for example this code stops right in the first STR and doesn't continue:

.data 
array: .word 0

.text
main:
    ldr r1, =array
    mov r2, #4
    str r1, [r2]
    ldr r3, [r1]
    add r3, r3, r3

Upvotes: 0

Views: 516

Answers (2)

Erlkoenig
Erlkoenig

Reputation: 2754

Just for the fun of it, I converted the program to use Linux syscalls:

.syntax unified
.cpu arm1176jzf-s
.arm

@ Las siguientes funciones fueron obtenidas del archivo ejemplo "UsefulFunctions.s" presente
@ en la pagina oficial de descarga del software ARMSIM#
@ (Agradecimientos al desarrollador original de estas funciones: R.N. Horspool)

.global _start


@ PROGRAMA
.text
.type _start, %function
_start:
    ldr r0, =primero
    bl prints
    ldr r0, adr_num                     @where n will be stored
    movs r1, #4                          @buffer for the first number
    movs r2, #0                          @indicates fgets must read from stdin
    bl fgets
    bl atoi                             @turns to int so as to evaluate without caring about a "\n"
    movs r5, r0                          @store de recived int
    mov r8, r0
    cmp r0, #100
    it hi
    bhi exit

    ldr r6, =arreglo
firstLoop:
    cmp r5, #0
    beq msg
    ldr r0, =segundo
    bl prints 
    ldr r0, adr_num                     @where n will be stored
    movs r1, #4                          @buffer for the first number
    movs r2, #0                          @indicates fgets must read from stdin
    bl fgets
    bl atoi                             @turns to int so as to evaluate without caring about a "\n"
    str r0, [r6], #4                    @--HERE IT CRASHES--store de recived int in register 
    sub r5, r5, #1                      
    b firstLoop
msg:
    ldr r0, =tercero
    bl prints

secondLoop:
    cmp r8, #0
    beq exit
    ldr r0, [r6, #-4]!                           @move result to register r0 so as to turn int to string
    ldr r1, adr_str                     @enough space is saved so as to store the result of transformation
    bl itoa
    bl prints
    sub r8, r8, #1
    b secondLoop                            


@Memory data adress in DATOS

adr_num: .word number       @Here the space for an int is defined (see end of document, section .DATA)
adr_str: .word string           @Here the space for a string is defined (see end of document, section .DATA)

.type exit, %function
exit:
    movs r7, #1
    movs r0, #0
    svc #0

@ prints: Returns an ASCII string ending in null to stdout
@
@ Abstract use:
@    prints(r0)
@ Inputs:
@    r0: memory adress to ASCII string ending in null
@ Resultado:
@    N/A, but string is writen to stdout (console)
.type prints, %function
prints:
    stmfd   sp!, {r0-r7,lr}
    bl  strlen

    movs r7, #4
    movs r2, r0
    ldr r1, [sp]
    movs r0, #1
    svc #0

    ldmfd   sp!, {r0-r7,pc}



@ fgets: read a line of ASCII text from a stream of inputs (open text file or stdin)
@
@ Abstract use:
@    r0 = fgets(r0, r1, r2)
@ Inputs:
@    r0: memory adress of a buffer where first line will be stored
@    r1: buffer size (must acomodate an ending character)
@    r2: name of a file to open for input or "0" to read from stdin
@ Resultado:
@    r0: buffer memory adress if characters where able to be read or = if    @        characters wheren't read by an EOF error.
@        One text line including a terminating linefeed character
@        is read into the buffer, if the buffer is large enough.
@        Otherwise the buffer holds size-1 characters and a null byte.
@        Note: the line stored in the buffer will have only a linefeed
@        (\n) line ending, even if the input source has a DOS line
@        ending (a \r\n pair).
.type fgets, %function
fgets:  stmfd   sp!, {r0-r9,lr}
    mov r8, r1  @ Count
    mov r9, r0  @ Buffer
1:  subs    r8, r8, #1
    ble 3f      @ jump if buffer has been filled
2:  

    ldr r0, [sp, #8]    @ File Descriptor
    mov r1, r9          @ Buffer
    movs r2, #1         @ Count

    movs r7, #3 @ read syscall
    svc #0

    cmp r0, #1
    bne 4f      @ branch if read failed
    ldrb    r0, [r9]
    cmp r0, #'\r'   @ ignore \r char (result is a Unix line)
    beq 2b
    adds r9, r9, #1
    cmp r0, #'\n'
    bne 1b
3:  movs r0, #0
    strb    r0, [r9]
    ldmfd   sp!, {r0-r9,pc}
4:  ldr r2, [sp, #4]
    cmp r8, r2
    bne 3b      @ some chars were read, so return them
    movs r0, #0      @ set failure code
    strb    r0, [r9]    @ put empty string in the buffer
    adds sp, #4         @ Skip over saved buffer pointer
    ldmfd   sp!, {r1-r9,pc}

@ strlen: computes the lenght of a string made form ASCII characters ending in null
@
@ Abstract use:
@    r0 = strlen(r0)
@ Inputs:
@    r0: memory adress of an ASCII string enfing in null.
@ Resultado:
@    r0: string lenght (excluding ending byte)
.type strlen, %function
strlen:
    stmfd   sp!, {r1-r2,lr}
    mov r2, r0
1:  ldrb    r1, [r2], #1
    tst r1, r1
    bne 1b
    sub r0, r2, r0
    ldmfd   sp!, {r1-r2,pc}


@ atoi: turns an ASCII string ending in null to it's int equivalent
@
@ Abstract use:
@    r0 = atoi(r0)
@ Inputs:
@    r0: memory adress of an ASCII string ending in null.
@ Resultado:
@    r0: value of te converted int
.type atoi, %function
atoi:
    stmfd   sp!, {r1-r4,lr}
    movs r2, #0      @ holds result
    movs r3, #0      @ set to 1 if a negative number
    movs r4, #10
1:  ldrb    r1, [r0], #1    @ get next char
    cmp r1, #0
    beq 4f
    cmp r1, #' '
    beq 1b
    cmp r1, #'\n' @se añadio la regla para que no procese los '\n'
    beq 1b
    cmp r1, #'-'
    itt eq
    moveq   r3, #1
    ldrbeq  r1, [r0], #1
    b   3f
2:  cmp r1, #9
    bgt 4f
    mul r2, r4, r2
    add r2, r2, r1
    ldrb    r1, [r0], #1
3:  subs    r1, r1, #'0'
    bge 2b
4:  cmp r3, #0
    ite eq
    moveq   r0, r2
    rsbne   r0, r2, #0
    ldmfd   sp!, {r1-r4,pc}


    @ itoa: int to ASCII
    @
    @ Abstract use:
    @    r0 = itoa(r0, r1)
    @ Exit parameters:
    @    r0: signed integer
    @    r1: buffer adress that is large enough to keep the functions result,   @   @    which will be an ASCII string ending in NULL characterla direccion   @   @    de un buffer suficientemente grande para mantener el
    @ Resultado:
    @    r0: buffers adress
    itoa:
        stmfd   sp!, {r1-r7,lr}
        mov r7, r1      @ remember buffer address
        cmp r0, #0      @ check if negative and if zero
        it lt
        movlt   r2, #'-'
        it eq
        moveq   r2, #'0'
        it le
        strble  r2, [r1],#1 @ store a '-' symbol or a '0' digit
        beq 3f
        it lt
        rsblt   r0, r0, #0
        ldr r3, =4f     @ R3: multiple pointer
        movs r6, #0      @ R6: write zero digits? (no, for leading zeros)
    1:  ldr r4, [r3],#4 @ R4: current power of ten
        cmp r4, #1      @ stop when power of ten < 1
        blt 3f
        movs r5, #0      @ R5: multiples count
    2:  subs    r0, r0, r4  @ subtract multiple from value
        itt pl
        addpl   r5, r5, #1  @ increment the multiples count
        bpl 2b
        add r0, r0, r4  @ correct overshoot
        cmp r5, #0      @ if digit is '0' and ...
        itt eq
        cmpeq   r6, #0      @    if it's a leading zero
        beq 1b      @ then skip it
        movs r6, #1
        add r2, r5, #'0'    @ ASCII code for the digit
        strb    r2, [r1],#1 @ store it
        b   1b
    3:  movs r0, #0
        strb    r0, [r1]
        movs r0, r7
        ldmfd   sp!, {r1-r7,pc}
    4:  .word   1000000000, 100000000, 10000000, 1000000
        .word   100000, 10000, 1000, 100, 10, 1, 0


@ DATOS
    .data
.align 4
.type string, %object
string: .space 32                           @buffer for a 32 character string

.type number, %object
number: .space 4                            @buffer for a number (n y then k) + ending character

.type list, %object
list: .space 4000                           @buffer for a 1000 numbers max list (only 999 will be used as true max)

.type found, %object
found: .ascii "NUmber is in the list."

.align 4

.type unfound, %object
unfound: .ascii "Number is not on the list."

.align 4

.type primero, %object
primero: .asciz "Enter array lenght: "

.align 4

.type segundo, %object
segundo: .asciz "Enter next number of the array: "

.align 4

.type tercero, %object
tercero: .asciz "Inverted array is: "

.align 4

.type arreglo, %object
arreglo: .space 100*4

On an x86 desktop PC with Ubuntu or Debian Linux, you can install the cross-assembler, -linker, -debugger and qemu via:

sudo apt-get install binutils-arm-linux-gnueabihf qemu-user gdb-multiarch

And then assemble, link and run it via:

arm-linux-gnueabihf-as -g test.S -o test.o && arm-linux-gnueabihf-ld test.o -o test && qemu-arm ./test

By running with qemu-arm -g 1234 ./test you can then debug it via GDB:

$ gdb-multiarch ./test
(gdb) target extended-remote :1234
Remote debugging using :1234
_start () at test.S:16
16      ldr r0, =primero
(gdb) stepi
...

On an ARM-based SBC such as Raspberry PI, you can test it by installing binutils and then:

as -g test.S -o test.o && ld test.o -o test && ./test

Upvotes: 1

Bito
Bito

Reputation: 105

Thanks to Erlkoeing's input in this issue i was able to finish the program. In the end i was using STR the wrong way. After i changed that and the way I moved across the array plus defining a space for 100 characters in the array like this:

.balign 4
array: .skip 400

i stored the array's adress in a register, then added an offset made by multiplying an index by 4. Thank all of you for your time

Upvotes: 0

Related Questions