564 lines
14 KiB
C
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;
|
|
}
|
|
}
|