Apparently, division has problems too. Here are the failed scenarios:
X/10 -> Results in -1 for all negative X
X/(-10) -> Results in -1 for all negative X, 0 for all positive X
N/X -> Internal compiler error for all N
The compiler error looks like it will be a pain to track down, so I'm looking at the incorrect math first.
Here's a simple function I'm using for testing, assume a=-100, b=10:
# int divide(int a, int b) {return a/b;}
divide: # R1=0xFF9B R2=0xA
mov r1, r4 # R4=0xFF9B
seto r3 # R3=0xFFFF
jlt $+>4
clr r3
div r2, r3 # [R3,R4]/R2 = 0xFFFFFF9B/0xA
mov r3, r1 # R3 holds quotient
b *r11
If DIV does signed division, we should see a result of 0xFFF5 (-10)
If DIV only does unsigned division, we should have a result of 0x998F. For some reason we see 0xFFFF, weird.
Nope, not weird, I just missed a really important note in the data sheet: "... if the divisor is less that the data contained in Rd ... Rd and Rd+1 are unchanged." I think this is the second time I've really messed up division. Anyway, DIV apparently only does unsigned division, so I need to think about this a bit.
div_unsigned: # return A/B, R1=A, R2=B
clr r0 # Clear high word of [R0,R1]
div r2, r0 # R0 = [R0,R1]/R2
mov r0, r1 # Move result to return position
b *r11 # Return result
Probably the best solution here is to find the sign of the quotient, convert both operands to their absolute value, divide, then set the sign of the result. No other approach I've looked at would be smaller or faster than this.
div_signed: # return A/B, R1=A, R2=B
mov r1, r3
xor r2, r3 # Calculate sign of result
abs r1 # Make arguments positive
abs r2
clr r0 # Clear high word
div r2, r0 # R0 = [R0,R1]/R2
inv r3
jlt posval
neg r0 # Negate result if necessary
posval: mov r0, r1 # Move result to return position
b *r11 # Return result
22 bytes
OK, what about modulo? As of the C 1999 and C++ 2011 standards, the sign of the modulo must match that of the dividend.
Examples:
103% 10 = 3
103%-10 = 3
-103% 10 = -3
-103%-10 = -3
For The unsigned case, we can easily modify the division function by returning the modulo stored in R1. For the signed case, it gets trickier. We need to save the sign of the dividend somewhere and use it after the calculation is complete.
mod_unsigned: # return A%B, R1=A, R2=B
clr r0 # Clear high word of [R0,R1]
div r2, r0 # R1 = [R0,R1]%R2, return value already in position
b *r11 # Return result
mod_signed: # return A/B, R1=A, R2=B
mov r1, r3 # Save unmodified dividend
abs r1 # Make arguments positive
abs r2
clr r0 # Clear high word
div r2, r0 # R1 = [R0,R1]%R2
inv r3
jlt posval
neg r1 # Negate result if needed, already in position
posval: b *r11 # Return result
18 bytes
Any way to do a signed divmod, combining these two? Without lots of extra work?
divmod_signed: # return A/B and A%B, R1=A, R2=B
mov r1, r3
xor r2, r3 # Calculate sign of quotient
mov r1, r4 # Save unmodified dividend
abs r1 # Make arguments positive
abs r2
clr r0 # Clear high word
div r2, r0 # R0 = [R0,R1]/R2
inv r3
jlt posdiv
neg r0 # Negate quotient if needed
posdiv: inv r4
jlt posmod
neg r1 # Negate modulus if needed
posmod: mov r0, r1 # Move result to return position
b *r11 # Return result
30 bytes, 5 registers
This is really bulky and ugly, but ultimately better than two separate calls. About 25% smaller and maybe 75% faster. It might make sense to move this into a library call because if there is a lot of math, the code could get really big. The overhead for the function call seems fairly small too. need to do the math and get real numbers though.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment