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.
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 the decimal part was less than 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 *