/*++ Copyright (c) 1995-1998 Microsoft Corporation Module Name: fpufprem.c Abstract: Floating point remainder fragments (FPREM, FPREM1) Author: 04-Oct-1995 BarryBo Revision History: --*/ #include #include #include #include #include #include #include #include #include "wx86.h" #include "fragp.h" #include "fpufrags.h" #include "fpufragp.h" // // Forward references // NPXFUNC2(FPREM_VALID_VALID); NPXFUNC2(FPREM_VALID_ZERO); NPXFUNC2(FPREM_VALID_SPECIAL); NPXFUNC2(FPREM_ZERO_VALIDORZERO); NPXFUNC2(FPREM_ZERO_SPECIAL); NPXFUNC2(FPREM_SPECIAL_VALIDORZERO); NPXFUNC2(FPREM_SPECIAL_SPECIAL); NPXFUNC2(FPREM_EMPTY_ANY); NPXFUNC2(FPREM_ANY_EMPTY); NPXFUNC2(FPREM1_VALID_VALID); //NPXFUNC2(FPREM1_VALID_ZERO); // same as FPREM_VALID_ZERO NPXFUNC2(FPREM1_VALID_SPECIAL); //NPXFUNC2(FPREM1_ZERO_VALIDORZERO); // same as FPREM_ZERO_VALIDORZERO NPXFUNC2(FPREM1_ZERO_SPECIAL); NPXFUNC2(FPREM1_SPECIAL_VALIDORZERO); NPXFUNC2(FPREM1_SPECIAL_SPECIAL); NPXFUNC2(FPREM1_EMPTY_ANY); NPXFUNC2(FPREM1_ANY_EMPTY); // // Jump tables // const NpxFunc2 FPREMTable[TAG_MAX][TAG_MAX] = { // left is TAG_VALID, right is ... { FPREM_VALID_VALID, FPREM_VALID_ZERO, FPREM_VALID_SPECIAL, FPREM_ANY_EMPTY }, // left is TAG_ZERO, right is ... { FPREM_ZERO_VALIDORZERO, FPREM_ZERO_VALIDORZERO, FPREM_ZERO_SPECIAL, FPREM_ANY_EMPTY }, // left is TAG_SPECIAL, right is ... { FPREM_SPECIAL_VALIDORZERO, FPREM_SPECIAL_VALIDORZERO, FPREM_SPECIAL_SPECIAL, FPREM_ANY_EMPTY }, // left is TAG_EMPTY, right is ... { FPREM_EMPTY_ANY, FPREM_EMPTY_ANY, FPREM_EMPTY_ANY, FPREM_EMPTY_ANY } }; const NpxFunc2 FPREM1Table[TAG_MAX][TAG_MAX] = { // left is TAG_VALID, right is ... { FPREM1_VALID_VALID, FPREM_VALID_ZERO, FPREM1_VALID_SPECIAL, FPREM1_ANY_EMPTY }, // left is TAG_ZERO, right is ... { FPREM_ZERO_VALIDORZERO, FPREM_ZERO_VALIDORZERO, FPREM1_ZERO_SPECIAL, FPREM1_ANY_EMPTY }, // left is TAG_SPECIAL, right is ... { FPREM1_SPECIAL_VALIDORZERO, FPREM1_SPECIAL_VALIDORZERO, FPREM1_SPECIAL_SPECIAL, FPREM1_ANY_EMPTY }, // left is TAG_EMPTY, right is ... { FPREM1_EMPTY_ANY, FPREM1_EMPTY_ANY, FPREM1_EMPTY_ANY, FPREM1_EMPTY_ANY } }; NPXFUNC2(FPREM_VALID_VALID) { int ExpL; int ExpR; int ExpDiff; LONG Q; double DQ; ExpL = (int)((l->rdw[1] >> 20) & 0x7ff) - 1023; ExpR = (int)((r->rdw[1] >> 20) & 0x7ff) - 1023; ExpDiff = abs(ExpL-ExpR); if (ExpDiff < 64) { // Do the division and chop the integer result towards zero DQ = r->r64 / l->r64; if (DQ < 0) { Q = (long)ceil(DQ); } else { Q = (long)floor(DQ); } // Store the remainder r->r64 -= (DOUBLE)Q * l->r64; SetTag(r); // Store the status bits if (Q < 0) { // // Take the absolute value of Q before returning the low 3 bits // of the quotient. // Q = -Q; } cpu->FpStatusC2 = 0; // indicate the final remainder is ready cpu->FpStatusC0 = (Q>>2) & 1; cpu->FpStatusC3 = (Q>>1) & 1; cpu->FpStatusC1 = Q & 1; } else { DOUBLE PowerOfTwo; cpu->FpStatusC2 = 1; // indicate the app must loop more PowerOfTwo = ldexp(1.0, ExpDiff-32); // get 2^(ExpDiff-32) // get Q by chopping towards zero DQ = (r->r64/PowerOfTwo) / (l->r64/PowerOfTwo); if (DQ < 0) { Q = (long)ceil(DQ); } else { Q = (long)floor(DQ); } r->r64 -= (DOUBLE)Q * l->r64 * PowerOfTwo; SetTag(r); } } NPXFUNC2(FPREM_VALID_ZERO) { // l is a number, but r is zero - return ST(0) unchanged cpu->FpStatusC2 = 0; // indicate the final remainder is ready // Q is 0, so store low 3 bits in the status word cpu->FpStatusC0 = 0; cpu->FpStatusC1 = 0; cpu->FpStatusC3 = 0; } NPXFUNC2(FPREM_VALID_SPECIAL) { switch (l->TagSpecial) { case TAG_SPECIAL_DENORM: FPREM_VALID_VALID(cpu, l, r); break; case TAG_SPECIAL_INFINITY: // Dividing infinity. SetIndefinite(r); break; case TAG_SPECIAL_SNAN: if (HandleSnan(cpu, r)) { return; } // else fall into QNAN case case TAG_SPECIAL_QNAN: case TAG_SPECIAL_INDEF: // r is the destination and it is a QNAN, while l is a VALID. Return // the QNAN as the result of the operation // x86 emulator leaves condition flags alone break; } } NPXFUNC2(FPREM_ZERO_VALIDORZERO) { // l is zero, and r is a number or zero - return INDEFINITE due to the // division by zero. if (!HandleInvalidOp(cpu)) { SetIndefinite(r); } } NPXFUNC2(FPREM_ZERO_SPECIAL) { if (r->TagSpecial == TAG_SPECIAL_INFINITY) { SetIndefinite(r); } else { FPREM_VALID_SPECIAL(cpu, l, r); } } NPXFUNC2(FPREM_SPECIAL_VALIDORZERO) { switch (l->TagSpecial) { case TAG_SPECIAL_DENORM: FPREM_VALID_VALID(cpu, l, r); break; case TAG_SPECIAL_INFINITY: // number / infinity - quotient == 0 cpu->FpStatusC2 = 0; cpu->FpStatusC0 = 0; cpu->FpStatusC1 = 0; cpu->FpStatusC3 = 0; break; case TAG_SPECIAL_SNAN: if (HandleSnan(cpu, l)) { return; } // else fall into QNAN case case TAG_SPECIAL_QNAN: case TAG_SPECIAL_INDEF: // r is the destination and it is a VALID, while l is a NAN. Return // the NAN as the result of the operation r->r64 = l->r64; r->Tag = l->Tag; r->TagSpecial = l->TagSpecial; // x86 emulator leaves condition flags alone break; } } NPXFUNC2(FPREM_SPECIAL_SPECIAL) { if (l->TagSpecial == TAG_SPECIAL_DENORM) { FPREM_VALID_SPECIAL(cpu, l, r); return; } if (r->TagSpecial == TAG_SPECIAL_DENORM) { FPREM_SPECIAL_VALIDORZERO(cpu, l, r); } if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) { return; } if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) { return; } if (l->TagSpecial == TAG_SPECIAL_INFINITY) { if (r->TagSpecial == TAG_SPECIAL_INFINITY) { SetIndefinite(r); } // // r is a NAN of some sort, and l is infinity - return the NAN // which is already in r. // } else { // // l is a NAN, and r is either a NAN or INFINITY. Have the native // FPU return the largest NAN, and re-tag it as appropriate. // r->r64 = l->r64 + r->r64; SetTag(r); } } NPXFUNC2(FPREM_EMPTY_ANY) { if (HandleStackEmpty(cpu, l)) { return; } (*FPREMTable[l->Tag][r->Tag])(cpu, l, r); } NPXFUNC2(FPREM_ANY_EMPTY) { if (HandleStackEmpty(cpu, l)) { return; } (*FPREMTable[l->Tag][r->Tag])(cpu, l, r); } FRAG0(FPREM) { // get remainder of r/l PFPREG l = &cpu->FpStack[ST(1)]; PFPREG r = cpu->FpST0; FpArithPreamble(cpu); (*FPREMTable[l->Tag][r->Tag])(cpu, l, r); } NPXFUNC2(FPREM1_VALID_VALID) { int ExpL; int ExpR; int ExpDiff; LONG Q; double DQ; double FloorQ, CeilQ; ExpL = (int)((l->rdw[1] >> 20) & 0x7ff) - 1023; ExpR = (int)((r->rdw[1] >> 20) & 0x7ff) - 1023; ExpDiff = abs(ExpL-ExpR); if (ExpDiff < 64) { // Do the division and get the integer nearest to the value DQ = r->r64 / l->r64; FloorQ = floor(DQ); CeilQ = ceil(DQ); if (DQ-FloorQ >= CeilQ-DQ) { // CeilQ is closer - use it Q = (long)CeilQ; } else { // FloorQ is closer - use it Q = (long)FloorQ; } // Store the remainder r->r64 -= (DOUBLE)Q * l->r64; SetTag(r); // Store the status bits if (Q < 0) { // // Take the absolute value of Q before returning the low 3 bits // of the quotient. // Q = -Q; } cpu->FpStatusC2 = 0; // indicate the final remainder is ready cpu->FpStatusC0 = (Q>>2) & 1; cpu->FpStatusC3 = (Q>>1) & 1; cpu->FpStatusC1 = Q & 1; } else { DOUBLE PowerOfTwo; cpu->FpStatusC2 = 1; // indicate the app must loop more PowerOfTwo = ldexp(1.0, ExpDiff-32); // get 2^(ExpDiff-32) // get Q by finding the integer nearest to the value DQ = (r->r64/PowerOfTwo) / (l->r64/PowerOfTwo); FloorQ = floor(DQ); CeilQ = ceil(DQ); if (DQ-FloorQ >= CeilQ-DQ) { // CeilQ is closer - use it Q = (long)CeilQ; } else { // FloorQ is closer - use it Q = (long)FloorQ; } r->r64 -= (DOUBLE)Q * l->r64 * PowerOfTwo; SetTag(r); } } NPXFUNC2(FPREM1_VALID_SPECIAL) { switch (l->TagSpecial) { case TAG_SPECIAL_DENORM: FPREM1_VALID_VALID(cpu, l, r); break; case TAG_SPECIAL_INFINITY: // dividing infinity SetIndefinite(r); break; case TAG_SPECIAL_SNAN: if (HandleSnan(cpu, r)) { return; } // else fall into QNAN case case TAG_SPECIAL_QNAN: case TAG_SPECIAL_INDEF: // r is the destination and it is a QNAN, while l is a VALID. Return // the QNAN as the result of the operation // x86 emulator leaves condition flags alone break; } } NPXFUNC2(FPREM1_ZERO_SPECIAL) { if (r->TagSpecial == TAG_SPECIAL_INFINITY) { SetIndefinite(r); } else { FPREM1_VALID_SPECIAL(cpu, l, r); } } NPXFUNC2(FPREM1_SPECIAL_VALIDORZERO) { switch (l->TagSpecial) { case TAG_SPECIAL_DENORM: FPREM1_VALID_VALID(cpu, l, r); break; case TAG_SPECIAL_INFINITY: // number / infinity - quotient == 0 cpu->FpStatusC2 = 0; cpu->FpStatusC0 = 0; cpu->FpStatusC1 = 0; cpu->FpStatusC3 = 0; break; case TAG_SPECIAL_SNAN: if (HandleSnan(cpu, l)) { return; } // else fall into QNAN case case TAG_SPECIAL_QNAN: case TAG_SPECIAL_INDEF: // r is the destination and it is a VALID, while l is a NAN. Return // the NAN as the result of the operation r->r64 = l->r64; r->Tag = l->Tag; r->TagSpecial = l->TagSpecial; break; } } NPXFUNC2(FPREM1_SPECIAL_SPECIAL) { if (l->TagSpecial == TAG_SPECIAL_DENORM) { FPREM1_VALID_SPECIAL(cpu, l, r); return; } if (r->TagSpecial == TAG_SPECIAL_DENORM) { FPREM1_SPECIAL_VALIDORZERO(cpu, l, r); } if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) { return; } if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) { return; } if (l->TagSpecial == TAG_SPECIAL_INFINITY) { if (r->TagSpecial == TAG_SPECIAL_INFINITY) { SetIndefinite(r); } // // r is a NAN of some sort, and l is infinity - return the NAN // which is already in r. // } else { // // l is a NAN, and r is either a NAN or INFINITY. Have the native // FPU return the largest NAN, and re-tag it as appropriate. // r->r64 = l->r64 + r->r64; SetTag(r); } } NPXFUNC2(FPREM1_EMPTY_ANY) { if (HandleStackEmpty(cpu, l)) { return; } (*FPREM1Table[l->Tag][r->Tag])(cpu, l, r); } NPXFUNC2(FPREM1_ANY_EMPTY) { if (HandleStackEmpty(cpu, l)) { return; } (*FPREM1Table[l->Tag][r->Tag])(cpu, l, r); } FRAG0(FPREM1) { // get remainder of r/l PFPREG l = &cpu->FpStack[ST(1)]; PFPREG r = cpu->FpST0; FpArithPreamble(cpu); (*FPREM1Table[l->Tag][r->Tag])(cpu, l, r); }