diff options
Diffstat (limited to 'private/mvdm/softpc.new/base/ccpu386/fpu.c')
-rw-r--r-- | private/mvdm/softpc.new/base/ccpu386/fpu.c | 5948 |
1 files changed, 5948 insertions, 0 deletions
diff --git a/private/mvdm/softpc.new/base/ccpu386/fpu.c b/private/mvdm/softpc.new/base/ccpu386/fpu.c new file mode 100644 index 000000000..cc6085479 --- /dev/null +++ b/private/mvdm/softpc.new/base/ccpu386/fpu.c @@ -0,0 +1,5948 @@ +/*[ + * ============================================================================ + * + * Name: fpu.c + * + * Author: Paul Murray + * + * Sccs ID: @(#)fpu.c 1.54 03/23/95 + * + * Purpose: + * + * Implements the Npx functionality of the Ccpu. + * + * (c)Copyright Insignia Solutions Ltd., 1993,1994. All rights reserved. + * + * ============================================================================ +]*/ +#include "insignia.h" +#include "host_def.h" +#include <math.h> +#include "cfpu_def.h" +#include "ckmalloc.h" + +typedef enum { +FPSTACK, +M16I, +M32I, +M64I, +M32R, +M64R, +M80R +} NPXOPTYPE; + + +/* Function prototypes - everything returns void */ +LOCAL FPH npx_rint IPT1(FPH, fpval); +LOCAL VOID GetIntelStatusWord IPT0(); +LOCAL VOID SetIntelTagword IPT1(IU32, new_tag); +LOCAL VOID ReadI16FromIntel IPT2(IU32 *, valI16, VOID *, memPtr); +LOCAL VOID ReadI32FromIntel IPT2(IU32 *, valI32, VOID *, memPtr); +LOCAL VOID WriteI16ToIntel IPT2(VOID *, memPtr, IU16, valI16); +LOCAL VOID WriteI32ToIntel IPT2(VOID *, memPtr, IU32, valI32); +LOCAL VOID WriteNaNToIntel IPT2(VOID *, memPtr, FPSTACKENTRY *, valPtr); +LOCAL VOID WriteZeroToIntel IPT2(VOID *, memPtr, IU16, negZero); +LOCAL VOID SetIntelStatusWord IPT1(IU32, new_stat); +LOCAL VOID AdjustOverflowResponse IPT0(); +LOCAL VOID AdjustUnderflowResponse IPT0(); +LOCAL VOID WriteIndefiniteToIntel IPT1(VOID *, memPtr); +LOCAL VOID SignalDivideByZero IPT1(FPSTACKENTRY *, stackPtr); +LOCAL VOID SetPrecisionBit IPT0(); +LOCAL VOID GetIntelTagword IPT1(IU32 *, current_tag); +LOCAL VOID WriteFP32ToIntel IPT2(VOID *, destPtr, FPSTACKENTRY *, srcPtr); +LOCAL VOID WriteFP64ToIntel IPT2(VOID *, destPtr, FPSTACKENTRY *, srcPtr); +LOCAL VOID WriteFP80ToIntel IPT2(VOID *, destPtr, FPSTACKENTRY *, srcPtr); +LOCAL VOID Mul64Bit8Bit IPT2(FPU_I64 *, as64, IU8, mul_count); +LOCAL VOID CopyFP IPT2(FPSTACKENTRY *, dest_addr, FPSTACKENTRY *, src_addr); +LOCAL VOID WriteBiggestNaN IPT3(IU16, destInd, FPSTACKENTRY *, val1Ptr, FPSTACKENTRY *, val2Ptr); +LOCAL VOID Sub64Bit64Bit IPT2(FPU_I64 *, as64a, FPU_I64 *, as64b); +LOCAL VOID CVTR80FPH IPT2(FPSTACKENTRY *, destPtr, FPSTACKENTRY *, srcPtr); +LOCAL BOOL Cmp64BitGTE IPT2(FPU_I64 *, as64a, FPU_I64 *, as64b); +LOCAL VOID CopyR32 IPT2(FPSTACKENTRY *, destPtr, VOID *, srcPtr); +LOCAL VOID CVTI64FPH IPT1(FPU_I64 *, as64); +LOCAL VOID CVTFPHI64 IPT2(FPU_I64 *, as64, FPH *, FPPtr); +LOCAL VOID Add64Bit8Bit IPT2(FPU_I64 *, as64, IU8, small_val); +LOCAL VOID CopyR64 IPT2(FPSTACKENTRY *, destPtr, VOID *, srcPtr); +LOCAL VOID CopyR80 IPT2(FPSTACKENTRY *, destPtr, VOID *, srcPtr); +LOCAL VOID CVTFPHR80 IPT1(FPSTACKENTRY *, memPtr); +LOCAL VOID WriteInfinityToIntel IPT2(VOID *, memPtr, IU16, neg_val); +LOCAL VOID PopStack IPT0(); +LOCAL VOID CPY64BIT8BIT IPT2(FPU_I64 *, as64, IU8 *, as8); +LOCAL VOID WriteIntegerIndefinite IPT1(VOID *, memPtr); +LOCAL VOID SignalStackOverflow IPT1(FPSTACKENTRY *, StackPtr); +LOCAL VOID Set64Bit IPT2(FPU_I64 *, as64, IU8, small_val); +LOCAL VOID Sub64Bit8Bit IPT2(FPU_I64 *, as64, IU8, small_val); +LOCAL VOID SignalBCDIndefinite IPT1(IU8 *, memPtr); +GLOBAL VOID InitNpx IPT1(IBOOL, disabled); +LOCAL VOID LoadValue IPT2(VOID *, SrcOp, IU16 *, IndexVal); +LOCAL VOID Loadi16ToFP IPT2(FPSTACKENTRY *, FPPtr, VOID *, memPtr); +LOCAL VOID Loadi32ToFP IPT2(FPSTACKENTRY *, FPPtr, VOID *, memPtr); +LOCAL VOID Loadi64ToFP IPT2(FPSTACKENTRY *, FPPtr, VOID *, memPtr); +LOCAL VOID Loadr32ToFP IPT3(FPSTACKENTRY *, FPPtr, VOID *, memPtr, BOOL, setTOS); +LOCAL VOID Loadr64ToFP IPT3(FPSTACKENTRY *, FPPtr, VOID *, memPtr, BOOL, setTOS); +LOCAL VOID Loadr80ToFP IPT2(FPSTACKENTRY *, FPPtr, VOID *, memPtr); +LOCAL VOID LoadTByteToFP IPT2(FPSTACKENTRY *, FPPtr, VOID *, memPtr); +LOCAL VOID ConvertR80 IPT1(FPSTACKENTRY *, memPtr); +LOCAL VOID PostCheckOUP IPT0(); +LOCAL VOID CalcTagword IPT1(FPSTACKENTRY *, FPPtr); +LOCAL VOID SignalStackUnderflow IPT1(FPSTACKENTRY *, StackPtr); +LOCAL VOID SignalSNaN IPT1(FPSTACKENTRY *, StackPtr); +LOCAL VOID SignalIndefinite IPT1(FPSTACKENTRY *, StackPtr); +LOCAL VOID SignalInvalid IPT0(); +LOCAL VOID WriteIndefinite IPT1(FPSTACKENTRY *, StackPtr); +LOCAL VOID Test2NaN IPT3(IU16, destIndex, FPSTACKENTRY *, src1_addr, FPSTACKENTRY *, src2_addr); +LOCAL VOID GenericAdd IPT3(IU16, destIndex, IU16, src1Index, IU16, src2Index); +LOCAL VOID AddBCDByte IPT2(FPU_I64 *, total, IU8, byte_val); +LOCAL VOID ConvertBCD IPT1(FPSTACKENTRY *, bcdPtr); +LOCAL VOID GenericCompare IPT1(IU16, src2Index); +LOCAL VOID GenericDivide IPT3(IU16, destIndex, IU16, src1Index, IU16, src2Index); +LOCAL VOID OpFpuStoreFpuState IPT2(VOID *, memPtr, IU32, fsave_offset); +LOCAL VOID OpFpuRestoreFpuState IPT2(VOID *, memPtr, IU32, frstor_offset); +LOCAL VOID GenericMultiply IPT3(IU16, destIndex, IU16, src1Index, IU16, src2Index); +LOCAL VOID CheckOUPForIntel IPT0(); +LOCAL VOID GenericSubtract IPT3(IU16, destIndex, IU16, src1Index, IU16, src2Index); +GLOBAL VOID F2XM1 IPT0(); +GLOBAL VOID FABS IPT0(); +GLOBAL VOID FADD IPT3(IU16, destIndex, IU16, src1Index, VOID *, src2); +GLOBAL VOID FBLD IPT1(IU8 *, memPtr); +GLOBAL VOID FBSTP IPT1(IU8 *, memPtr); +GLOBAL VOID FCHS IPT0(); +GLOBAL VOID FCLEX IPT0(); +GLOBAL VOID FCOM IPT1(VOID *, src2); +GLOBAL VOID FCOS IPT0(); +GLOBAL VOID FDECSTP IPT0(); +GLOBAL VOID FDIV IPT3(IU16, destIndex, IU16, src1Index, VOID *, src2); +GLOBAL VOID FFREE IPT1(IU16, destIndex); +GLOBAL VOID FLD IPT1(VOID *, memPtr); +GLOBAL VOID FINCSTP IPT0(); +GLOBAL VOID FINIT IPT0(); +GLOBAL VOID FIST IPT1(VOID *, memPtr); +GLOBAL VOID FLDCONST IPT1(IU8, const_index); +GLOBAL VOID FLDCW IPT1(VOID *, memPtr); +GLOBAL VOID FLDCW16 IPT1(VOID *, memPtr); +GLOBAL VOID FLDENV IPT1(VOID *, memPtr); +GLOBAL VOID FMUL IPT3(IU16, destIndex, IU16, src1Index, VOID *, src2); +GLOBAL VOID PTOP IPT0(); +GLOBAL VOID FPATAN IPT0(); +GLOBAL VOID FPREM IPT0(); +GLOBAL VOID FPREM1 IPT0(); +GLOBAL VOID FPTAN IPT0(); +GLOBAL VOID FRNDINT IPT0(); +GLOBAL VOID FSTCW IPT1(VOID *, memPtr); +GLOBAL VOID FRSTOR IPT1(VOID *, memPtr); +GLOBAL VOID FSAVE IPT1(VOID *, memPtr); +GLOBAL VOID FSCALE IPT0(); +GLOBAL VOID FSIN IPT0(); +GLOBAL VOID FSINCOS IPT0(); +GLOBAL VOID FSQRT IPT0(); +GLOBAL VOID FST IPT1(VOID *, memPtr); +GLOBAL VOID FSTENV IPT1(VOID *, memPtr); +GLOBAL VOID FSTSW IPT2(VOID *, memPtr, BOOL, toAX); +GLOBAL VOID FSUB IPT3(IU16, destIndex, IU16, src1Index, VOID *, src2); +GLOBAL VOID FTST IPT0(); +GLOBAL VOID FXAM IPT0(); +GLOBAL VOID FXCH IPT1(IU16, destIndex); +GLOBAL VOID FXTRACT IPT1(IU16, destIndex); +GLOBAL VOID FYL2X IPT0(); +GLOBAL VOID FYL2XP1 IPT0(); +GLOBAL IU32 getNpxControlReg IPT0(); +GLOBAL VOID setNpxControlReg IPT1(IU32, newControl); +GLOBAL IU32 getNpxStatusReg IPT0(); +GLOBAL VOID setNpxStatusReg IPT1(IU32, newStatus); +GLOBAL IU32 getNpxTagwordReg IPT0(); +GLOBAL VOID setNpxTagwordReg IPT1(IU32, newTag); +GLOBAL void getNpxStackRegs IPT1(FPSTACKENTRY *, dumpPtr); +GLOBAL void setNpxStackRegs IPT1(FPSTACKENTRY *, loadPtr); + +/* DEFINED values */ +#ifndef NULL +#define NULL ((VOID *)0) +#endif +#define TAG_NEGATIVE_MASK 1 +#define TAG_ZERO_MASK 2 +#define TAG_INFINITY_MASK 4 +#define TAG_DENORMAL_MASK 8 +#define TAG_NAN_MASK 16 +#define TAG_SNAN_MASK 32 +#define TAG_UNSUPPORTED_MASK 64 +#define TAG_EMPTY_MASK 128 +#define TAG_FSCALE_MASK 256 +#define TAG_BCD_MASK 512 +#define TAG_R80_MASK 1024 +#define UNEVALMASK 1536 +#define FPTEMP_INDEX (IU16)-1 +#define SW_IE_MASK 1 +#define SW_DE_MASK 2 +#define SW_ZE_MASK 4 +#define SW_OE_MASK 8 +#define SW_UE_MASK 16 +#define SW_PE_MASK 32 +#define SW_SF_MASK 64 +#define SW_ES_MASK 128 +#define C3C2C0MASK 0xb8ff +#define FCLEX_MASK 0x7f00 +#define CW_IM_MASK 1 +#define CW_DM_MASK 2 +#define CW_ZM_MASK 4 +#define CW_OM_MASK 8 +#define CW_UM_MASK 16 +#define CW_PM_MASK 32 +#define COMP_LT 0 +#define COMP_GT 1 +#define COMP_EQ 2 +#define INTEL_COMP_NC 0x4500 +#define INTEL_COMP_GT 0x0000 +#define INTEL_COMP_LT 0x0100 +#define INTEL_COMP_EQ 0x4000 +#define ROUND_NEAREST 0x0000 +#define ROUND_NEG_INFINITY 0x0400 +#define ROUND_POS_INFINITY 0x0800 +#define ROUND_ZERO 0x0c00 + +/* MACROS */ +#define FlagC0(x) NpxStatus &= 0xfeff; \ + NpxStatus |= ((x) << 8) +#define FlagC1(x) NpxStatus &= 0xfdff; \ + NpxStatus |= ((x) << 9) +#define FlagC2(x) NpxStatus &= 0xfbff; \ + NpxStatus |= ((x) << 10) +#define FlagC3(x) NpxStatus &= 0xbfff; \ + NpxStatus |= ((x) << 14) +#define TestUneval(testPtr) \ + if (((testPtr)->tagvalue & UNEVALMASK) != 0) { \ + switch ((testPtr)->tagvalue & UNEVALMASK) { \ + case TAG_BCD_MASK: ConvertBCD((testPtr)); \ + break; \ + case TAG_R80_MASK: ConvertR80((testPtr)); \ + break; \ + } \ + } + +#define StackEntryByIndex(i) (i==FPTEMP_INDEX? &FPTemp : &FPUStackBase[(TOSPtr-FPUStackBase+i)%8]) + +/* + * Pigging the FYL2X & FYL2XP1 opcodes requires that we use the same + * maths functions as the assembler CPU to avoid pig errors due to slight + * algorithmic differences; so allow host to specify different functions + * if it wants - by default we only require log(). + */ +#ifndef host_log2 +#define host_log2(x) (log(x)/log(2.0)) +#endif /* !host_log2 */ + +#ifndef host_log1p +#define host_log1p(x) (host_log2(1.0 + x)) +#endif /* !host_log1p */ + +/* + * System wide variables + */ +GLOBAL IU8 FPtype; +GLOBAL IU32 NpxLastSel; +GLOBAL IU32 NpxLastOff; +GLOBAL IU32 NpxFEA; +GLOBAL IU32 NpxFDS; +GLOBAL IU32 NpxFIP; +GLOBAL IU32 NpxFOP; +GLOBAL IU32 NpxFCS; +GLOBAL BOOL POPST; +GLOBAL BOOL DOUBLEPOP; +GLOBAL BOOL UNORDERED; +GLOBAL BOOL REVERSE; +GLOBAL BOOL NPX_ADDRESS_SIZE_32; +GLOBAL BOOL NPX_PROT_MODE; +GLOBAL BOOL NpxException; + +/* + * FPU-wide variables +*/ + +#ifdef SUN4 +LOCAL IU8 *FPout; /* HostGet*Exception() macros need this for Sparc ports. */ +#endif /* SUN4 */ + +LOCAL IU32 NpxControl; +LOCAL IU32 NpxStatus; +LOCAL BOOL DoAPop; +LOCAL IU16 tag_or; +LOCAL IU16 tag_xor; +LOCAL FPSTACKENTRY IntelSpecial; +LOCAL FPSTACKENTRY *FPUpload = &IntelSpecial; +LOCAL FPSTACKENTRY FPTemp; +LOCAL FPSTACKENTRY *FPUStackBase; +LOCAL FPSTACKENTRY *TOSPtr; +LOCAL IU16 npxRounding; +LOCAL FPH FPRes; +LOCAL FPH MaxBCDValue=999999999999999999.0; + +LOCAL IU8 zero_string[] = {"zero"}; +LOCAL IU8 minus_zero_string[] = {"minus zero"}; +LOCAL IU8 infinity_string[] = {"infinity"}; +LOCAL IU8 minus_infinity_string[] = {"minus infinity"}; +LOCAL IU8 nan_string[] = {" NaN"}; +LOCAL IU8 minus_nan_string[] = {" Negative NaN"}; +LOCAL IU8 unsupported_string[] = {"unsupported"}; +LOCAL IU8 unevaluated_string[] = {"unevaluated"}; +LOCAL IU8 empty_string[] = {"empty"}; +LOCAL IU8 convert_string[100]; + +LOCAL IU16 FscaleTable[] = { +0, +0, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_INFINITY_MASK, +TAG_ZERO_MASK, +0, +0, +0, +0, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_INFINITY_MASK | TAG_NEGATIVE_MASK, +TAG_ZERO_MASK | TAG_NEGATIVE_MASK, +0, +0, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK | TAG_UNSUPPORTED_MASK, +TAG_FSCALE_MASK, +0, +0, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK | TAG_UNSUPPORTED_MASK, +TAG_ZERO_MASK | TAG_NEGATIVE_MASK, +0, +0, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_INFINITY_MASK, +TAG_FSCALE_MASK | TAG_UNSUPPORTED_MASK, +0, +0, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK, +TAG_FSCALE_MASK | TAG_UNSUPPORTED_MASK, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0}; + +LOCAL FPSTACKENTRY ConstTable[]= { +{1.0, 0, 0}, /* 1.0 */ +{M_LN10/M_LN2, 0, 0}, /* Log2(10) */ +{M_LOG2E, 0, 0}, /* Log2(e) */ +{M_PI, 0, 0}, /* pi */ +{M_LN2/M_LN10, 0, 0}, /* Log10(2) */ +{M_LN2, 0, 0}, /* Loge(2) */ +{0.0, 0, TAG_ZERO_MASK} /* 0.0 */ +}; + +LOCAL FPSTACKENTRY FPConstants[] = { +{0.0, 0, TAG_ZERO_MASK}, +{-0.0, 0, (TAG_ZERO_MASK | TAG_NEGATIVE_MASK)}, +{1.0, 0, 0}, +{2.0, 0, 0}, +{M_PI, 0, 0}, +{-M_PI, 0, TAG_NEGATIVE_MASK}, +{M_PI_2, 0, 0}, +{-(M_PI_2), 0, TAG_NEGATIVE_MASK}, +{M_PI_4, 0, 0}, +{-(M_PI_4), 0, TAG_NEGATIVE_MASK}, +{3.0*M_PI_4, 0, 0}, +{-(3.0*M_PI_4), 0, TAG_NEGATIVE_MASK} +}; + +LOCAL FPSTACKENTRY *npx_zero = FPConstants + 0; +LOCAL FPSTACKENTRY *npx_minus_zero = FPConstants + 1; +LOCAL FPSTACKENTRY *npx_one = FPConstants + 2; +LOCAL FPSTACKENTRY *npx_two = FPConstants + 3; +LOCAL FPSTACKENTRY *npx_pi = FPConstants + 4; +LOCAL FPSTACKENTRY *npx_minus_pi = FPConstants + 5; +LOCAL FPSTACKENTRY *npx_pi_by_two = FPConstants + 6; +LOCAL FPSTACKENTRY *npx_minus_pi_by_two = FPConstants + 7; +LOCAL FPSTACKENTRY *npx_pi_by_four = FPConstants + 8; +LOCAL FPSTACKENTRY *npx_minus_pi_by_four = FPConstants + 9; +LOCAL FPSTACKENTRY *npx_three_pi_by_four = FPConstants + 10; +LOCAL FPSTACKENTRY *npx_minus_three_pi_by_four = FPConstants + 11; + +LOCAL IU32 CompZeroTable[] = { +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_GT, +INTEL_COMP_GT, +INTEL_COMP_LT, +INTEL_COMP_GT, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_LT, +INTEL_COMP_LT, +INTEL_COMP_LT, +INTEL_COMP_GT, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_LT, /* 16 */ +INTEL_COMP_GT, +INTEL_COMP_EQ, +INTEL_COMP_EQ, +INTEL_COMP_LT, +INTEL_COMP_GT, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_LT, +INTEL_COMP_GT, +INTEL_COMP_EQ, +INTEL_COMP_EQ, +INTEL_COMP_LT, +INTEL_COMP_GT, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_GT, /* 32 */ +INTEL_COMP_GT, +INTEL_COMP_GT, +INTEL_COMP_GT, +INTEL_COMP_EQ, +INTEL_COMP_GT, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_LT, +INTEL_COMP_LT, +INTEL_COMP_LT, +INTEL_COMP_LT, +INTEL_COMP_LT, +INTEL_COMP_EQ, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, /* 48 */ +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC, +INTEL_COMP_NC +}; + +#ifdef BIGEND +/* Note enforcement of word ordering as high word/low word */ +LOCAL FPU_I64 BCDLowNibble[] = { +{0x002386f2, 0x6fc10000}, +{0x00005af3, 0x107a4000}, +{0x000000e8, 0xd4a51000}, +{0x00000002, 0x540be400}, +{0x00000000, 0x05f5e100}, +{0x00000000, 0x000f4240}, +{0x00000000, 0x00002710}, +{0x00000000, 0x00000064}, +{0x00000000, 0x00000001} +}; + +LOCAL FPU_I64 BCDHighNibble[] = { +{0x01634578, 0x5d8a0000}, +{0x00038d7e, 0xa4c68000}, +{0x00000918, 0x4e72a000}, +{0x00000017, 0x4876e800}, +{0x00000000, 0x3b9aca00}, +{0x00000000, 0x00989680}, +{0x00000000, 0x000186a0}, +{0x00000000, 0x000003e8}, +{0x00000000, 0x0000000a} +}; +#else /* !BIGEND */ +LOCAL FPU_I64 BCDLowNibble[] = { +{0x6fc10000, 0x002386f2}, +{0x107a4000, 0x00005af3}, +{0xd4a51000, 0x000000e8}, +{0x540be400, 0x00000002}, +{0x05f5e100, 0x00000000}, +{0x000f4240, 0x00000000}, +{0x00002710, 0x00000000}, +{0x00000064, 0x00000000}, +{0x00000001, 0x00000000} +}; + +LOCAL FPU_I64 BCDHighNibble[] = { +{0x5d8a0000, 0x01634578}, +{0xa4c68000, 0x00038d7e}, +{0x4e72a000, 0x00000918}, +{0x4876e800, 0x00000017}, +{0x3b9aca00, 0x00000000}, +{0x00989680, 0x00000000}, +{0x000186a0, 0x00000000}, +{0x000003e8, 0x00000000}, +{0x0000000a, 0x00000000} +}; +#endif /* !BIGEND */ + + +LOCAL FPSTACKENTRY *FpatanTable[64]; + +LOCAL IBOOL NpxDisabled = FALSE; /* Set by the UIF */ + +/* Imported functions */ +IMPORT VOID DoNpxException(); + + +LOCAL FPH npx_rint IFN1(FPH, fpval) +{ + FPH localfp; + + switch (NpxControl & ROUND_ZERO) { + case ROUND_NEAREST : + localfp = fpval - floor(fpval); + if (localfp > 0.5) { + localfp = ceil(fpval); + } else { + if (localfp < 0.5) { + localfp = floor(fpval); + } else { + if ((fpval-localfp)/2.0 != floor((fpval-localfp)/2.0)) { + localfp = ceil(fpval); + } else { + localfp = floor(fpval); + } + } + } + break; + case ROUND_NEG_INFINITY : + localfp = floor(fpval); + /* help the poor HP over this hurdle... */ + if ( fpval >= localfp + 1.0 ) + localfp += 1.0; + break; + case ROUND_POS_INFINITY : + localfp = ceil(fpval); + /* help the poor HP over this hurdle... */ + if ( fpval <= localfp - 1.0 ) + localfp -= 1.0; + break; + case ROUND_ZERO : + if (fpval < 0.0) { + localfp = ceil(fpval); + } else { + localfp = floor(fpval); + } + break; + } + /* Check sign of zero */ + if (localfp == 0.0) { + if (fpval < 0.0) { + ((FPHOST *)&(localfp))->hiword.sign = 1; + } else { + ((FPHOST *)&(localfp))->hiword.sign = 0; + } + } + return(localfp); +} + + +LOCAL VOID GetIntelStatusWord IFN0() +{ + /* The status word already contains the correct 'sticky' bits */ + /* for any potential exceptions. What need to be filled in are */ + /* the flag bits and the ST value */ + NpxStatus &= 0xc7ff; /* Clear the st bits */ + NpxStatus |= ((TOSPtr-FPUStackBase) << 11); +} + + +LOCAL VOID SetIntelTagword IFN1(IU32, new_tag) +{ + FPSTACKENTRY *tagPtr = FPUStackBase; + IU8 counter = 0; + + /* We only consider whether the thing is marked as empty or not. + If it is anything other than empty we will want to precisely calculate + it by using CalcTagword() */ + while (counter++ < 8) { + if ((new_tag & 3) == 3) { + /* It's empty */ + tagPtr->tagvalue = TAG_EMPTY_MASK; + } else { + tagPtr->tagvalue = 0; + } + new_tag >>= 2; + tagPtr++; + } +} + + +/* Reads and writes for 16 and 32 bit integers are easy as they are handled +correctly in order to satisfy the integer CPU */ +/* This function is only called from fldenv/frstor where 16-bit data has to +be extracted from a large (bigendian organised) buffer */ +LOCAL VOID ReadI16FromIntel IFN2(IU32 *, valI16, VOID *, memPtr) +{ + IU32 res; + + res = *((IU8 *)memPtr + 0); + res <<= 8; + res |= *((IU8 *)memPtr + 1); + *valI16 = res; +} + + +/* This function is only called from fldwnv/frstor where 32-bit data has to +be extrated from a large (bigendian organised) buffer */ +LOCAL VOID ReadI32FromIntel IFN2(IU32 *, valI32, VOID *, memPtr) +{ + IU32 res; + + res = *((IU8 *)memPtr + 0); + res <<= 8; + res |= *((IU8 *)memPtr + 1); + res <<= 8; + res |= *((IU8 *)memPtr + 2); + res <<= 8; + res |= *((IU8 *)memPtr + 3); + *valI32 = res; +} + +/* This function is only used in fsave/fstenv */ +LOCAL VOID WriteI16ToIntel IFN2(VOID *, memPtr, IU16, valI16) +{ + *((IU8 *)memPtr + 1) = (IU8)(valI16 & 0xff); + valI16 >>= 8; + *((IU8 *)memPtr + 0) = (IU8)(valI16 & 0xff); +} + + +/* And so is this one */ +LOCAL VOID WriteI32ToIntel IFN2(VOID *, memPtr, IU32, valI32) +{ + *((IU8 *)memPtr + 3) = (IU8)(valI32 & 0xff); + valI32 >>= 8; + *((IU8 *)memPtr + 2) = (IU8)(valI32 & 0xff); + valI32 >>= 8; + *((IU8 *)memPtr + 1) = (IU8)(valI32 & 0xff); + valI32 >>= 8; + *((IU8 *)memPtr + 0) = (IU8)(valI32 & 0xff); +} + + +/* Anything over 32-bits becomes painful as data is read and written using +the vir_read_bytes and vir_write_bytes routines respectively, which simply +dump data from the topmost intel address to the lowest intel address. The +value of the offsets is defined one way round for bigendian ports and the +other way for little-endian */ +LOCAL VOID WriteNaNToIntel IFN2(VOID *, memPtr, FPSTACKENTRY *, valPtr) +{ + IU32 mant_hi; + IU32 mant_lo; + + /* Ok for endian-ness as we FORCE this presentation */ + mant_hi = ((IU32 *)&(valPtr->fpvalue))[NPX_HIGH_32_BITS]; + mant_lo = ((IU32 *)&(valPtr->fpvalue))[NPX_LOW_32_BITS]; + if (FPtype == M32R) { + /* OK since this forces the output to be independent of + endian-ness. */ + mant_hi |= 0x40000000; /* Make it quiet */ + mant_hi >>= 8; + if ((valPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + mant_hi |= 0xff000000; + } else { + mant_hi |= 0x7f000000; + } + *(IU32 *)memPtr = mant_hi; + } + if (FPtype == M64R) { + mant_hi |= 0x40000000; /* Make it quiet */ + if ((valPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + *((IU8 *)memPtr + 0) = 0xff; + } else { + *((IU8 *)memPtr + 0) = 0x7f; + } + mant_lo >>= 3; + mant_lo |= (mant_hi << 29); + mant_hi >>= 3; + mant_hi |= 0xe0000000; + mant_lo >>= 8; + *((IU8 *)memPtr + 7) = (mant_lo & 0xff); + mant_lo >>= 8; + *((IU8 *)memPtr + 6) = (mant_lo & 0xff); + mant_lo >>= 8; + *((IU8 *)memPtr + 5) = (mant_lo & 0xff); + *((IU8 *)memPtr + 4) = (mant_hi & 0xff); + mant_hi >>= 8; + *((IU8 *)memPtr + 3) = (mant_hi & 0xff); + mant_hi >>= 8; + *((IU8 *)memPtr + 2) = (mant_hi & 0xff); + mant_hi >>= 8; + *((IU8 *)memPtr + 1) = (mant_hi & 0xff); + } + if (FPtype == M80R) { + if ((valPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + *((IU8 *)memPtr + 0) = 0xff; + } else { + *((IU8 *)memPtr + 0) = 0x7f; + } + *((IU8 *)memPtr + 1) = 0xff; + *((IU8 *)memPtr + 9) = (mant_lo & 0xff); + mant_lo >>= 8; + *((IU8 *)memPtr + 8) = (mant_lo & 0xff); + mant_lo >>= 8; + *((IU8 *)memPtr + 7) = (mant_lo & 0xff); + mant_lo >>= 8; + *((IU8 *)memPtr + 6) = (mant_lo & 0xff); + *((IU8 *)memPtr + 5) = (mant_hi & 0xff); + mant_hi >>= 8; + *((IU8 *)memPtr + 4) = (mant_hi & 0xff); + mant_hi >>= 8; + *((IU8 *)memPtr + 3) = (mant_hi & 0xff); + mant_hi >>= 8; + *((IU8 *)memPtr + 2) = (mant_hi & 0xff); + } +} + + +LOCAL VOID WriteZeroToIntel IFN2(VOID *, memPtr, IU16, negZero) +{ + if (FPtype == M32R) { + if (negZero == 0) { + *(IU32 *)memPtr = 0x00000000; + } else { + *(IU32 *)memPtr = 0x80000000; + } + } else { + if (FPtype == M80R) { + if (negZero == 0) { + *((IU8 *)memPtr + 0) = 0; + } else { + *((IU8 *)memPtr + 0) = 0x80; + } + *((IU8 *)memPtr + 1) = 0; + *((IU8 *)memPtr + 2) = 0; + *((IU8 *)memPtr + 3) = 0; + *((IU8 *)memPtr + 4) = 0; + *((IU8 *)memPtr + 5) = 0; + *((IU8 *)memPtr + 6) = 0; + *((IU8 *)memPtr + 7) = 0; + *((IU8 *)memPtr + 8) = 0; + *((IU8 *)memPtr + 9) = 0; + } else { + if (negZero == 0) { + *((IU8 *)memPtr + 0) = 0; + } else { + *((IU8 *)memPtr + 0) = 0x80; + } + *((IU8 *)memPtr + 1) = 0; + *((IU8 *)memPtr + 2) = 0; + *((IU8 *)memPtr + 3) = 0; + *((IU8 *)memPtr + 4) = 0; + *((IU8 *)memPtr + 5) = 0; + *((IU8 *)memPtr + 6) = 0; + *((IU8 *)memPtr + 7) = 0; + } + } +} + + +LOCAL VOID SetIntelStatusWord IFN1(IU32, new_stat) +{ + TOSPtr = &FPUStackBase[(new_stat >> 11) & 0x7]; + NpxStatus = new_stat; +} + + +LOCAL VOID AdjustOverflowResponse IFN0() +{ +} + + +LOCAL VOID AdjustUnderflowResponse IFN0() +{ +} + + +LOCAL VOID WriteIndefiniteToIntel IFN1(VOID *, memPtr) +{ + switch (FPtype) { + case M32R : *(IU32 *)memPtr = 0xffc00000; + break; + case M64R : *((IU8 *)memPtr + 0) = 0xff; + *((IU8 *)memPtr + 1) = 0xf8; + *((IU8 *)memPtr + 2) = 0; + *((IU8 *)memPtr + 3) = 0; + *((IU8 *)memPtr + 4) = 0; + *((IU8 *)memPtr + 5) = 0; + *((IU8 *)memPtr + 6) = 0; + *((IU8 *)memPtr + 7) = 0; + break; + case M80R : *((IU8 *)memPtr + 0) = 0xff; + *((IU8 *)memPtr + 1) = 0xff; + *((IU8 *)memPtr + 2) = 0xc0; + *((IU8 *)memPtr + 3) = 0; + *((IU8 *)memPtr + 4) = 0; + *((IU8 *)memPtr + 5) = 0; + *((IU8 *)memPtr + 6) = 0; + *((IU8 *)memPtr + 7) = 0; + *((IU8 *)memPtr + 8) = 0; + *((IU8 *)memPtr + 9) = 0; + break; + } +} + + +LOCAL VOID SignalDivideByZero IFN1(FPSTACKENTRY *, stackPtr) +{ + /* Raise divide by zero */ + NpxStatus |= SW_ZE_MASK; + if ((NpxControl & CW_ZM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } + stackPtr->tagvalue = TAG_INFINITY_MASK + (tag_xor & TAG_NEGATIVE_MASK); +} + +LOCAL VOID SetPrecisionBit IFN0() +{ + NpxStatus |= SW_PE_MASK; + if (npxRounding == ROUND_POS_INFINITY) { + FlagC1(1); + } else { + FlagC1(0); + } +} + +LOCAL VOID GetIntelTagword IFN1(IU32 *, current_tag) +{ + FPSTACKENTRY *tagPtr = &FPUStackBase[7]; + IU8 counter = 0; + + *current_tag = 0; + while (counter++ < 8) { + TestUneval(tagPtr); + *current_tag <<= 2; + if ((tagPtr->tagvalue & TAG_EMPTY_MASK) != 0) { + *current_tag |= 3; + } else { + if ((tagPtr->tagvalue & TAG_ZERO_MASK) != 0) { + *current_tag |= 1; + } else { + if ((tagPtr->tagvalue & ~TAG_NEGATIVE_MASK) != 0) { + *current_tag |= 2; + } + } + } + tagPtr--; + } +} + + +/* These functions write host format quantities out to the (bigendian +organised) intel memory. This requires that we define an ordering between the +two. The values in HOST_xxx are dependent upon the endian-ness of the port */ +/* According to this organisation, HOST_nnn_BYTE_0 is the offset to the most +significant byte in the representation of this format, and so on. */ +LOCAL VOID WriteFP32ToIntel IFN2(VOID *, destPtr, FPSTACKENTRY *, srcPtr) +{ + *(IU32 *)destPtr = *(IU32 *)srcPtr; +} + + +LOCAL VOID WriteFP64ToIntel IFN2(VOID *, destPtr, FPSTACKENTRY *, srcPtr) +{ + *((IU8 *)destPtr + 0) = *((IU8 *)srcPtr + HOST_R64_BYTE_0); + *((IU8 *)destPtr + 1) = *((IU8 *)srcPtr + HOST_R64_BYTE_1); + *((IU8 *)destPtr + 2) = *((IU8 *)srcPtr + HOST_R64_BYTE_2); + *((IU8 *)destPtr + 3) = *((IU8 *)srcPtr + HOST_R64_BYTE_3); + *((IU8 *)destPtr + 4) = *((IU8 *)srcPtr + HOST_R64_BYTE_4); + *((IU8 *)destPtr + 5) = *((IU8 *)srcPtr + HOST_R64_BYTE_5); + *((IU8 *)destPtr + 6) = *((IU8 *)srcPtr + HOST_R64_BYTE_6); + *((IU8 *)destPtr + 7) = *((IU8 *)srcPtr + HOST_R64_BYTE_7); +} + + +LOCAL VOID WriteFP80ToIntel IFN2(VOID *, destPtr, FPSTACKENTRY *, srcPtr) +{ + *((IU8 *)destPtr + 0) = *((IU8 *)srcPtr + HOST_R80_BYTE_0); + *((IU8 *)destPtr + 1) = *((IU8 *)srcPtr + HOST_R80_BYTE_1); + *((IU8 *)destPtr + 2) = *((IU8 *)srcPtr + HOST_R80_BYTE_2); + *((IU8 *)destPtr + 3) = *((IU8 *)srcPtr + HOST_R80_BYTE_3); + *((IU8 *)destPtr + 4) = *((IU8 *)srcPtr + HOST_R80_BYTE_4); + *((IU8 *)destPtr + 5) = *((IU8 *)srcPtr + HOST_R80_BYTE_5); + *((IU8 *)destPtr + 6) = *((IU8 *)srcPtr + HOST_R80_BYTE_6); + *((IU8 *)destPtr + 7) = *((IU8 *)srcPtr + HOST_R80_BYTE_7); + *((IU8 *)destPtr + 8) = *((IU8 *)srcPtr + HOST_R80_BYTE_8); + *((IU8 *)destPtr + 9) = *((IU8 *)srcPtr + HOST_R80_BYTE_9); +} + + +LOCAL VOID Mul64Bit8Bit IFN2(FPU_I64 *, as64, IU8, mul_count) +{ + CVTI64FPH(as64); + FPRes *= (FPH)mul_count; + CVTFPHI64(as64, &FPRes); +} + + +LOCAL VOID CopyFP IFN2(FPSTACKENTRY *, dest_addr, FPSTACKENTRY *, src_addr) +{ + (VOID)memcpy((VOID *)dest_addr, (VOID *)src_addr, sizeof(FPSTACKENTRY)); +} + + +LOCAL VOID MakeNaNQuiet IFN1(FPSTACKENTRY *, srcPtr) +{ + NpxStatus |= SW_IE_MASK; + NpxStatus &= ~SW_SF_MASK; + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + DoAPop=FALSE; + } else { + srcPtr->tagvalue ^= TAG_SNAN_MASK; + ((IU32 *)&(srcPtr->fpvalue))[NPX_HIGH_32_BITS] |= 0x40000000; + } +} + + +LOCAL VOID WriteBiggestNaN IFN3(IU16, destInd, FPSTACKENTRY *, val1Ptr, FPSTACKENTRY *, val2Ptr) +{ + FPSTACKENTRY *destPtr = StackEntryByIndex(destInd); + + /* We explicitely and deliberately store NaNs as two 32-bit values high word then low word */ + if (((IU32 *)&(val1Ptr->fpvalue))[NPX_HIGH_32_BITS] == ((IU32 *)&(val2Ptr->fpvalue))[NPX_HIGH_32_BITS]) { + if (((IU32 *)&(val1Ptr->fpvalue))[NPX_LOW_32_BITS] >= ((IU32 *)&(val2Ptr->fpvalue))[NPX_LOW_32_BITS]) { + /* It's val1 */ + CopyFP(destPtr, val1Ptr); + } else { + CopyFP(destPtr, val2Ptr); + } + } else { + if (((IU32 *)&(val1Ptr->fpvalue))[NPX_HIGH_32_BITS] > ((IU32 *)&(val2Ptr->fpvalue))[NPX_HIGH_32_BITS]) { + /* It's val1 */ + CopyFP(destPtr, val1Ptr); + } else { + CopyFP(destPtr, val2Ptr); + } + } + /* Always make it a quiet NaN */ + ((IU32 *)&(destPtr->fpvalue))[NPX_HIGH_32_BITS] |= 0x40000000; + destPtr->tagvalue &= ~TAG_SNAN_MASK; +} + + +LOCAL VOID Sub64Bit64Bit IFN2(FPU_I64 *, as64a, FPU_I64 *, as64b) +{ + FPH FPlocal; + + CVTI64FPH(as64b); + FPlocal = FPRes; + CVTI64FPH(as64a); + FPRes -= FPlocal; + CVTFPHI64(as64a, &FPRes); +} + + +LOCAL VOID CVTR80FPH IFN2(FPSTACKENTRY *, destPtr, FPSTACKENTRY *, srcPtr) +{ + IU32 munger; + IU16 bitleft; + + /* First, copy the sign bit */ + ((FPHOST *)&(destPtr->fpvalue))->hiword.sign = ((FP80 *)&(srcPtr->fpvalue))->sign_exp.sign; + /* Then, copy the modified exponent */ + munger = (IU32)((FP80 *)&(srcPtr->fpvalue))->sign_exp.exp; + munger -= (16383 - HOST_BIAS); + ((FPHOST *)&(destPtr->fpvalue))->hiword.exp = munger; + /* Finally, the mantissa */ + munger = (IU32)((FP80 *)&(srcPtr->fpvalue))->mant_hi; + munger <<= 1; + ((FPHOST *)&(destPtr->fpvalue))->hiword.mant_hi = (munger >> 12); + munger <<= 20; + munger |= ((FP80 *)&(srcPtr->fpvalue))->mant_lo >> 11; + bitleft = ((FP80 *)&(srcPtr->fpvalue))->mant_lo & 0x7ff; + + if (bitleft != 0) { + switch (NpxControl & ROUND_ZERO) { + case ROUND_NEAREST : + if (bitleft > 0x3ff) { + munger += 1; + } + break; + case ROUND_NEG_INFINITY : + if (((FPHOST *)&(destPtr->fpvalue))->hiword.sign = 1) { + munger += 1; + } + break; + case ROUND_POS_INFINITY : + if (((FPHOST *)&(destPtr->fpvalue))->hiword.sign = 0) { + munger += 1; + } + break; + case ROUND_ZERO : + /* Do nothing */ + break; + } + } + ((FPHOST *)&(destPtr->fpvalue))->mant_lo = munger; +} + + +LOCAL BOOL Cmp64BitGTE IFN2(FPU_I64 *, as64a, FPU_I64 *, as64b) +{ + FPH FPlocal; + + CVTI64FPH(as64b); + FPlocal = FPRes; + CVTI64FPH(as64a); + return(FPRes >= FPlocal); +} + + +LOCAL VOID CopyR32 IFN2(FPSTACKENTRY *, destPtr, VOID *, srcPtr) +{ + *(IU32 *)destPtr = *(IU32 *)srcPtr; +} + + +LOCAL VOID CVTI64FPH IFN1(FPU_I64 *, as64) +{ + FPRes = (FPH)as64->high_word * 4294967296.0 + (FPH)as64->low_word; +} + + +LOCAL VOID CVTFPHI64 IFN2(FPU_I64 *, as64, FPH *, FPPtr) +{ + IU32 high32 = 0; + IU32 low32 = 0; + IS32 exp; + IU32 holder; + IU32 signbit = 0; + + exp = ((FPHOST *)FPPtr)->hiword.exp; + if (exp != 0) { + high32 = ((FPHOST *)FPPtr)->hiword.mant_hi; + low32 = ((FPHOST *)FPPtr)->mant_lo; + /* Now stick a 1 at the top of the mantissa */ + /* Calculate where this is */ + holder = HOST_MAX_EXP+1; + signbit = 1; + while (holder >>= 1) { + signbit += 1; + } + high32 |= (1 << (32-signbit)); + exp -= HOST_BIAS; + exp -= (64-signbit); + + signbit = ((FPHOST *)FPPtr)->hiword.sign; + + /* high32 and low32 are (mantissa)*(2^52 ) + * exp is (true exponent-52) = number of bit positions to shift + * +ve implies shift left, -ve implies shift right + */ + if (exp > 0) { + if (exp >= 32) { + high32 = low32 << ( exp - 32 ) ; + low32 = 0; + } else { + high32 = high32 << exp ; + holder = low32 >> ( 32 -exp ) ; + high32 = high32 | holder ; + low32 = low32 << exp ; + } + } else { + if ( exp < 0) { + exp = -exp; + if ( exp >= 32 ) { + low32 = high32 >> ( exp - 32 ) ; + high32 = 0 ; + } else { + low32 = low32 >> exp ; + holder = high32 << ( 32 -exp ) ; + low32 = low32 | holder ; + high32 = high32 >> exp ; + } + } + } + } + if (signbit != 0) { + /* Make it negative */ + high32 ^= 0xffffffff; + low32 ^= 0xffffffff; + low32 += 1; + if (low32 == 0) { + high32 += 1; + } + } + as64->high_word = high32; + as64->low_word = low32; +} + + +LOCAL VOID Add64Bit8Bit IFN2(FPU_I64 *, as64, IU8, small_val) +{ + CVTI64FPH(as64); + FPRes += (FPH)small_val; + CVTFPHI64(as64, &FPRes); +} + + +LOCAL VOID CopyR64 IFN2(FPSTACKENTRY *, destPtr, VOID *, srcPtr) +{ + *((IU8 *)destPtr + HOST_R64_BYTE_0) = *((IU8 *)srcPtr + 0); + *((IU8 *)destPtr + HOST_R64_BYTE_1) = *((IU8 *)srcPtr + 1); + *((IU8 *)destPtr + HOST_R64_BYTE_2) = *((IU8 *)srcPtr + 2); + *((IU8 *)destPtr + HOST_R64_BYTE_3) = *((IU8 *)srcPtr + 3); + *((IU8 *)destPtr + HOST_R64_BYTE_4) = *((IU8 *)srcPtr + 4); + *((IU8 *)destPtr + HOST_R64_BYTE_5) = *((IU8 *)srcPtr + 5); + *((IU8 *)destPtr + HOST_R64_BYTE_6) = *((IU8 *)srcPtr + 6); + *((IU8 *)destPtr + HOST_R64_BYTE_7) = *((IU8 *)srcPtr + 7); +} + +/* + * CopyR80 is different from the above as it is called to copy + * between FPSTACKENTRYs. Copy straight through. + */ +LOCAL VOID CopyR80 IFN2(FPSTACKENTRY *, destPtr, VOID *, srcPtr) +{ + *(FP80 *)destPtr = *(FP80 *)srcPtr; +} + + +LOCAL VOID CVTFPHR80 IFN1(FPSTACKENTRY *, memPtr) +{ + IU32 munger; + + /* First, copy the sign bit */ + ((FP80 *)&(FPTemp.fpvalue))->sign_exp.sign = ((FPHOST *)&(memPtr->fpvalue))->hiword.sign; + /* Then, copy the modified exponent */ + munger = (IU32)((FPHOST *)&(memPtr->fpvalue))->hiword.exp; + munger += (16383 - HOST_BIAS); + ((FP80 *)&(FPTemp.fpvalue))->sign_exp.exp = munger; + /* Finally, the mantissa */ + munger = (IU32)((FPHOST *)&(memPtr->fpvalue))->hiword.mant_hi; + munger <<= 11; + munger |= 0x80000000; + ((FP80 *)&(FPTemp.fpvalue))->mant_hi = munger | (((FPHOST *)&(memPtr->fpvalue))->mant_lo >> 21); + ((FP80 *)&(FPTemp.fpvalue))->mant_lo = ((((FPHOST *)&(memPtr->fpvalue))->mant_lo) << 11); +} + + +LOCAL VOID WriteInfinityToIntel IFN2(VOID *, memPtr, IU16, neg_val) +{ + if (FPtype == M32R) { + if (neg_val == 0) { + *(IU32 *)memPtr = 0x7f800000; + } else { + *(IU32 *)memPtr = 0xff800000; + } + } else { + if (FPtype == M80R) { + if (neg_val == 0) { + *((IU8 *)memPtr + 0) = 0x7f; + } else { + *((IU8 *)memPtr + 0) = 0xff; + } + *((IU8 *)memPtr + 1) = 0xff; + *((IU8 *)memPtr + 2) = 0x80; + *((IU8 *)memPtr + 3) = 0; + *((IU8 *)memPtr + 4) = 0; + *((IU8 *)memPtr + 5) = 0; + *((IU8 *)memPtr + 6) = 0; + *((IU8 *)memPtr + 7) = 0; + *((IU8 *)memPtr + 8) = 0; + *((IU8 *)memPtr + 9) = 0; + } else { + if (neg_val == 0) { + *((IU8 *)memPtr + 0) = 0x7f; + } else { + *((IU8 *)memPtr + 0) = 0xff; + } + *((IU8 *)memPtr + 1) = 0xf0; + *((IU8 *)memPtr + 2) = 0; + *((IU8 *)memPtr + 3) = 0; + *((IU8 *)memPtr + 4) = 0; + *((IU8 *)memPtr + 5) = 0; + *((IU8 *)memPtr + 6) = 0; + *((IU8 *)memPtr + 7) = 0; + } + } +} + + +LOCAL VOID PopStack IFN0() +{ + /* Mark current TOS as free */ + TOSPtr->tagvalue = TAG_EMPTY_MASK; + TOSPtr = StackEntryByIndex(1); + DoAPop = FALSE; +} + + +LOCAL VOID CPY64BIT8BIT IFN2(FPU_I64 *, as64, IU8 *, as8) +{ + *as8 = (as64->low_word & 0xff); +} + + +LOCAL VOID WriteIntegerIndefinite IFN1(VOID *, memPtr) +{ + switch (FPtype) { + case M16I : *((IU32 *)memPtr) = 0x8000; + break; + case M32I : *((IU32 *)memPtr) = 0x80000000; + break; + case M64I : *((IU8 *)memPtr + 0) = 0x80; + *((IU8 *)memPtr + 1) = 0; + *((IU8 *)memPtr + 2) = 0; + *((IU8 *)memPtr + 3) = 0; + *((IU8 *)memPtr + 4) = 0; + *((IU8 *)memPtr + 5) = 0; + *((IU8 *)memPtr + 6) = 0; + *((IU8 *)memPtr + 7) = 0; + break; + } +} + + +/*( +Name : SignalStackOverflow +Function : To set the required bits in the status word following + a stack overflow exception, and to issue the required + response. +)*/ + + +LOCAL VOID SignalStackOverflow IFN1(FPSTACKENTRY *, StackPtr) +{ + NpxStatus |= (SW_IE_MASK | SW_SF_MASK); + FlagC1(1); + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + DoAPop=FALSE; /* Just in case it was set */ + } else { + WriteIndefinite(StackPtr); + } +} + + +LOCAL VOID Set64Bit IFN2(FPU_I64 *, as64, IU8, small_val) +{ + as64->high_word = 0; + as64->low_word = small_val; +} + + +LOCAL VOID Sub64Bit8Bit IFN2(FPU_I64 *, as64, IU8, small_val) +{ + CVTI64FPH(as64); + FPRes -= (FPH)small_val; + CVTFPHI64(as64, &FPRes); +} + + +LOCAL VOID SignalBCDIndefinite IFN1(IU8 *, memPtr) +{ + *((IU8 *)memPtr + 0) = 0xff; + *((IU8 *)memPtr + 1) = 0xff; + *((IU8 *)memPtr + 2) = 0xc0; + *((IU8 *)memPtr + 3) = 0; + *((IU8 *)memPtr + 4) = 0; + *((IU8 *)memPtr + 5) = 0; + *((IU8 *)memPtr + 6) = 0; + *((IU8 *)memPtr + 7) = 0; + *((IU8 *)memPtr + 8) = 0; + *((IU8 *)memPtr + 9) = 0; +} + +/* Called from cpu_init and cpu_reset */ + +GLOBAL VOID InitNpx IFN1(IBOOL, disabled) +{ + IU16 i; + IU8 *bottom_ptr; + IU16 stackPtr = 0; + SAVED IBOOL first = TRUE; + + /* Set up a couple of control type things */ + NpxException = FALSE; + NPX_ADDRESS_SIZE_32 = FALSE; + NPX_PROT_MODE = FALSE; + + if (first) + { + /* Get the required memory */ +#ifndef SFELLOW + check_malloc(FPUStackBase, 8, FPSTACKENTRY); +#else + FPUStackBase = (FPSTACKENTRY *)SFMalloc(8*sizeof(FPSTACKENTRY), FALSE); +#endif /* SFELLOW */ + first = FALSE; + } + + for (i=0; i<8; i++) { + (FPUStackBase+i)->tagvalue = TAG_EMPTY_MASK; + } + TOSPtr = FPUStackBase; + DoAPop = FALSE; + + i=0; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = npx_pi_by_two; + FpatanTable[i++] = npx_pi_by_two; + FpatanTable[i++] = npx_zero; + FpatanTable[i++] = npx_pi; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = npx_minus_pi_by_two; + FpatanTable[i++] = npx_minus_pi_by_two; + FpatanTable[i++] = npx_minus_zero; + FpatanTable[i++] = npx_minus_pi; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = npx_zero; + FpatanTable[i++] = npx_pi; + FpatanTable[i++] = npx_zero; + FpatanTable[i++] = npx_pi; + FpatanTable[i++] = npx_zero; + FpatanTable[i++] = npx_pi; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = npx_minus_zero; + FpatanTable[i++] = npx_minus_pi; + FpatanTable[i++] = npx_minus_zero; + FpatanTable[i++] = npx_minus_pi; + FpatanTable[i++] = npx_minus_zero; + FpatanTable[i++] = npx_minus_pi; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = npx_pi_by_two; + FpatanTable[i++] = npx_pi_by_two; + FpatanTable[i++] = npx_pi_by_two; + FpatanTable[i++] = npx_pi_by_two; + FpatanTable[i++] = npx_pi_by_four; + FpatanTable[i++] = npx_three_pi_by_four; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = npx_minus_pi_by_two; + FpatanTable[i++] = npx_minus_pi_by_two; + FpatanTable[i++] = npx_minus_pi_by_two; + FpatanTable[i++] = npx_minus_pi_by_two; + FpatanTable[i++] = npx_minus_pi_by_four; + FpatanTable[i++] = npx_minus_three_pi_by_four; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i++] = NULL; + FpatanTable[i] = NULL; + + /* Finally, the rest of the FINIT functionality */ + + NpxDisabled = disabled; /* If disabled via the UIF we must ignore FSTSW/FSTCW */ + + NpxControl = 0x037f; + npxRounding = ROUND_NEAREST; + NpxStatus = 0; + NpxLastSel=0; + NpxLastOff=0; + NpxFEA=0; + NpxFDS=0; + NpxFIP=0; + NpxFOP=0; + NpxFCS=0; + +} + + +/*( +Name : LoadValue +Function : Load up the value for any flavour of operand. + This is ALWAYS inlined. +)*/ + + +LOCAL VOID LoadValue IFN2(VOID *, SrcOp, IU16 *, IndexVal) +{ + if (FPtype == FPSTACK) { + *IndexVal = *(IU16 *)SrcOp; + } else { + switch (FPtype) { + case M16I: Loadi16ToFP(&FPTemp, SrcOp); + break; + case M32I: Loadi32ToFP(&FPTemp, SrcOp); + break; + case M64I: Loadi64ToFP(&FPTemp, SrcOp); + break; + case M32R: Loadr32ToFP(&FPTemp, SrcOp, FALSE); + break; + case M64R: Loadr64ToFP(&FPTemp, SrcOp, FALSE); + break; + case M80R: Loadr80ToFP(&FPTemp, SrcOp); + break; + } + *IndexVal = FPTEMP_INDEX; + } +} + + +/*( +Name : Loadi16ToFP +Function : Load a 16-bit value from intel memory and convert it + to FPH +)*/ + +LOCAL VOID Loadi16ToFP IFN2(FPSTACKENTRY *, FPPtr, VOID *, memPtr) +{ + IS16 asint; + + asint = (IS16)*((IU32 *)memPtr); /* High byte */ + if (asint == 0) { + /* Fast pass through */ + FPPtr->tagvalue = TAG_ZERO_MASK; + } else { + FPPtr->fpvalue = (FPH)asint; + if (asint < 0) { + FPPtr->tagvalue = TAG_NEGATIVE_MASK; + } else { + FPPtr->tagvalue = 0; + } + } +} + + + +/*( +Name : Loadi32ToFP +Function : Load a 32-bit value from intel memory and convert it + to FPH +)*/ + + +LOCAL VOID Loadi32ToFP IFN2(FPSTACKENTRY *, FPPtr, VOID *, memPtr) +{ + IS32 asint; + + asint = *((IS32 *)memPtr); + if (asint == 0) { + /* Fast pass through */ + FPPtr->tagvalue = TAG_ZERO_MASK; + } else { + FPPtr->fpvalue = (FPH)asint; + if (asint < 0) { + FPPtr->tagvalue = TAG_NEGATIVE_MASK; + } else { + FPPtr->tagvalue = 0; + } + } +} + + + +/*( +Name : Loadi64ToFP +Function : Load a 64-bit value from intel memory and convert it + to FPH +)*/ + + +LOCAL VOID Loadi64ToFP IFN2(FPSTACKENTRY *, FPPtr, VOID *, memPtr) +{ + IS32 asint_hi; + IU32 asint_lo; + + asint_hi = *((IS8 *)memPtr + 0); + asint_hi <<= 8; + asint_hi += *((IU8 *)memPtr + 1); + asint_hi <<= 8; + asint_hi += *((IU8 *)memPtr + 2); + asint_hi <<= 8; + asint_hi += *((IU8 *)memPtr + 3); + + asint_lo = *((IU8 *)memPtr + 4); + asint_lo <<= 8; + asint_lo += *((IU8 *)memPtr + 5); + asint_lo <<= 8; + asint_lo += *((IU8 *)memPtr + 6); + asint_lo <<= 8; + asint_lo += *((IU8 *)memPtr + 7); + + if ((asint_hi | asint_lo) == 0) { + /* Fast pass through */ + FPPtr->tagvalue = TAG_ZERO_MASK; + } else { + FPPtr->fpvalue = (FPH)asint_hi*4294967296.0 + (FPH)asint_lo; + if (asint_hi < 0) { + FPPtr->tagvalue = TAG_NEGATIVE_MASK; + } else { + FPPtr->tagvalue = 0; + } + } +} + + + +/*( +Name : Loadr32ToFP +Function : Load a 32-bit real value from intel memory and convert + it to FPH +)*/ + + +LOCAL VOID Loadr32ToFP IFN3(FPSTACKENTRY *, FPPtr, VOID *, memPtr, BOOL, setTOS) +{ + IU16 localtag; + IS32 mantissa; + + /* Note that this, being a 32-bit quantity, is loaded with correct + host endianness */ + if (((FP32 *)memPtr)->sign == 1) { + localtag = TAG_NEGATIVE_MASK; + } else { + localtag = 0; + } + /* Now check the exponent... */ + if (((FP32 *)memPtr)->exp == 0) { + /* It's either zero or denormal */ + mantissa = ((FP32 *)memPtr)->mant; + if (mantissa == 0x0) { + /* It's zero */ + localtag |= TAG_ZERO_MASK; + } else { + /* It's a denormal */ + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + if (setTOS) + TOSPtr = FPPtr; + DoNpxException(); + return; + } else { + FPPtr->fpvalue = (FPH)(*(float *)memPtr); + } + } + } else { + if (((FP32 *)memPtr)->exp == 255) { + /* It's either infinity or a NaN */ + mantissa = ((FP32 *)memPtr)->mant; + if (mantissa == 0x0) { + /* It's infinity */ + localtag |= TAG_INFINITY_MASK; + } else { + localtag |= TAG_NAN_MASK; + /* Is it quiet or signalling? */ + if ((mantissa & 0x400000) == 0) { + /* It's a signalling NaN */ + NpxStatus |= SW_IE_MASK; + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + } + /* Must load up the mantissa of the NaN */ + ((IU32 *)FPPtr)[NPX_HIGH_32_BITS] = ((mantissa << 8) | 0x80000000); + ((IU32 *)FPPtr)[NPX_LOW_32_BITS] = 0; + if ((mantissa & 0x400000) == 0) { + if (setTOS) + ((IS32 *)FPPtr)[NPX_HIGH_32_BITS] |= 0x40000000; + else + localtag |= TAG_SNAN_MASK; + } + } + } else { + /* It's a boring ordinary number */ + FPPtr->fpvalue = (FPH)(*(float *)memPtr); + } + } + FPPtr->tagvalue = localtag; +} + + +/*( +Name : Loadr64ToFP +Function : Load a 64-bit real value from intel memory and convert + it to FPH +)*/ + +LOCAL VOID Loadr64ToFP IFN3(FPSTACKENTRY *, FPPtr, VOID *, memPtr, BOOL, setTOS) +{ + IU16 localtag; + IS32 mantissa_lo; + IS32 mantissa_hi; + + CopyR64(FPUpload, memPtr); + if (((FP64 *)&(FPUpload->fpvalue))->hiword.sign != 0) { + localtag = TAG_NEGATIVE_MASK; + } else { + localtag = 0; + } + /* Now check the exponent... */ + if (((FP64 *)&(FPUpload->fpvalue))->hiword.exp == 0) { + /* It's either zero or denormal */ + mantissa_lo = ((FP64 *)&(FPUpload->fpvalue))->mant_lo; + mantissa_hi = ((FP64 *)&(FPUpload->fpvalue))->hiword.mant_hi; + if ((mantissa_lo | mantissa_hi) == 0) { + /* It's zero */ + localtag |= TAG_ZERO_MASK; + } else { + /* It's a denormal */ + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + if (setTOS) + TOSPtr = FPPtr; + DoNpxException(); + } else { + FPPtr->fpvalue = (FPH)(*(DOUBLE *)&(FPUpload->fpvalue)); + /* Really need a sort of host denormal detection */ + /* localtag |= TAG_DENORMAL_MASK; */ + } + } + } else { + if (((FP64 *)&(FPUpload->fpvalue))->hiword.exp == 2047) { + /* It's either infinity or a NaN */ + mantissa_lo = ((FP64 *)&(FPUpload->fpvalue))->mant_lo; + mantissa_hi = ((FP64 *)&(FPUpload->fpvalue))->hiword.mant_hi; + if ((mantissa_lo | mantissa_hi) == 0) { + /* It's infinity */ + localtag |= TAG_INFINITY_MASK; + } else { + localtag |= TAG_NAN_MASK; + /* Is it quiet or signalling? */ + if ((mantissa_hi & 0x80000) == 0) { + /* It's a signalling NaN */ + NpxStatus |= SW_IE_MASK; + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + } + /* Must load up the mantissa of the NaN */ + ((IS32 *)FPPtr)[NPX_HIGH_32_BITS] = ((mantissa_hi << 11) | 0x80000000); + ((IS32 *)FPPtr)[NPX_HIGH_32_BITS] |= ((IU32)mantissa_lo >> 21); + ((IS32 *)FPPtr)[NPX_LOW_32_BITS] = (mantissa_lo << 11); + if ((mantissa_hi & 0x80000) == 0) { + if (setTOS) + ((IS32 *)FPPtr)[NPX_HIGH_32_BITS] |= 0x40000000; + else + localtag |= TAG_SNAN_MASK; + } + } + } else { + /* It's a boring ordinary number */ + FPPtr->fpvalue = (FPH)(*(DOUBLE *)FPUpload); + } + } + FPPtr->tagvalue = localtag; +} + + +/*( +Name : LoadrTByteToFP +Function : Load a 80-bit real value from intel memory and convert + it to FPH +)*/ + + +/* + * The R80 representation is { IU64 mant; IU16 signexp } + * in order to be compatible with the Acpu representation of things. + */ +LOCAL VOID LoadTByteToFP IFN2(FPSTACKENTRY *, FPPtr, VOID *, memPtr) +{ + *((IU8 *)FPPtr + HOST_R80_BYTE_0) = *((IU8 *)memPtr + 0); + *((IU8 *)FPPtr + HOST_R80_BYTE_1) = *((IU8 *)memPtr + 1); + *((IU8 *)FPPtr + HOST_R80_BYTE_2) = *((IU8 *)memPtr + 2); + *((IU8 *)FPPtr + HOST_R80_BYTE_3) = *((IU8 *)memPtr + 3); + *((IU8 *)FPPtr + HOST_R80_BYTE_4) = *((IU8 *)memPtr + 4); + *((IU8 *)FPPtr + HOST_R80_BYTE_5) = *((IU8 *)memPtr + 5); + *((IU8 *)FPPtr + HOST_R80_BYTE_6) = *((IU8 *)memPtr + 6); + *((IU8 *)FPPtr + HOST_R80_BYTE_7) = *((IU8 *)memPtr + 7); + *((IU8 *)FPPtr + HOST_R80_BYTE_8) = *((IU8 *)memPtr + 8); + *((IU8 *)FPPtr + HOST_R80_BYTE_9) = *((IU8 *)memPtr + 9); +} + + +/*( +Name : Loadr80ToFP +Function : Load a 80-bit real value from intel memory +)*/ + + +LOCAL VOID Loadr80ToFP IFN2(FPSTACKENTRY *, FPPtr, VOID *, memPtr) +{ + LoadTByteToFP(FPPtr, memPtr); + FPPtr->tagvalue = TAG_R80_MASK; +} + + +LOCAL VOID ConvertR80 IFN1(FPSTACKENTRY *, memPtr) +{ +IU32 mantissa_hi; +IU32 mantissa_lo; +IU16 exp_value; + + CopyR80(FPUpload, (VOID *)&(memPtr->fpvalue)); + if (((FP80 *)&(FPUpload->fpvalue))->sign_exp.sign != 0) { + memPtr->tagvalue = TAG_NEGATIVE_MASK; + } else { + memPtr->tagvalue = 0; + } + exp_value = ((FP80 *)&(FPUpload->fpvalue))->sign_exp.exp; + mantissa_hi = ((FP80 *)&(FPUpload->fpvalue))->mant_hi; + mantissa_lo = ((FP80 *)&(FPUpload->fpvalue))->mant_lo; + /* Now check the exponent... */ + if ((exp_value >= (16383-HOST_BIAS)) && (exp_value <= (16383+HOST_BIAS))) { + /* It's a boring ordinary number */ + /* But let's check that it isn't an unnormal */ + if ((mantissa_hi & 0x80000000) == 0) { + memPtr->tagvalue |= TAG_UNSUPPORTED_MASK; + } else { + CVTR80FPH(memPtr, FPUpload); + } + return; + } + if (exp_value == 0) { + /* It's either zero or denormal */ + /* It's only meaningful to check for a denorm if HOST_BIAS + is equal to or greater than 16383. Otherwise we can do + nothing except set the thing to zero. + */ +#if (HOST_BIAS >= 16383) + if ((mantissa_hi | mantissa_lo) == 0) { + /* It's zero */ + memPtr->tagvalue |= TAG_ZERO_MASK; + } else { + /* It's a denormal */ + /* First, check it isn't a pseudodenorm */ + if ((mantissa_hi & 0x80000000) != 0) { + memPtr->tagvalue |= TAG_UNSUPPORTED_MASK; + } else { + memPtr->tagvalue |= TAG_DENORMAL_MASK; + CVTR80FPH(memPtr, FPUpload); + } + } +#else + /* It's zero either way */ + if ((mantissa_hi | mantissa_lo) != 0) { + /* It's a denormal */ + memPtr->tagvalue |= TAG_DENORMAL_MASK; + } + memPtr->tagvalue |= TAG_ZERO_MASK; +#endif + } else { + if ((mantissa_hi & 0x80000000) == 0) { + memPtr->tagvalue |= TAG_UNSUPPORTED_MASK; + } else { + if (exp_value == 32767) { + /* It's either infinity or a NaN */ + if ((mantissa_hi == 0x80000000) && mantissa_lo == 0) { + /* It's infinity */ + memPtr->tagvalue |= TAG_INFINITY_MASK; + } else { + memPtr->tagvalue |= TAG_NAN_MASK; + /* Is it quiet or signalling? */ + if ((mantissa_hi & 0x40000000) == 0) { + /* It's a signalling NaN */ + memPtr->tagvalue |= TAG_SNAN_MASK; + } + /* Must load up the mantissa of the NaN */ + ((IU32 *)memPtr)[NPX_HIGH_32_BITS] = mantissa_hi; + ((IU32 *)memPtr)[NPX_LOW_32_BITS] = mantissa_lo; + } + } else { + if (exp_value > 16384) { + /* Default to infinity */ + memPtr->tagvalue |= TAG_INFINITY_MASK; + } else { + /* Default to zero */ + memPtr->tagvalue |= TAG_ZERO_MASK; + } + } + } + } +} + + + +/*( +Name : PostCheckOUP +Function : This generator is associated with the result of an + instruction emulation whose result, an FPH, is to + be written out to the stack. We check for O, U anf + P exceptions here, but we make no attempt to write out + the result. This is because the writing of the result + is independent of these exceptions, since for results + being written to the stack, delivery of the result + cannot be prevented even where these exceptions are + unmasked. +)*/ + + +LOCAL VOID PostCheckOUP IFN0() +{ + if (HostGetOverflowException() != 0) { + NpxStatus |= SW_OE_MASK; /* Set the overflow bit */ + /* For the masked overflow case, the result delivered by */ + /* the host will be correct, provided it is IEEE compliant. */ + if ((NpxControl & CW_OM_MASK) == 0) { + AdjustOverflowResponse(); + NpxStatus |= SW_ES_MASK; + NpxException = TRUE; + } + } else { + /* Overflow and underflow being mutually exclusive... */ + if (HostGetUnderflowException() != 0) { + NpxStatus |= SW_UE_MASK; + if ((NpxControl & CW_UM_MASK) == 0) { + AdjustUnderflowResponse(); + NpxStatus |= SW_ES_MASK; + NpxException = TRUE; + } + } + } + if (HostGetPrecisionException() != 0) { + SetPrecisionBit(); + if ((NpxControl & CW_PM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + NpxException = TRUE; + } + } +} + + + +/*( +Name : CalcTagword +Function : To calculate the tagword associated with a value + and write out the result where appropriate. +)*/ + + +LOCAL VOID CalcTagword IFN1(FPSTACKENTRY *, FPPtr) +{ + IU16 tagword; + + FPPtr->fpvalue = FPRes; + if (((FPHOST *)&(FPPtr->fpvalue))->hiword.sign == 1) { + tagword = TAG_NEGATIVE_MASK; + } else { + tagword = 0; + } + if (((FPHOST *)&(FPPtr->fpvalue))->hiword.exp == 0) { + /* It's either a zero or a denorm */ + if (FPPtr->fpvalue == 0.0) { + /* It's a zero */ + tagword |= TAG_ZERO_MASK; +#if (HOST_BIAS >= 16383) + } else { + /* It's a denorm */ + tagword |= TAG_DENORMAL_MASK; +#endif + } + } else { + if (((FPHOST *)&(FPPtr->fpvalue))->hiword.exp == HOST_MAX_EXP) { + /* It MUST be infinity as we can't generate NaNs */ + tagword |= TAG_INFINITY_MASK; + } + } + FPPtr->tagvalue = tagword; + if (NpxException) { + DoNpxException(); + } +} + + + +/*( +Name : SignalStackUnderflow +Function : To set the required bits in the status word following + a stack underflow exception, and to issue the required + response. +)*/ + +LOCAL VOID SignalStackUnderflow IFN1(FPSTACKENTRY *, StackPtr) +{ + NpxStatus |= (SW_IE_MASK | SW_SF_MASK); + FlagC1(0); + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + DoAPop=FALSE; /* Just in case it was set */ + } else { + WriteIndefinite(StackPtr); + } +} + + +/*( +Name : SignalSNaN +Function : To set the required bits in the status word following + detection of a signalling NaN. +)*/ + + +LOCAL VOID SignalSNaN IFN1(FPSTACKENTRY *, StackPtr) +{ + NpxStatus |= SW_IE_MASK; + NpxStatus &= ~SW_SF_MASK; + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + DoAPop=FALSE; + } +} + + +/*( +Name : SignalInvalid +Function : To set the required bits in the status word following + any standard "invalid" exception +)*/ + + +LOCAL VOID SignalIndefinite IFN1(FPSTACKENTRY *, StackPtr) +{ + NpxStatus |= SW_IE_MASK; + NpxStatus &= ~SW_SF_MASK; + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + DoAPop=FALSE; + } else { + WriteIndefinite(StackPtr); + } +} + + + +LOCAL VOID SignalInvalid IFN0() +{ + NpxStatus |= SW_IE_MASK; + NpxStatus &= ~SW_SF_MASK; + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + DoAPop=FALSE; + } +} + + + +/*( +Name : WriteIndefinite +Function : Write the value "indefinite" into the location +)*/ + +LOCAL VOID WriteIndefinite IFN1(FPSTACKENTRY *, StackPtr) +{ + StackPtr->tagvalue = (TAG_NEGATIVE_MASK | TAG_NAN_MASK); + (((IU32 *)StackPtr)[NPX_HIGH_32_BITS]) = 0xc0000000; + (((IU32 *)StackPtr)[NPX_LOW_32_BITS]) = 0; +} + + + +/* This generator should always be inlined. */ + + +LOCAL VOID Test2NaN IFN3(IU16, destIndex, FPSTACKENTRY *, src1_addr, FPSTACKENTRY *, src2_addr) +{ + /* Are they both NaNs? */ + if ((tag_xor & TAG_NAN_MASK) == 0) { + /* Yes, they are. */ + WriteBiggestNaN(destIndex, src1_addr, src2_addr); + } else { + /* No, only one NaN. */ + if ((src1_addr->tagvalue & TAG_NAN_MASK) != 0) { + /* It was src1. */ + src2_addr = StackEntryByIndex(destIndex); + CopyFP(src2_addr, src1_addr); + if ((src2_addr->tagvalue & TAG_SNAN_MASK) != 0) { + src2_addr->tagvalue ^= TAG_SNAN_MASK; + SignalInvalid(); + (((IU32 *)src2_addr)[NPX_HIGH_32_BITS]) |= 0x40000000; + } + } else { + /* It was src2. */ + src1_addr = StackEntryByIndex(destIndex); + CopyFP(src1_addr, src2_addr); + if ((src1_addr->tagvalue & TAG_SNAN_MASK) != 0) { + src1_addr->tagvalue ^= TAG_SNAN_MASK; + SignalInvalid(); + (((IU32 *)src1_addr)[NPX_HIGH_32_BITS]) |= 0x40000000; + } + } + } +} + + + +/* +Name : F2XM1 +Function : Compute 2**x - 1 +Operation : ST <- (2**ST - 1) +Flags : C1 set as per table 15-1 +Exceptions : P, U, D, I, IS +Valid range : -1 < ST < +1 +Notes : If ST is outside the required range, the result is + undefined. +)*/ + + +GLOBAL VOID F2XM1 IFN0() +{ + /* Clear C1 */ + FlagC1(0); + TestUneval(TOSPtr); + /* Check if a real value... */ + if ((TOSPtr->tagvalue & ~TAG_NEGATIVE_MASK) == 0) { + HostClearExceptions(); + /* We can just write the value straight out */ + FPRes = pow(2.0, TOSPtr->fpvalue) - 1.0; + PostCheckOUP(); + /* This could return anything really.... */ + CalcTagword(TOSPtr); + return; + } else { + /* Some funny bit was set. Check for the possibilities */ + /* We begin with the most obvious cases... */ + /* Response to zero is to return zero with same sign */ + if ((TOSPtr->tagvalue & TAG_ZERO_MASK) != 0) { + return; /* The required result! */ + } + /* We do denorm checking and bit setting ourselves because this */ + /* reduces the overhead if the thing is masked. */ + if ((TOSPtr->tagvalue & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } else { + HostClearExceptions(); + FPRes = pow(2.0, TOSPtr->fpvalue) - 1.0; + PostCheckOUP(); + /* Could return a denorm, zero, real, infinity... */ + CalcTagword(TOSPtr); + } + return; + } + /* If -infinity, return -1. If +infinity, return that */ + /* Sensible enough really, I suppose */ + if ((TOSPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + memset((char*)TOSPtr,0,sizeof(FPSTACKENTRY)); + TOSPtr->fpvalue = -1.0; + TOSPtr->tagvalue = TAG_NEGATIVE_MASK; + } + return; + } + if ((TOSPtr->tagvalue & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(TOSPtr); + return; + } + if ((TOSPtr->tagvalue & TAG_SNAN_MASK) != 0) { + MakeNaNQuiet(TOSPtr); + return; + } + if ((TOSPtr->tagvalue & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(TOSPtr); + return; + } + } +} + +/*( +Name : FABS +Function : Make the value absolute +Operation : sign bit of ST <- 0 +Flags : C1 as per table 15-1. C0, C2 and C3 undefined +Exceptions : IS +Valid range : Any +Notes : Note that only the IS exception can be flagged. All + other error conditions are ignored, even a signalling + NaN! We ALWAYS attempt to make the value positive. +)*/ + + +GLOBAL VOID FABS IFN0() +{ + /* Clear C1 */ + FlagC1(0); + TestUneval(TOSPtr); + if ((TOSPtr->tagvalue & TAG_EMPTY_MASK) == 0) { + /* Now clear the negative bit. */ + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + TOSPtr->tagvalue ^= TAG_NEGATIVE_MASK; + /* If the value is real or denormal, we'll want to change the MSB */ + if ((TOSPtr->tagvalue & ~TAG_DENORMAL_MASK) == 0) { + ((FPHOST *)&(TOSPtr->fpvalue))->hiword.sign = 0; + } + } + } else { + SignalStackUnderflow(TOSPtr); + } +} + +/*( +Name : FADD +Function : Add two numbers together +Operation : Dest <- Src1 + Src2 +Flags : C1 as per table 15-1. C0, C2 and C3 undefined +Exceptions : IS +Valid range : Any +Notes : Note the dependence on the rounding mode when + calculating the sign of zero for situations + where two zeroes of different sign are input. +)*/ + + +GLOBAL VOID FADD IFN3(IU16, destIndex, IU16, src1Index, VOID *, src2) +{ + IU16 src2Index; + + LoadValue(src2, &src2Index); + if (POPST) { + DoAPop=TRUE; + } + GenericAdd(destIndex, src1Index, src2Index); + if (POPST) { + if (DoAPop) { + PopStack(); + } + } +} + + + +/*( +Name : GenericAdd +Function : To return dest <- src1+src2 +)*/ + + +LOCAL VOID GenericAdd IFN3(IU16, destIndex, IU16, src1Index, IU16, src2Index) +{ + FPSTACKENTRY *src1_addr; + FPSTACKENTRY *src2_addr; + + src1_addr = StackEntryByIndex(src1Index); + src2_addr = StackEntryByIndex(src2Index); + + /* Clear C1 */ + FlagC1(0); + /* If the only tagword bits set are negative or denormal then just proceed */ + TestUneval(src1_addr); + TestUneval(src2_addr); + tag_or = (src1_addr->tagvalue | src2_addr->tagvalue); + if ((tag_or & ~TAG_NEGATIVE_MASK) == 0) { + HostClearExceptions(); + FPRes = src1_addr->fpvalue + src2_addr->fpvalue; + /* Reuse one of the above to calculate the destination */ + src1_addr = StackEntryByIndex(destIndex); + PostCheckOUP(); + /* Could return virtually anything */ + CalcTagword(src1_addr); + } else { + /* Some funny bit was set. Check for the possibilities */ + /* The odds on an 'empty', 'unsupported' or 'nan' must be low... */ + if ((tag_or & ((TAG_EMPTY_MASK | TAG_UNSUPPORTED_MASK) | TAG_NAN_MASK)) != 0) { + if ((tag_or & TAG_UNSUPPORTED_MASK) != 0) { + src1_addr = StackEntryByIndex(destIndex); + SignalIndefinite(src1_addr); + } else { + if ((tag_or & TAG_EMPTY_MASK) != 0) { + src1_addr = StackEntryByIndex(destIndex); + SignalStackUnderflow(src1_addr); + } else { + /* It must be a NaN type thing. */ + /* Calculate the xor of the tagwords. */ + tag_xor = (src1_addr->tagvalue ^ src2_addr->tagvalue); + Test2NaN(destIndex, src1_addr, src2_addr); + } + } + return; + } + /* Check for the denorm case...I think the odds on it are low, however */ + if ((tag_or & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + DoAPop=FALSE; + return; + } else { + /* First, make sure that we don't have any zeros or */ + /* infinities lurking around... */ + if ((tag_or & ~(TAG_DENORMAL_MASK | TAG_NEGATIVE_MASK)) == 0) { + HostClearExceptions(); + FPRes = src1_addr->fpvalue + src2_addr->fpvalue; + /* Reuse one of the above to calculate the destination */ + src1_addr = StackEntryByIndex(destIndex); + PostCheckOUP(); + /* Could return anything */ + CalcTagword(src1_addr); + return; + } + /* If there were zeros or infinities then we go on to the */ + /* appropriate code */ + } + } + tag_xor = (src1_addr->tagvalue ^ src2_addr->tagvalue); + /* Check for the case of zero... This is very likely */ + if ((tag_or & TAG_ZERO_MASK) != 0) { + if ((tag_xor & TAG_ZERO_MASK) != 0) { + /* Only one zero. */ + if ((src1_addr->tagvalue & TAG_ZERO_MASK) != 0) { + src1_addr = StackEntryByIndex(destIndex); + CopyFP(src1_addr, src2_addr); + } else { + src2_addr = StackEntryByIndex(destIndex); + CopyFP(src2_addr, src1_addr); + } + } else { + /* Both are zeros. Do they have the same sign? */ + src1_addr = StackEntryByIndex(destIndex); + if ((tag_xor & TAG_NEGATIVE_MASK) != 0) { + /* No, they don't */ + if (npxRounding == ROUND_NEG_INFINITY) { + src1_addr->tagvalue = (TAG_ZERO_MASK | TAG_NEGATIVE_MASK); + } else { + src1_addr->tagvalue = TAG_ZERO_MASK; + } + } + } + return; + } + /* The only funny bit left is infinity */ + if ((tag_xor & TAG_INFINITY_MASK) == 0) { + /* They are both infinity. */ + /* If they are the same sign, copy either */ + src1_addr = StackEntryByIndex(destIndex); + if ((tag_xor & TAG_NEGATIVE_MASK) == 0) { + src1_addr->tagvalue = tag_or; + } else { + /* If opposite signed, raise Invalid */ + SignalIndefinite(src1_addr); + } + } else { + /* Only one is infinity. That is the result. */ + if ((src1_addr->tagvalue & TAG_INFINITY_MASK) != 0) { + src2_addr = StackEntryByIndex(destIndex); + src2_addr->tagvalue = src1_addr->tagvalue; + } else { + src1_addr = StackEntryByIndex(destIndex); + src1_addr->tagvalue = src2_addr->tagvalue; + } + } + } +} + + + +/* AddBCDByte(). This generator should be inlined. + This generator add in a BCD byte to a grand total. +*/ + +LOCAL VOID AddBCDByte IFN2(FPU_I64 *, total, IU8, byte_val) +{ + Add64Bit8Bit(total, byte_val); + if (byte_val >= 0x10) { /* Odds ought to be 16 to 1 on. */ + /* We've added in 16 times the high BCD digit, */ + /* so we need to subtract off 6 times that amount. */ + byte_val &= 0xf0; /* Isolate the high digit */ + byte_val >>= 2; /* This is now four times the high digit */ + Sub64Bit8Bit(total, byte_val); + byte_val >>= 1; /* This is twice the high digit */ + Sub64Bit8Bit(total, byte_val); + } +} + + + +/* FBLD: Load BCD value from intel memory. + The alorithm used here is identical to that in the generic NPX. + We take each BCD digit and multiply it up by an appropriate amount + (1, 10, 100, 1000 etc) in order to create two nine digit 32-bit binary + values. We then convert the word with the high digits (d17-d9) into + floating point format and multiply by the representation of the value + for 10**9. This is then stored away (in FPTEMP) and the word with the + low digits (d8-d0) is converted to floating point format and added to + the value in FPTEMP. This is then the final binary representation of + the original BCD value that can be stored at TOS. */ + +/*( +Name : FBLD +Function : Load the BCD value in intel memory onto TOS +Operation : ST <- Convert to FPH(memPtr); +Flags : C1 as per table 15-1. C0, C2 and C3 undefined +Exceptions : IS +Valid range : -999999999999999999 to 999999999999999999 +)*/ + + +GLOBAL VOID FBLD IFN1(IU8 *, memPtr) +{ + + /* Clear C1 */ + FlagC1(0); + /* All we shall do is load it up without consideration */ + TOSPtr = StackEntryByIndex(7); + if ((TOSPtr->tagvalue & TAG_EMPTY_MASK) == 0) { /* Highly unlikely, see notes. */ + SignalStackOverflow(TOSPtr); + } else { + /* We just copy the bytes directly */ + LoadTByteToFP(TOSPtr, memPtr); + TOSPtr->tagvalue = TAG_BCD_MASK; + } +} + + +LOCAL VOID ConvertBCD IFN1(FPSTACKENTRY *, bcdPtr) +{ + IU8 *memPtr = (IU8 *)&(bcdPtr->fpvalue); + FPU_I64 total; + + Set64Bit(&total, 0); + AddBCDByte(&total, memPtr[HOST_R80_BYTE_1]); /* Get d17d16 */ + Mul64Bit8Bit(&total, 100); + AddBCDByte(&total, memPtr[HOST_R80_BYTE_2]); + Mul64Bit8Bit(&total, 100); + AddBCDByte(&total, memPtr[HOST_R80_BYTE_3]); + Mul64Bit8Bit(&total, 100); + AddBCDByte(&total, memPtr[HOST_R80_BYTE_4]); + Mul64Bit8Bit(&total, 100); + AddBCDByte(&total, memPtr[HOST_R80_BYTE_5]); + Mul64Bit8Bit(&total, 100); + AddBCDByte(&total, memPtr[HOST_R80_BYTE_6]); + Mul64Bit8Bit(&total, 100); + AddBCDByte(&total, memPtr[HOST_R80_BYTE_7]); + Mul64Bit8Bit(&total, 100); + AddBCDByte(&total, memPtr[HOST_R80_BYTE_8]); + Mul64Bit8Bit(&total, 100); + AddBCDByte(&total, memPtr[HOST_R80_BYTE_9]); + CVTI64FPH(&total); + if ((*(memPtr + 0) & 0x80) != 0) { + FPRes = -FPRes; /* Make it negative! */ + } + CalcTagword(bcdPtr); /* Silly...it can only be negative */ + /* or zero. */ +} + + +/* FBSTP: Store binary coded decimal and pop. +This uses much the same algorithm as before, but reversed. You begin +by checking that the value at TOS is real, then compare it against the +maximum possible value (having first forced the sign bit to be zero). +If it's OK, then turn it into a 64 bit integer and perform the +required repeated subtractions to calculate each of the BCD digits. */ + + +GLOBAL VOID FBSTP IFN1(IU8 *, memPtr) +{ + FPH local_fp; + IS8 nibble_num; + IU8 byte_val; + FPU_I64 as64bit; + + /* Clear C1 */ + FlagC1(0); + if ((TOSPtr->tagvalue & UNEVALMASK) != 0) { + switch (TOSPtr->tagvalue & UNEVALMASK) { + case TAG_BCD_MASK: /* We just copy the bytes directly */ + WriteFP80ToIntel(memPtr, TOSPtr); + PopStack(); + return; + break; + case TAG_R80_MASK: ConvertR80(TOSPtr); + break; + } + } + if ((TOSPtr->tagvalue & ~(TAG_DENORMAL_MASK | TAG_NEGATIVE_MASK)) == 0) { + /* We're OK. Let's do some checking... */ + if (fabs(TOSPtr->fpvalue) >= MaxBCDValue) { + /* It's all gone horribly wrong */ + SignalInvalid(); + SignalBCDIndefinite((IU8 *)memPtr); + PopStack(); + return; + } + /* The value is OK. Do the conversion. */ + local_fp = npx_rint(TOSPtr->fpvalue); + ((FPHOST *)&local_fp)->hiword.sign = 0; /* Force it to be positive */ + CVTFPHI64(&as64bit, &local_fp); + byte_val = 0; + while (Cmp64BitGTE(&as64bit, &BCDHighNibble[0])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDHighNibble[0]); + } + byte_val <<= 4; + while (Cmp64BitGTE(&as64bit, &BCDLowNibble[0])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDLowNibble[0]); + } + *(memPtr + 1) = byte_val; + + byte_val = 0; + while (Cmp64BitGTE(&as64bit, &BCDHighNibble[1])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDHighNibble[1]); + } + byte_val <<= 4; + while (Cmp64BitGTE(&as64bit, &BCDLowNibble[1])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDLowNibble[1]); + } + *(memPtr + 2) = byte_val; + + byte_val = 0; + while (Cmp64BitGTE(&as64bit, &BCDHighNibble[2])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDHighNibble[2]); + } + byte_val <<= 4; + while (Cmp64BitGTE(&as64bit, &BCDLowNibble[2])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDLowNibble[2]); + } + *(memPtr + 3) = byte_val; + + byte_val = 0; + while (Cmp64BitGTE(&as64bit, &BCDHighNibble[3])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDHighNibble[3]); + } + byte_val <<= 4; + while (Cmp64BitGTE(&as64bit, &BCDLowNibble[3])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDLowNibble[3]); + } + *(memPtr + 4) = byte_val; + + byte_val = 0; + while (Cmp64BitGTE(&as64bit, &BCDHighNibble[4])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDHighNibble[4]); + } + byte_val <<= 4; + while (Cmp64BitGTE(&as64bit, &BCDLowNibble[4])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDLowNibble[4]); + } + *(memPtr + 5) = byte_val; + + byte_val = 0; + while (Cmp64BitGTE(&as64bit, &BCDHighNibble[5])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDHighNibble[5]); + } + byte_val <<= 4; + while (Cmp64BitGTE(&as64bit, &BCDLowNibble[5])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDLowNibble[5]); + } + *(memPtr + 6) = byte_val; + + byte_val = 0; + while (Cmp64BitGTE(&as64bit, &BCDHighNibble[6])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDHighNibble[6]); + } + byte_val <<= 4; + while (Cmp64BitGTE(&as64bit, &BCDLowNibble[6])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDLowNibble[6]); + } + *(memPtr + 7) = byte_val; + + byte_val = 0; + while (Cmp64BitGTE(&as64bit, &BCDHighNibble[7])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDHighNibble[7]); + } + byte_val <<= 4; + while (Cmp64BitGTE(&as64bit, &BCDLowNibble[7])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDLowNibble[7]); + } + *(memPtr + 8) = byte_val; + + byte_val = 0; + while (Cmp64BitGTE(&as64bit, &BCDHighNibble[8])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDHighNibble[8]); + } + byte_val <<= 4; + while (Cmp64BitGTE(&as64bit, &BCDLowNibble[8])) { + byte_val += 1; + Sub64Bit64Bit(&as64bit, &BCDLowNibble[8]); + } + *(memPtr + 9) = byte_val; + + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + *(memPtr + 0) = 0x80; + ((FPHOST *)&local_fp)->hiword.sign = 1; + } else { + *(memPtr + 0) = 0; + } + /* Can't prevent delivery of result with unmasked precision + exception... */ + if (local_fp != TOSPtr->fpvalue) { + SetPrecisionBit(); + if ((NpxControl & CW_PM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + PopStack(); + DoNpxException(); + return; + } + } + } else { + if ((TOSPtr->tagvalue & TAG_ZERO_MASK) == 0) { + /* Anything else: Infinity, NaN or whatever... */ + SignalInvalid(); + SignalBCDIndefinite((IU8 *)memPtr); + PopStack(); + return; + } + *(memPtr + 3) = (IU8)0; + *(memPtr + 4) = (IU8)0; + *(memPtr + 5) = (IU8)0; + *(memPtr + 6) = (IU8)0; + *(memPtr + 7) = (IU8)0; + *(memPtr + 8) = (IU8)0; + *(memPtr + 9) = (IU8)0; + if ((TOSPtr->tagvalue & TAG_ZERO_MASK) == 0) { /* Again, to check what top bytes should be. */ + *(memPtr + 0) = (IU8)0xff; /* Not the zero case...It must be indefinite */ + *(memPtr + 1) = (IU8)0xff; + *(memPtr + 2) = (IU8)0xc0; + } else { + *(memPtr + 1) = (IU8)0; + *(memPtr + 2) = (IU8)0; + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + *(memPtr + 0) = 0x80; + } else { + *(memPtr + 0) = 0; + } + } + } + PopStack(); +} + + + +/*( +Name : FCHS +Function : Change the sign of the value at TOS +Operation : ST <- Change sign (ST) +Flags : C1 as per table 15-1. C0, C2 and C3 undefined +Exceptions : IS +Valid range : Any +)*/ + + +GLOBAL VOID FCHS IFN0() +{ + /* Clear C1 */ + FlagC1(0); + TestUneval(TOSPtr); + if ((TOSPtr->tagvalue & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(TOSPtr); + return; + } + /* That is the only exception condition possible. FCHS always */ + /* succeeds! What a strange instruction! */ + TOSPtr->tagvalue ^= TAG_NEGATIVE_MASK; /* Twiddle the tagword bit */ + /* We only twiddle the sign bit in numbers that are really */ + /* being represented. */ + if ((TOSPtr->tagvalue & ~(TAG_DENORMAL_MASK | TAG_NEGATIVE_MASK)) == 0) { + ((FPHOST *)&(TOSPtr->fpvalue))->hiword.sign ^= 1; + } +} + + + +/*( +Name : FCLEX +Function : Clear the exception flags, exception status flag + and busy flag in the FPU status word. +Operation : SW[0..7]<-0; SW[15]<-0 +Flags : C0, C1, C2 and C3 undefined +Exceptions : None +Valid range : Any +)*/ + + +GLOBAL VOID FCLEX IFN0() +{ + NpxStatus &= FCLEX_MASK; +} + + +/* Comparision opcodes: The following opcodes are all taken care of +in this routine: FCOM m32r, FCOM m64r, FCOM ST(i), FCOM, FCOMP m32real, +FCOMP m64real, FCOMP ST(i), FCOMP, FCOMPP, FICOM m16i, FICOM m32i, +FICOMP m16i, FICOMP m32i. +The method is simple: In every case, one of the two operands for which +comparison is to occur is ST. The second operand is either one of the +four memory operand types specified, or another stack element, ST(i). +There are, in addition, two possible control variables - POPST and +DOUBLEPOP, which set appropriate values in global variables. +*/ + + +GLOBAL VOID FCOM IFN1(VOID *, src2) +{ + IU16 src2Index; + + LoadValue(src2, &src2Index); + if (POPST || DOUBLEPOP) { + DoAPop=TRUE; + } + GenericCompare(src2Index); + if (POPST || DOUBLEPOP) { + if (DoAPop) { + PopStack(); + if (DOUBLEPOP) { + PopStack(); + } + } + } +} + + + +LOCAL VOID GenericCompare IFN1(IU16, src2Index) +{ + FPSTACKENTRY *src2_addr; + + src2_addr = StackEntryByIndex(src2Index); + + /* Clear C1 */ + FlagC1(0); + TestUneval(TOSPtr); + TestUneval(src2_addr); + tag_or = (TOSPtr->tagvalue | src2_addr->tagvalue); + /* If the only tagword bit set is negative then just proceed */ + if ((tag_or & ~TAG_NEGATIVE_MASK) == 0) { + NpxStatus &= C3C2C0MASK; /* Clear those bits */ + if (TOSPtr->fpvalue > src2_addr->fpvalue) { + NpxStatus |= INTEL_COMP_GT; + } else { + if (TOSPtr->fpvalue < src2_addr->fpvalue) { + NpxStatus |= INTEL_COMP_LT; + } else { + NpxStatus |= INTEL_COMP_EQ; + } + } + } else { + /* Everything was not sweetness and light... */ + if ((tag_or & ((TAG_EMPTY_MASK | TAG_UNSUPPORTED_MASK) | TAG_NAN_MASK)) != 0) { + if ((tag_or & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(TOSPtr); + } else { + if ((tag_or & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(TOSPtr); + } else { + /* It must be a NaN. Just set the "not comparable" result */ + if (UNORDERED) { + if ((tag_or & TAG_SNAN_MASK) != 0) { + SignalIndefinite(TOSPtr); + } + } else { + SignalIndefinite(TOSPtr); + } + } + } + NpxStatus &= C3C2C0MASK; + NpxStatus |= INTEL_COMP_NC; + return; + } + if ((tag_or & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } else { + /* We can do it now, providing we've got no zeros or infinities */ + if ((tag_or & ~(TAG_NEGATIVE_MASK | TAG_DENORMAL_MASK)) == 0) { + NpxStatus &= C3C2C0MASK; /* Clear those bits */ + if (TOSPtr->fpvalue > src2_addr->fpvalue) { + NpxStatus |= INTEL_COMP_GT; + } else { + if (TOSPtr->fpvalue < src2_addr->fpvalue) { + NpxStatus |= INTEL_COMP_LT; + } else { + NpxStatus |= INTEL_COMP_EQ; + } + } + return; + } + } + } + /* We can calculate the result immediately based on any combination */ + /* of zero, infinity and negative bits. These are the only bits left. */ + /* We will calculate the result using a little table */ + /* First, get the index: */ + tag_or = (TOSPtr->tagvalue & 0x7); + tag_or <<= 3; + tag_or |= (src2_addr->tagvalue & 0x7); + /* This table looks as shown below: */ + /* TOSPtr Other Value Result */ + /* INF ZERO NEG INF ZERO NEG */ + /* 0 0 0 0 1 0 COMP_GT */ + /* 0 0 0 0 1 1 COMP_GT */ + /* 0 0 0 1 0 0 COMP_LT */ + /* 0 0 0 1 0 1 COMP_GT */ + /* 0 1 0 0 0 0 COMP_LT */ + /* 0 1 0 0 0 1 COMP_GT */ + /* 0 1 0 0 1 0 COMP_EQ */ + /* 0 1 0 0 1 1 COMP_EQ */ + /* 0 1 0 1 0 0 COMP_LT */ + /* 0 1 0 1 0 1 COMP_GT */ + /* 0 1 1 0 0 0 COMP_LT */ + /* 0 1 1 0 0 1 COMP_GT */ + /* 0 1 1 0 1 0 COMP_EQ */ + /* 0 1 1 0 1 1 COMP_EQ */ + /* 0 1 1 1 0 0 COMP_LT */ + /* 0 1 1 1 0 1 COMP_GT */ + /* 1 0 0 0 0 0 COMP_GT */ + /* 1 0 0 0 0 1 COMP_GT */ + /* 1 0 0 0 1 0 COMP_GT */ + /* 1 0 0 0 1 1 COMP_GT */ + /* 1 0 0 1 0 0 COMP_EQ */ + /* 1 0 0 1 0 1 COMP_GT */ + /* 1 0 1 0 0 0 COMP_LT */ + /* 1 0 1 0 0 1 COMP_LT */ + /* 1 0 1 0 1 0 COMP_LT */ + /* 1 0 1 0 1 1 COMP_LT */ + /* 1 0 1 1 0 0 COMP_LT */ + /* 1 0 1 1 0 1 COMP_EQ */ + /* */ + /* All other values are not possible. */ + NpxStatus &= C3C2C0MASK; + NpxStatus |= CompZeroTable[tag_or]; + return; + } +} + + +/*( +Name : FCOS +Function : Calculate the cosine of ST +Operation : ST <- COSINE(ST) +Flags : C1, C2 as per table 15-2. C0 and C3 undefined. +Exceptions : P. U, D, I, IS +Valid range : |ST| < 2**63. +)*/ + +GLOBAL VOID FCOS IFN0() +{ + /* Clear C1 */ + FlagC1(0); + /* Clear C2 */ + FlagC2(0); + TestUneval(TOSPtr); + if ((TOSPtr->tagvalue & ~TAG_NEGATIVE_MASK) == 0) { + HostClearExceptions(); + /* We can just write the value straight out */ + FPRes = cos(TOSPtr->fpvalue); + PostCheckOUP(); + /* The return value must be in the range -1 to +1. */ + CalcTagword(TOSPtr); + return; + } else { + /* Lets do the most probable cases first... */ + /* Response to either zero is to return +1 */ + if ((TOSPtr->tagvalue & TAG_ZERO_MASK) != 0) { + memset((char*)TOSPtr,0,sizeof(FPSTACKENTRY)); + TOSPtr->fpvalue = 1.0; + TOSPtr->tagvalue = 0; + return; + } + /* Lets check for a denormal */ + if ((TOSPtr->tagvalue & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } else { + HostClearExceptions(); + FPRes = cos(TOSPtr->fpvalue); + PostCheckOUP(); + /* The return value must be in the range -1 to +1 */ + CalcTagword(TOSPtr); + } + return; + } + /* Or it could possibly be infinity... */ + /* For this, the C2 bit is set and the result remains */ + /* unchanged. */ + if ((TOSPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + FlagC2(1); + return; + } + /* It was one of the really wacky bits... */ + if ((TOSPtr->tagvalue & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(TOSPtr); + return; + } + if ((TOSPtr->tagvalue & TAG_SNAN_MASK) != 0) { + MakeNaNQuiet(TOSPtr); + return; + } + if ((TOSPtr->tagvalue & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(TOSPtr); + return; + } + } +} + + + +/*( +Name : FDECSTP +Function : Subtract one from the TOS +Operation : if (ST != 0) { ST <- ST-1 else { ST <- 7 } +Flags : C1 as per table 15-1. C0, C2 and C3 undefined. +Exceptions : None +Valid range : N/A +)*/ + + +GLOBAL VOID FDECSTP IFN0() +{ + /* Clear C1 */ + FlagC1(0); + TOSPtr = StackEntryByIndex(7); +} + + + +/*( +Name : FDIV +Function : Divide the two numbers +Operation : Dest <- Src1 / Src2 or Dest <- Src2 / Src1 +Flags : C1 as per table 15-1. C0, C2 and C3 undefined +Exceptions : P, U, O, Z, D, I, IS +Valid range : Any +Notes : The REVERSE control variable determines which of the + two forms of the operation is used. Popping after a + successful execution is controlled by POPST. +)*/ + + +GLOBAL VOID FDIV IFN3(IU16, destIndex, IU16, src1Index, VOID *, src2) +{ + IU16 src2Index; + + LoadValue(src2, &src2Index); + if (POPST) { + DoAPop=TRUE; + } + GenericDivide(destIndex, REVERSE?src2Index:src1Index, REVERSE?src1Index:src2Index); + if (POPST) { + if (DoAPop) { + PopStack(); + } + } +} + + +/*( +Name : GenericDivide +Function : To return dest <- src1/src2 +)*/ + + +LOCAL VOID GenericDivide IFN3(IU16, destIndex, IU16, src1Index, IU16, src2Index) +{ + FPSTACKENTRY *src1_addr; + FPSTACKENTRY *src2_addr; + + src1_addr = StackEntryByIndex(src1Index); + src2_addr = StackEntryByIndex(src2Index); + + /* Clear C1 */ + FlagC1(0); + TestUneval(src1_addr); + TestUneval(src2_addr); + tag_or = (src1_addr->tagvalue | src2_addr->tagvalue); + /* If the only tagword bit set is negative then just proceed */ + if ((tag_or & (~TAG_NEGATIVE_MASK)) == 0) { + HostClearExceptions(); + FPRes = src1_addr->fpvalue/src2_addr->fpvalue; + /* Reuse one of the above to calculate the destination */ + src1_addr = StackEntryByIndex(destIndex); + PostCheckOUP(); + /* Value could be anything */ + CalcTagword(src1_addr); + } else { + /* Some funny bit was set. Check for the possibilities */ + if ((tag_or & ((TAG_EMPTY_MASK | TAG_UNSUPPORTED_MASK) | TAG_NAN_MASK)) != 0) { + if ((tag_or & TAG_EMPTY_MASK) != 0) { + src1_addr = StackEntryByIndex(destIndex); + SignalStackUnderflow(src1_addr); + } else { + if ((tag_or & TAG_UNSUPPORTED_MASK) != 0) { + src1_addr = StackEntryByIndex(destIndex); + SignalIndefinite(src1_addr); + } else { + /* Well, I suppose it has to be the NaN case... */ + /* Calculate the xor of the tagwords */ + tag_xor = (src1_addr->tagvalue ^ src2_addr->tagvalue); + Test2NaN(destIndex, src1_addr, src2_addr); + } + } + return; + } + if ((tag_or & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + DoAPop = FALSE; + return; + } else { + if ((tag_or & ~(TAG_NEGATIVE_MASK | TAG_DENORMAL_MASK)) == 0) { + /* OK to proceed */ + HostClearExceptions(); + FPRes = src1_addr->fpvalue/src2_addr->fpvalue; + /* Reuse one of the above to calculate the destination */ + src1_addr = StackEntryByIndex(destIndex); + PostCheckOUP(); + /* Value could be anything */ + CalcTagword(src1_addr); + return; + } + } + } + tag_xor = (src1_addr->tagvalue ^ src2_addr->tagvalue); + /* Check for infinity as it has higher precendence than zero. */ + if ((tag_or & TAG_INFINITY_MASK) != 0) { + if ((tag_xor & TAG_INFINITY_MASK) == 0) { + /* They are both infinity. This is invalid. */ + src1_addr = StackEntryByIndex(destIndex); + SignalIndefinite(src1_addr); + } else { + /* Only one is infinity. If src1 in infinity, then so */ + /* is the result (even if src2 is zero). */ + src2_addr = StackEntryByIndex(destIndex); + if ((src1_addr->tagvalue & TAG_INFINITY_MASK) != 0) { + tag_or = TAG_INFINITY_MASK; + } else { + tag_or = TAG_ZERO_MASK; + } + tag_or |= (tag_xor & TAG_NEGATIVE_MASK); + src2_addr->tagvalue = tag_or; + } + return; + } + /* The only funny bit left is zero */ + if ((tag_xor & TAG_ZERO_MASK) != 0) { + /* Only one zero. */ + if ((src1_addr->tagvalue & TAG_ZERO_MASK) == 0) { + /* Src2 is zero. Raise divide by zero */ + NpxStatus |= SW_ZE_MASK; + if ((NpxControl & CW_ZM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + DoAPop=FALSE; + return; + } else { + /* Unmasked. Infinity with xor of signs. */ + tag_or = TAG_INFINITY_MASK; + } + } else { + /* Src1 is zero. The result is zero with */ + /* the xor of the sign bits. */ + tag_or = TAG_ZERO_MASK; + } + src1_addr = StackEntryByIndex(destIndex); + tag_or |= (tag_xor & TAG_NEGATIVE_MASK); + src1_addr->tagvalue = tag_or; + } else { + /* Both are zeros. This is an invalid operation */ + src1_addr = StackEntryByIndex(destIndex); + SignalIndefinite(src1_addr); + } + } +} + + +/* +Name : FFREE +Function : Set the 'empty' tagword bit in the destination +Operation : Tag(dest) <- 'empty' +Flags : All undefined +Exceptions : None +Valid range : Any +Notes : +*/ + + +GLOBAL VOID FFREE IFN1(IU16, destIndex) +{ + FPSTACKENTRY *dest_addr; + + dest_addr = StackEntryByIndex(destIndex); + dest_addr->tagvalue = TAG_EMPTY_MASK; + if (POPST) { + PopStack(); + } +} + + +/* +Name : FILD +Function : Push the memory integer onto the stack +Operation : Decrement TOS; ST(0) <- SRC. +Flags : C1 as per table 15-1. Others undefined. +Exceptions : IS +Valid range : Any +Notes : FLD Instruction only: source operand is denormal. + Masked response: No special action, load as usual. + fld gives an Invalid exception if the stack is full. Unmasked + Invalid exceptions leave the stack unchanged. Neither the MIPS + nor the 68k code notice stack full, so it is probably safe to + assume that it rarely happens, and optimise for the case where + there is no exception. + fld does not generate an Invalid exception if the ST is a NaN. + When loading a Short real or Long real NaN, fld extends the + significand by adding zeros at the least significant end. + Load operations raise denormal as an "after" exception: the + register stack is already updated when the exception is raised + fld produces a denormal result only when loading from memory: + using fld to transfer a denormal value between registers has + no effect. +*/ + + +GLOBAL VOID FLD IFN1(VOID *, memPtr) +{ + FPSTACKENTRY *src_addr; + IU16 IndexVal; + + /* Clear C1 */ + FlagC1(0); + src_addr = StackEntryByIndex(7); + if ((src_addr->tagvalue & TAG_EMPTY_MASK) == 0) { /* Highly unlikely, see notes. */ + NpxStatus |= (SW_IE_MASK | SW_SF_MASK); + FlagC1(1); + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } else { + TOSPtr = src_addr; + WriteIndefinite(TOSPtr); + } + } else { + if (FPtype == FPSTACK) { + IndexVal = *(IU16 *)memPtr; + src_addr = StackEntryByIndex(IndexVal); + TOSPtr = StackEntryByIndex(7); + CopyFP(TOSPtr, src_addr); + } else { + switch (FPtype) { + case M16I : TOSPtr = src_addr; + Loadi16ToFP(TOSPtr, memPtr); + break; + case M32I : TOSPtr = src_addr; + Loadi32ToFP(TOSPtr, memPtr); + break; + case M64I : TOSPtr = src_addr; + Loadi64ToFP(TOSPtr, memPtr); + break; + case M32R : Loadr32ToFP(src_addr, memPtr, TRUE); + TOSPtr = src_addr; + break; + case M64R : Loadr64ToFP(src_addr, memPtr, TRUE); + TOSPtr = src_addr; + break; + case M80R : TOSPtr = src_addr; + Loadr80ToFP(TOSPtr, memPtr); + break; + } + } + } +} + + + +/*( +Name : FINCSTP +Function : Add one to the TOS +Operation : if (ST != 7) { ST <- ST+1 else { ST <- 0 ENDif +Flags : C1 as per table 15-1. C0, C2 and C3 undefined. +Exceptions : None +Valid range : N/A +)*/ + + +GLOBAL VOID FINCSTP IFN0() +{ + /* Clear C1 */ + FlagC1(0); + TOSPtr = StackEntryByIndex(1); +} + + + +/*( +Name : FINIT +Function : Initialise the floating point unit +Operation : CW<-037F; SW<-0; TW<-FFFFH; FEA<-0; FDS<-0; + FIP<-0; FOP<-0; FCS<-0; +Flags : All reset +Exceptions : None +Valid range : N/A +)*/ + + +GLOBAL VOID FINIT IFN0() +{ + IU8 counter; + + NpxControl = 0x037f; + npxRounding = ROUND_NEAREST; + NpxStatus = 0; + NpxLastSel=0; + NpxLastOff=0; + NpxFEA=0; + NpxFDS=0; + NpxFIP=0; + NpxFOP=0; + NpxFCS=0; + TOSPtr = FPUStackBase; + counter=0; + while (counter++ < 8) { + TOSPtr->tagvalue = TAG_EMPTY_MASK; + TOSPtr++; + } + TOSPtr = FPUStackBase; +} + + + +/*( +Name : FIST(P) +Function : Store integer from top of stack to memory +Operation : [mem] <- (I)ST +Flags : C1 as per table 15-1. All other underfined. +Exceptions : P, I, IS +Valid range : N/A +Notes : FIST (integer store) rounds the content of the stack top to an + integer according to the RC field of the control word and transfers + the result to the destination. The destination may define a word or + short integer variable. Negative zero is stored in the same encoding + as positive zero: 0000..00. + Where the source register is empty, a NaN, denormal, unsupported, + infinity, or exceeds the representable range of destination, the + Masked Response: Store integer indefinite. +*/ + + +GLOBAL VOID FIST IFN1(VOID *, memPtr) +{ + IS16 exp_value; + IS32 res_out; + + /* Clear C1 */ + FlagC1(0); + if (POPST) { + DoAPop = TRUE; + } + /* If anything other than the negative bit is set then we should deal */ + /* with it here... */ + TestUneval(TOSPtr); + if ((TOSPtr->tagvalue & (~TAG_NEGATIVE_MASK)) != 0) { /* Must be unlikely */ + if ((TOSPtr->tagvalue & TAG_ZERO_MASK) != 0) { /* But this is the most likely of them */ + switch (FPtype) { + case M16I : + case M32I : *((IS32 *)memPtr) = 0; + break; + case M64I : *((IU8 *)memPtr + 0) = 0; + *((IU8 *)memPtr + 1) = 0; + *((IU8 *)memPtr + 2) = 0; + *((IU8 *)memPtr + 3) = 0; + *((IU8 *)memPtr + 4) = 0; + *((IU8 *)memPtr + 5) = 0; + *((IU8 *)memPtr + 6) = 0; + *((IU8 *)memPtr + 7) = 0; + break; + } + } else { + NpxStatus |= SW_IE_MASK; + if ((TOSPtr->tagvalue & TAG_EMPTY_MASK) != 0) { + NpxStatus |= SW_SF_MASK; + } + FlagC1(0); + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + if (POPST) { + DoAPop=FALSE; /* Unset it - we won't be popping. */ + } + } else { + WriteIntegerIndefinite(memPtr); + } + } + } else { + HostClearExceptions(); + exp_value = 0; + /* The result of conversion is written out */ + /* to FPTemp? */ + switch (FPtype) { + case M16I : *(IS16 *)&FPTemp = (IS16)npx_rint(TOSPtr->fpvalue); + /* Check for overflow */ + if ((FPH)(*(IS16 *)&FPTemp) != npx_rint(TOSPtr->fpvalue)) { + exp_value = 1; /* flag exception */ + } + break; + case M32I : *(IS32 *)&FPTemp = (IS32)npx_rint(TOSPtr->fpvalue); + /* Check for overflow */ + if ((FPH)(*(IS32 *)&FPTemp) != npx_rint(TOSPtr->fpvalue)) { + exp_value = 1; /* flag exception */ + } + break; + case M64I : CVTFPHI64((FPU_I64 *)&FPTemp, &(TOSPtr->fpvalue)); /* Must be writing the result to FPTemp as well... */ + CVTI64FPH((FPU_I64 *)&FPTemp); /* Result in FPRes */ + /* Check for overflow */ + if (FPRes != npx_rint(TOSPtr->fpvalue)) { + exp_value = 1; /* flag exception */ + } + break; + } + if (exp_value == 1) { + NpxStatus |= SW_IE_MASK; /* Set the invalid bit */ + /* For the masked overflow case, the result delivered by */ + /* the host will be correct, provided it is IEEE compliant. */ + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + DoAPop = FALSE; + } else { + WriteIntegerIndefinite(memPtr); + } + } + if (exp_value == 0) { + switch (FPtype) { + case M16I : res_out = *(IS16 *)&FPTemp; + *((IU32 *)memPtr) = (IU32)res_out; + break; + case M32I : res_out = *(IS32 *)&FPTemp; + *((IS32 *)memPtr) = (IS32)res_out; + break; + case M64I : res_out = ((FPU_I64 *)&FPTemp)->high_word; + *((IU8 *)memPtr + 3) = res_out & 0xff; + res_out >>= 8; + *((IU8 *)memPtr + 2) = res_out & 0xff; + res_out >>= 8; + *((IU8 *)memPtr + 1) = res_out & 0xff; + res_out >>= 8; + *((IU8 *)memPtr + 0) = res_out & 0xff; + res_out = ((FPU_I64 *)&FPTemp)->low_word; + *((IU8 *)memPtr + 7) = res_out & 0xff; + res_out >>= 8; + *((IU8 *)memPtr + 6) = res_out & 0xff; + res_out >>= 8; + *((IU8 *)memPtr + 5) = res_out & 0xff; + res_out >>= 8; + *((IU8 *)memPtr + 4) = res_out & 0xff; + break; + } + /* Check for precision */ + if (TOSPtr->fpvalue != npx_rint(TOSPtr->fpvalue)) { + SetPrecisionBit(); + if ((NpxControl & CW_PM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + if (POPST) { + if (DoAPop) { + PopStack(); + } + } + DoNpxException(); + return; + } + } + } + } + if (POPST) { + if (DoAPop) { + PopStack(); + } + } +} + + + +/*( +Name : FLDconstant +Function : Load constant value to TOS +Operation : Push ST: ST(0) <- constant +Flags : C1 as per table 15-1. All other underfined. +Exceptions : IS +Valid range : N/A +*/ + + +GLOBAL VOID FLDCONST IFN1(IU8, const_index) +{ + + /* Clear C1 */ + FlagC1(0); + TOSPtr = StackEntryByIndex(7); + if ((TOSPtr->tagvalue & TAG_EMPTY_MASK) == 0) { + SignalStackOverflow(TOSPtr); + } else { + memset((char*)TOSPtr,0,sizeof(FPSTACKENTRY)); + TOSPtr->fpvalue = ConstTable[const_index].fpvalue; + TOSPtr->tagvalue = ConstTable[const_index].tagvalue; + } +} + + + +/*( +Name : FLDCW +Function : Replace the current value of the FPU control word with + the value in the specified memory location. +Operation : CW <- SRC. +Flags : All undefined. +Exceptions : None - but unmasking previously masked exceptions will + cause the unmasked exception to be triggered if the + matching bit is set in the status word. +Valid range : N/A +*/ + + +GLOBAL VOID FLDCW IFN1(VOID *, memPtr) +{ + IU32 result; +/* +This function has to modify things. The control word contains the +following information: +Precision control - not implemented. +Rounding control - implemented. +Exception masks - implemented. +Thus when we read in a value for the control word, we have to update +the host's rounding mode and also the exception masks. +*/ + /* First, set the rounding mode */ + result = *(IU32 *)memPtr; + NpxControl = (IU16)result; + npxRounding = (NpxControl & 0xc00); + switch (npxRounding) { + case ROUND_NEAREST : HostSetRoundToNearest(); + break; + case ROUND_NEG_INFINITY : HostSetRoundDown(); + break; + case ROUND_POS_INFINITY : HostSetRoundUp(); + break; + case ROUND_ZERO : HostSetRoundToZero(); + break; + } + /* Now adjust the exceptions. If an exception is unmasked, then the */ + /* bit value in NpxControl in '0'. If the exception has been */ + /* triggered then the corresponding bit in NpxStatus is '1'.Thus, */ + /* the expression ~NpxControl(5..0) | NpxStatus(5..0) will be */ + /* non-zero when we have unmasked exceptions that were previously */ + /* masked. */ + if (((~(NpxControl & 0x3f)) & (NpxStatus & 0x3f)) != 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } +} + +GLOBAL VOID FLDCW16 IFN1(VOID *, memPtr) +{ +/* +This function has to modify things. The control word contains the +following information: +Precision control - not implemented. +Rounding control - implemented. +Exception masks - implemented. +Thus when we read in a value for the control word, we have to update +the host's rounding mode and also the exception masks. +*/ + /* First, set the rounding mode */ + NpxControl = *(IU16 *)memPtr; + npxRounding = (NpxControl & 0xc00); + switch (npxRounding) { + case ROUND_NEAREST : HostSetRoundToNearest(); + break; + case ROUND_NEG_INFINITY : HostSetRoundDown(); + break; + case ROUND_POS_INFINITY : HostSetRoundUp(); + break; + case ROUND_ZERO : HostSetRoundToZero(); + break; + } + /* Now adjust the exceptions. If an exception is unmasked, then the */ + /* bit value in NpxControl in '0'. If the exception has been */ + /* triggered then the corresponding bit in NpxStatus is '1'.Thus, */ + /* the expression ~NpxControl(5..0) | NpxStatus(5..0) will be */ + /* non-zero when we have unmasked exceptions that were previously */ + /* masked. */ + if (((~(NpxControl & 0x3f)) & (NpxStatus & 0x3f)) != 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } +} + +/*( +Name : FLDENV +Function : Reload the FPU state from memory. +Operation : FPU state <- SRC +Flags : As loaded. +Exceptions : None - but unmasking previously masked exceptions will + cause the unmasked exception to be triggered if the + matching bit is set in the status word. +Valid range : N/A +*/ + + +GLOBAL VOID FLDENV IFN1(VOID *, memPtr) +{ + /* First. load the control, status, tagword regs. etc. */ + OpFpuRestoreFpuState(memPtr, 0); + /* Finally, check to see if any previously unmasked exceptions */ + /* are now needed to go off. Do this by anding the "triggered" bits in */ + /* NpxStatus with the one's complement of the "masked" bits in NpxControl. */ + if (((NpxStatus & 0x3f) & (~(NpxControl & 0x3f))) != 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } +} + +/* This generator is used to write out the 14/28 bytes stored by FSTENV, +and FSAVE. */ + + +LOCAL VOID OpFpuStoreFpuState IFN2(VOID *, memPtr, IU32, fsave_offset) +{ + IU32 result; + + /* how the copy takes place depends on the addressing mode */ + /* NPX_ADDRESS_SIZE_32 and NPX_PROT_MODE settings */ + /*************************************************************** */ + /* Need to do similar thing to strings to check that space */ + /* is available and that there is not paging fault!!!! */ + /*************************************************************** */ + /* The operation should store the control word, tag word */ + /* and status word, so these need to be calculated. It also */ + /* stores the last instruction and data pointers and the opcode */ + /* (if in real mode) */ + /* The offsets from memPtr look strange. Remember that we are going to*/ + /* write this data using the "write bytes" function. This assumes that*/ + /* the data is stored bigendian and writes it out back to front for */ + /* the little-endian intel, as it were. Are you with me? */ + /* fsave offset is required since if we are asked to do an "fsave" */ + /* (as opposed to an fstenv), then the "string" that we are going to */ + /* write will be even bigger, and this stuff must be at the top end */ + /* of it. Horrible but logical */ + if (NPX_PROT_MODE) { + if (NPX_ADDRESS_SIZE_32) { + WriteI32ToIntel(((IU8 *)memPtr+24+fsave_offset), (IU32)NpxControl); + GetIntelStatusWord(); + WriteI32ToIntel(((IU8 *)memPtr+20+fsave_offset), (IU32)NpxStatus); + GetIntelTagword(&result); + WriteI32ToIntel(((IU8 *)memPtr+16+fsave_offset), (IU32)result); + WriteI32ToIntel(((IU8 *)memPtr+12+fsave_offset), (IU32)NpxFIP); + WriteI32ToIntel(((IU8 *)memPtr+8+fsave_offset), (IU32)NpxFCS); + WriteI32ToIntel(((IU8 *)memPtr+4+fsave_offset), (IU32)NpxFEA); + WriteI32ToIntel(((IU8 *)memPtr+0+fsave_offset), (IU32)NpxFDS); + } else { + WriteI16ToIntel(((IU8 *)memPtr+12+fsave_offset), (IU16)NpxControl); + GetIntelStatusWord(); + WriteI16ToIntel(((IU8 *)memPtr+10+fsave_offset), (IU16)NpxStatus); + GetIntelTagword(&result); + WriteI16ToIntel(((IU8 *)memPtr+8+fsave_offset), (IU16)result); + WriteI16ToIntel(((IU8 *)memPtr+6+fsave_offset), (IU16)NpxFIP); + WriteI16ToIntel(((IU8 *)memPtr+4+fsave_offset), (IU16)NpxFCS); + WriteI16ToIntel(((IU8 *)memPtr+2+fsave_offset), (IU16)NpxFEA); + WriteI16ToIntel(((IU8 *)memPtr+0+fsave_offset), (IU16)NpxFDS); + } + } else { + if (NPX_ADDRESS_SIZE_32) { + WriteI32ToIntel(((IU8 *)memPtr+24+fsave_offset), (IU32)NpxControl); + GetIntelStatusWord(); + WriteI32ToIntel(((IU8 *)memPtr+20+fsave_offset), (IU32)NpxStatus); + GetIntelTagword(&result); + WriteI32ToIntel(((IU8 *)memPtr+16+fsave_offset), (IU32)result); + WriteI32ToIntel(((IU8 *)memPtr+12+fsave_offset), (IU32)((NpxFIP+(NpxFCS<<4)) & 0xffff)); + WriteI32ToIntel(((IU8 *)memPtr+8+fsave_offset), (IU32)((((NpxFIP+(NpxFCS<<4)) & 0xffff0000) >> 4) | ((IU32)(NpxFOP & 0x7ff)))); + WriteI32ToIntel(((IU8 *)memPtr+4+fsave_offset), (IU32)((NpxFEA+(NpxFDS<<4)) & 0xffff)); + WriteI32ToIntel(((IU8 *)memPtr+0+fsave_offset), (IU32)(((NpxFEA+(NpxFDS<<4)) & 0xffff0000) >> 4)); + } else { + WriteI16ToIntel(((IU8 *)memPtr+12+fsave_offset), (IU16)NpxControl); + GetIntelStatusWord(); + WriteI16ToIntel(((IU8 *)memPtr+10+fsave_offset), (IU16)NpxStatus); + GetIntelTagword(&result); + WriteI16ToIntel(((IU8 *)memPtr+8+fsave_offset), (IU16)result); + WriteI16ToIntel(((IU8 *)memPtr+6+fsave_offset), (IU16)((NpxFIP+(NpxFCS<<4)) & 0xffff)); + WriteI16ToIntel(((IU8 *)memPtr+4+fsave_offset), (IU16)((((NpxFIP+(NpxFCS<<4)) & 0xffff0000) >> 4) | ((IU16)(NpxFOP & 0x7ff)))); + WriteI16ToIntel(((IU8 *)memPtr+2+fsave_offset), (IU16)(((NpxFDS<<4)+NpxFEA) & 0xffff)); + WriteI16ToIntel(((IU8 *)memPtr+0+fsave_offset), (IU16)(((NpxFEA+(NpxFDS<<4)) & 0xffff0000) >> 4)); + } + } +} + +/* This generator is called by FLDENV and FRSTOR, to load up the 14/28 +byte block. */ + + +LOCAL VOID OpFpuRestoreFpuState IFN2(VOID *, memPtr, IU32, frstor_offset) +{ + IU32 result; + + /* how the copy takes place depends on the addressing mode */ + /* NPX_ADDRESS_SIZE_32 and NPX_PROT_MODE settings */ + /*************************************************************** */ + /* Need to do similar thing to strings to check that space */ + /* is available and that there is not paging fault!!!! */ + /************************************************************** */ + /* The operation should restore the control word, tag word */ + /* and status word, so these need to be translated. It also */ + /* restores the last instruction and data pointers and the opcode */ + /* (if in real mode) */ + + + /* get the rest of the data, instruction and data pointers */ + if ( NPX_PROT_MODE ) { + if (NPX_ADDRESS_SIZE_32) { + ReadI32FromIntel(&result, ((IU8 *)memPtr+24+frstor_offset)); + FLDCW((VOID *)&result); + ReadI32FromIntel(&result, ((IU8 *)memPtr+20+frstor_offset)); + SetIntelStatusWord(result); + ReadI32FromIntel(&result, ((IU8 *)memPtr+16+frstor_offset)); + SetIntelTagword(result); + ReadI32FromIntel(&NpxFIP, ((IU8 *)memPtr+12+frstor_offset)); + ReadI32FromIntel(&NpxFCS, ((IU8 *)memPtr+8+frstor_offset)); + ReadI32FromIntel(&NpxFEA, ((IU8 *)memPtr+4+frstor_offset)); + ReadI32FromIntel(&NpxFDS, ((IU8 *)memPtr+0+frstor_offset)); + } else { + ReadI16FromIntel(&result, ((IU8 *)memPtr+12+frstor_offset)); + /* Note this is a 32-bit result ! */ + FLDCW((VOID *)&result); + ReadI16FromIntel(&result, ((IU8 *)memPtr+10+frstor_offset)); + SetIntelStatusWord(result); + ReadI16FromIntel(&result, ((IU8 *)memPtr+8+frstor_offset)); + SetIntelTagword(result); + ReadI16FromIntel(&NpxFIP, ((IU8 *)memPtr+6+frstor_offset)); + ReadI16FromIntel(&NpxFCS, ((IU8 *)memPtr+4+frstor_offset)); + ReadI16FromIntel(&NpxFEA, ((IU8 *)memPtr+2+frstor_offset)); + ReadI16FromIntel(&NpxFDS, ((IU8 *)memPtr+0+frstor_offset)); + } + } else { + if (NPX_ADDRESS_SIZE_32) { + ReadI32FromIntel(&result, ((IU8 *)memPtr+24+frstor_offset)); + FLDCW((VOID *)&result); + ReadI32FromIntel(&result, ((IU8 *)memPtr+20+frstor_offset)); + SetIntelStatusWord(result); + ReadI32FromIntel(&result, ((IU8 *)memPtr+16+frstor_offset)); + SetIntelTagword(result); + ReadI32FromIntel(&NpxFIP, ((IU8 *)memPtr+12+frstor_offset)); + NpxFIP &= 0xffff; + ReadI32FromIntel(&result, ((IU8 *)memPtr+8+frstor_offset)); + NpxFIP |= ((result & 0x0ffff000) << 4); + ReadI32FromIntel(&NpxFOP, ((IU8 *)memPtr+8+frstor_offset)); + NpxFOP &= 0x7ff; + ReadI32FromIntel(&NpxFEA, ((IU8 *)memPtr+4+frstor_offset)); + NpxFEA &= 0xffff; + ReadI32FromIntel(&result, ((IU8 *)memPtr+0+frstor_offset)); + NpxFEA |= ((result & 0x0ffff000) << 4); + } else { + ReadI16FromIntel(&result, ((IU8 *)memPtr+12+frstor_offset)); + FLDCW((VOID *)&result); + ReadI16FromIntel(&result, ((IU8 *)memPtr+10+frstor_offset)); + SetIntelStatusWord(result); + ReadI16FromIntel(&result, ((IU8 *)memPtr+8+frstor_offset)); + SetIntelTagword(result); + ReadI16FromIntel(&NpxFIP, ((IU8 *)memPtr+6+frstor_offset)); + ReadI16FromIntel(&result, ((IU8 *)memPtr+4+frstor_offset)); + NpxFIP |= ((result & 0xf000) << 4); + ReadI16FromIntel(&NpxFOP, ((IU8 *)memPtr+4+frstor_offset)); + NpxFOP &= 0x7ff; + ReadI16FromIntel(&NpxFEA, ((IU8 *)memPtr+2+frstor_offset)); + ReadI16FromIntel(&result, ((IU8 *)memPtr+0+frstor_offset)); + NpxFEA |= (IU32)((result & 0xf000) << 4); + } + } +} + + + +/*( +Name : FMUL +Function : Multiply two numbers together +Operation : Dest <- Src1 * Src2 +Flags : C1 as per table 15-1. C0, C2 and C3 undefined +Exceptions : P, U, O, D, I, IS +Valid range : Any +Notes : +)*/ + + +GLOBAL VOID FMUL IFN3(IU16, destIndex, IU16, src1Index, VOID *, src2) +{ + IU16 src2Index; + + LoadValue(src2, &src2Index); + if (POPST) { + DoAPop=TRUE; + } + GenericMultiply(destIndex, src1Index, src2Index); + if (POPST) { + if (DoAPop) { + PopStack(); + } + } +} + + + +LOCAL VOID GenericMultiply IFN3(IU16, destIndex, IU16, src1Index, IU16, src2Index) +{ + FPSTACKENTRY *src1_addr; + FPSTACKENTRY *src2_addr; + + src1_addr = StackEntryByIndex(src1Index); + src2_addr = StackEntryByIndex(src2Index); + + /* Clear C1 */ + FlagC1(0); + TestUneval(src1_addr); + TestUneval(src2_addr); + tag_or = (src1_addr->tagvalue | src2_addr->tagvalue); + /* If the only tagword bits set are negative or denormal then just proceed */ + if ((tag_or & ~TAG_NEGATIVE_MASK) == 0) { + HostClearExceptions(); + FPRes = src1_addr->fpvalue * src2_addr->fpvalue; + /* Reuse one of the above to calculate the destination */ + src1_addr = StackEntryByIndex(destIndex); + PostCheckOUP(); + /* Value could be anything */ + CalcTagword(src1_addr); + } else { + /* Some funny bit was set. Check for the possibilities */ + if ((tag_or & ((TAG_EMPTY_MASK | TAG_UNSUPPORTED_MASK) | TAG_NAN_MASK)) != 0) { + if ((tag_or & TAG_EMPTY_MASK) != 0) { + src1_addr = StackEntryByIndex(destIndex); + SignalStackUnderflow(src1_addr); + } else { + if ((tag_or & TAG_UNSUPPORTED_MASK) != 0) { + src1_addr = StackEntryByIndex(destIndex); + SignalIndefinite(src1_addr); + } else { + /* It must be NaN */ + tag_xor = (src1_addr->tagvalue ^ src2_addr->tagvalue); + Test2NaN(destIndex, src1_addr, src2_addr); + } + } + return; + } + /* Check for the denorm case... */ + if ((tag_or & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + DoAPop=FALSE; /* Just in case */ + return; + } else { + /* Proceed if we've no zeroes or infinities. */ + if ((tag_or & ~(TAG_DENORMAL_MASK | TAG_NEGATIVE_MASK)) == 0) { + HostClearExceptions(); + FPRes = src1_addr->fpvalue * src2_addr->fpvalue; + /* Reuse one of the above to calculate the destination */ + src1_addr = StackEntryByIndex(destIndex); + PostCheckOUP(); + /* Value could be anything */ + CalcTagword(src1_addr); + return; + } + } + } + tag_xor = (src1_addr->tagvalue ^ src2_addr->tagvalue); + /* For zero or infinity operands we will have the result */ + src2_addr = StackEntryByIndex(destIndex); + if ((tag_or & TAG_ZERO_MASK) != 0) { + /* Multiplying zero by infinity yields zero with the xor of the signs */ + if ((tag_or & TAG_INFINITY_MASK) != 0) { + SignalIndefinite(src2_addr); + } else { + /* Zero by anything else is zero with sign equal */ + /* to the xor of the signs of the two sources. */ + src2_addr->tagvalue = (TAG_ZERO_MASK | (tag_xor & TAG_NEGATIVE_MASK)); + } + return; + } + /* The only funny bit left is infinity. The result is going */ + /* to be infinity with sign equal to the xor of the signs of */ + /* the sources. */ + src2_addr->tagvalue = TAG_INFINITY_MASK | (tag_xor & TAG_NEGATIVE_MASK); + } +} + + + +/* The FNOP operation doesn't do anything, it just does the normal +checks for exceptions. */ + + +GLOBAL VOID FNOP IFN0() +{ +} + + +/* FPATAN: This generator returns the value ARCTAN(ST(1)/ST) to ST(1) +then pops the stack. Its response to zeros and infinities is rather +unusual... ++-0 / +X = 0 with sign of original zero ++-0 / -X = pi with sign of original zero ++-X /+-0 = pi/2 with sign of original X ++-0 / +0 = 0 with sign of original zero ++-0 / -0 = pi with sign of original zero ++inf / +-0 = +pi/2 +-inf / +-0 = -pi/2 ++-0 / +inf = 0 with sign of original zero ++-0 / -inf = pi with sign of original zero ++-inf / +-X = pi/2 with sign of original infinity ++-Y / +inf = 0 with sign of original Y ++-Y / -inf = pi with sign of original Y ++-inf / +inf = pi/4 with sign of original inf ++-inf / -inf = 3*pi/4 with sign of original inf +Otherwise, we just take the two operands from the stack and call the +appropriate EDL to do the instruction. +The use of an invalid operand with masked exception set causes +the pop to go off, cruds up the contents of the stack and doesn't set +the invalid exception, although if the invalid is infinity or NaN, +overflow and precision exceptions are also generated, while if it is +a denorm, underflow and precision exceptions are generated. +With unmasked exceptions, exactly the same chain of events occurs. +UNDER ALL CIRCUMSTANCES, THE STACK GETS POPPED. +*/ + + +GLOBAL VOID FPATAN IFN0() +{ + FPSTACKENTRY *st1_addr; + + st1_addr = StackEntryByIndex(1); + /* Clear C1 */ + FlagC1(0); + /* If only the negative bit is set, just proceed.... */ + TestUneval(TOSPtr); + TestUneval(st1_addr); + tag_or = (TOSPtr->tagvalue | st1_addr->tagvalue); + if ((tag_or & ~TAG_NEGATIVE_MASK) == 0) { + HostClearExceptions(); + FPRes = atan2(st1_addr->fpvalue, TOSPtr->fpvalue); + PostCheckOUP(); + /* The retrun value has to be in the range -pi to +pi */ + CalcTagword(st1_addr); + } else { + /* Some funny bit set.... */ + if ((tag_or & ((TAG_EMPTY_MASK | TAG_UNSUPPORTED_MASK) | TAG_NAN_MASK)) != 0) { + if ((tag_or & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(st1_addr); + } else { + if ((tag_or & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(st1_addr); + } else { + /* It must be a NaN. */ + tag_xor = (TOSPtr->tagvalue ^ st1_addr->tagvalue); + Test2NaN(0, TOSPtr, st1_addr); + } + } + PopStack(); + return; + } + if ((tag_or & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + PopStack(); + return; + } else { + /* Proceed if we've no zeroes or infinities. */ + if ((tag_or & ~(TAG_DENORMAL_MASK | TAG_NEGATIVE_MASK)) == 0) { + HostClearExceptions(); + FPRes = atan2(st1_addr->fpvalue, TOSPtr->fpvalue); + PostCheckOUP(); + /* The return value is -pi to +pi */ + CalcTagword(st1_addr); + PopStack(); + return; + } + } + } + /* It must have been a zero or an infinity. As can be seen */ + /* from the table above, there is a complicated interaction */ + /* between the result for each type and its option. */ + /* Let's simplify it by use of a little table. */ + /* ST ST(1) Result */ + /* Z I S Z I S */ + /* 0 0 0 0 1 0 pi/2 */ + /* 0 0 0 0 1 1 -pi/2 */ + /* 0 0 0 1 0 0 +0 */ + /* 0 0 0 1 0 1 -0 */ + /* 0 1 0 0 1 0 pi/4 */ + /* 0 1 0 0 1 1 3*pi/4 */ + /* 0 1 0 1 0 0 pi/2 */ + /* 0 1 0 1 0 1 pi/2 */ + /* 0 1 1 0 1 0 -pi/4 */ + /* 0 1 1 0 1 1 -3*pi/4 */ + /* 0 1 1 1 0 0 -pi/2 */ + /* 0 1 1 1 0 1 -pi/2 */ + /* 1 0 0 0 1 0 +0 */ + /* 1 0 0 0 1 1 pi */ + /* 1 0 0 1 0 0 +0 */ + /* 1 0 0 1 0 1 pi */ + /* 1 0 1 0 1 0 -0 */ + /* 1 0 1 0 1 1 -pi */ + /* 1 0 1 1 0 0 -0 */ + /* 1 0 1 1 0 1 -pi */ + /* */ + /* All other combinations are invalid, as they would involve */ + /* a tagword having both infinity and zero bits set. */ + tag_xor = (st1_addr->tagvalue & 7); + tag_xor <<= 3; + tag_xor |= (TOSPtr->tagvalue & 7); + CopyFP(st1_addr, FpatanTable[tag_xor]); + } + /* No matter what has happened... We ALWAYS pop on FPATAN!!! */ + PopStack(); +} + + + +/* FPREM: This is the same function as implemented on the 80287. It is +NOT the same as the IEEE required REM function, this is now supplied as +FPREM1. FPREM predates the final draft of IEEE 754 and is maintained for +the purpose of backward compatibility. +*/ + + +GLOBAL VOID FPREM IFN0() +{ + IS16 exp_diff; + IU8 little_rem; + FPU_I64 remainder; + FPH fprem_val; + FPSTACKENTRY *st1_addr; + + st1_addr = StackEntryByIndex(1); + TestUneval(TOSPtr); + TestUneval(st1_addr); + tag_or = (TOSPtr->tagvalue | st1_addr->tagvalue); + /* First, check if the values are real. If so, we can proceed. */ + if ((tag_or & ~(TAG_NEGATIVE_MASK | TAG_DENORMAL_MASK)) == 0) { + /* First, check for the denormal possibility... */ + if ((tag_or & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + } + /* Make both values positive */ + ((FPHOST *)&(TOSPtr->fpvalue))->hiword.sign = 0; + ((FPHOST *)&(st1_addr->fpvalue))->hiword.sign = 0; + + /* Find the difference in exponents... */ + exp_diff = ((FPHOST *)&(TOSPtr->fpvalue))->hiword.exp - ((FPHOST *)&(st1_addr->fpvalue))->hiword.exp; + /* If it's more than 63, we can't do it at once... */ + if (exp_diff >= 64) { + ((FPHOST *) &fprem_val) -> hiword.sign = 0; + ((FPHOST *) &fprem_val) -> hiword.mant_hi = 0; + ((FPHOST *) &fprem_val) -> mant_lo = 0; + ((FPHOST *) &fprem_val) -> hiword.exp = (exp_diff - 50) + HOST_BIAS; + FlagC2(1); /* This will be incomplete reduction */ + } else { + FlagC2(0); /* This will be complete reduction */ + } + HostClearExceptions(); + tag_xor = (NpxControl & 0xc00); + NpxControl &= 0xf3ff; + NpxControl |= ROUND_ZERO; + HostSetRoundToZero(); + /* Unfortunately, because the function isn't the strict */ + /* IEEE compliant style, if we use an IEEE compliant FREM */ + /* operation, as like as not we'd get the wrong answer. So */ + /* we perform the operation by doing the steps given in the */ + /* page in the instruction set. */ + FPRes = TOSPtr->fpvalue / st1_addr->fpvalue; + if ((NpxStatus & 0x0400) != 0) { /* The incomplete reduction case */ + FPRes = FPRes / fprem_val; + } + FPRes = npx_rint(FPRes); + /* Calculate the remainder */ + if ((NpxStatus & 0x0400) == 0) { + CVTFPHI64(&remainder, &FPRes); + CPY64BIT8BIT(&remainder, &little_rem); + } + switch (tag_xor) { + case ROUND_NEAREST : HostSetRoundToNearest(); + break; + case ROUND_NEG_INFINITY : HostSetRoundDown(); + break; + case ROUND_POS_INFINITY : HostSetRoundUp(); + break; + case ROUND_ZERO : HostSetRoundToZero(); + break; + } + NpxControl &= 0xf3ff; + NpxControl |= tag_xor; + FPRes *= st1_addr->fpvalue; + if ((NpxStatus & 0x0400) != 0) { /* The incomplete reduction case */ + FPRes *= fprem_val; + FPRes = TOSPtr->fpvalue - FPRes; + } else { /* Complete reduction */ + FPRes = TOSPtr->fpvalue - FPRes; + FlagC0((little_rem&4)?1:0); + FlagC3((little_rem&2)?1:0); + FlagC1((little_rem&1)); + } + /* Check for an underflow response */ + if (HostGetUnderflowException() != 0) { + NpxStatus |= SW_UE_MASK; + if ((NpxControl & CW_UM_MASK) == 0) { + AdjustUnderflowResponse(); + NpxStatus |= SW_ES_MASK; + NpxException = TRUE; + } + } + /* But the remainder must have the sign of the original ST! */ + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + ((FPHOST *)&(FPRes))->hiword.sign = 1; + } else { + ((FPHOST *)&(FPRes))->hiword.sign = 0; + } + /* And restore st1 sign bit if required */ + if ((st1_addr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + ((FPHOST *)&(st1_addr->fpvalue))->hiword.sign = 1; + } + CalcTagword(TOSPtr); + } else { + /* We had a funny thing */ + if ((tag_or & ((TAG_EMPTY_MASK | TAG_UNSUPPORTED_MASK) | TAG_NAN_MASK)) != 0) { + if ((tag_or & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(TOSPtr); + } else { + if ((tag_or & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(TOSPtr); + } else { + /* It must be a NaN. */ + tag_xor = (TOSPtr->tagvalue ^ st1_addr->tagvalue); + Test2NaN(0, TOSPtr, st1_addr); + } + } + return; + } + /* The logical way to arrange zeroes and infinities is zero first. */ + if ((tag_or & TAG_ZERO_MASK) != 0) { + /* A zero in ST(1) is ALWAYS invalid... */ + if ((st1_addr->tagvalue & TAG_ZERO_MASK) != 0) { + SignalIndefinite(TOSPtr); + } + /* The zero must be in ST, the result is what is there... */ + FlagC0(0); + FlagC1(0); + FlagC2(0); + FlagC3(0); + return; + } + /* OK, it HAS to be infinity */ + /* An infinity at ST is ALWAYS invalid... */ + if ((TOSPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + SignalIndefinite(TOSPtr); + } + /* An infinity at ST(1) leaves ST untouched */ + FlagC0(0); + FlagC1(0); + FlagC2(0); + FlagC3(0); + } +} + + + + +/* FPREM1: This is the IEEE required REM function, this is now supplied as +FPREM1. FPREM predates the final draft of IEEE 754 and is maintained for +the purpose of backward compatibility. +*/ + + +GLOBAL VOID FPREM1 IFN0() +{ + IS16 exp_diff; + IU8 little_rem; + FPU_I64 remainder; + FPH fprem_val; + FPSTACKENTRY *st1_addr; + + st1_addr = StackEntryByIndex(1); + TestUneval(TOSPtr); + TestUneval(st1_addr); + tag_or = (TOSPtr->tagvalue | st1_addr->tagvalue); + /* First, check if the values are real. If so, we can proceed. */ + if ((tag_or & ~(TAG_NEGATIVE_MASK | TAG_DENORMAL_MASK)) == 0) { + /* First, check for the denormal possibility... */ + if ((tag_or & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + } + /* Make both values positive */ + ((FPHOST *)&(TOSPtr->fpvalue))->hiword.sign = 0; + ((FPHOST *)&(st1_addr->fpvalue))->hiword.sign = 0; + + /* Find the difference in exponents... */ + exp_diff = ((FPHOST *)&(TOSPtr->fpvalue))->hiword.exp - ((FPHOST *)&(st1_addr->fpvalue))->hiword.exp; + /* If it's more than 63, we can't do it at once... */ + if (exp_diff >= 64) { + ((FPHOST *) &fprem_val) -> hiword.sign = 0; + ((FPHOST *) &fprem_val) -> hiword.mant_hi = 0; + ((FPHOST *) &fprem_val) -> mant_lo = 0; + ((FPHOST *) &fprem_val) -> hiword.exp = (exp_diff - 50) + HOST_BIAS; + FlagC2(1); /* This will be incomplete reduction */ + } else { + FlagC2(0); /* This will be complete reduction */ + } + HostClearExceptions(); + /* Note that this is the only difference between FPREM and + FPREM1. For the incomplete reduction case we use "round + to nearest" rather than "round to zero". + */ + tag_xor = (NpxControl & 0xc00); + NpxControl &= 0xf3ff; + if ((NpxStatus & 0x0400) == 0) { + HostSetRoundToZero(); + NpxControl |= ROUND_ZERO; + } else { + HostSetRoundToNearest(); + NpxControl |= ROUND_NEAREST; + } + FPRes = TOSPtr->fpvalue / st1_addr->fpvalue; + if ((NpxStatus & 0x0400) != 0) { /* The incomplete reduction case */ + FPRes = FPRes / fprem_val; + } + FPRes = npx_rint(FPRes); + /* Calculate the remainder */ + if ((NpxStatus & 0x0400) == 0) { + CVTFPHI64(&remainder, &FPRes); + CPY64BIT8BIT(&remainder, &little_rem); + } + switch (tag_xor) { + case ROUND_NEAREST : HostSetRoundToNearest(); + break; + case ROUND_NEG_INFINITY : HostSetRoundDown(); + break; + case ROUND_POS_INFINITY : HostSetRoundUp(); + break; + case ROUND_ZERO : HostSetRoundToZero(); + break; + } + NpxControl &= 0xf3ff; + NpxControl |= tag_xor; + FPRes = st1_addr->fpvalue * FPRes; + if ((NpxStatus & 0x0400) != 0) { /* The incomplete reduction case */ + FPRes = FPRes * fprem_val; + FPRes = TOSPtr->fpvalue - FPRes; + } else { /* Complete reduction */ + FPRes = TOSPtr->fpvalue - FPRes; + FlagC0((little_rem&4)?1:0); + FlagC3((little_rem&2)?1:0); + FlagC1(little_rem&1); + } + /* Check for an underflow response */ + if (HostGetUnderflowException() != 0) { + NpxStatus |= SW_UE_MASK; + if ((NpxControl & CW_UM_MASK) == 0) { + AdjustUnderflowResponse(); + NpxStatus |= SW_ES_MASK; + NpxException = TRUE; + } + } + /* But the remainder must have the sign of the original ST! */ + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + ((FPHOST *)&(FPRes))->hiword.sign = 1; + } else { + ((FPHOST *)&(FPRes))->hiword.sign = 0; + } + /* And restore st1 sign bit if required */ + if ((st1_addr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + ((FPHOST *)&(st1_addr->fpvalue))->hiword.sign = 1; + } + CalcTagword(TOSPtr); + } else { + /* We had a funny thing */ + if ((tag_or & ((TAG_EMPTY_MASK | TAG_UNSUPPORTED_MASK) | TAG_NAN_MASK)) != 0) { + if ((tag_or & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(TOSPtr); + } else { + if ((tag_or & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(TOSPtr); + } else { + /* It must be a NaN. */ + tag_xor = (TOSPtr->tagvalue ^ st1_addr->tagvalue); + Test2NaN(0, TOSPtr, st1_addr); + } + } + return; + } + /* The logical way to arrange zeroes and infinities is zero first. */ + if ((tag_or & TAG_ZERO_MASK) != 0) { + /* A zero in ST(1) is ALWAYS invalid... */ + if ((st1_addr->tagvalue & TAG_ZERO_MASK) != 0) { + SignalIndefinite(TOSPtr); + } + /* The zero must be in ST, the result is what is there... */ + FlagC0(0); + FlagC1(0); + FlagC2(0); + FlagC3(0); + return; + } + /* OK, it HAS to be infinity */ + /* An infinity at ST is ALWAYS invalid... */ + if ((TOSPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + SignalIndefinite(TOSPtr); + } + /* An infinity at ST(1) leaves ST untouched */ + FlagC0(0); + FlagC1(0); + FlagC2(0); + FlagC3(0); + } +} + + + +/*( + * Name : FPTAN + * Operation : Compute the value of TAN(ST) + * Flags : C1 as per table 15-1, others undefined. + * Exceptions : P, U, D, I, IS + * Valid range : |ST| < 2**63 + * Notes : This function has been substantially overhauled + since the 80287. It now has a much wider range + (it previously had to be 0<ST<(PI/4). In addition, + the return value is now really the tan of ST, with + a 1 pushed above it on the stack to maintain + compatibility with the 8087/80287. Previously the + result was a ratio of two values, neither of which + could be guaranteed. +)*/ + + +GLOBAL VOID FPTAN IFN0() +{ + FPSTACKENTRY *st1_addr; + + /* Clear C1 */ + FlagC1(0); + /* Set C2 to zero */ + FlagC2(0); + st1_addr = StackEntryByIndex(7); + /* Make sure that the stack element is free */ + if ((st1_addr->tagvalue & TAG_EMPTY_MASK) == 0) { + WriteIndefinite(TOSPtr); + TOSPtr = st1_addr; + SignalStackOverflow(TOSPtr); + return; + } + TestUneval(TOSPtr); + /* Check if a real value...We won't bother with limit checking */ + if ((TOSPtr->tagvalue & ~TAG_NEGATIVE_MASK) == 0) { + HostClearExceptions(); + /* We can just write the value straight out */ + FPRes = tan(TOSPtr->fpvalue); + PostCheckOUP(); + /* The return value could be absolutely anything */ + CalcTagword(TOSPtr); + TOSPtr = st1_addr; + CopyFP(TOSPtr, npx_one); + } else { + /* Some funny bit was set. Check for the possibilities */ + /* We begin with the most obvious cases... */ + /* Response to zero is to return zero with same sign */ + if ((TOSPtr->tagvalue & TAG_ZERO_MASK) != 0) { + TOSPtr = st1_addr; + CopyFP(TOSPtr, npx_one); + return; /* The required result! */ + } + /* We do denorm checking and bit setting ourselves because this */ + /* reduces the overhead if the thing is masked. */ + if ((TOSPtr->tagvalue & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } else { + HostClearExceptions(); + FPRes = tan(TOSPtr->fpvalue); + PostCheckOUP(); + /* The return value could be anything */ + CalcTagword(TOSPtr); + TOSPtr = st1_addr; + CopyFP(TOSPtr, npx_one); + } + return; + } + /* If the value is outside the acceptable range (including */ + /* infinity) then we set the C2 flag and leave everything */ + /* unchanged. */ + /* Sensible enough really, I suppose */ + if ((TOSPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + FlagC2(1); + return; + } + if ((TOSPtr->tagvalue & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(TOSPtr); + return; + } + if ((TOSPtr->tagvalue & TAG_SNAN_MASK) != 0) { + MakeNaNQuiet(TOSPtr); + return; + } + if ((TOSPtr->tagvalue & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(TOSPtr); + return; + } + } +} + + + +/*( + * Name : FRNDINT + * Operation : ST <- rounded ST + * Flags : C1 as per table 15-1, others undefined. + * Exceptions : P, U, D, I, IS + * Valid range : All + * Notes : On the 80287, a precision exception would be + raised if the operand wasn't an integer. + I begin by ASSUMING that on the 486 the response + is IEEE compliant so no OUP exceptions. +)*/ + + +GLOBAL VOID FRNDINT IFN0() +{ + /* Clear C1 */ + FlagC1(0); + TestUneval(TOSPtr); + if ((TOSPtr->tagvalue & ~TAG_NEGATIVE_MASK) == 0) { + HostClearExceptions(); + /* We can just write the value straight out */ + FPRes = npx_rint(TOSPtr->fpvalue); + if (FPRes != TOSPtr->fpvalue) { + SetPrecisionBit(); + /* If the rounding mode is "round to nearest" and we've + rounded up then we'll set C1 */ + if (npxRounding == ROUND_NEAREST) { + if (TOSPtr->fpvalue < FPRes) { + FlagC1(1); + } + } + if ((NpxControl & CW_PM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + } + /* It was a real before, it still must be one now. It could */ + /* be zero possibly. */ + CalcTagword(TOSPtr); + } else { + /* Lets do the most probable cases first... */ + /* If it's a zero or infinity, we do nothing. */ + if ((TOSPtr->tagvalue & (TAG_ZERO_MASK | TAG_INFINITY_MASK)) == 0) { + /* Lets check for a denormal */ + if ((TOSPtr->tagvalue & TAG_DENORMAL_MASK) != 0) { + SetPrecisionBit(); + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } else { + /* The result of rounding a denorm is dependent on */ + /* its sign and the prevailing rounding mode */ + switch (npxRounding) { + case ROUND_ZERO : + case ROUND_NEAREST : + TOSPtr->tagvalue &= TAG_NEGATIVE_MASK; + TOSPtr->tagvalue |= TAG_ZERO_MASK; + + break; + case ROUND_NEG_INFINITY : + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + memset((char*)TOSPtr,0,sizeof(FPSTACKENTRY)); + TOSPtr->fpvalue = -1.0; + TOSPtr->tagvalue = TAG_NEGATIVE_MASK; + } else { + TOSPtr->tagvalue &= TAG_NEGATIVE_MASK; + TOSPtr->tagvalue |= TAG_ZERO_MASK; + } + break; + case ROUND_POS_INFINITY : + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) == 0) { + memset((char*)TOSPtr,0,sizeof(FPSTACKENTRY)); + TOSPtr->fpvalue = 1.0; + TOSPtr->tagvalue = 0; + } else { + TOSPtr->tagvalue &= TAG_NEGATIVE_MASK; + TOSPtr->tagvalue |= TAG_ZERO_MASK; + } + break; + } + } + return; + } + /* It was one of the really wacky bits... */ + if ((TOSPtr->tagvalue & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(TOSPtr); + return; + } + if ((TOSPtr->tagvalue & TAG_SNAN_MASK) != 0) { + MakeNaNQuiet(TOSPtr); + return; + } + if ((TOSPtr->tagvalue & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(TOSPtr); + return; + } + } + } +} + + + + +/*( +Name : FSTCW +Function : Write the FPU control word to memory +Operation : DEST <- Cw +Flags : All undefined. +Exceptions : None - but unmasking previously masked exceptions will + cause the unmasked exception to be triggered if the + matching bit is set in the status word. +Valid range : N/A +*/ + + +GLOBAL VOID FSTCW IFN1(VOID *, memPtr) +{ + if (NpxDisabled) + { + /* UIF has told us to pretend we do not have an NPX */ + *(IU32 *)memPtr = (IU16)0xFFFF; + } + else + { + *(IU32 *)memPtr = (IU16)NpxControl; + } +} + + + +/*( +Name : FRSTOR +Function : Reload the FPU state from memory. +Operation : FPU state <- SRC +Flags : As loaded. +Exceptions : None - but unmasking previously masked exceptions will + cause the unmasked exception to be triggered if the + matching bit is set in the status word. +Valid range : N/A +*/ + + +GLOBAL VOID FRSTOR IFN1(VOID *, memPtr) +{ + IU8 *FPPtr; + IU32 i; + /* First. load the control, status, tagword regs. etc. */ + OpFpuRestoreFpuState(memPtr, 80); + FPPtr = (IU8 *)((IU8 *)memPtr+70); + FPtype = M80R; + for ( i=8; i--; ) + { + if ((TOSPtr->tagvalue & TAG_EMPTY_MASK) == 0) { + /* We have to do a bit of fiddling to make FLD happy */ + TOSPtr->tagvalue = TAG_EMPTY_MASK; + TOSPtr = StackEntryByIndex(1); + FLD(FPPtr); + } + TOSPtr = StackEntryByIndex(1); + FPPtr -= 10; + } + /* Finally, check to see if any previously unmasked exceptions */ + /* are now needed to go off. Do this by anding the "triggered" bits in */ + /* NpxStatus with the one's complement of the "masked" bits in NpxControl. */ + if (((NpxStatus & 0x3f) & (~(NpxControl & 0x3f))) != 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } +} + + + +/*( +Name : FSAVE +Function : Write the FPU state to memory. +Operation : DEST <- FPU STATE +Flags : All cleared. +Exceptions : None. +Valid range : N/A +*/ + +GLOBAL VOID FSAVE IFN1(VOID *, memPtr) +{ + IU8 *FPPtr; + IU32 i; + + OpFpuStoreFpuState(memPtr, 80); + FPPtr = (IU8 *)((IU8 *)memPtr+70); + /* Now store out the eight values... */ + FPtype = M80R; + FST(FPPtr); + for ( i=7; i--; ) + { + FPPtr -= 10; /* Go back to the next entry */ + TOSPtr = StackEntryByIndex(1); + FST(FPPtr); + } + /* Finally, reset the FPU... */ + FINIT(); +} + + + +/*( +Name : FSCALE +Function : Scale up ST by a factor involving ST(1) +Operation : ST <- ST * 2**ST(1) +Flags : C1 as per table 15-1. Others undefined. +Exceptions : P, U, O, D, I, IS +Valid range : Any +)*/ + + +GLOBAL VOID FSCALE IFN0() +{ + FPSTACKENTRY *st1_addr; + + st1_addr = StackEntryByIndex(1); + /* Clear C1 */ + FlagC1(0); + TestUneval(TOSPtr); + TestUneval(st1_addr); + tag_or = (TOSPtr->tagvalue | st1_addr->tagvalue); + /* First, check if the values are real. If so, we can proceed. */ + if ((tag_or & ~(TAG_NEGATIVE_MASK | TAG_DENORMAL_MASK)) == 0) { + /* First, check for the denormal case. */ + if ((tag_or & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + } + /* OK. ST(1) has to be rounded to an integer. */ + /* We want a 'chop' function */ + if (st1_addr->fpvalue > 0.0) { + FPRes = floor(st1_addr->fpvalue); + } else { + FPRes = ceil(st1_addr->fpvalue); + } + HostClearExceptions(); + FPRes = pow(2.0, FPRes); + FPRes = TOSPtr->fpvalue * FPRes; + PostCheckOUP(); + /* Return value could be anything */ + CalcTagword(TOSPtr); + } else { + /* A funny thing happened on the way to the answer */ + if ((tag_or & ((TAG_EMPTY_MASK | TAG_UNSUPPORTED_MASK) | TAG_NAN_MASK)) != 0) { + if ((tag_or & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(TOSPtr); + } else { + if ((tag_or & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(TOSPtr); + } else { + /* It must be a NaN. */ + tag_xor = (TOSPtr->tagvalue ^ st1_addr->tagvalue); + Test2NaN(0, TOSPtr, st1_addr); + } + } + return; + } + /* The rules for scaling combinations of zeroes, reals and infinities, both */ + /* positive and negative, are so complex that I don't intend to do lots of */ + /* logic to figure them out. Basically, there are six options: */ + /* 1. Leave the TOS alone */ + /* 2. +Infinity */ + /* 3. +0 */ + /* 4. -Infinity */ + /* 5. -0 */ + /* 6. Raise Invalid operation exception */ + /* */ + /* TOS ST(1) RESULT */ + /* I S Z I S Z */ + /* 0 0 0 0 0 1 1 */ + /* 0 0 0 0 1 1 1 */ + /* 0 0 0 1 0 0 2 */ + /* 0 0 0 1 1 0 3 */ + /* 0 0 1 0 0 0 1 */ + /* 0 0 1 0 0 1 1 */ + /* 0 0 1 0 1 0 1 */ + /* 0 0 1 0 1 1 1 */ + /* 0 0 1 1 0 0 6 */ + /* 0 0 1 1 1 0 1 */ + /* 0 1 0 0 0 1 1 */ + /* 0 1 0 0 1 1 1 */ + /* 0 1 0 1 0 0 4 */ + /* 0 1 0 1 1 0 5 */ + /* 0 1 1 0 0 0 1 */ + /* 0 1 1 0 0 1 1 */ + /* 0 1 1 0 1 0 1 */ + /* 0 1 1 0 1 1 1 */ + /* 0 1 1 1 0 0 6 */ + /* 0 1 1 1 1 0 1 */ + /* 1 0 0 0 0 0 1 */ + /* 1 0 0 0 0 1 1 */ + /* 1 0 0 0 1 0 1 */ + /* 1 0 0 0 1 1 1 */ + /* 1 0 0 1 0 0 6 */ + /* 1 1 0 0 0 0 1 */ + /* 1 1 0 0 0 1 1 */ + /* 1 1 0 0 1 0 1 */ + /* 1 1 0 0 1 1 1 */ + /* 1 1 0 1 0 0 1 */ + /* 1 1 0 1 1 0 6 */ + /* */ + /* All other combinations are impossible. This can be done as a look up */ + /* table with an enumerated type. */ + tag_or = (TOSPtr->tagvalue & 7); + tag_or <<= 3; + tag_or |= (st1_addr->tagvalue & 7); + tag_or = FscaleTable[tag_or]; + if ((tag_or & TAG_FSCALE_MASK) != 0) { + if ((tag_or & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(TOSPtr); + } + } else { + TOSPtr->tagvalue = tag_or; + } + } +} + + + +/*( +Name : FSIN +Function : Calculate the sine of ST +Operation : ST <- SINE(ST) +Flags : C1, C2 as per table 15-2. C0 and C3 undefined. +Exceptions : P. U, D, I, IS +Valid range : |ST| < 2**63. +)*/ + + +GLOBAL VOID FSIN IFN0() +{ + /* Clear C1 */ + FlagC1(0); + /* Clear C2 */ + FlagC2(0); + TestUneval(TOSPtr); + if ((TOSPtr->tagvalue & ~TAG_NEGATIVE_MASK) == 0) { + HostClearExceptions(); + /* We can just write the value straight out */ + FPRes = sin(TOSPtr->fpvalue); + PostCheckOUP(); + /* Return value must be in the range -1 to +1 */ + CalcTagword(TOSPtr); + } else { + /* Lets do the most probable cases first... */ + /* A zero returns exactly the same thing */ + if ((TOSPtr->tagvalue & TAG_ZERO_MASK) != 0) { + return; + } + /* Lets check for a denormal */ + if ((TOSPtr->tagvalue & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } else { + HostClearExceptions(); + FPRes = sin(TOSPtr->fpvalue); + PostCheckOUP(); + /* Return value must be in the range -1 to +1 */ + CalcTagword(TOSPtr); + } + return; + } + /* Or it could possibly be infinity... */ + /* For this, the C2 bit is set and the result remains */ + /* unchanged. */ + if ((TOSPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + FlagC2(1); + return; + } + /* It was one of the really wacky bits... */ + if ((TOSPtr->tagvalue & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(TOSPtr); + return; + } + if ((TOSPtr->tagvalue & TAG_SNAN_MASK) != 0) { + MakeNaNQuiet(TOSPtr); + return; + } + if ((TOSPtr->tagvalue & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(TOSPtr); + return; + } + } +} + + + +/*( +Name : FSINCOS +Function : Calculate the sine and cosine of ST +Operation : TEMP <-COSINE(ST); ST <- SINE(ST); PUSH; ST <- TEMP +Flags : C1, C2 as per table 15-2. C0 and C3 undefined. +Exceptions : P. U, D, I, IS +Valid range : |ST| < 2**63. +)*/ + + +GLOBAL VOID FSINCOS IFN0() +{ + FPSTACKENTRY *st1_addr; + + /* Clear C1 */ + FlagC1(0); + /* Clear C2 */ + FlagC2(0); + st1_addr = StackEntryByIndex(7); + /* First, check that this one is empty. */ + if ((st1_addr->tagvalue & TAG_EMPTY_MASK) == 0) { + WriteIndefinite(TOSPtr); + TOSPtr = st1_addr; + SignalStackOverflow(TOSPtr); + return; + } + TestUneval(TOSPtr); + if ((TOSPtr->tagvalue & ~TAG_NEGATIVE_MASK) == 0) { + HostClearExceptions(); + /* We can just write the value straight out */ + FPRes = cos(TOSPtr->fpvalue); + /* The range for a cosine is -1 through to +1. */ + CalcTagword(st1_addr); + /* I can write out the SINE myself, since as we are */ + /* writing to the stack, even an unmasked U or P */ + /* cannot stop delivery of the result. */ + /* The range for a sine is -1 through to +1. */ + FPRes = sin(TOSPtr->fpvalue); + CalcTagword(TOSPtr); + TOSPtr = st1_addr; + PostCheckOUP(); + return; + } else { + /* Lets do the most probable cases first... */ + /* A zero returns exactly the same thing */ + if ((TOSPtr->tagvalue & TAG_ZERO_MASK) != 0) { + /* The sine of zero is zero so just push the stack */ + TOSPtr = st1_addr; + /* Now write out plus one */ + CopyFP(TOSPtr, npx_one); + return; + } + /* Lets check for a denormal */ + if ((TOSPtr->tagvalue & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } else { + HostClearExceptions(); + /* We can just write the value straight out */ + FPRes = cos(TOSPtr->fpvalue); + /* The range for a cos is -1 through to +1 */ + CalcTagword(st1_addr); + /* I can write out the SINE myself, since as we are */ + /* writing to the stack, even an unmasked U or P */ + /* cannot stop delivery of the result. */ + /* The range for a sine is -1 through to +1 */ + FPRes = sin(TOSPtr->fpvalue); + CalcTagword(TOSPtr); + TOSPtr = st1_addr; + PostCheckOUP(); + } + return; + } + /* Or it could possibly be infinity... */ + /* For this, the C2 bit is set and the result remains */ + /* unchanged. */ + if ((TOSPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + FlagC2(1); + return; + } + /* It was one of the really wacky bits... */ + if ((TOSPtr->tagvalue & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(TOSPtr); + return; + } + if ((TOSPtr->tagvalue & TAG_SNAN_MASK) != 0) { + MakeNaNQuiet(TOSPtr); + return; + } + if ((TOSPtr->tagvalue & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(TOSPtr); + return; + } + } +} + + + +/*( +Name : FSQRT +Function : Calculate the square root of ST +Operation : ST <- SQRT(ST) +Flags : C1 as per table 15-1. Others undefined. +Exceptions : P. D, I, IS +Valid range : ST >= -0.0 +)*/ + + +GLOBAL VOID FSQRT IFN0() +{ + + /* Clear C1 */ + FlagC1(0); + TestUneval(TOSPtr); + if (TOSPtr->tagvalue == 0) { + HostClearExceptions(); + /* We can just write the value straight out */ + FPRes = sqrt(TOSPtr->fpvalue); + PostCheckOUP(); + TOSPtr->fpvalue = FPRes; + /* The tagword can't have changed! */ + return; + } else { + /* Lets do the most probable cases first... */ + /* A zero returns exactly the same thing */ + if ((TOSPtr->tagvalue & TAG_ZERO_MASK) != 0) { + return; + } + if ((TOSPtr->tagvalue & TAG_NAN_MASK) != 0) { + if ((TOSPtr->tagvalue & TAG_SNAN_MASK) != 0) { + MakeNaNQuiet(TOSPtr); + } + return; + } + /* Having taken care of that case, lets check for negative... */ + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + SignalIndefinite(TOSPtr); + return; + } + /* Lets check for a denormal */ + if ((TOSPtr->tagvalue & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } else { + HostClearExceptions(); + FPRes = sqrt(TOSPtr->fpvalue); + PostCheckOUP(); + /* It might not be a denorm anymore */ + CalcTagword(TOSPtr); + } + return; + } + /* Or it could possibly be infinity...This just returns. */ + if ((TOSPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + return; + } + /* It was one of the really wacky bits... */ + if ((TOSPtr->tagvalue & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(TOSPtr); + return; + } + if ((TOSPtr->tagvalue & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(TOSPtr); + return; + } + } +} + + +/* CheckOUPForIntel: This is a special version of the PostCheckOUP +routine that is designed for use in situations where the result +is to be written to intel memory space. It just looks at the +excpetions bits and sets the appropriate bits, it doesn't write +the value back or anything like that. */ + + +LOCAL VOID CheckOUPForIntel IFN0() +{ + tag_or=0; /* Prime tag_or */ + if (HostGetOverflowException() != 0) { + NpxStatus |= SW_OE_MASK; /* Set the overflow bit */ + /* For the masked overflow case, the result delivered by */ + /* the host will be correct, provided it is IEEE compliant. */ + if ((NpxControl & CW_OM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + NpxException = TRUE; + tag_or = 1; + } + } else { + /* Overflow and underflow being mutually exclusive... */ + if (HostGetUnderflowException() != 0) { + NpxStatus |= SW_UE_MASK; + if ((NpxControl & CW_UM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + NpxException = TRUE; + tag_or=1; + } + } + } + if (HostGetPrecisionException() != 0) { + SetPrecisionBit(); + if ((NpxControl & CW_PM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + NpxException = TRUE; + /* An unmasked precision exception cannot prevent + delivery of the result */ + } + } + /* Only call for overflow or underflow */ + if (NpxException && (tag_or == 1)) { + NpxException = FALSE; + DoNpxException(); + } +} + + + +/*( +Name : FST{P} +Function : Copy ST to the specified location +Operation : DEST <- ST(0); if FSTP { pop ST FI; +Flags : C1 as per table 15-1. Others undefined. +Exceptions : For stack or extended-real, IS. + For single or double-real P. U, O, D, I, IS +Valid range : N/A +)*/ + + +GLOBAL VOID FST IFN1(VOID *, memPtr) +{ + /* Clear C1 */ + FlagC1(0); + if (POPST) { + DoAPop=TRUE; + } + if ((TOSPtr->tagvalue & UNEVALMASK) != 0) { + if ((TOSPtr->tagvalue & TAG_BCD_MASK) != 0) { + ConvertBCD(TOSPtr); + } else { + /* Doesn't apply for FPStack or M80R types */ + if ((FPtype == M32R) || (FPtype == M64R)) { + ConvertR80(TOSPtr); + } + } + } + if ( ((TOSPtr->tagvalue & TAG_R80_MASK) != 0) + || ((TOSPtr->tagvalue & ~(TAG_NEGATIVE_MASK | TAG_DENORMAL_MASK)) == 0) + || (FPtype == FPSTACK)) { + if (FPtype == FPSTACK) { + /* check for empty here */ + if (TOSPtr->tagvalue & TAG_EMPTY_MASK) { + NpxStatus |= SW_IE_MASK|SW_SF_MASK; + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + WriteIndefinite(StackEntryByIndex(*(IU16 *)memPtr)); + } else + /* The invalid operation doesn't apply to non-empty */ + /* stack locations. We carry on regardless. */ + CopyFP(StackEntryByIndex(*(IU16 *)memPtr), TOSPtr); + } else { + if (FPtype == M80R) { + if ((TOSPtr->tagvalue & TAG_R80_MASK) == 0) { + CVTFPHR80(TOSPtr); + WriteFP80ToIntel(memPtr, &FPTemp); + } else { + WriteFP80ToIntel(memPtr, TOSPtr); + } + } else { + /* First, check for the denormal case... */ + if ((TOSPtr->tagvalue & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + } + HostClearExceptions(); + /* The result of the conversion should be written to FPTemp. */ + if (FPtype == M32R) { + *(float *)&(FPTemp.fpvalue) = (float)TOSPtr->fpvalue; + /* Our host MUST have double precision, so we will have to */ + /* test for problems caused by the conversion... */ + CheckOUPForIntel(); + if (tag_or == 0) { + WriteFP32ToIntel(memPtr, &FPTemp); + } + } + if (FPtype == M64R) { + *(DOUBLE *)&(FPTemp.fpvalue) = (DOUBLE)TOSPtr->fpvalue; + /* If we are dealing with a 64-bit host, then the J-code for */ + /* the above is nothing at all, and we don't need to do any */ + /* testing, but if the host precision is, say 80-bit, then */ + /* we do! Note that this doesn't use the @if format in order */ + /* to avoid generating different J-code for different hosts... */ + CheckOUPForIntel(); + if (tag_or == 0) { + WriteFP64ToIntel(memPtr, &FPTemp); + } + } + } + } + } else { + /* Test for funny values */ + if ((TOSPtr->tagvalue & TAG_ZERO_MASK) != 0) { + /* In this case, we'll allow the casting to be done for us! */ + WriteZeroToIntel(memPtr, TOSPtr->tagvalue & TAG_NEGATIVE_MASK); + } else if ((TOSPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + if ((FPtype == M32R) || (FPtype == M64R)) { + NpxStatus |= SW_OE_MASK; + if ((NpxControl & CW_OM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + } + WriteInfinityToIntel(memPtr, TOSPtr->tagvalue & TAG_NEGATIVE_MASK); + } else if ((TOSPtr->tagvalue & TAG_NAN_MASK) != 0) { + if ((TOSPtr->tagvalue & TAG_SNAN_MASK) != 0) { + /* Signal invalid for sNaN */ + if (((FPtype == M32R) || (FPtype == M64R))) { + NpxStatus |= SW_IE_MASK; + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + } + } + WriteNaNToIntel(memPtr, TOSPtr); + } else if ( (TOSPtr->tagvalue & TAG_EMPTY_MASK) != 0 ) { + NpxStatus |= (SW_IE_MASK | SW_SF_MASK); + FlagC1(0); + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + WriteIndefiniteToIntel(memPtr); + } else { /* Must be unsupported. */ + if (FPtype == M80R) { + /* unsupported: Write back the unresolved string */ + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + ((FP80 *)&(TOSPtr->fpvalue))->sign_exp.sign = 1; + } else { + ((FP80 *)&(TOSPtr->fpvalue))->sign_exp.sign = 0; + } + WriteFP80ToIntel(memPtr, TOSPtr); + } else { + NpxStatus |= SW_IE_MASK; + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + WriteIndefiniteToIntel(memPtr); + } + } + } + if (POPST) { + if (DoAPop == TRUE) { + PopStack(); + } + } + /* Check for the case of an unmasked precision exception */ + if (NpxException) { + NpxException = FALSE; + DoNpxException(); + } +} + + + +/*( +Name : FSTENV +Function : Store the FPU environment +Operation : DEST <- FPU environment +Flags : All undefined. +Exceptions : None +Valid range : N/A +*/ + + +GLOBAL VOID FSTENV IFN1(VOID *, memPtr) +{ + /* First. load the control, status, tagword regs. etc. */ + OpFpuStoreFpuState(memPtr,0); + /* Then set all the exceptions to be masked */ + NpxControl |= 0x0000003f; +} + + +/*( +Name : FSTSW +Function : Write the FPU status word to memory +Operation : DEST <- SW +Flags : All undefined. +Exceptions : None +Valid range : N/A +*/ + + +GLOBAL VOID FSTSW IFN2(VOID *, memPtr, BOOL, toAX) +{ + GetIntelStatusWord(); + + if (NpxDisabled) + { + /* UIF has told us to pretend we do not have an NPX */ + + if (toAX) { + *(IU16 *)memPtr = 0xFFFF; + } else { + /* Write it out host format */ + + *(IU16 *)memPtr = (IU16)NpxStatus; + } + } else { + if (toAX) { + *(IU16 *)memPtr = (IU16)NpxStatus; + } else { + *(IU32 *)memPtr = (IU32)NpxStatus; + } + } +} + +/*( +Name : FSUB +Function : Subtract one number from the other +Operation : Dest <- Src1 - Src2 or Dest <- Src2 - Src1 +Flags : C1 as per table 15-1. C0, C2 and C3 undefined +Exceptions : P, U, O, D, I, IS +Valid range : Any +Notes : The REVERSE control variable determines which of the + two forms of the operation is used. Popping after a + successful execution is controlled by POPST. +)*/ + + +GLOBAL VOID FSUB IFN3(IU16, destIndex, IU16, src1Index, VOID *, src2) +{ + IU16 src2Index; + + LoadValue(src2, &src2Index); + if (POPST) { + DoAPop=TRUE; + } + GenericSubtract(destIndex, REVERSE?src2Index:src1Index, REVERSE?src1Index:src2Index); + if (POPST) { + if (DoAPop) { + PopStack(); + } + } +} + + +/*( +Name : GenericSubtract +Function : To return dest <- src1-src2 +)*/ + + +LOCAL VOID GenericSubtract IFN3(IU16, destIndex, IU16, src1Index, IU16, src2Index) +{ + FPSTACKENTRY *src1_addr; + FPSTACKENTRY *src2_addr; + + src1_addr = StackEntryByIndex(src1Index); + src2_addr = StackEntryByIndex(src2Index); + + /* Clear C1 */ + FlagC1(0); + TestUneval(src1_addr); + TestUneval(src2_addr); + tag_or = (src1_addr->tagvalue | src2_addr->tagvalue); + /* If the only tagword bit set is negative then just proceed */ + if ((tag_or & ~TAG_NEGATIVE_MASK) == 0) { + HostClearExceptions(); + FPRes=src1_addr->fpvalue - src2_addr->fpvalue; + /* Reuse one of the above to calculate the destination */ + src1_addr = StackEntryByIndex(destIndex); + PostCheckOUP(); + /* Could be anything */ + CalcTagword(src1_addr); + } else { + /* Some funny bit was set. Check for the possibilities */ + if ((tag_or & ((TAG_EMPTY_MASK | TAG_UNSUPPORTED_MASK) | TAG_NAN_MASK)) != 0) { + if ((tag_or & TAG_EMPTY_MASK) != 0) { + src1_addr = StackEntryByIndex(destIndex); + SignalStackUnderflow(src1_addr); + } else { + if ((tag_or & TAG_UNSUPPORTED_MASK) != 0) { + src1_addr = StackEntryByIndex(destIndex); + SignalIndefinite(src1_addr); + } else { + /* Well, I suppose it has to be the NaN case... */ + /* Calculate the xor of the tagwords */ + tag_xor = (src1_addr->tagvalue ^ src2_addr->tagvalue); + Test2NaN(destIndex, src1_addr, src2_addr); + } + } + return; + } + if ((tag_or & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + DoAPop = FALSE; + return; + } else { + if ((tag_or & ~(TAG_NEGATIVE_MASK | TAG_DENORMAL_MASK)) == 0) { + /* OK to proceed */ + HostClearExceptions(); + FPRes=src1_addr->fpvalue - src2_addr->fpvalue; + /* Reuse one of the above to calculate the destination */ + src1_addr = StackEntryByIndex(destIndex); + PostCheckOUP(); + /* Could be anything */ + CalcTagword(src1_addr); + return; + } + } + } + tag_xor = (src1_addr->tagvalue ^ src2_addr->tagvalue); + /* Check for infinity as it has higher precendence than zero. */ + if ((tag_or & TAG_INFINITY_MASK) != 0) { + if ((tag_xor & TAG_INFINITY_MASK) == 0) { + /* Have they the same sign? */ + if ((tag_xor & TAG_NEGATIVE_MASK) == 0) { + /* They are both the same sign infinity. This is invalid. */ + src1_addr = StackEntryByIndex(destIndex); + SignalIndefinite(src1_addr); + } else { + /* If of different sign then src1 is the answer */ + src2_addr = StackEntryByIndex(destIndex); + src2_addr->tagvalue = src1_addr->tagvalue; + } + } else { + /* Only one is infinity. If src1 in infinity, then the result */ + /* is the same. If src2 is infinity, then the result is an */ + /* infinity of opposite sign. */ + tag_or = src2_addr->tagvalue; + src2_addr = StackEntryByIndex(destIndex); + if ((src1_addr->tagvalue & TAG_INFINITY_MASK) != 0) { + src2_addr->tagvalue = src1_addr->tagvalue; + } else { + src2_addr->tagvalue = tag_or ^ TAG_NEGATIVE_MASK; + } + } + return; + } + /* Check for the case of zero... This is very likely */ + if ((tag_or & TAG_ZERO_MASK) != 0) { + if ((tag_xor & TAG_ZERO_MASK) != 0) { + /* Only one zero. */ + if ((src1_addr->tagvalue & TAG_ZERO_MASK) != 0) { + /* If src1 is zero, -src2 is result */ + src1_addr = StackEntryByIndex(destIndex); + CopyFP(src1_addr, src2_addr); + src1_addr->tagvalue ^= TAG_NEGATIVE_MASK; + ((FPHOST *)&(src1_addr->fpvalue))->hiword.sign ^= 1; + } else { + /* If src2 is zero, src1 is result. */ + src2_addr = StackEntryByIndex(destIndex); + CopyFP(src2_addr, src1_addr); + } + } else { + /* Both are zeros. Do they have the same sign? */ + src2_addr = StackEntryByIndex(destIndex); + if ((tag_xor & TAG_NEGATIVE_MASK) != 0) { + /* No, they don't - the result is src1 */ + src2_addr->tagvalue = src1_addr->tagvalue; + } else { + /* Yes, they do... */ + if (npxRounding == ROUND_NEG_INFINITY) { + src2_addr->tagvalue = (TAG_ZERO_MASK | TAG_NEGATIVE_MASK); + } else { + src2_addr->tagvalue = TAG_ZERO_MASK; + } + } + } + return; + } + } +} + + + +/*( +Name : FTST +Function : Compare ST against 0.0 +Operation : Set C023 on result of comparison +Flags : C1 as per table 15-1. C0, C2 and C3 as result of comparison. +Exceptions : D, I, IS +Valid range : Any +)*/ + + +GLOBAL VOID FTST IFN0() +{ + /* Clear C1 */ + FlagC1(0); + TestUneval(TOSPtr); + if ((TOSPtr->tagvalue & ~((TAG_NEGATIVE_MASK | TAG_DENORMAL_MASK) | TAG_INFINITY_MASK)) == 0) { + /* First, check for the denormal case... */ + if ((TOSPtr->tagvalue & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + } + FlagC2(0); + FlagC3(0); + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + /* ST is less than zero */ + FlagC0(1); + } else { + /* ST is greater than zero */ + FlagC0(0); + } + } else { + if ((TOSPtr->tagvalue & TAG_ZERO_MASK) != 0) { + FlagC0(0); + FlagC2(0); + FlagC3(1); + } else { + /* For anything else the result is "unordered" */ + FlagC0(1); + FlagC2(1); + FlagC3(1); + NpxStatus |= SW_IE_MASK; + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } + } + } +} + + +/*( +Name : FXAM +Function : Report on the type of object in ST +Operation : Set C0123 on result of comparison +Flags : C0, C1, C2 and C3 as required. +Exceptions : None +Valid range : Any +)*/ + + +GLOBAL VOID FXAM IFN0() +{ + TestUneval(TOSPtr); + tag_or = TOSPtr->tagvalue; + if ((tag_or & TAG_NEGATIVE_MASK) == 0) { + FlagC1(0); + } else { + FlagC1(1); + tag_or &= ~TAG_NEGATIVE_MASK; + } + tag_or &= ~TAG_SNAN_MASK; + /* This gets rid of all the confusing bits... */ + /* There is now only one bit set or none at all... */ + if (tag_or == 0) { + FlagC0(0); + FlagC2(1); + FlagC3(0); + return; + } + if ((tag_or & TAG_ZERO_MASK) != 0) { + FlagC0(0); + FlagC2(0); + FlagC3(1); + return; + } + if ((tag_or & TAG_INFINITY_MASK) != 0) { + FlagC0(1); + FlagC2(1); + FlagC3(0); + return; + } + if ((tag_or & TAG_DENORMAL_MASK) != 0) { + FlagC0(0); + FlagC2(1); + FlagC3(1); + return; + } + if ((tag_or & TAG_NAN_MASK) != 0) { + FlagC0(1); + FlagC2(0); + FlagC3(0); + return; + } + if ((tag_or & TAG_UNSUPPORTED_MASK) != 0) { + FlagC0(0); + FlagC2(0); + FlagC3(0); + return; + } + /* MUST be empty */ + FlagC0(1); + FlagC2(0); + FlagC3(1); +} + + +/*( +Name : FXCH +Function : Swap the contents of two stack registers. +Operation : TEMP <- ST; ST <- DEST; DEST <- TEMP +Flags : C1 as per table 15-1. Others undefined +Exceptions : IS +Valid range : Any +Notes : If either of the registers is tagged empty then it is + loaded with indefinite and the exchange performed. +)*/ + + +GLOBAL VOID FXCH IFN1(IU16, destIndex) +{ + FPSTACKENTRY *dest_addr; + + dest_addr = StackEntryByIndex(destIndex); + /* Clear C1 */ + FlagC1(0); + tag_or = (TOSPtr->tagvalue | dest_addr->tagvalue); + if ((tag_or & TAG_EMPTY_MASK) != 0) { + NpxStatus |= SW_IE_MASK; + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + if ((TOSPtr->tagvalue & TAG_EMPTY_MASK) != 0) { + WriteIndefinite(TOSPtr); + } + if ((dest_addr->tagvalue & TAG_EMPTY_MASK) != 0) { + WriteIndefinite(dest_addr); + } + } + CopyFP(&FPTemp, TOSPtr); + CopyFP(TOSPtr, dest_addr); + CopyFP(dest_addr, &FPTemp); +} + + + +/*( +Name : FXTRACT +Function : Split the value in ST into its exponent and significand +Operation : TEMP<-sig(ST); ST<-exp(ST); Dec ST; ST<-TEMP +Flags : C1 as per table 15-1. Others undefined +Exceptions : Z, D, I, IS +Valid range : Any +Notes : If the original operand is zero, result is ST(1) is -infinity + and ST is the original zero. The zero divide exception is also + raised. If the original operand is infinity, ST(1) is +infinity + and ST is the original infinity. If ST(7) is not empty, the + invalid operation exception is raised. +)*/ + + +GLOBAL VOID FXTRACT IFN1(IU16, destIndex) +{ + FPSTACKENTRY *dest_addr; + IS16 exp_val; + + dest_addr = StackEntryByIndex(7); + /* Clear C1 */ + FlagC1(0); + if ((dest_addr->tagvalue & TAG_EMPTY_MASK) == 0) { + NpxStatus |= SW_IE_MASK; + NpxStatus &= ~SW_SF_MASK; + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } else { + WriteIndefinite(TOSPtr); + TOSPtr=dest_addr; + WriteIndefinite(TOSPtr); + } + return; + } + TestUneval(TOSPtr); + if ((TOSPtr->tagvalue & ~(TAG_NEGATIVE_MASK | TAG_DENORMAL_MASK)) == 0) { + if ((TOSPtr->tagvalue & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + return; + } + /* It won't be a denormal after we've finished */ + TOSPtr->tagvalue ^= TAG_DENORMAL_MASK; + } + /* It is entirely valid */ + exp_val = ((FPHOST *)&(TOSPtr->fpvalue))->hiword.exp-HOST_BIAS; + ((FPHOST *)&(TOSPtr->fpvalue))->hiword.exp=HOST_BIAS; + TOSPtr->tagvalue &= TAG_NEGATIVE_MASK; + CopyFP(dest_addr, TOSPtr); + FPRes = (FPH)exp_val; + /* This MUST be a real number, it could be negative. */ + CalcTagword(TOSPtr); + TOSPtr = dest_addr; + } else { + /* Check if it was a zero */ + if ((TOSPtr->tagvalue & TAG_ZERO_MASK) != 0) { + dest_addr->tagvalue = TOSPtr->tagvalue; + TOSPtr->tagvalue = (TAG_INFINITY_MASK | TAG_NEGATIVE_MASK); + TOSPtr = dest_addr; + NpxStatus |= SW_ZE_MASK; + if ((NpxControl & CW_ZM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } + return; + } + /* Check if it was an infinity */ + if ((TOSPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + dest_addr->tagvalue = TOSPtr->tagvalue; + TOSPtr->tagvalue = TAG_INFINITY_MASK; + TOSPtr = dest_addr; + return; + } + /* There was something funny...Was it empty or unsupported? */ + if ((TOSPtr->tagvalue & (TAG_EMPTY_MASK | TAG_UNSUPPORTED_MASK)) != 0) { + NpxStatus |= SW_IE_MASK; + NpxStatus &= ~SW_SF_MASK; + if ((NpxControl & CW_IM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + } else { + WriteIndefinite(TOSPtr); + TOSPtr=dest_addr; + WriteIndefinite(TOSPtr); + } + return; + } + CopyFP(dest_addr, TOSPtr); + TOSPtr = dest_addr; + } +} + + + +/*( +FYL2X (Y log base 2 of X) calculates the function Z=Y*LOG2(X). X is +taken from ST(0) and Y is taken from ST(1). The operands must be in +the range 0 < X < +inf and -inf < Y < +inf. The instruction pops the +)*/ + + +GLOBAL VOID FYL2X IFN0() +{ + FPSTACKENTRY *st1_addr; + + /* Clear C1 */ + FlagC1(0); + st1_addr = StackEntryByIndex(1); + TestUneval(TOSPtr); + TestUneval(st1_addr); + tag_or = (TOSPtr->tagvalue | st1_addr->tagvalue); + /* First, check if the values are real. If so, we can proceed. */ + if ((tag_or & ~(TAG_DENORMAL_MASK | TAG_NEGATIVE_MASK)) == 0) { + /* Check for the denorm case... */ + if ((tag_or & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + /* We ALWAYS pop!!! */ + TOSPtr->tagvalue = TAG_EMPTY_MASK; + TOSPtr = st1_addr; + return; + } + } + /* Check for the case of a negative in ST */ + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + SignalIndefinite(st1_addr); + TOSPtr->tagvalue = TAG_EMPTY_MASK; + TOSPtr = st1_addr; + return; + } + + /* OK, we can do the operation ... */ + + FPRes = st1_addr->fpvalue * host_log2(TOSPtr->fpvalue); + + PostCheckOUP(); + /* Tgis is just a multiplication, result could be anything */ + CalcTagword(st1_addr); + } else { + if ((tag_or & ((TAG_EMPTY_MASK | TAG_UNSUPPORTED_MASK) | TAG_NAN_MASK)) != 0) { + if ((tag_or & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(st1_addr); + } else { + if ((tag_or & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(st1_addr); + } else { + /* Well, I suppose it has to be the NaN case... */ + /* Calculate the xor of the tagwords */ + tag_xor = (TOSPtr->tagvalue ^ st1_addr->tagvalue); + Test2NaN(1, TOSPtr, st1_addr); + } + } + TOSPtr->tagvalue = TAG_EMPTY_MASK; + TOSPtr = st1_addr; + return; + } + /* The only possibilities left are infinity and zero.. */ + /* Let's begin with the zeroes case.. */ + if ((tag_or & TAG_ZERO_MASK) != 0) { + if ((TOSPtr->tagvalue & TAG_ZERO_MASK) != 0) { + /* ST is zero. Can have two possibilities */ + /* if ST(1) is zero, raise invalid */ + /* Otherwise raise divide by zero */ + if ((st1_addr->tagvalue & TAG_ZERO_MASK) != 0) { + SignalIndefinite(st1_addr); + } else { + if ((st1_addr->tagvalue & TAG_INFINITY_MASK) == 0) { + /* Calculate the xor of the tagwords */ + tag_xor = (TOSPtr->tagvalue ^ st1_addr->tagvalue); + SignalDivideByZero(st1_addr); + } else { + st1_addr->tagvalue ^= TAG_NEGATIVE_MASK; + } + } + } else { + /* ST(1) must be zero */ + /* We already know that TOSPtr isn't zero. */ + /* There are three possibilities again. */ + /* If TOSPtr is infinity, raise invalid exception. */ + /* If TOSPtr < 1.0 then the result is zero with the */ + /* complement of the sign of ST(1) */ + /* If TOSPtr >= 1.0 then the result is ST(1) */ + if ((TOSPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + SignalIndefinite(st1_addr); + } else { + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + SignalIndefinite(st1_addr); + } else { + if (TOSPtr->fpvalue < 1.0) { + st1_addr->tagvalue ^= TAG_NEGATIVE_MASK; + } + } + } + } + TOSPtr->tagvalue = TAG_EMPTY_MASK; + TOSPtr = st1_addr; + return; + } + /* The only thing left is infinity... */ + /* If ST is infinity then there are two possibilities... */ + /* If it is +infinity the result is infinity with sign of ST(1) */ + /* If it is -infinity the result is an invalid operation */ + if ((TOSPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) == 0) { + st1_addr->tagvalue &= TAG_NEGATIVE_MASK; + st1_addr->tagvalue |= TAG_INFINITY_MASK; + } else { + SignalIndefinite(st1_addr); + } + } else { + /* ST(1) MUST be infinity (and ST is real). */ + /* There are three possibilities: */ + /* If ST is exactly 1.0 then raise Invalid */ + /* If ST is less than 1.0 then the result is the */ + /* infinity with the complement of its sign. */ + /* If ST is greater than 1.0 the result is the infinity. */ + if (TOSPtr->fpvalue == 1.0) { + SignalIndefinite(st1_addr); + } else { + if (TOSPtr->fpvalue < 1.0) { + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + SignalIndefinite(st1_addr); + } else { + st1_addr->tagvalue ^= TAG_NEGATIVE_MASK; + } + } + } + } + } + TOSPtr->tagvalue = TAG_EMPTY_MASK; + TOSPtr = st1_addr; +} + + + +/*( +FYL2XP1 (Y log base 2 of (X+1)) calculates the function Z=Y*LOG2(X+1). X is +taken from ST(0) and Y is taken from ST(1). The operands must be in +the range 0 < X < +inf and -inf < Y < +inf. The instruction pops the +TOS value. This is better than FYL2X when X is very small, since more significant +digits can be retained for 1+X than can be for X alone. +)*/ + + +GLOBAL VOID FYL2XP1 IFN0() +{ + FPSTACKENTRY *st1_addr; + + /* Clear C1 */ + FlagC1(0); + st1_addr = StackEntryByIndex(1); + TestUneval(TOSPtr); + TestUneval(st1_addr); + tag_or = (TOSPtr->tagvalue | st1_addr->tagvalue); + /* First, check if the values are real. If so, we can proceed. */ + if ((tag_or & ~(TAG_DENORMAL_MASK | TAG_NEGATIVE_MASK)) == 0) { + /* Check for the denorm case... */ + if ((tag_or & TAG_DENORMAL_MASK) != 0) { + NpxStatus |= SW_DE_MASK; + if ((NpxControl & CW_DM_MASK) == 0) { + NpxStatus |= SW_ES_MASK; + DoNpxException(); + /* We ALWAYS pop!!! */ + TOSPtr->tagvalue = TAG_EMPTY_MASK; + TOSPtr = st1_addr; + return; + } + } + /* Check for the case of a value less than -1 */ + if (TOSPtr->fpvalue <= -1.0) { + SignalIndefinite(st1_addr); + TOSPtr->tagvalue = TAG_EMPTY_MASK; + TOSPtr = st1_addr; + return; + } + + /* OK, we can do the operation ... */ + + FPRes = st1_addr->fpvalue * host_log1p(TOSPtr->fpvalue); + + PostCheckOUP(); + /* This is just a numtiplication - result could be anything */ + CalcTagword(st1_addr); + } else { + if ((tag_or & ((TAG_EMPTY_MASK | TAG_UNSUPPORTED_MASK) | TAG_NAN_MASK)) != 0) { + if ((tag_or & TAG_EMPTY_MASK) != 0) { + SignalStackUnderflow(st1_addr); + } else { + if ((tag_or & TAG_UNSUPPORTED_MASK) != 0) { + SignalIndefinite(st1_addr); + } else { + /* Well, I suppose it has to be the NaN case... */ + /* Calculate the xor of the tagwords */ + tag_xor = (TOSPtr->tagvalue ^ st1_addr->tagvalue); + Test2NaN(1, TOSPtr, st1_addr); + } + } + TOSPtr->tagvalue = TAG_EMPTY_MASK; + TOSPtr = st1_addr; + return; + } + /* The only possibilities left are infinity and zero.. */ + /* Let's begin with the zeroes case.. */ + if ((tag_or & TAG_ZERO_MASK) != 0) { + if ((TOSPtr->tagvalue & TAG_ZERO_MASK) != 0) { + /* ST is zero. Can have two possibilities */ + /* if ST(1) is positive, result is ST */ + /* if ST(1) is negative, result is -ST */ + if ((st1_addr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + st1_addr->tagvalue = (TAG_ZERO_MASK | (TOSPtr->tagvalue & TAG_NEGATIVE_MASK)); + } else { + st1_addr->tagvalue = (TAG_ZERO_MASK | (TOSPtr->tagvalue ^ TAG_NEGATIVE_MASK)); + } + } else { + /* ST(1) must be zero */ + /* We already know that TOSPtr isn't zero. */ + /* There are three possibilities again. */ + /* If TOSPtr is infinity, raise invalid exception. */ + /* If TOSPtr < 0 then the result is zero with the */ + /* complement of the sign of ST(1) */ + /* If TOSPtr >= 0 then the result is ST(1) */ + if ((TOSPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + SignalIndefinite(st1_addr); + } else { + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + st1_addr->tagvalue ^= TAG_NEGATIVE_MASK; + } + } + } + TOSPtr->tagvalue = TAG_EMPTY_MASK; + TOSPtr = st1_addr; + return; + } + /* The only thing left is infinity... */ + /* If ST is infinity then there are two possibilities... */ + /* If it is +infinity the result is infinity with sign of ST(1) */ + /* If it is -infinity the result is an invalid operation */ + if ((TOSPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + if ((TOSPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + st1_addr->tagvalue &= TAG_NEGATIVE_MASK; + st1_addr->tagvalue |= TAG_INFINITY_MASK; + } else { + SignalIndefinite(st1_addr); + } + } else { + /* ST(1) MUST be infinity (and ST is non-zero). */ + /* There are three possibilities: */ + /* If ST is exactly 1.0 then raise Invalid */ + /* If ST is less than 0.0 then the result is the */ + /* infinity with the complement of its sign. */ + /* If ST is greater than 0.0 the result is the infinity. */ + if (TOSPtr->fpvalue == 1.0) { + SignalIndefinite(st1_addr); + } else { + if (TOSPtr->fpvalue < 0.0) { + st1_addr->tagvalue ^= TAG_NEGATIVE_MASK; + } + } + } + } + TOSPtr->tagvalue = TAG_EMPTY_MASK; + TOSPtr = st1_addr; +} + +/* These functions are provided in order to facilitate pigging */ + +#ifndef PIG +/* copied here from FmNpx.c */ + +GLOBAL void NpxStackRegAsString IFN3(FPSTACKENTRY *, fpStPtr, char *, buf, IU32, prec) +{ + /* The overwhelmingly most likely option is empty. */ + if ((fpStPtr->tagvalue & TAG_EMPTY_MASK) != 0) { + strcpy(buf, "empty"); + return; + } + if ((fpStPtr->tagvalue & ~(TAG_NEGATIVE_MASK | TAG_DENORMAL_MASK)) == 0) { + sprintf(buf, "%.*g", prec, fpStPtr->fpvalue); + return; + } + /* OK, one of the funny bits was set. But which? */ + if ((fpStPtr->tagvalue & TAG_ZERO_MASK) != 0) { + if ((fpStPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + strcpy(buf, "-0"); + } else { + strcpy(buf, "0"); + } + return; + } + if ((fpStPtr->tagvalue & UNEVALMASK) != 0) { + sprintf(buf, "%04x %08x%08x", + ((FP80*)fpStPtr)->sign_exp, + ((FP80*)fpStPtr)->mant_hi, + ((FP80*)fpStPtr)->mant_lo); + strcat(buf, " uneval"); + return; + } + if ((fpStPtr->tagvalue & TAG_INFINITY_MASK) != 0) { + if ((fpStPtr->tagvalue & TAG_NEGATIVE_MASK) != 0) { + strcpy(buf, "minus infinity"); + } else { + strcpy(buf, "infinity"); + } + return; + } + if ((fpStPtr->tagvalue & (TAG_NAN_MASK|TAG_SNAN_MASK)) != 0) { + if ( ((FP80*)fpStPtr)->mant_lo == 0 + && ((FP80*)fpStPtr)->mant_hi == 0xC0000000 + && *(IU16*)&((FP80*)fpStPtr)->sign_exp == 0xFFFF ) + strcpy(buf, "indefinite"); + else + sprintf(buf, "%08x%08x %s %sNan", + ((FP80*)fpStPtr)->mant_hi, + ((FP80*)fpStPtr)->mant_lo, + (fpStPtr->tagvalue & TAG_NEGATIVE_MASK) ? "negative" : "", + (fpStPtr->tagvalue & TAG_SNAN_MASK) ? "S" : ""); + return; + } + /* It MUST be unsupported */ + sprintf(buf, "%04 %08x%08x unsupported", + ((FP80*)fpStPtr)->sign_exp, + ((FP80*)fpStPtr)->mant_hi, + ((FP80*)fpStPtr)->mant_lo); + return; +} + +/* this one is only ever used in trace.c and only if pure CCPU */ +GLOBAL char * getNpxStackReg IFN2(IU32, reg_num, char *, buffer) +{ + reg_num += TOSPtr - FPUStackBase; + NpxStackRegAsString (&FPUStackBase[reg_num&7], buffer, 12); + return buffer; +} +#endif /* !PIG */ + +GLOBAL IU32 getNpxControlReg IFN0() +{ + return(NpxControl); +} + +GLOBAL VOID setNpxControlReg IFN1(IU32, newControl) +{ + NpxControl = newControl; + npxRounding = (NpxControl & 0xc00); + switch (npxRounding) { + case ROUND_NEAREST : HostSetRoundToNearest(); + break; + case ROUND_NEG_INFINITY : HostSetRoundDown(); + break; + case ROUND_POS_INFINITY : HostSetRoundUp(); + break; + case ROUND_ZERO : HostSetRoundToZero(); + break; + } +} + +GLOBAL IU32 getNpxStatusReg IFN0() +{ + GetIntelStatusWord(); + return(NpxStatus); +} + +GLOBAL VOID setNpxStatusReg IFN1( IU32, newStatus) +{ + TOSPtr = FPUStackBase + ((newStatus >> 11) & 7); + NpxStatus = newStatus; +} + +GLOBAL IU32 getNpxTagwordReg IFN0() +{ + IU32 result; + FPSTACKENTRY *tagPtr = &FPUStackBase[7]; + IU8 counter = 0; + + result = 0; + while (counter++ < 8) { + result <<= 2; + if ((tagPtr->tagvalue & TAG_EMPTY_MASK) != 0) { + result |= 3; + } else { + if ((tagPtr->tagvalue & TAG_ZERO_MASK) != 0) { + result |= 1; + } else { + if ((tagPtr->tagvalue & ~TAG_NEGATIVE_MASK) != 0) { + result |= 2; + } + } + } + tagPtr--; + } + return(result); +} + +GLOBAL VOID setNpxTagwordReg IFN1(IU32, newTag) +{ + /* Don't do it!! It fucks you up!! */ + /* SetIntelTagword(newTag); */ +} + +GLOBAL void getNpxStackRegs IFN1(FPSTACKENTRY *, dumpPtr) +{ + memcpy((char *)dumpPtr, (char *)FPUStackBase, 8 * sizeof(FPSTACKENTRY)); +} + +GLOBAL void setNpxStackRegs IFN1(FPSTACKENTRY *, loadPtr) +{ + memcpy((char *)FPUStackBase, (char *)loadPtr, 8 * sizeof(FPSTACKENTRY)); +} + + +/* And finally some stubs */ +GLOBAL void initialise_npx IFN0() +{ +} + +GLOBAL void npx_reset IFN0() +{ + +} |