mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5948 lines
161 KiB
5948 lines
161 KiB
/*[
|
|
* ============================================================================
|
|
*
|
|
* 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()
|
|
{
|
|
|
|
}
|