phobos/internal/llmath.d
2007-09-10 04:28:31 +00:00

280 lines
4.7 KiB
D

// llmath.d
// Copyright (C) 1993-2003 by Digital Mars, www.digitalmars.com
// All Rights Reserved
// Written by Walter Bright
// Compiler runtime support for 64 bit longs
extern (C):
/***************************************
* Unsigned long divide.
* Input:
* [EDX,EAX],[ECX,EBX]
* Output:
* [EDX,EAX] = [EDX,EAX] / [ECX,EBX]
* [ECX,EBX] = [EDX,EAX] % [ECX,EBX]
* ESI,EDI destroyed
*/
void __ULDIV__()
{
asm
{
naked ;
test ECX,ECX ;
jz uldiv ;
push EBP ;
// left justify [ECX,EBX] and leave count of shifts + 1 in EBP
mov EBP,1 ; // at least 1 shift
test ECX,ECX ; // left justified?
js L1 ; // yes
jnz L2 ;
add EBP,8 ;
mov CH,CL ;
mov CL,BH ;
mov BH,BL ;
xor BL,BL ; // [ECX,EBX] <<= 8
test ECX,ECX ;
js L1 ;
even ;
L2: inc EBP ; // another shift
shl EBX,1 ;
rcl ECX,1 ; // [ECX,EBX] <<= 1
jno L2 ; // not left justified yet
L1: mov ESI,ECX ;
mov EDI,EBX ; // [ESI,EDI] = divisor
mov ECX,EDX ;
mov EBX,EAX ; // [ECX,EBX] = [EDX,EAX]
xor EAX,EAX ;
cwd ; // [EDX,EAX] = 0
even ;
L4: cmp ESI,ECX ; // is [ECX,EBX] > [ESI,EDI]?
ja L3 ; // yes
jb L5 ; // definitely less than
cmp EDI,EBX ; // check low order word
ja L3 ;
L5: sub EBX,EDI ;
sbb ECX,ESI ; // [ECX,EBX] -= [ESI,EDI]
stc ; // rotate in a 1
L3: rcl EAX,1 ;
rcl EDX,1 ; // [EDX,EAX] = ([EDX,EAX] << 1) + C
shr ESI,1 ;
rcr EDI,1 ; // [ESI,EDI] >>= 1
dec EBP ; // control count
jne L4 ;
pop EBP ;
ret ;
div0: mov EAX,-1 ;
cwd ; // quotient is -1
// xor ECX,ECX ;
// mov EBX,ECX ; // remainder is 0 (ECX and EBX already 0)
pop EBP ;
ret ;
uldiv: test EDX,EDX ;
jnz D3 ;
// Both high words are 0, we can use the DIV instruction
div EBX ;
mov EBX,EDX ;
mov EDX,ECX ; // EDX = ECX = 0
ret ;
even ;
D3: // Divide [EDX,EAX] by EBX
mov ECX,EAX ;
mov EAX,EDX ;
xor EDX,EDX ;
div EBX ;
xchg ECX,EAX ;
div EBX ;
// ECX,EAX = result
// EDX = remainder
mov EBX,EDX ;
mov EDX,ECX ;
xor ECX,ECX ;
ret ;
}
}
/***************************************
* Signed long divide.
* Input:
* [EDX,EAX],[ECX,EBX]
* Output:
* [EDX,EAX] = [EDX,EAX] / [ECX,EBX]
* [ECX,EBX] = [EDX,EAX] % [ECX,EBX]
* ESI,EDI destroyed
*/
void __LDIV__()
{
asm
{
naked ;
test EDX,EDX ; // [EDX,EAX] negative?
jns L10 ; // no
//neg64 EDX,EAX ; // [EDX,EAX] = -[EDX,EAX]
neg EDX ;
neg EAX ;
sbb EDX,0 ;
test ECX,ECX ; // [ECX,EBX] negative?
jns L11 ; // no
//neg64 ECX,EBX ;
neg ECX ;
neg EBX ;
sbb ECX,0 ;
call __ULDIV__ ;
//neg64 ECX,EBX ; // remainder same sign as dividend
neg ECX ;
neg EBX ;
sbb ECX,0 ;
ret ;
L11: call __ULDIV__ ;
//neg64 ECX,EBX ; // remainder same sign as dividend
neg ECX ;
neg EBX ;
sbb ECX,0 ;
//neg64 EDX,EAX ; // quotient is negative
neg EDX ;
neg EAX ;
sbb EDX,0 ;
ret ;
L10: test ECX,ECX ; // [ECX,EBX] negative?
jns L12 ; // no (all is positive)
//neg64 ECX,EBX ;
neg ECX ;
neg EBX ;
sbb ECX,0 ;
call __ULDIV__ ;
//neg64 EDX,EAX ; // quotient is negative
neg EDX ;
neg EAX ;
sbb EDX,0 ;
ret ;
L12: jmp __ULDIV__ ;
}
}
/***************************************
* Compare [EDX,EAX] with [ECX,EBX]
* Signed
* Returns result in flags
*/
void __LCMP__()
{
asm
{
naked ;
cmp EDX,ECX ;
jne C1 ;
push EDX ;
xor EDX,EDX ;
cmp EAX,EBX ;
jz C2 ;
ja C3 ;
dec EDX ;
pop EDX ;
ret ;
C3: inc EDX ;
C2: pop EDX ;
C1: ret ;
}
}
// Convert ulong to real
private real adjust = cast(real)0x800_0000_0000_0000 * 0x10;
real __U64_LDBL()
{
asm
{ naked ;
push EDX ;
push EAX ;
and dword ptr 4[ESP], 0x7FFFFFFF ;
fild qword ptr [ESP] ;
test EDX,EDX ;
jns L1 ;
fld real ptr adjust ;
faddp ST(1), ST ;
L1: ;
add ESP, 8 ;
ret ;
}
}
// Convert double to ulong
private short roundTo0 = 0xFBF;
ulong __DBLULLNG()
{
// BUG: should handle NAN's and overflows
asm
{ naked ;
push EDX ;
push EAX ;
fld double ptr [ESP] ;
sub ESP,8 ;
fld real ptr adjust ;
fcomp ;
fstsw AX ;
fstcw 8[ESP] ;
fldcw roundTo0 ;
sahf ;
jae L1 ;
fld real ptr adjust ;
fsubp ST(1), ST ;
fistp qword ptr [ESP] ;
pop EAX ;
pop EDX ;
fldcw [ESP] ;
add ESP,8 ;
add EDX,0x8000_0000 ;
ret ;
L1: ;
fistp qword ptr [ESP] ;
pop EAX ;
pop EDX ;
fldcw [ESP] ;
add ESP,8 ;
ret ;
}
}
// Convert double in ST0 to uint
uint __DBLULNG()
{
// BUG: should handle NAN's and overflows
asm
{ naked ;
sub ESP,16 ;
fstcw 8[ESP] ;
fldcw roundTo0 ;
fistp qword ptr [ESP] ;
fldcw 8[ESP] ;
pop EAX ;
add ESP,12 ;
ret ;
}
}