levi
Still fresh, damnit!
Ah, quite right, I'd missed that. So you're not printing out the number at all yet?
I'll check out this.
Meanwhile, my code worked when I replaced div reciprocal with :
MOV R2, #0x19000000 ; R2 is constantly 0x1999999A
ORR R2, R2, #0x00990000
ORR R2, R2, #0x00009900
ORR R2, R2, #0x0000009A
taken from here :
http://www.toves.org/books/arm/
Hmm, in this code you load 0x000009A into R2. In your original code you loaded a pointer to some memory containing 0x19999D1E into R10. I don't understand the umull instruction as I've said before, so I can't say which number is more correct, if either, but in one case you use a number, in the other an address.
LDR R10, [div_10_reciprocal]
Hmm, in this code you load 0x199999A into R2. In your original code you loaded a pointer to some memory containing 0x19999D1E into R10. I don't understand the umull instruction as I've said before, so I can't say which number is more correct, if either, but in one case you use a number, in the other an address.
Edit: As Yoyobuae said.
umull r3, r2, r5, r10 @ r2 = r5 / 10, r3 is overwritten
mov r5, r2 @ save the divided value
umull r3, r5, r5, r10 @ r5 = r5 / 10, r3 is overwritten
However, I note that the reciprocal you're using already rounds up the number (2^32/10 comes out as a number 0x19999999.6), and in my tests, using 0x1999999A rather than 0x9999999 seems to work for both 1000000 and 999999, providing you always round down (which discarding to lowest 32 bits does, in practice). So perhaps my test to round to nearest isn't actually needed in practice. It provides the wrong result with 1 followed by 11 zeroes, but the wrong answer still has the right number of digits.
That said, 1 followed by 10 zeroes timed by 0x19999999 divided by 2^32 gives you 999999998.6..., so even if you round that to nearest, you're out by one the other way. Not surprising I suppose, as 2^32 maths only gives you 9 and a half significant digits accuracy.
(a * reciprocal_b) - (a / b) < 1
reciprocal_b - (a / b) < (1 / a)
The error must be < 1 / a. With the format I used reciprocal_b has 0 integer bits and 32 fractional bits, so the positive error is < (1 / (2^32)). So long as a < (2^32) (which is the limit of the number format) this will hold.
With reciprocal_b=0x1999999A, the assumption:
(a * reciprocal_b) - (a / b) < 1
Fails for a=0x40000005 (1073741829) and other higher numbers ending in 9 decimal digit. For much higher numbers it starts failing for the values of a ending in 7 or 8 decimal digit.
Works perfectly for values of a under 30 bits (0x0 to 0x3FFFFFFF) though.
.text
.align 2
.macro syswrite @ print what r1 points at, must be ascii, buffer length r2 must be set
mov r0, #1 @ file descriptor 1 - stdout
svc 0 @ call the Linux kernel and print the ascii text
.endm
.macro newline
ldr r1, =newline @ point on the buffer to write, \n, a newline
mov r2, #1 @ newline buffer length
syswrite
.endm
.global _start
_start:
mov r7, #4 @ system call number, 4 is 'write'
mov r5, #8192 @ set the number
mov r11, #0 @ set the digit counter
_loop:
add r11, #1 @ increment the digit counter
cmp r5, #10 @ compare the number with 10
blt _end @ branch if < 10
mov r8, r5
ldr r10, =div_10_reciprocal @ load the magic number for /10
ldr r10, [r10]
umull r3, r2, r5, r10 @ r2 = r5 / 10, r3 is overwritten
mov r5, r2 @ save the divided value
mov r10, #10
mul r9, r2, r10 @ multiply the divided value by 10
sub r8, r9 @ substract the original value to get the remainder
push {r8} @ push the remainder on the stack
b _loop @ back to loop
_end:
mov r8, r5 @ push the last digit, with the highest weight
push {r8}
ldr r1, =asciicounter @ point on =asciicounter
add r6, r11, #48 @ convert the digit counter to ascii
str r6, [r1] @ store it to be displayed
mov r2, #1 @ buffer length
syswrite
newline
_reversed:
pop {r8} @ display the number, beginning with the highest weight
ldr r1, =asciicounter @ point on =asciicounter
mov r6, r8
add r6, #48 @ convert the number to ascii
str r6, [r1] @ store it to be displayed
mov r2, #1 @ buffer length
syswrite
subs r11, #1 @ the digit counter is now used for the number display loop
bne _reversed
newline
mov r7, #1 @ exit
svc 0 @ call the Linux kernel and exit
.data
asciicounter: .word 0 @ must be initialized
newline: .ascii "\n" @ \n is a newline
div_10_reciprocal: .word 429496730 @ ceil((2^32)/10)
mov r10, #10
mul r9, r2, r10 @ multiply the divided value by 10