windows-nt/Source/XPSP1/NT/base/wow64/mscpu/fraglib/fpuload.c
2020-09-26 16:20:57 +08:00

564 lines
14 KiB
C

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
fpuload.c
Abstract:
Floating point load functions
Author:
04-Oct-1995 BarryBo
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <float.h>
#include <math.h>
#include <stdio.h>
#include "wx86.h"
#include "cpuassrt.h"
#include "fragp.h"
#include "fpufragp.h"
#include "fpuarith.h"
ASSERTNAME;
VOID GetIntelR4(
PFPREG Fp,
FLOAT *pIntelReal
)
/*++
Routine Description:
Load an Intel R4 and convert it to a native R4, accounting for
the difference in how MIPS represents QNAN/SNAN.
NOTE: This is not in fpufrag.c due to a code-generator bug on PPC -
irbexpr.c:932 asserts trying to inline this function. Moving it
to a different file defeats the inliner.
Arguments:
Fp - floating-point register to load the R4 into
pIntelReal - R4 value to load (in Intel format)
Return Value:
None.
--*/
{
DWORD d = GET_LONG(pIntelReal);
if ((d & 0x7f800000) == 0x7f800000) {
Fp->Tag = TAG_SPECIAL;
// Found some sort of NAN
if (d == 0xffc00000) { // Indefinite
// Create the native indefinite form
#if NATIVE_NAN_IS_INTEL_FORMAT
Fp->rdw[0] = 0;
Fp->rdw[1] = 0xfff80000;
#else
Fp->rdw[0] = 0xffffffff;
Fp->rdw[1] = 0x7ff7ffff;
#endif
Fp->TagSpecial = TAG_SPECIAL_INDEF;
} else if (d == 0x7f800000) { // +infinity
Fp->r64 = R8PositiveInfinity;
Fp->TagSpecial = TAG_SPECIAL_INFINITY;
} else if (d == 0xff800000) { // -infinity
Fp->r64 = R8NegativeInfinity;
Fp->TagSpecial = TAG_SPECIAL_INFINITY;
} else { // SNAN/QNAN
DWORD Sign;
if (d & 0x00400000) {
//
// Intel QNAN
//
Fp->TagSpecial = TAG_SPECIAL_QNAN;
} else {
//
// Intel SNAN
//
Fp->TagSpecial = TAG_SPECIAL_SNAN;
}
#if !NATIVE_NAN_IS_INTEL_FORMAT
//
// Toggle the NAN to native format
//
d ^= 0x00400000;
#endif
//
// Cast the r4 RISC QNAN to double. Don't trust the CRT to
// do the right thing - MIPS converts them both to INDEFINITE.
//
Sign = d & 0x80000000;
d &= 0x007fffff; // grab the mantissa from the r4 (23 bits)
Fp->rdw[1] = Sign | 0x7ff00000 | (d >> 3); // store 20 bits of mantissa, plus sign
Fp->rdw[0] = d << 25; // store 3 bits of mantissa
}
} else { // denormal, zero, or number
// Coerce it to an R8
Fp->r64 = (DOUBLE)*(FLOAT *)&d;
// Compute its tag by looking at the value *after* the conversion,
// as the native FPU may have normalized the value
if (Fp->r64 == 0.0) {
Fp->Tag = TAG_ZERO;
} else if ((Fp->rdw[1] & 0x7ff00000) == 0) {
// Exponent is 0 - R8 denormal
Fp->Tag = TAG_SPECIAL;
Fp->TagSpecial = TAG_SPECIAL_DENORM;
} else {
Fp->Tag = TAG_VALID;
#if DBG
SetTag(Fp);
CPUASSERT(Fp->Tag == TAG_VALID);
#endif
}
}
}
#if !NATIVE_NAN_IS_INTEL_FORMAT
VOID GetIntelR8(
PFPREG Fp,
DOUBLE *pIntelReal
)
/*++
Routine Description:
Load an Intel R8 and convert it to a native R8, accounting for
the difference in how MIPS represents QNAN/SNAN.
Arguments:
Fp - floating-point register to load the R8 into
pIntelReal - R8 value to load (in Intel format)
Return Value:
None.
--*/
{
//
// Copy the R8 into the FP register
//
Fp->r64 = *(UNALIGNED DOUBLE *)pIntelReal;
//
// Compute its tag
//
SetTag(Fp);
//
// If the value is QNAN/SNAN/INDEF, convert it to native format
//
if (IS_TAG_NAN(Fp)) {
if (Fp->rdw[0] == 0 && Fp->rdw[1] == 0xfff80000) {
// indefinite - make the R8 into a native indefinite
Fp->TagSpecial = TAG_SPECIAL_INDEF;
Fp->rdw[0] = 0xffffffff;
Fp->rdw[1] = 0x7ff7ffff;
} else {
if (Fp->rdw[1] & 0x00080000) {
// top bit of mantissa is set - QNAN
Fp->TagSpecial = TAG_SPECIAL_QNAN;
} else {
// top bit of mantissa clear - SNAN
Fp->TagSpecial = TAG_SPECIAL_SNAN;
}
Fp->rdw[1] ^= 0x00080000; // invert the top bit of the mantissa
}
}
}
#endif //!NATIVE_NAN_IS_INTEL_FORMAT
VOID
SetTag(
PFPREG FpReg
)
/*++
Routine Description:
Sets the Tag value corresponding to a r64 value in an FP register.
Assumes the R8 value is in native format (ie. Intel NANs are already
converted to native NANs).
Arguments:
FpReg - register to set Tag field in.
Return Value:
None
--*/
{
DWORD Exponent;
/* On average, the value will be zero or a valid real, so those cases
* have the fastest code paths. NANs tend to be less frequent and are
* slower to calculate.
*/
Exponent = FpReg->rdw[1] & 0x7ff00000;
if (Exponent == 0x7ff00000) {
// exponent is all 1's - NAN of some sort
FpReg->Tag = TAG_SPECIAL;
if (FpReg->rdw[0] == 0 && (FpReg->rdw[1] & 0x7fffffff) == 0x7ff00000) {
// Exponent is all 1s, mantissa is all 0s - Infinity
FpReg->TagSpecial = TAG_SPECIAL_INFINITY;
} else {
#if NATIVE_NAN_IS_INTEL_FORMAT
if (FpReg->rdw[0] == 0 && FpReg->rdw[1] == 0xfff80000) {
// indefinite
FpReg->TagSpecial = TAG_SPECIAL_INDEF;
} else if (FpReg->rdw[1] & 0x00080000) {
// top bit of mantissa is set - QNAN
FpReg->TagSpecial = TAG_SPECIAL_QNAN;
} else {
// Top bit of mantissa clear - but some mantissa bit set - QNAN
FpReg->TagSpecial = TAG_SPECIAL_SNAN;
}
#else //!NATIVE_NAN_IS_INTEL_FORMAT
if (FpReg->rdw[0] == 0xffffffff && FpReg->rdw[1] == 0x7ff7ffff) {
// indefinite
FpReg->TagSpecial = TAG_SPECIAL_INDEF;
} else if (FpReg->rdw[1] & 0x00080000) {
// top bit of mantissa is set - SNAN
FpReg->TagSpecial = TAG_SPECIAL_SNAN;
} else {
// top bit of mantissa clear - QNAN
FpReg->TagSpecial = TAG_SPECIAL_QNAN;
}
#endif //!NATIVE_NAN_IS_INTEL_FORMAT
}
} else if (Exponent == 0) {
// exponent is 0 - DENORMAL or ZERO
if ((FpReg->rdw[1] & 0x1ffff) == 0 && FpReg->rdw[0] == 0) {
// mantissa is all zeroes - ZERO
FpReg->Tag = TAG_ZERO;
} else {
FpReg->Tag = TAG_SPECIAL;
FpReg->TagSpecial = TAG_SPECIAL_DENORM;
}
} else {
// Exponent is not all 1's and not all 0's - a VALID
FpReg->Tag = TAG_VALID;
}
}
FRAG1(FILD16, SHORT) // FILD m16int
{
PFPREG ST0;
FpArithDataPreamble(cpu, pop1);
cpu->FpStatusC1 = 0; // assume no error
PUSHFLT(ST0);
if (ST0->Tag != TAG_EMPTY) {
HandleStackFull(cpu, ST0);
} else {
SHORT s;
s = (SHORT)GET_SHORT(pop1);
ST0->r64 = (DOUBLE)s;
if (s) {
ST0->Tag = TAG_VALID;
} else {
ST0->Tag = TAG_ZERO;
}
}
}
FRAG1(FILD32, LONG) // FILD m32int
{
PFPREG ST0;
FpArithDataPreamble(cpu, pop1);
cpu->FpStatusC1 = 0; // assume no error
PUSHFLT(ST0);
if (ST0->Tag != TAG_EMPTY) {
HandleStackFull(cpu, ST0);
} else {
LONG l;
l = (LONG)GET_LONG(pop1);
ST0->r64 = (DOUBLE)l;
if (l) {
ST0->Tag = TAG_VALID;
} else {
ST0->Tag = TAG_ZERO;
}
}
}
FRAG1(FILD64, LONGLONG) // FILD m64int
{
PFPREG ST0;
FpArithDataPreamble(cpu, pop1);
cpu->FpStatusC1 = 0; // assume no error
PUSHFLT(ST0);
if (ST0->Tag != TAG_EMPTY) {
HandleStackFull(cpu, ST0);
} else {
LONGLONG ll;
ll = *(UNALIGNED LONGLONG *)pop1;
ST0->r64 = (DOUBLE)ll;
if (ll) {
ST0->Tag = TAG_VALID;
} else {
ST0->Tag = TAG_ZERO;
}
}
}
FRAG1(FLD32, FLOAT) // FLD m32real
{
PFPREG ST0;
FpArithDataPreamble(cpu, pop1);
cpu->FpStatusC1 = 0; // assume no error
PUSHFLT(ST0);
if (ST0->Tag != TAG_EMPTY) {
HandleStackFull(cpu, ST0);
} else {
GetIntelR4(ST0, pop1);
if (ST0->Tag == TAG_SPECIAL) {
if (ST0->TagSpecial == TAG_SPECIAL_DENORM) {
if (!(cpu->FpControlMask & FPCONTROL_DM)) {
cpu->FpStatusES = 1; // Unmasked exception
//
// Instruction needs to be aborted due to unmasked
// exception. We've already hosed ST0, so "correct"
// it by popping the FP stack. Note that
// the contents of the register have been lost, which
// is a compatibility break with Intel.
//
POPFLT;
}
cpu->FpStatusExceptions |= FPCONTROL_DM;
} else if (ST0->TagSpecial == TAG_SPECIAL_SNAN) {
if (HandleSnan(cpu, ST0)) {
//
// Instruction needs to be aborted due to unmasked
// exception. We've already hosed ST0, so "correct"
// it by popping the FP stack. Note that
// the contents of the register have been lost, which
// is a compatibility break with Intel.
//
POPFLT;
}
}
}
}
}
FRAG1(FLD64, DOUBLE) // FLD m64real
{
PFPREG ST0;
FpArithDataPreamble(cpu, pop1);
cpu->FpStatusC1 = 0; // assume no error
PUSHFLT(ST0);
if (ST0->Tag != TAG_EMPTY) {
HandleStackFull(cpu, ST0);
} else {
GetIntelR8(ST0, pop1);
if (ST0->Tag == TAG_SPECIAL) {
if (ST0->TagSpecial == TAG_SPECIAL_DENORM) {
if (!(cpu->FpControlMask & FPCONTROL_DM)) {
cpu->FpStatusES = 1; // Unmasked exception
//
// Instruction needs to be aborted due to unmasked
// exception. We've already hosed ST0, so "correct"
// it by popping the FP stack. Note that
// the contents of the register have been lost, which
// is a compatibility break with Intel.
//
POPFLT;
}
cpu->FpStatusExceptions |= FPCONTROL_DM;
} else if (ST0->TagSpecial == TAG_SPECIAL_SNAN) {
if (HandleSnan(cpu, ST0)) {
//
// Instruction needs to be aborted due to unmasked
// exception. We've already hosed ST0, so "correct"
// it by popping the FP stack. Note that
// the contents of the register have been lost, which
// is a compatibility break with Intel.
//
POPFLT;
}
}
}
}
}
FRAG0(FLD1)
{
PFPREG ST0;
FpArithPreamble(cpu);
cpu->FpStatusC1 = 0; // assume no error
PUSHFLT(ST0);
if (ST0->Tag != TAG_EMPTY) {
HandleStackFull(cpu, ST0);
} else {
ST0->r64 = 1.0;
ST0->Tag = TAG_VALID;
}
}
FRAG0(FLDL2T)
{
PFPREG ST0;
FpArithPreamble(cpu);
cpu->FpStatusC1 = 0; // assume no error
PUSHFLT(ST0);
if (ST0->Tag != TAG_EMPTY) {
HandleStackFull(cpu, ST0);
} else {
ST0->r64 = 2.3025850929940456840E0 / 6.9314718055994530942E-1; //log2(10) = ln10/ln2
ST0->Tag = TAG_VALID;
}
}
FRAG0(FLDL2E)
{
PFPREG ST0;
FpArithPreamble(cpu);
cpu->FpStatusC1 = 0; // assume no error
PUSHFLT(ST0);
if (ST0->Tag != TAG_EMPTY) {
HandleStackFull(cpu, ST0);
} else {
ST0->r64 = 1.4426950408889634074E0;
ST0->Tag = TAG_VALID;
}
}
FRAG0(FLDPI)
{
PFPREG ST0;
FpArithPreamble(cpu);
cpu->FpStatusC1 = 0; // assume no error
PUSHFLT(ST0);
if (ST0->Tag != TAG_EMPTY) {
HandleStackFull(cpu, ST0);
} else {
ST0->r64 = 3.14159265358979323846;
ST0->Tag = TAG_VALID;
}
}
FRAG0(FLDLG2)
{
PFPREG ST0;
FpArithPreamble(cpu);
cpu->FpStatusC1 = 0; // assume no error
PUSHFLT(ST0);
if (ST0->Tag != TAG_EMPTY) {
HandleStackFull(cpu, ST0);
} else {
ST0->r64 = 6.9314718055994530942E-1 / 2.3025850929940456840E0;
ST0->Tag = TAG_VALID;
}
}
FRAG0(FLDLN2)
{
PFPREG ST0;
FpArithPreamble(cpu);
cpu->FpStatusC1 = 0; // assume no error
PUSHFLT(ST0);
if (ST0->Tag != TAG_EMPTY) {
HandleStackFull(cpu, ST0);
} else {
ST0->r64 = 6.9314718055994530942E-1;
ST0->Tag = TAG_VALID;
}
}
FRAG1IMM(FLD_STi, INT)
{
PFPREG ST0;
PFPREG STi;
FpArithPreamble(cpu);
cpu->FpStatusC1 = 0; // assume no error
STi = &cpu->FpStack[ST(op1)];
PUSHFLT(ST0);
if (ST0->Tag != TAG_EMPTY) {
HandleStackFull(cpu, ST0);
} else {
ST0->r64 = STi->r64;
ST0->Tag = STi->Tag;
ST0->TagSpecial = STi->TagSpecial;
}
}
FRAG0(FLDZ)
{
PFPREG ST0;
FpArithPreamble(cpu);
cpu->FpStatusC1 = 0; // assume no error
PUSHFLT(ST0);
if (ST0->Tag != TAG_EMPTY) {
HandleStackFull(cpu, ST0);
} else {
ST0->r64 = 0.0;
ST0->Tag = TAG_ZERO;
}
}