Rewrote the x86 64-bit division stubs, which now actually work.

This commit is contained in:
Jonas 'Sortie' Termansen 2011-12-04 19:02:35 +01:00
parent a2dd8be7aa
commit de70b1804d
1 changed files with 37 additions and 56 deletions

View File

@ -31,76 +31,57 @@ extern "C" void __cxa_pure_virtual()
#ifdef PLATFORM_X86 #ifdef PLATFORM_X86
// TODO: These two stubs are buggy! extern "C" uint64_t __udivdi3(uint64_t a, uint64_t b)
// Divides two 64-bit integers in O(logN). I think.
extern "C" uint64_t __udivdi3(uint64_t A, uint64_t B)
{ {
uint64_t ACC = 0; uint64_t result = 0;
uint64_t R = 0; uint64_t power = 1;
uint64_t PWR = 1; uint64_t remainder = a;
uint64_t EXP = B; uint64_t divisor = b;
while ( divisor * 2 <= remainder )
while ( EXP <= A - ACC )
{ {
R += PWR; power *= 2;
ACC += EXP; divisor *= 2;
}
if ( 2 * EXP <= A - ACC ) while ( divisor <= remainder )
{
remainder -= divisor;
result += power;
while ( power > 1 && remainder < divisor )
{ {
PWR++; divisor /= 2;
EXP *= 2; power /= 2;
}
else if ( A - ACC < B )
{
break;
}
else
{
while ( A - ACC < EXP )
{
PWR--;
EXP /= 2;
}
} }
} }
return R; return result;
} }
// Mods two 64-bit integers in O(logN). I think. extern "C" uint64_t __umoddi3(uint64_t a, uint64_t b)
extern "C" uint64_t __umoddi3(uint64_t A, uint64_t B)
{ {
uint64_t ACC = 0; uint64_t result = 0;
uint64_t R = 0; uint64_t power = 1;
uint64_t PWR = 1; uint64_t remainder = a;
uint64_t EXP = B; uint64_t divisor = b;
while ( divisor * 2 <= remainder )
while ( EXP <= A - ACC )
{ {
R += PWR; power *= 2;
ACC += EXP; divisor *= 2;
}
if ( 2 * EXP <= A - ACC ) while ( divisor <= remainder )
{
remainder -= divisor;
result += power;
while ( power > 1 && remainder < divisor )
{ {
PWR++; divisor /= 2;
EXP *= 2; power /= 2;
}
else if ( A - ACC < B )
{
break;
}
else
{
while ( A - ACC < EXP )
{
PWR--;
EXP /= 2;
}
} }
} }
return A - ACC; return remainder;
} }
#endif #endif