0

I need to print a floating-point (VFP) number to screen for an assignment. I have been given prints and fgets functions to handle standard input and output but this only works for integers or strings. I have been asked (by my professor) to use ARMSim for debugging for this class so I'm limited to ARMv5 (don't ask why, beacause i don't know). I don't know too much about arm language, please bear that in mind.

Here is my code with a simple main:

.global prints, fprints, fgets


@ PROGRAM
.text
.global main
main:
    ldr r0, =euler    
    vldr s0, [r0]        @load 2.718 into s0
    vmov.f32 r0, s0      @result is moved to r0 so as to turn it to string
    ldr r1, adr_str      @enough space is saved up to store the transformation to string result
    bl itoa
    bl prints               
    b exit

@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)

@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 recived 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 adress to ASCII string ending in null
@ Resultado:
@    N/A, but string is writen 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 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).
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 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)
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 adress 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 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
        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
euler: .float 2.718
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."    

As you can see i tried directly puting the floating-point value into an arm register but it didn't work. If anyone could tell me what i need to change it would be amazing. Thanks.

Bito
  • 75
  • 7
  • 1
    Handling floating point is a tricky business. As a quick hack you could separate the integer and the fractional part and print them using the integer print function with a dot between them. Also be careful with leading zeroes. – Jester Mar 23 '20 at 02:11
  • Related: [How to input and output real numbers in assembly language](https://stackoverflow.com/q/47536322) has some details about how float <-> decimal string is implemented. Also [Turn float into string](https://stackoverflow.com/q/17632150) describes the naive algorithms you might use if you don't care about perfect results in corner cases. – Peter Cordes Mar 23 '20 at 08:26
  • @Jester that would actually be just what i need, but how can i separate the integer and fractional part? – Bito Mar 23 '20 at 17:41
  • 2
    Try using `vcvt` to convert to integer, convert that back to float, then subtract that from the original number to get the fraction, then multiply that by however many decimal places you want finally convert that to integer too. – Jester Mar 23 '20 at 17:57

1 Answers1

1

Thanks to Jester for the idea. I ended up doing just what he comented, but in the case of leading zeroes i made sure to print a zero every time te decimal part was less thank one:

main:
    ldr r0, =euler              @en .data esta el valor euler
    vldr s0, [r0]               @cargo euler = S0

    vcvt.u32.f32 s1, s0         @convierto euler a entero = S1
    vmov.f32 r0, s1             @lo paso a R0 para imprimir
    ldr r1, adr_str             @*
    bl itoa                     @*
    bl prints                   @*
    ldr r0, =dot                @cargo el punto decimal que esta en punto data
    bl prints                   @*
    vcvt.f32.u32 s1, s1         @convierto la parte entera de euler de nuevo a flotante S1
    vsub.f32 s2, s0, s1         @le quito la parte entera a euler = S2
    ldr r0, =decExp             @en .data esta el valor decExp
    vldr s3, [r0]               @cargo 100 para obtener los decimales 
    vmul.f32 s2, s2, s3         @multiplico los decimales por 1000 = S2
    vcvt.u32.f32 s3, s2         @transformo los "decimales" a entero = S3
    vmov.f32 r0, s3             @lo muevo a R0 para imprimir
    ldr r1, adr_str             @*
    bl itoa                     @*
    bl prints                   @*
    b exit                      @*

the coments are in spanish because they are for the homework, but it's pretty basic spanish so you can put them in a translator. The ldr r1, adr_str is part of the method to print with the prints function i have, if you have a diferent one then you don't need that part, so if you are going to use this just replace the parts that are comented with an *

Bito
  • 75
  • 7