// 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 ; cdq ; // [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 ; } } // Same as __U64_LDBL, but return result as double in [EDX,EAX] ulong __ULLNGDBL() { asm { naked ; call __U64_LDBL ; sub ESP,8 ; fstp double ptr [ESP] ; pop EAX ; pop EDX ; 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 ; } }