590 lines
14 KiB
C
590 lines
14 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1995-1998 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
fpustore.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Floating point store 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;
|
||
|
|
||
|
//
|
||
|
// Forward declarations
|
||
|
//
|
||
|
|
||
|
__int64 CastDoubleToInt64(double d); // in alpha\fphelp.s
|
||
|
|
||
|
|
||
|
#if !NATIVE_NAN_IS_INTEL_FORMAT
|
||
|
|
||
|
//
|
||
|
// Forward declarations
|
||
|
//
|
||
|
NPXPUTINTELR4(PutIntelR4_VALID);
|
||
|
NPXPUTINTELR4(PutIntelR4_ZERO);
|
||
|
NPXPUTINTELR4(PutIntelR4_SPECIAL);
|
||
|
NPXPUTINTELR4(PutIntelR4_EMPTY);
|
||
|
NPXPUTINTELR8(PutIntelR8_VALID);
|
||
|
NPXPUTINTELR8(PutIntelR8_ZERO);
|
||
|
NPXPUTINTELR8(PutIntelR8_SPECIAL);
|
||
|
NPXPUTINTELR8(PutIntelR8_EMPTY);
|
||
|
|
||
|
//
|
||
|
// Jump tables
|
||
|
//
|
||
|
const NpxPutIntelR4 PutIntelR4Table[TAG_MAX] = {
|
||
|
PutIntelR4_VALID,
|
||
|
PutIntelR4_ZERO,
|
||
|
PutIntelR4_SPECIAL,
|
||
|
PutIntelR4_EMPTY
|
||
|
};
|
||
|
|
||
|
const NpxPutIntelR8 PutIntelR8Table[TAG_MAX] = {
|
||
|
PutIntelR8_VALID,
|
||
|
PutIntelR8_ZERO,
|
||
|
PutIntelR8_SPECIAL,
|
||
|
PutIntelR8_EMPTY
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
NPXPUTINTELR4(PutIntelR4_VALID)
|
||
|
{
|
||
|
FLOAT f = (FLOAT)Fp->r64;
|
||
|
PUT_LONG(pIntelReal, *(DWORD *)&f);
|
||
|
}
|
||
|
|
||
|
NPXPUTINTELR4(PutIntelR4_ZERO)
|
||
|
{
|
||
|
//
|
||
|
// This cannot simply write a constant 0.0 to memory as it must
|
||
|
// copy the correct sign from the 0.0 in the FP register.
|
||
|
//
|
||
|
PUT_LONG(pIntelReal, Fp->rdw[1]);
|
||
|
}
|
||
|
|
||
|
NPXPUTINTELR4(PutIntelR4_SPECIAL)
|
||
|
{
|
||
|
switch (Fp->TagSpecial) {
|
||
|
default:
|
||
|
CPUASSERT(FALSE); // unknown tag - fall into TAG_INDEF
|
||
|
|
||
|
case TAG_SPECIAL_INFINITY:
|
||
|
case TAG_SPECIAL_DENORM:
|
||
|
PutIntelR4_VALID(pIntelReal, Fp);
|
||
|
break;
|
||
|
|
||
|
case TAG_SPECIAL_INDEF:
|
||
|
// Write out the R4 indefinite bit pattern
|
||
|
PUT_LONG(pIntelReal, 0xffc00000);
|
||
|
break;
|
||
|
|
||
|
case TAG_SPECIAL_QNAN:
|
||
|
case TAG_SPECIAL_SNAN: {
|
||
|
DWORD d[2];
|
||
|
FLOAT f;
|
||
|
//
|
||
|
// Truncate the R8 to an R4, and toggle the top bit of the mantissa
|
||
|
// to form an Intel QNAN/SNAN (which is different than a native
|
||
|
// QNAN/SNAN).
|
||
|
//
|
||
|
d[0] = Fp->rdw[0];
|
||
|
d[1] = Fp->rdw[1] ^ 0x00400000;
|
||
|
f = *(FLOAT *)d;
|
||
|
PUT_LONG(pIntelReal, *(DWORD *)&f);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NPXPUTINTELR4(PutIntelR4_EMPTY)
|
||
|
{
|
||
|
//
|
||
|
// It is assumed that callers of PutIntelR4() have already handled
|
||
|
// TAG_EMPTY by raising an exception or converting it to TAG_INDEF.
|
||
|
//
|
||
|
CPUASSERT(FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
NPXPUTINTELR8(PutIntelR8_VALID)
|
||
|
{
|
||
|
*(UNALIGNED DOUBLE *)pIntelReal = Fp->r64;
|
||
|
}
|
||
|
|
||
|
NPXPUTINTELR8(PutIntelR8_ZERO)
|
||
|
{
|
||
|
//
|
||
|
// This cannot simply write a constant 0.0 to memory as it must
|
||
|
// copy the correct sign from the 0.0 in the FP register.
|
||
|
//
|
||
|
*(UNALIGNED DOUBLE *)pIntelReal = Fp->r64;
|
||
|
}
|
||
|
|
||
|
NPXPUTINTELR8(PutIntelR8_SPECIAL)
|
||
|
{
|
||
|
DWORD *pdw = (DWORD *)pIntelReal;
|
||
|
|
||
|
switch (Fp->TagSpecial) {
|
||
|
default:
|
||
|
CPUASSERT(FALSE); // unknown tag - fall into TAG_INDEF
|
||
|
|
||
|
case TAG_SPECIAL_DENORM:
|
||
|
case TAG_SPECIAL_INFINITY:
|
||
|
// Both can be done as a simple R8-toR8 copy
|
||
|
PutIntelR8_VALID(pIntelReal, Fp);
|
||
|
break;
|
||
|
|
||
|
case TAG_SPECIAL_INDEF:
|
||
|
// Write out an Intel Indefinite
|
||
|
PUT_LONG(pdw, 0);
|
||
|
PUT_LONG((pdw+1), 0xfff80000);
|
||
|
break;
|
||
|
|
||
|
case TAG_SPECIAL_QNAN:
|
||
|
case TAG_SPECIAL_SNAN:
|
||
|
//
|
||
|
// Toggle the top bit of the mantissa to form an Intel QNAN/SNAN
|
||
|
// (which is different than a native QNAN/SNAN).
|
||
|
//
|
||
|
PUT_LONG(pdw, Fp->rdw[0]);
|
||
|
PUT_LONG((pdw+1), Fp->rdw[1] ^ 0x00080000);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NPXPUTINTELR8(PutIntelR8_EMPTY)
|
||
|
{
|
||
|
//
|
||
|
// It is assumed that callers of PutIntelR8() have already handled
|
||
|
// TAG_EMPTY by raising an exception or converting it to TAG_INDEF.
|
||
|
//
|
||
|
CPUASSERT(FALSE);
|
||
|
}
|
||
|
|
||
|
#endif //!NATIVE_NAN_IS_INTEL_FORMAT
|
||
|
|
||
|
|
||
|
|
||
|
FRAG1(FIST16, SHORT) // FIST m16int
|
||
|
{
|
||
|
PFPREG ST0 = cpu->FpST0;
|
||
|
__int64 i64;
|
||
|
int Exponent;
|
||
|
SHORT i16;
|
||
|
|
||
|
FpArithDataPreamble(cpu, pop1);
|
||
|
|
||
|
switch (ST0->Tag) {
|
||
|
case TAG_VALID:
|
||
|
Exponent = (int)((ST0->rdw[1] >> 20) & 0x7ff) - 1023;
|
||
|
//
|
||
|
if (Exponent >= 64) {
|
||
|
//
|
||
|
// Exponent is too big - this cannot be converted to an __int64
|
||
|
// Raise I exception on overflow, or write 0x8000 for masked
|
||
|
// exception.
|
||
|
//
|
||
|
IntOverflow:
|
||
|
if (HandleInvalidOp(cpu)) {
|
||
|
return;
|
||
|
}
|
||
|
PUT_SHORT(pop1, 0x8000);
|
||
|
} else {
|
||
|
i64 = CastDoubleToInt64(ST0->r64);
|
||
|
i16 = (SHORT)i64;
|
||
|
if ((__int64)i16 != i64) {
|
||
|
goto IntOverflow;
|
||
|
}
|
||
|
PUT_SHORT(pop1, i16);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case TAG_ZERO:
|
||
|
PUT_SHORT(pop1, 0);
|
||
|
break;
|
||
|
|
||
|
case TAG_SPECIAL:
|
||
|
if (ST0->TagSpecial == TAG_SPECIAL_DENORM) {
|
||
|
i64 = CastDoubleToInt64(ST0->r64);
|
||
|
PUT_SHORT(pop1, (SHORT)i64);
|
||
|
} else if (!HandleInvalidOp(cpu)) {
|
||
|
// INFINITY and NANs are all invalid operations, and the masked
|
||
|
// behavior is to write 0x8000
|
||
|
PUT_SHORT(pop1, 0x8000);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case TAG_EMPTY:
|
||
|
if (!HandleStackEmpty(cpu, ST0)) {
|
||
|
PUT_SHORT(pop1, 0x8000);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FRAG1(FISTP16, SHORT) // FISTP m16int
|
||
|
{
|
||
|
PFPREG ST0 = cpu->FpST0;
|
||
|
__int64 i64;
|
||
|
int Exponent;
|
||
|
SHORT i16;
|
||
|
|
||
|
FpArithDataPreamble(cpu, pop1);
|
||
|
|
||
|
switch (ST0->Tag) {
|
||
|
case TAG_VALID:
|
||
|
Exponent = (int)((ST0->rdw[1] >> 20) & 0x7ff) - 1023;
|
||
|
if (Exponent >= 64) {
|
||
|
//
|
||
|
// Exponent is too big - this cannot be converted to an __int64
|
||
|
// Raise I exception on overflow, or write 0x8000 for masked
|
||
|
// exception.
|
||
|
//
|
||
|
IntOverflow:
|
||
|
if (HandleInvalidOp(cpu)) {
|
||
|
return;
|
||
|
}
|
||
|
PUT_SHORT(pop1, 0x8000);
|
||
|
} else {
|
||
|
i64 = CastDoubleToInt64(ST0->r64);
|
||
|
i16 = (SHORT)i64;
|
||
|
if ((__int64)i16 != i64) {
|
||
|
goto IntOverflow;
|
||
|
}
|
||
|
PUT_SHORT(pop1, i16);
|
||
|
}
|
||
|
POPFLT;
|
||
|
break;
|
||
|
|
||
|
case TAG_ZERO:
|
||
|
PUT_SHORT(pop1, 0);
|
||
|
break;
|
||
|
|
||
|
case TAG_SPECIAL:
|
||
|
if (ST0->TagSpecial == TAG_SPECIAL_DENORM) {
|
||
|
i64 = CastDoubleToInt64(ST0->r64);
|
||
|
PUT_SHORT(pop1, (SHORT)i64);
|
||
|
} else if (!HandleInvalidOp(cpu)) {
|
||
|
// INFINITY and NANs are all invalid operations, and the masked
|
||
|
// behavior is to write 0x8000
|
||
|
PUT_SHORT(pop1, 0x8000);
|
||
|
}
|
||
|
POPFLT;
|
||
|
break;
|
||
|
|
||
|
case TAG_EMPTY:
|
||
|
if (!HandleStackEmpty(cpu, ST0)) {
|
||
|
PUT_SHORT(pop1, 0x8000);
|
||
|
POPFLT;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
FRAG1(FIST32, LONG) // FIST m32int
|
||
|
{
|
||
|
PFPREG ST0 = cpu->FpST0;
|
||
|
__int64 i64;
|
||
|
int Exponent;
|
||
|
LONG i32;
|
||
|
|
||
|
FpArithDataPreamble(cpu, pop1);
|
||
|
|
||
|
switch (ST0->Tag) {
|
||
|
case TAG_VALID:
|
||
|
Exponent = (int)((ST0->rdw[1] >> 20) & 0x7ff) - 1023;
|
||
|
if (Exponent >= 64) {
|
||
|
//
|
||
|
// Exponent is too big - this cannot be converted to an __int64
|
||
|
// Raise I exception on overflow, or write 0x80000000 for masked
|
||
|
// exception.
|
||
|
//
|
||
|
IntOverflow:
|
||
|
if (HandleInvalidOp(cpu)) {
|
||
|
return;
|
||
|
}
|
||
|
PUT_LONG(pop1, 0x80000000);
|
||
|
} else {
|
||
|
i64 = CastDoubleToInt64(ST0->r64);
|
||
|
i32 = (LONG)i64;
|
||
|
if ((__int64)i32 != i64) {
|
||
|
goto IntOverflow;
|
||
|
}
|
||
|
PUT_LONG(pop1, i32);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case TAG_ZERO:
|
||
|
PUT_LONG(pop1, 0);
|
||
|
break;
|
||
|
|
||
|
case TAG_SPECIAL:
|
||
|
if (ST0->TagSpecial == TAG_SPECIAL_DENORM) {
|
||
|
i64 = CastDoubleToInt64(ST0->r64);
|
||
|
PUT_LONG(pop1, (LONG)i64);
|
||
|
} else if (!HandleInvalidOp(cpu)) {
|
||
|
// INFINITY and NANs are all invalid operations, and the masked
|
||
|
// behavior is to write 0x80000000
|
||
|
PUT_LONG(pop1, 0x80000000);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case TAG_EMPTY:
|
||
|
if (!HandleStackEmpty(cpu, ST0)) {
|
||
|
PUT_LONG(pop1, 0x80000000);
|
||
|
POPFLT;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FRAG1(FISTP32, LONG) // FISTP m32int
|
||
|
{
|
||
|
PFPREG ST0 = cpu->FpST0;
|
||
|
__int64 i64;
|
||
|
int Exponent;
|
||
|
LONG i32;
|
||
|
|
||
|
FpArithDataPreamble(cpu, pop1);
|
||
|
|
||
|
switch (ST0->Tag) {
|
||
|
case TAG_VALID:
|
||
|
Exponent = (int)((ST0->rdw[1] >> 20) & 0x7ff) - 1023;
|
||
|
if (Exponent >= 64) {
|
||
|
//
|
||
|
// Exponent is too big - this cannot be converted to an __int64
|
||
|
// Raise I exception on overflow, or write 0x80000000 for masked
|
||
|
// exception.
|
||
|
//
|
||
|
IntOverflow:
|
||
|
if (HandleInvalidOp(cpu)) {
|
||
|
return;
|
||
|
}
|
||
|
PUT_LONG(pop1, 0x80000000);
|
||
|
} else {
|
||
|
i64 = CastDoubleToInt64(ST0->r64);
|
||
|
i32 = (LONG)i64;
|
||
|
if ((__int64)i32 != i64) {
|
||
|
goto IntOverflow;
|
||
|
}
|
||
|
PUT_LONG(pop1, i32);
|
||
|
}
|
||
|
POPFLT;
|
||
|
break;
|
||
|
|
||
|
case TAG_ZERO:
|
||
|
PUT_LONG(pop1, 0);
|
||
|
POPFLT;
|
||
|
break;
|
||
|
|
||
|
case TAG_SPECIAL:
|
||
|
if (ST0->TagSpecial == TAG_SPECIAL_DENORM) {
|
||
|
i64 = CastDoubleToInt64(ST0->r64);
|
||
|
PUT_LONG(pop1, (LONG)i64);
|
||
|
} else if (!HandleInvalidOp(cpu)) {
|
||
|
// INFINITY and NANs are all invalid operations, and the masked
|
||
|
// behavior is to write 0x80000000
|
||
|
PUT_LONG(pop1, 0x80000000);
|
||
|
}
|
||
|
POPFLT;
|
||
|
break;
|
||
|
|
||
|
case TAG_EMPTY:
|
||
|
if (!HandleStackEmpty(cpu, ST0)) {
|
||
|
PUT_LONG(pop1, 0x80000000);
|
||
|
POPFLT;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
FRAG1(FISTP64, LONGLONG) // FISTP m64int
|
||
|
{
|
||
|
PFPREG ST0 = cpu->FpST0;
|
||
|
__int64 i64;
|
||
|
int Exponent;
|
||
|
|
||
|
FpArithDataPreamble(cpu, pop1);
|
||
|
|
||
|
switch (ST0->Tag) {
|
||
|
case TAG_VALID:
|
||
|
Exponent = (int)((ST0->rdw[1] >> 20) & 0x7ff) - 1023;
|
||
|
if (Exponent >= 64) {
|
||
|
//
|
||
|
// Exponent is too big - this cannot be converted to an __int64,
|
||
|
// Raise I exception on overflow, or write 0x800...0 for masked
|
||
|
// exception
|
||
|
//
|
||
|
if (HandleInvalidOp(cpu)) {
|
||
|
return;
|
||
|
}
|
||
|
i64 = (__int64)0x8000000000000000i64;
|
||
|
} else {
|
||
|
i64 = CastDoubleToInt64(ST0->r64);
|
||
|
}
|
||
|
*(UNALIGNED LONGLONG *)pop1 = (LONGLONG)i64;
|
||
|
POPFLT;
|
||
|
break;
|
||
|
|
||
|
case TAG_ZERO:
|
||
|
*(UNALIGNED LONGLONG *)pop1 = 0;
|
||
|
POPFLT;
|
||
|
break;
|
||
|
|
||
|
case TAG_SPECIAL:
|
||
|
if (ST0->TagSpecial == TAG_SPECIAL_DENORM) {
|
||
|
i64 = CastDoubleToInt64(ST0->r64);
|
||
|
*(UNALIGNED LONGLONG *)pop1 = (LONGLONG)i64;
|
||
|
} else if (!HandleInvalidOp(cpu)) {
|
||
|
DWORD *pdw = (DWORD *)pop1;
|
||
|
|
||
|
// INFINITY and NANs are all invalid operations, and the masked
|
||
|
// behavior is to write 0x80000000
|
||
|
PUT_LONG(pdw, 0x00000000);
|
||
|
PUT_LONG((pdw+1), 0x80000000);
|
||
|
}
|
||
|
POPFLT;
|
||
|
break;
|
||
|
|
||
|
case TAG_EMPTY:
|
||
|
if (!HandleStackEmpty(cpu, ST0)) {
|
||
|
DWORD *pdw = (DWORD *)pop1;
|
||
|
|
||
|
PUT_LONG(pdw, 0x00000000);
|
||
|
PUT_LONG((pdw+1), 0x80000000);
|
||
|
POPFLT;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FRAG1(FST32, FLOAT) // FST m32real
|
||
|
{
|
||
|
FpArithDataPreamble(cpu, pop1);
|
||
|
|
||
|
if (cpu->FpST0->Tag == TAG_EMPTY) {
|
||
|
if (HandleStackEmpty(cpu, cpu->FpST0)) {
|
||
|
// unmasked exception - abort the instruction
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
PutIntelR4(pop1, cpu->FpST0);
|
||
|
}
|
||
|
|
||
|
FRAG1(FSTP32, FLOAT) // FSTP m32real
|
||
|
{
|
||
|
FpArithDataPreamble(cpu, pop1);
|
||
|
|
||
|
if (cpu->FpST0->Tag == TAG_EMPTY) {
|
||
|
if (HandleStackEmpty(cpu, cpu->FpST0)) {
|
||
|
// unmasked exception - abort the instruction
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
PutIntelR4(pop1, cpu->FpST0);
|
||
|
POPFLT;
|
||
|
}
|
||
|
|
||
|
FRAG1(FST64, DOUBLE) // FST m64real
|
||
|
{
|
||
|
FpArithDataPreamble(cpu, pop1);
|
||
|
|
||
|
if (cpu->FpST0->Tag == TAG_EMPTY) {
|
||
|
if (HandleStackEmpty(cpu, cpu->FpST0)) {
|
||
|
// unmasked exception - abort the instruction
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
PutIntelR8(pop1, cpu->FpST0);
|
||
|
}
|
||
|
|
||
|
FRAG1(FSTP64, DOUBLE) // FSTP m64real
|
||
|
{
|
||
|
FpArithDataPreamble(cpu, pop1);
|
||
|
|
||
|
if (cpu->FpST0->Tag == TAG_EMPTY) {
|
||
|
if (HandleStackEmpty(cpu, cpu->FpST0)) {
|
||
|
// unmasked exception - abort the instruction
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
PutIntelR8(pop1, cpu->FpST0);
|
||
|
POPFLT;
|
||
|
}
|
||
|
|
||
|
FRAG1IMM(FST_STi, INT) // FST ST(i)
|
||
|
{
|
||
|
FpArithPreamble(cpu);
|
||
|
|
||
|
CPUASSERT( (op1 & 0x07) == op1);
|
||
|
|
||
|
if (cpu->FpST0->Tag == TAG_EMPTY) {
|
||
|
if (HandleStackEmpty(cpu, cpu->FpST0)) {
|
||
|
// unmasked exception - abort the instruction
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
cpu->FpStack[ST(op1)] = *cpu->FpST0;
|
||
|
}
|
||
|
|
||
|
FRAG1IMM(FSTP_STi, INT) // FSTP ST(i)
|
||
|
{
|
||
|
FpArithPreamble(cpu);
|
||
|
|
||
|
CPUASSERT( (op1 & 0x07) == op1);
|
||
|
|
||
|
if (cpu->FpST0->Tag == TAG_EMPTY) {
|
||
|
if (HandleStackEmpty(cpu, cpu->FpST0)) {
|
||
|
// unmasked exception - abort the instruction
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
//CONSIDER: According to TimP, FSTP ST(0) is commonly used to pop the
|
||
|
// stack. It may be worthwhile to test if op1==0 and skip the
|
||
|
// assignment and go right to the POPFLT.
|
||
|
cpu->FpStack[ST(op1)] = *cpu->FpST0;
|
||
|
POPFLT;
|
||
|
}
|
||
|
|
||
|
FRAG0(OPT_FSTP_ST0) // FSTP ST(0)
|
||
|
{
|
||
|
FpArithPreamble(cpu);
|
||
|
|
||
|
if (cpu->FpST0->Tag == TAG_EMPTY) {
|
||
|
if (HandleStackEmpty(cpu, cpu->FpST0)) {
|
||
|
// unmasked exception - abort the instruction
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
POPFLT;
|
||
|
}
|