[FAST486]
[reactos.git] / reactos / lib / fast486 / fpu.c
index 3da63f4..1e4ce78 100644 (file)
 #include "common.h"
 #include "fpu.h"
 
+/* CONSTANTS ******************************************************************/
+
+/* 0.00 */
+static const FAST486_FPU_DATA_REG FpuZero = {0ULL, 0, FALSE};
+
+/* 1.00 */
+static const FAST486_FPU_DATA_REG FpuOne = {0x8000000000000000ULL, FPU_REAL10_BIAS, FALSE};
+
+/* Pi */
+static const FAST486_FPU_DATA_REG FpuPi = {0xC90FDAA22168C234ULL, FPU_REAL10_BIAS + 1, FALSE};
+
+/* lb(10) */
+static const FAST486_FPU_DATA_REG FpuL2Ten = {0xD49A784BCD1D8AFEULL, FPU_REAL10_BIAS + 1, FALSE};
+
+/* lb(e) */
+static const FAST486_FPU_DATA_REG FpuL2E = {0xB8AA3B295C17F0BBULL, FPU_REAL10_BIAS, FALSE};
+
+/* lg(2) */
+static const FAST486_FPU_DATA_REG FpuLgTwo = {0x9A209A84FBCFF798ULL, FPU_REAL10_BIAS - 2, FALSE};
+
+/* ln(2) */
+static const FAST486_FPU_DATA_REG FpuLnTwo = {0xB17217F7D1CF79ABULL, FPU_REAL10_BIAS - 1, FALSE};
+
 /* PRIVATE FUNCTIONS **********************************************************/
 
 #ifndef FAST486_NO_FPU
@@ -42,7 +65,7 @@ UnsignedMult128(ULONGLONG Multiplicand,
     ULONG MultiplicandLow, MultiplicandHigh, MultiplierLow, MultiplierHigh;
     ULONG IntermediateLow, IntermediateHigh;
     ULONGLONG LowProduct, Intermediate, Intermediate1, Intermediate2;
-    
+
     MultiplicandLow = (ULONG)(Multiplicand & 0xFFFFFFFFULL);
     MultiplicandHigh = (ULONG)(Multiplicand >> 32);
     MultiplierLow = (ULONG)(Multiplier & 0xFFFFFFFFULL);
@@ -66,10 +89,204 @@ UnsignedMult128(ULONGLONG Multiplicand,
     return LowProduct;
 }
 
-static VOID
-Fast486FpuGetSingleReal(PFAST486_STATE State,
-                        ULONG Value,
-                        PFAST486_FPU_DATA_REG Result)
+static ULONGLONG
+UnsignedDivMod128(ULONGLONG DividendLow,
+                  ULONGLONG DividendHigh,
+                  ULONGLONG Divisor,
+                  PULONGLONG QuotientLow,
+                  PULONGLONG QuotientHigh)
+{
+    ULONGLONG ValueLow = DividendLow;
+    ULONGLONG ValueHigh = DividendHigh;
+    ULONGLONG CurrentLow = 0ULL;
+    ULONGLONG CurrentHigh = Divisor;
+    ULONG Bits;
+
+    ASSERT(Divisor != 0ULL);
+
+    /* Initialize the quotient */
+    *QuotientLow = *QuotientHigh = 0ULL;
+
+    /* Exit early if the dividend is lower than the divisor */
+    if ((DividendHigh == 0ULL) && (DividendLow < Divisor)) return ValueLow;
+
+    /* Normalize the current divisor */
+    Bits = CountLeadingZeros64(CurrentHigh);
+    CurrentHigh <<= Bits;
+
+    while (TRUE)
+    {
+        /* Shift the quotient left by one bit */
+        *QuotientHigh <<= 1;
+        *QuotientHigh |= *QuotientLow >> 63;
+        *QuotientLow <<= 1;
+
+        /* Check if the value is higher than or equal to the current divisor */
+        if ((ValueHigh > CurrentHigh)
+            || ((ValueHigh == CurrentHigh) && (ValueLow >= CurrentLow)))
+        {
+            BOOLEAN Carry = ValueLow < CurrentLow;
+
+            /* Subtract the current divisor from the value */
+            ValueHigh -= CurrentHigh;
+            ValueLow -= CurrentLow;
+            if (Carry) ValueHigh--;
+
+            /* Set the lowest bit of the quotient */
+            *QuotientLow |= 1;
+
+            /* Stop if the value is lower than the original divisor */
+            if ((ValueHigh == 0ULL) && (ValueLow < Divisor)) break;
+        }
+
+        /* Shift the current divisor right by one bit */
+        CurrentLow >>= 1;
+        CurrentLow |= (CurrentHigh & 1) << 63;
+        CurrentHigh >>= 1;
+    }
+
+    /*
+     * Calculate the number of significant bits the current
+     * divisor has more than the original divisor
+     */
+    Bits = CountLeadingZeros64(Divisor) + 64;
+    Bits -= (CurrentHigh > 0ULL) ? CountLeadingZeros64(CurrentHigh) : 64;
+    Bits -= (CurrentLow > 0ULL) ? CountLeadingZeros64(CurrentLow) : 64;
+
+    if (Bits)
+    {
+        /* Shift the quotient left by that amount */
+        *QuotientHigh <<= Bits;
+        *QuotientHigh |= *QuotientLow >> (64 - Bits);
+        *QuotientLow <<= Bits;
+    }
+
+    /* Return the remainder */
+    return ValueLow;
+}
+
+static inline VOID FASTCALL
+Fast486FpuRound(PFAST486_STATE State,
+                PULONGLONG Result,
+                BOOLEAN Sign,
+                ULONGLONG Remainder,
+                INT RemainderHighBit)
+{
+    switch (State->FpuControl.Rc)
+    {
+        case FPU_ROUND_NEAREST:
+        {
+            /* Check if the highest bit of the remainder is set */
+            if (Remainder & (1ULL << RemainderHighBit))
+            {
+                (*Result)++;
+
+                /* Check if all the other bits are clear */
+                if (!(Remainder & ((1ULL << RemainderHighBit) - 1)))
+                {
+                    /* Round to even */
+                    *Result &= ~1;
+                }
+            }
+
+            break;
+        }
+
+        case FPU_ROUND_DOWN:
+        {
+            if ((Remainder != 0ULL) && Sign) (*Result)++;
+            break;
+        }
+
+        case FPU_ROUND_UP:
+        {
+            if ((Remainder != 0ULL) && !Sign) (*Result)++;
+            break;
+        }
+
+        default:
+        {
+            /* Leave it truncated */
+        }
+    }
+}
+
+static inline VOID FASTCALL
+Fast486FpuFromInteger(PFAST486_STATE State,
+                      LONGLONG Value,
+                      PFAST486_FPU_DATA_REG Result)
+{
+    ULONG ZeroCount;
+
+    Result->Sign = Result->Exponent = Result->Mantissa = 0;
+    if (Value == 0LL) return;
+
+    if (Value < 0LL)
+    {
+        Result->Sign = 1;
+        Value = -Value;
+    }
+
+    Result->Mantissa = (ULONGLONG)Value;
+    ZeroCount = CountLeadingZeros64(Result->Mantissa);
+
+    Result->Mantissa <<= ZeroCount;
+    Result->Exponent = FPU_REAL10_BIAS + 63 - ZeroCount;
+}
+
+static inline BOOLEAN FASTCALL
+Fast486FpuToInteger(PFAST486_STATE State,
+                    PCFAST486_FPU_DATA_REG Value,
+                    PLONGLONG Result)
+{
+    ULONG Bits;
+    ULONGLONG Remainder;
+    SHORT UnbiasedExp = (SHORT)Value->Exponent - FPU_REAL10_BIAS;
+
+    if (FPU_IS_ZERO(Value))
+    {
+        *Result = 0LL;
+        return TRUE;
+    }
+
+    if (FPU_IS_NAN(Value) || !FPU_IS_NORMALIZED(Value)
+        || (UnbiasedExp < 0) || (UnbiasedExp > 63))
+    {
+        /* Raise an invalid operation exception */
+        State->FpuStatus.Ie = TRUE;
+
+        if (State->FpuControl.Im)
+        {
+            *Result = 0LL;
+            return TRUE;
+        }
+        else
+        {
+            Fast486FpuException(State);
+            return FALSE;
+        }
+    }
+
+    Bits = 63 - UnbiasedExp;
+
+    /* Calculate the result and the remainder */
+    *Result = (LONGLONG)(Value->Mantissa >> Bits);
+    Remainder = Value->Mantissa & ((1 << Bits) - 1);
+
+    /* The result must be positive here */
+    ASSERT(*Result >= 0LL);
+
+    /* Perform rounding */
+    Fast486FpuRound(State, (PULONGLONG)Result, Value->Sign, Remainder, Bits - 1);
+
+    if (Value->Sign) *Result = -*Result;
+    return TRUE;
+}
+
+static inline VOID FASTCALL
+Fast486FpuFromSingleReal(PFAST486_STATE State,
+                         ULONG Value,
+                         PFAST486_FPU_DATA_REG Result)
 {
     /* Extract the sign, exponent and mantissa */
     Result->Sign = (UCHAR)(Value >> 31);
@@ -87,10 +304,89 @@ Fast486FpuGetSingleReal(PFAST486_STATE State,
     }
 }
 
-static VOID
-Fast486FpuGetDoubleReal(PFAST486_STATE State,
-                        ULONGLONG Value,
-                        PFAST486_FPU_DATA_REG Result)
+static inline BOOLEAN FASTCALL
+Fast486FpuToSingleReal(PFAST486_STATE State,
+                       PCFAST486_FPU_DATA_REG Value,
+                       PULONG Result)
+{
+    ULONGLONG Remainder;
+    SHORT UnbiasedExp = (SHORT)Value->Exponent - FPU_REAL10_BIAS;
+    ULONGLONG Result64;
+
+    if (FPU_IS_ZERO(Value))
+    {
+        *Result = 0;
+        return TRUE;
+    }
+
+    /* Calculate the mantissa */
+    *Result = (ULONG)(Value->Mantissa >> 40) & 0x7FFFFF;
+
+    if (FPU_IS_NAN(Value))
+    {
+        *Result |= FPU_REAL4_INFINITY;
+        goto SetSign;
+    }
+
+    /* Check for underflow */
+    if (!FPU_IS_NORMALIZED(Value) || (UnbiasedExp < -127))
+    {
+        /* Raise the underflow exception */
+        State->FpuStatus.Ue = TRUE;
+
+        if (State->FpuControl.Um)
+        {
+            /* The result is zero due to underflow */
+            *Result = 0ULL;
+            return TRUE;
+        }
+        else
+        {
+            Fast486FpuException(State);
+            return FALSE;
+        }
+    }
+
+    /* Check for overflow */
+    if (UnbiasedExp > 127)
+    {
+        /* Raise the overflow exception */
+        State->FpuStatus.Oe = TRUE;
+
+        if (State->FpuControl.Om)
+        {
+            /* The result is infinity due to overflow */
+            *Result = FPU_REAL4_INFINITY;
+            goto SetSign;
+        }
+        else
+        {
+            Fast486FpuException(State);
+            return FALSE;
+        }
+    }
+
+    /* Calculate the remainder */
+    Remainder = Value->Mantissa & ((1ULL << 40) - 1);
+
+    /* Perform rounding */
+    Result64 = (ULONGLONG)*Result;
+    Fast486FpuRound(State, &Result64, Value->Sign, Remainder, 39);
+    *Result = (ULONG)Result64;
+
+    /* Store the biased exponent */
+    *Result |= (ULONG)(UnbiasedExp + FPU_REAL4_BIAS) << 23;
+
+SetSign:
+
+    if (Value->Sign) *Result |= 0x80000000;
+    return TRUE;
+}
+
+static inline VOID FASTCALL
+Fast486FpuFromDoubleReal(PFAST486_STATE State,
+                         ULONGLONG Value,
+                         PFAST486_FPU_DATA_REG Result)
 {
     /* Extract the sign, exponent and mantissa */
     Result->Sign = (UCHAR)(Value >> 63);
@@ -108,20 +404,149 @@ Fast486FpuGetDoubleReal(PFAST486_STATE State,
     }
 }
 
-static VOID
+static inline BOOLEAN FASTCALL
+Fast486FpuToDoubleReal(PFAST486_STATE State,
+                       PCFAST486_FPU_DATA_REG Value,
+                       PULONGLONG Result)
+{
+    ULONGLONG Remainder;
+    SHORT UnbiasedExp = (SHORT)Value->Exponent - FPU_REAL10_BIAS;
+
+    if (FPU_IS_ZERO(Value))
+    {
+        *Result = 0LL;
+        return TRUE;
+    }
+
+    /* Calculate the mantissa */
+    *Result = (Value->Mantissa >> 11) & ((1ULL << 52) - 1);
+
+    if (FPU_IS_NAN(Value))
+    {
+        *Result |= FPU_REAL8_INFINITY;
+        goto SetSign;
+    }
+
+    /* Check for underflow */
+    if (!FPU_IS_NORMALIZED(Value) || (UnbiasedExp < -1023))
+    {
+        /* Raise the underflow exception */
+        State->FpuStatus.Ue = TRUE;
+
+        if (State->FpuControl.Um)
+        {
+            /* The result is zero due to underflow */
+            *Result = 0ULL;
+            return TRUE;
+        }
+        else
+        {
+            Fast486FpuException(State);
+            return FALSE;
+        }
+    }
+
+    /* Check for overflow */
+    if (UnbiasedExp > 1023)
+    {
+        /* Raise the overflow exception */
+        State->FpuStatus.Oe = TRUE;
+
+        if (State->FpuControl.Om)
+        {
+            /* The result is infinity due to overflow */
+            *Result = FPU_REAL8_INFINITY;
+            goto SetSign;
+        }
+        else
+        {
+            Fast486FpuException(State);
+            return FALSE;
+        }
+    }
+
+    /* Calculate the remainder */
+    Remainder = Value->Mantissa & ((1 << 11) - 1);
+
+    /* Perform rounding */
+    Fast486FpuRound(State, Result, Value->Sign, Remainder, 10);
+
+    /* Store the biased exponent */
+    *Result |= (ULONGLONG)(UnbiasedExp + FPU_REAL8_BIAS) << 52;
+
+SetSign:
+
+    if (Value->Sign) *Result |= 1ULL << 63;
+    return TRUE;
+}
+
+static inline VOID FASTCALL
+Fast486FpuFromPackedBcd(PFAST486_STATE State,
+                        PUCHAR Value,
+                        PFAST486_FPU_DATA_REG Result)
+{
+    INT i;
+    LONGLONG IntVal = 0LL;
+
+    for (i = 8; i >= 0; i--)
+    {
+        IntVal *= 100LL;
+        IntVal += (Value[i] >> 4) * 10 + (Value[i] & 0x0F);
+    }
+
+    /* Apply the sign */
+    if (Value[9] & 0x80) IntVal = -IntVal;
+
+    /* Now convert the integer to FP80 */
+    Fast486FpuFromInteger(State, IntVal, Result);
+}
+
+static inline BOOLEAN FASTCALL
+Fast486FpuToPackedBcd(PFAST486_STATE State,
+                      PCFAST486_FPU_DATA_REG Value,
+                      PUCHAR Result)
+{
+    INT i;
+    LONGLONG IntVal;
+
+    /* Convert it to an integer first */
+    if (!Fast486FpuToInteger(State, Value, &IntVal)) return FALSE;
+
+    if (IntVal < 0LL)
+    {
+        IntVal = -IntVal;
+        Result[9] = 0x80;
+    }
+
+    for (i = 0; i < 9; i++)
+    {
+        Result[i] = (UCHAR)((IntVal % 10) + (((IntVal / 10) % 10) << 4));
+        IntVal /= 100LL;
+    }
+
+    return TRUE;
+}
+
+static inline VOID FASTCALL
 Fast486FpuAdd(PFAST486_STATE State,
-              PFAST486_FPU_DATA_REG FirstOperand,
-              PFAST486_FPU_DATA_REG SecondOperand,
+              PCFAST486_FPU_DATA_REG FirstOperand,
+              PCFAST486_FPU_DATA_REG SecondOperand,
               PFAST486_FPU_DATA_REG Result)
 {
     FAST486_FPU_DATA_REG FirstAdjusted = *FirstOperand;
     FAST486_FPU_DATA_REG SecondAdjusted = *SecondOperand;
     FAST486_FPU_DATA_REG TempResult;
 
-    if (!FPU_IS_NORMALIZED(FirstOperand) || !FPU_IS_NORMALIZED(SecondOperand))
+    if ((!FPU_IS_NORMALIZED(FirstOperand) || !FPU_IS_NORMALIZED(SecondOperand)))
     {
-        /* Denormalized */
+        /* Raise the denormalized exception */
         State->FpuStatus.De = TRUE;
+
+        if (!State->FpuControl.Dm)
+        {
+            Fast486FpuException(State);
+            return;
+        }
     }
 
     /* Find the largest exponent */
@@ -155,8 +580,8 @@ Fast486FpuAdd(PFAST486_STATE State,
         else TempResult.Sign = FALSE;
 
         /* Invert the negative mantissa */
-        if (FirstAdjusted.Sign) FirstAdjusted.Mantissa = -FirstAdjusted.Mantissa;
-        if (SecondAdjusted.Sign) SecondAdjusted.Mantissa = -SecondAdjusted.Mantissa;
+        if (FirstAdjusted.Sign) FirstAdjusted.Mantissa = -(LONGLONG)FirstAdjusted.Mantissa;
+        if (SecondAdjusted.Sign) SecondAdjusted.Mantissa = -(LONGLONG)SecondAdjusted.Mantissa;
 
         /* Calculate the mantissa of the result */
         TempResult.Mantissa = FirstAdjusted.Mantissa + SecondAdjusted.Mantissa;
@@ -167,12 +592,20 @@ Fast486FpuAdd(PFAST486_STATE State,
     {
         if (TempResult.Exponent == FPU_MAX_EXPONENT)
         {
-            /* Total overflow, return infinity */
-            TempResult.Mantissa = FPU_MANTISSA_HIGH_BIT;
-            TempResult.Exponent = FPU_MAX_EXPONENT + 1;
-
-            /* Update flags */
+            /* Raise the overflow exception */
             State->FpuStatus.Oe = TRUE;
+
+            if (State->FpuControl.Om)
+            {
+                /* Total overflow, return infinity */
+                TempResult.Mantissa = FPU_MANTISSA_HIGH_BIT;
+                TempResult.Exponent = FPU_MAX_EXPONENT + 1;
+            }
+            else
+            {
+                Fast486FpuException(State);
+                return;
+            }
         }
         else
         {
@@ -182,16 +615,16 @@ Fast486FpuAdd(PFAST486_STATE State,
             TempResult.Exponent++;
         }
     }
-    
+
     /* Normalize the result and return it */
     Fast486FpuNormalize(State, &TempResult);
     *Result = TempResult;
 }
 
-static VOID
+static inline VOID FASTCALL
 Fast486FpuSubtract(PFAST486_STATE State,
-                   PFAST486_FPU_DATA_REG FirstOperand,
-                   PFAST486_FPU_DATA_REG SecondOperand,
+                   PCFAST486_FPU_DATA_REG FirstOperand,
+                   PCFAST486_FPU_DATA_REG SecondOperand,
                    PFAST486_FPU_DATA_REG Result)
 {
     FAST486_FPU_DATA_REG NegativeSecondOperand = *SecondOperand;
@@ -200,13 +633,13 @@ Fast486FpuSubtract(PFAST486_STATE State,
     NegativeSecondOperand.Sign = !NegativeSecondOperand.Sign;
 
     /* And perform an addition instead */
-    Fast486FpuAdd(State, Result, FirstOperand, &NegativeSecondOperand);
+    Fast486FpuAdd(State, FirstOperand, &NegativeSecondOperand, Result);
 }
 
-static VOID
+static inline VOID FASTCALL
 Fast486FpuCompare(PFAST486_STATE State,
-                  PFAST486_FPU_DATA_REG FirstOperand,
-                  PFAST486_FPU_DATA_REG SecondOperand)
+                  PCFAST486_FPU_DATA_REG FirstOperand,
+                  PCFAST486_FPU_DATA_REG SecondOperand)
 {
     if (FPU_IS_NAN(FirstOperand) || FPU_IS_NAN(SecondOperand))
     {
@@ -256,139 +689,217 @@ Fast486FpuCompare(PFAST486_STATE State,
     }
 }
 
-static VOID
+static inline VOID FASTCALL
 Fast486FpuMultiply(PFAST486_STATE State,
-                   PFAST486_FPU_DATA_REG FirstOperand,
-                   PFAST486_FPU_DATA_REG SecondOperand,
+                   PCFAST486_FPU_DATA_REG FirstOperand,
+                   PCFAST486_FPU_DATA_REG SecondOperand,
                    PFAST486_FPU_DATA_REG Result)
 {
     FAST486_FPU_DATA_REG TempResult;
+    LONG Exponent;
+
+    if (FPU_IS_INDEFINITE(FirstOperand)
+        || FPU_IS_INDEFINITE(SecondOperand)
+        || (FPU_IS_ZERO(FirstOperand) && FPU_IS_INFINITY(SecondOperand))
+        || (FPU_IS_INFINITY(FirstOperand) && FPU_IS_ZERO(SecondOperand)))
+    {
+        /* The result will be indefinite */
+        Result->Sign = TRUE;
+        Result->Exponent = FPU_MAX_EXPONENT + 1;
+        Result->Mantissa = FPU_INDEFINITE_MANTISSA;
+        return;
+    }
+
+    if (FPU_IS_ZERO(FirstOperand) || FPU_IS_ZERO(SecondOperand))
+    {
+        /* The result will be zero */
+        Result->Sign = FirstOperand->Sign ^ SecondOperand->Sign;
+        Result->Exponent = 0;
+        Result->Mantissa = 0ULL;
+        return;
+    }
+
+    if (FPU_IS_INFINITY(FirstOperand) || FPU_IS_INFINITY(SecondOperand))
+    {
+        /* The result will be infinity */
+        Result->Sign = FirstOperand->Sign ^ SecondOperand->Sign;
+        Result->Exponent = FPU_MAX_EXPONENT + 1;
+        Result->Mantissa = FPU_MANTISSA_HIGH_BIT;
+        return;
+    }
 
-    if (!FPU_IS_NORMALIZED(FirstOperand) || !FPU_IS_NORMALIZED(SecondOperand))
+    if ((!FPU_IS_NORMALIZED(FirstOperand) || !FPU_IS_NORMALIZED(SecondOperand)))
     {
-        /* Denormalized */
+        /* Raise the denormalized exception */
         State->FpuStatus.De = TRUE;
+
+        if (!State->FpuControl.Dm)
+        {
+            Fast486FpuException(State);
+            return;
+        }
     }
 
+    /* Calculate the sign */
+    TempResult.Sign = FirstOperand->Sign ^ SecondOperand->Sign;
+
+    /* Calculate the exponent */
+    Exponent = (LONG)FirstOperand->Exponent + (LONG)SecondOperand->Exponent - FPU_REAL10_BIAS;
+
+    /* Calculate the mantissa */
     UnsignedMult128(FirstOperand->Mantissa,
                     SecondOperand->Mantissa,
                     &TempResult.Mantissa);
 
-    TempResult.Exponent = FirstOperand->Exponent + SecondOperand->Exponent;
-    TempResult.Sign = FirstOperand->Sign ^ SecondOperand->Sign;
+    if (Exponent < 0)
+    {
+        /* Raise the underflow exception */
+        State->FpuStatus.Ue = TRUE;
+
+        if (!State->FpuControl.Um)
+        {
+            Fast486FpuException(State);
+            return;
+        }
+
+        /* The exponent will be zero */
+        TempResult.Exponent = 0;
+
+        /* If possible, denormalize the result, otherwise make it zero */
+        if (Exponent > -64) TempResult.Mantissa >>= (-Exponent);
+        else TempResult.Mantissa = 0ULL;
+    }
+    else if (Exponent > FPU_MAX_EXPONENT)
+    {
+        /* Raise the overflow exception */
+        State->FpuStatus.Oe = TRUE;
+
+        if (!State->FpuControl.Om)
+        {
+            Fast486FpuException(State);
+            return;
+        }
+
+        /* Make the result infinity */
+        TempResult.Exponent = FPU_MAX_EXPONENT + 1;
+        TempResult.Mantissa = FPU_MANTISSA_HIGH_BIT;
+    }
+    else TempResult.Exponent = (USHORT)Exponent;
 
     /* Normalize the result */
     Fast486FpuNormalize(State, &TempResult);
     *Result = TempResult;
 }
 
-static VOID
+static inline VOID FASTCALL
 Fast486FpuDivide(PFAST486_STATE State,
-                 PFAST486_FPU_DATA_REG FirstOperand,
-                 PFAST486_FPU_DATA_REG SecondOperand,
+                 PCFAST486_FPU_DATA_REG FirstOperand,
+                 PCFAST486_FPU_DATA_REG SecondOperand,
                  PFAST486_FPU_DATA_REG Result)
 {
     FAST486_FPU_DATA_REG TempResult;
+    ULONGLONG QuotientLow, QuotientHigh, Remainder;
+    LONG Exponent;
 
-    if (FPU_IS_ZERO(SecondOperand))
+    if (FPU_IS_INDEFINITE(FirstOperand)
+        || FPU_IS_INDEFINITE(SecondOperand)
+        || (FPU_IS_INFINITY(FirstOperand) && FPU_IS_INFINITY(SecondOperand))
+        || (FPU_IS_ZERO(FirstOperand) && FPU_IS_ZERO(SecondOperand)))
     {
-        /* Division by zero */
-        State->FpuStatus.Ze = TRUE;
+        /* Raise the invalid operation exception */
+        State->FpuStatus.Ie = TRUE;
+
+        if (State->FpuControl.Im)
+        {
+            /* Return the indefinite NaN */
+            Result->Sign = TRUE;
+            Result->Exponent = FPU_MAX_EXPONENT + 1;
+            Result->Mantissa = FPU_INDEFINITE_MANTISSA;
+        }
+        else Fast486FpuException(State);
+
         return;
     }
 
-    TempResult.Exponent = FirstOperand->Exponent - SecondOperand->Exponent;
-    TempResult.Sign = FirstOperand->Sign ^ SecondOperand->Sign;
-
-    // TODO: NOT IMPLEMENTED
-    UNREFERENCED_PARAMETER(TempResult);
-    UNIMPLEMENTED;
-}
+    if (FPU_IS_ZERO(SecondOperand) || FPU_IS_INFINITY(FirstOperand))
+    {
+        /* Raise the division by zero exception */
+        State->FpuStatus.Ze = TRUE;
 
-#endif
+        if (State->FpuControl.Zm)
+        {
+            /* Return infinity */
+            Result->Sign = FirstOperand->Sign;
+            Result->Exponent = FPU_MAX_EXPONENT + 1;
+            Result->Mantissa = FPU_MANTISSA_HIGH_BIT;
+        }
+        else Fast486FpuException(State);
 
-/* PUBLIC FUNCTIONS ***********************************************************/
+        return;
+    }
 
-FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8DC)
-{
-    FAST486_MOD_REG_RM ModRegRm;
-    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
-    PFAST486_FPU_DATA_REG SourceOperand, DestOperand;
-    FAST486_FPU_DATA_REG MemoryData;
+    /* Calculate the sign of the result */
+    TempResult.Sign = FirstOperand->Sign ^ SecondOperand->Sign;
 
-    /* Get the operands */
-    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
+    if (FPU_IS_ZERO(FirstOperand) || FPU_IS_INFINITY(SecondOperand))
     {
-        /* Exception occurred */
+        /* Return zero */
+        Result->Sign = TempResult.Sign;
+        Result->Mantissa = 0ULL;
+        Result->Exponent = 0;
         return;
     }
 
-    FPU_CHECK();
+    /* Calculate the exponent of the result */
+    Exponent = (LONG)FirstOperand->Exponent - (LONG)SecondOperand->Exponent - 64;
 
-#ifndef FAST486_NO_FPU
+    /* Divide the two mantissas */
+    Remainder = UnsignedDivMod128(0ULL,
+                                  /* Notice the 64 above - this is the high part */
+                                  FirstOperand->Mantissa,
+                                  SecondOperand->Mantissa,
+                                  &QuotientLow,
+                                  &QuotientHigh);
+    UNREFERENCED_PARAMETER(Remainder); // TODO: Rounding
 
-    if (ModRegRm.Memory)
+    TempResult.Mantissa = QuotientLow;
+
+    if (QuotientHigh > 0ULL)
     {
-        /* Load the source operand from memory */
+        ULONG BitsToShift = 64 - CountLeadingZeros64(QuotientHigh);
 
-        if (Opcode == 0xDC)
-        {
-            ULONGLONG Value;
+        TempResult.Mantissa >>= BitsToShift;
+        TempResult.Mantissa |= QuotientHigh << (64 - BitsToShift);
+        Exponent += BitsToShift;
 
-            if (!Fast486ReadMemory(State,
-                                   (State->PrefixFlags & FAST486_PREFIX_SEG)
-                                   ? State->SegmentOverride : FAST486_REG_DS,
-                                   ModRegRm.MemoryAddress,
-                                   FALSE,
-                                   &Value,
-                                   sizeof(ULONGLONG)))
-            {
-                /* Exception occurred */
-                return;
-            }
+        // TODO: Rounding
+    }
 
-            Fast486FpuGetDoubleReal(State, Value, &MemoryData);
-        }
-        else
-        {
-            ULONG Value;
-
-            if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value))
-            {
-                /* Exception occurred */
-                return;
-            }
-
-            Fast486FpuGetSingleReal(State, Value, &MemoryData);
-        }
-
-        SourceOperand = &MemoryData;
-    }
-    else
+    if (Exponent < -FPU_REAL10_BIAS)
     {
-        /* Load the source operand from an FPU register */
-        SourceOperand = &FPU_ST(ModRegRm.SecondRegister);
+        TempResult.Mantissa >>= -(Exponent + FPU_REAL10_BIAS);
+        Exponent = -FPU_REAL10_BIAS;
 
-        if (FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY)
-        {
-            /* Invalid operation */
-            State->FpuStatus.Ie = TRUE;
-            return;
-        }
+        // TODO: Rounding
     }
 
-    /* The destination operand is always ST0 */
-    DestOperand = &FPU_ST(0);
+    TempResult.Exponent = (USHORT)(Exponent + FPU_REAL10_BIAS);
 
-    if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
-    {
-        /* Invalid operation */
-        State->FpuStatus.Ie = TRUE;
-        return;
-    }
+    /* Normalize the result */
+    Fast486FpuNormalize(State, &TempResult);
+    *Result = TempResult;
+}
+
+static inline VOID FASTCALL
+Fast486FpuArithmeticOperation(PFAST486_STATE State,
+                              INT Operation,
+                              PFAST486_FPU_DATA_REG SourceOperand,
+                              PFAST486_FPU_DATA_REG DestOperand)
+{
+    ASSERT(!(Operation & ~7));
 
     /* Check the operation */
-    switch (ModRegRm.Register)
+    switch (Operation)
     {
         /* FADD */
         case 0:
@@ -410,7 +921,7 @@ FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8DC)
         case 3:
         {
             Fast486FpuCompare(State, DestOperand, SourceOperand);
-            if (ModRegRm.Register == 3) Fast486FpuPop(State);
+            if (Operation == 3) Fast486FpuPop(State);
 
             break;
         }
@@ -443,36 +954,100 @@ FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8DC)
             break;
         }
     }
-
-#endif
 }
 
-FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9)
+static inline BOOLEAN FASTCALL
+Fast486FpuLoadEnvironment(PFAST486_STATE State,
+                          INT Segment,
+                          ULONG Address,
+                          BOOLEAN Size)
 {
-    FAST486_MOD_REG_RM ModRegRm;
-    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+    UCHAR Buffer[28];
 
-    /* Get the operands */
-    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
+    if (!Fast486ReadMemory(State, Segment, Address, FALSE, Buffer, (Size + 1) * 14))
     {
         /* Exception occurred */
-        return;
+        return FALSE;
     }
 
-    FPU_CHECK();
+    /* Check if this is a 32-bit save or a 16-bit save */
+    if (Size)
+    {
+        PULONG Data = (PULONG)Buffer;
+
+        State->FpuControl.Value = (USHORT)Data[0];
+        State->FpuStatus.Value = (USHORT)Data[1];
+        State->FpuTag = (USHORT)Data[2];
+        State->FpuLastInstPtr.Long = Data[3];
+        State->FpuLastCodeSel = (USHORT)Data[4];
+        State->FpuLastOpPtr.Long = Data[5];
+        State->FpuLastDataSel = (USHORT)Data[6];
+    }
+    else
+    {
+        PUSHORT Data = (PUSHORT)Buffer;
+
+        State->FpuControl.Value = Data[0];
+        State->FpuStatus.Value = Data[1];
+        State->FpuTag = Data[2];
+        State->FpuLastInstPtr.LowWord = Data[3];
+        State->FpuLastCodeSel = Data[4];
+        State->FpuLastOpPtr.LowWord = Data[5];
+        State->FpuLastDataSel = Data[6];
+    }
 
-#ifndef FAST486_NO_FPU
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
-#else
-    /* Do nothing */
-#endif
+    return TRUE;
 }
 
-FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA)
+static inline BOOLEAN FASTCALL
+Fast486FpuSaveEnvironment(PFAST486_STATE State,
+                          INT Segment,
+                          ULONG Address,
+                          BOOLEAN Size)
+{
+    UCHAR Buffer[28];
+
+    /* Check if this is a 32-bit save or a 16-bit save */
+    if (Size)
+    {
+        PULONG Data = (PULONG)Buffer;
+
+        Data[0] = (ULONG)State->FpuControl.Value;
+        Data[1] = (ULONG)State->FpuStatus.Value;
+        Data[2] = (ULONG)State->FpuTag;
+        Data[3] = State->FpuLastInstPtr.Long;
+        Data[4] = (ULONG)State->FpuLastCodeSel;
+        Data[5] = State->FpuLastOpPtr.Long;
+        Data[6] = (ULONG)State->FpuLastDataSel;
+    }
+    else
+    {
+        PUSHORT Data = (PUSHORT)Buffer;
+
+        Data[0] = State->FpuControl.Value;
+        Data[1] = State->FpuStatus.Value;
+        Data[2] = State->FpuTag;
+        Data[3] = State->FpuLastInstPtr.LowWord;
+        Data[4] = State->FpuLastCodeSel;
+        Data[5] = State->FpuLastOpPtr.LowWord;
+        Data[6] = State->FpuLastDataSel;
+    }
+
+    return Fast486WriteMemory(State, Segment, Address, Buffer, (Size + 1) * 14);
+}
+
+#endif
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8)
 {
     FAST486_MOD_REG_RM ModRegRm;
     BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+    PFAST486_FPU_DATA_REG SourceOperand, DestOperand;
+    FAST486_FPU_DATA_REG MemoryData;
+
+    TOGGLE_ADSIZE(AddressSize);
 
     /* Get the operands */
     if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
@@ -484,17 +1059,86 @@ FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA)
     FPU_CHECK();
 
 #ifndef FAST486_NO_FPU
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
-#else
-    /* Do nothing */
+
+    FPU_SAVE_LAST_INST();
+
+    /* The destination operand is ST0 */
+    DestOperand = &FPU_ST(0);
+
+    if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+    {
+        /* Raise the invalid operation exception */
+        State->FpuStatus.Ie = TRUE;
+
+        if (State->FpuControl.Im)
+        {
+            /* Return the indefinite NaN */
+            DestOperand->Sign = TRUE;
+            DestOperand->Exponent = FPU_MAX_EXPONENT + 1;
+            DestOperand->Mantissa = FPU_INDEFINITE_MANTISSA;
+
+            FPU_SET_TAG(0, FPU_TAG_SPECIAL);
+        }
+        else Fast486FpuException(State);
+
+        return;
+    }
+
+    if (ModRegRm.Memory)
+    {
+        /* Load the source operand from memory */
+        ULONG Value;
+
+        if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value))
+        {
+            /* Exception occurred */
+            return;
+        }
+
+        Fast486FpuFromSingleReal(State, Value, &MemoryData);
+        SourceOperand = &MemoryData;
+
+        FPU_SAVE_LAST_OPERAND();
+    }
+    else
+    {
+        if (FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY)
+        {
+            /* Raise the invalid operation exception */
+            State->FpuStatus.Ie = TRUE;
+
+            if (State->FpuControl.Im)
+            {
+                /* Return the indefinite NaN */
+                DestOperand->Sign = TRUE;
+                DestOperand->Exponent = FPU_MAX_EXPONENT + 1;
+                DestOperand->Mantissa = FPU_INDEFINITE_MANTISSA;
+
+                FPU_SET_TAG(0, FPU_TAG_SPECIAL);
+            }
+            else Fast486FpuException(State);
+
+            return;
+        }
+
+        /* Load the source operand from an FPU register */
+        SourceOperand = &FPU_ST(ModRegRm.SecondRegister);
+    }
+
+    /* Perform the requested operation */
+    Fast486FpuArithmeticOperation(State, ModRegRm.Register, SourceOperand, DestOperand);
+
 #endif
 }
 
-FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB)
+FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9)
 {
     FAST486_MOD_REG_RM ModRegRm;
-    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+    BOOLEAN OperandSize, AddressSize;
+
+    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+    TOGGLE_OPSIZE(OperandSize);
+    TOGGLE_ADSIZE(AddressSize);
 
     /* Get the operands */
     if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
@@ -509,48 +1153,99 @@ FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB)
 
     if (ModRegRm.Memory)
     {
-        // TODO: NOT IMPLEMENTED
-        UNIMPLEMENTED;
-    }
-    else
-    {
-        /* Only a few of these instructions have any meaning on a 487 */
-        switch ((ModRegRm.Register << 3) | ModRegRm.SecondRegister)
+        switch (ModRegRm.Register)
         {
-            /* FCLEX */
-            case 0x22:
+            /* FLD */
+            case 0:
             {
-                /* Clear exception data */
-                State->FpuStatus.Ie =
-                State->FpuStatus.De =
-                State->FpuStatus.Ze =
-                State->FpuStatus.Oe =
-                State->FpuStatus.Ue =
-                State->FpuStatus.Pe =
-                State->FpuStatus.Sf =
-                State->FpuStatus.Es =
-                State->FpuStatus.Busy = FALSE;
+                ULONG Value;
+                FAST486_FPU_DATA_REG MemoryData;
+
+                FPU_SAVE_LAST_INST();
+                FPU_SAVE_LAST_OPERAND();
+
+                if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                Fast486FpuFromSingleReal(State, Value, &MemoryData);
+                Fast486FpuPush(State, &MemoryData);
 
                 break;
             }
 
-            /* FINIT */
-            case 0x23:
+            /* FST */
+            case 2:
+            /* FSTP */
+            case 3:
             {
-                /* Restore the state */
-                State->FpuControl.Value = FAST486_FPU_DEFAULT_CONTROL;
-                State->FpuStatus.Value = 0;
-                State->FpuTag = 0xFFFF;
+                ULONG Value = FPU_REAL4_INDEFINITE;
+
+                FPU_SAVE_LAST_INST();
+                FPU_SAVE_LAST_OPERAND();
+
+                if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+                {
+                    /* Raise the invalid operation exception */
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (!State->FpuControl.Im)
+                    {
+                        Fast486FpuException(State);
+                        return;
+                    }
+                }
+                else if (!Fast486FpuToSingleReal(State, &FPU_ST(0), &Value))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                if (!Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Value))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                if (ModRegRm.Register == 3) Fast486FpuPop(State);
+                break;
+            }
 
+            /* FLDENV */
+            case 4:
+            {
+                Fast486FpuLoadEnvironment(State,
+                                          (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                          ? FAST486_REG_DS : State->SegmentOverride,
+                                          ModRegRm.MemoryAddress,
+                                          OperandSize);
                 break;
             }
 
-            /* FENI */
-            case 0x20:
-            /* FDISI */
-            case 0x21:
+            /* FLDCW */
+            case 5:
             {
-                /* These do nothing */
+                Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &State->FpuControl.Value);
+                break;
+            }
+
+            /* FSTENV */
+            case 6:
+            {
+                Fast486FpuSaveEnvironment(State,
+                                          (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                          ? FAST486_REG_DS : State->SegmentOverride,
+                                          ModRegRm.MemoryAddress,
+                                          OperandSize);
+                break;
+            }
+
+            /* FSTCW */
+            case 7:
+            {
+                Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, State->FpuControl.Value);
                 break;
             }
 
@@ -558,76 +1253,1534 @@ FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB)
             default:
             {
                 Fast486Exception(State, FAST486_EXCEPTION_UD);
+                return;
             }
         }
     }
+    else
+    {
+        switch ((ModRegRm.Register << 3) | ModRegRm.SecondRegister)
+        {
+            /* FLD */
+            case 0x00:
+            case 0x01:
+            case 0x02:
+            case 0x03:
+            case 0x04:
+            case 0x05:
+            case 0x06:
+            case 0x07:
+            {
+                if (FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY)
+                {
+                    /* Raise the invalid operation exception */
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (!State->FpuControl.Im)
+                    {
+                        Fast486FpuException(State);
+                        return;
+                    }
+                }
+
+                Fast486FpuPush(State, &FPU_ST(ModRegRm.SecondRegister));
+                break;
+            }
 
-#endif
-}
+            /* FXCH */
+            case 0x08:
+            case 0x09:
+            case 0x0A:
+            case 0x0B:
+            case 0x0C:
+            case 0x0D:
+            case 0x0E:
+            case 0x0F:
+            {
+                FAST486_FPU_DATA_REG Temp;
 
-FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD)
-{
-    FAST486_MOD_REG_RM ModRegRm;
-    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+                if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+                    || FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY)
+                {
+                    State->FpuStatus.Ie = TRUE;
 
-    /* Get the operands */
-    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
-    {
-        /* Exception occurred */
-        return;
-    }
+                    if (!State->FpuControl.Im) Fast486FpuException(State);
+                    break;
+                }
 
-    FPU_CHECK();
+                /* Exchange */
+                Temp = FPU_ST(0);
+                FPU_ST(0) = FPU_ST(ModRegRm.SecondRegister);
+                FPU_ST(ModRegRm.SecondRegister) = Temp;
 
-#ifndef FAST486_NO_FPU
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
-#else
-    /* Do nothing */
-#endif
-}
+                FPU_UPDATE_TAG(0);
+                FPU_UPDATE_TAG(ModRegRm.SecondRegister);
 
-FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDE)
-{
-    FAST486_MOD_REG_RM ModRegRm;
-    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+                break;
+            }
 
-    /* Get the operands */
-    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
-    {
-        /* Exception occurred */
-        return;
-    }
+            /* FNOP */
+            case 0x10:
+            {
+                /* Do nothing */
+                break;
+            }
 
-    FPU_CHECK();
+            /* FSTP */
+            case 0x18:
+            case 0x19:
+            case 0x1A:
+            case 0x1B:
+            case 0x1C:
+            case 0x1D:
+            case 0x1E:
+            case 0x1F:
+            {
+                FPU_ST(ModRegRm.SecondRegister) = FPU_ST(0);
+                FPU_UPDATE_TAG(ModRegRm.SecondRegister);
 
-#ifndef FAST486_NO_FPU
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
-#else
-    /* Do nothing */
-#endif
-}
+                Fast486FpuPop(State);
+                break;
+            }
 
-FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDF)
-{
-    FAST486_MOD_REG_RM ModRegRm;
-    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+            /* FCHS */
+            case 0x20:
+            {
+                if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+                {
+                    State->FpuStatus.Ie = TRUE;
 
-    /* Get the operands */
-    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
-    {
-        /* Exception occurred */
-        return;
-    }
+                    if (!State->FpuControl.Im) Fast486FpuException(State);
+                    break;
+                }
 
-    FPU_CHECK();
+                /* Invert the sign */
+                FPU_ST(0).Sign = !FPU_ST(0).Sign;
+
+                break;
+            }
+
+            /* FABS */
+            case 0x21:
+            {
+                if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+                {
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (!State->FpuControl.Im) Fast486FpuException(State);
+                    break;
+                }
+
+                /* Set the sign to positive */
+                FPU_ST(0).Sign = FALSE;
+
+                break;
+            }
+
+            /* FTST */
+            case 0x24:
+            {
+                Fast486FpuCompare(State, &FPU_ST(0), &FpuZero);
+                break;
+            }
+
+            /* FXAM */
+            case 0x25:
+            {
+                /* The sign bit goes in C1, even if the register's empty */
+                State->FpuStatus.Code1 = FPU_ST(0).Sign;
+
+                if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+                {
+                    State->FpuStatus.Code0 = 1;
+                    State->FpuStatus.Code2 = 0;
+                    State->FpuStatus.Code3 = 1;
+                }
+                else if (FPU_GET_TAG(0) == FPU_TAG_SPECIAL)
+                {
+                    if (FPU_IS_INFINITY(&FPU_ST(0)))
+                    {
+                        State->FpuStatus.Code0 = 1;
+                        State->FpuStatus.Code2 = 1;
+                        State->FpuStatus.Code3 = 0;
+                    }
+                    else
+                    {
+                        State->FpuStatus.Code0 = 1;
+                        State->FpuStatus.Code2 = 0;
+                        State->FpuStatus.Code3 = 0;
+                    }
+                }
+                else if (FPU_GET_TAG(0) == FPU_TAG_ZERO)
+                {
+                    State->FpuStatus.Code0 = 0;
+                    State->FpuStatus.Code2 = 0;
+                    State->FpuStatus.Code3 = 1;
+                }
+                else
+                {
+                    if (FPU_IS_NORMALIZED(&FPU_ST(0)))
+                    {
+                        State->FpuStatus.Code0 = 0;
+                        State->FpuStatus.Code2 = 1;
+                        State->FpuStatus.Code3 = 0;
+                    }
+                    else
+                    {
+                        State->FpuStatus.Code0 = 0;
+                        State->FpuStatus.Code2 = 1;
+                        State->FpuStatus.Code3 = 1;
+                    }
+                }
+
+                break;
+            }
+
+            /* FLD1 */
+            case 0x28:
+            /* FLDL2T */
+            case 0x29:
+            /* FLDL2E */
+            case 0x2A:
+            /* FLDPI */
+            case 0x2B:
+            /* FLDLG2 */
+            case 0x2C:
+            /* FLDLN2 */
+            case 0x2D:
+            /* FLDZ */
+            case 0x2E:
+            {
+                PCFAST486_FPU_DATA_REG Constants[] =
+                {
+                    &FpuOne,
+                    &FpuL2Ten,
+                    &FpuL2E,
+                    &FpuPi,
+                    &FpuLgTwo,
+                    &FpuLnTwo,
+                    &FpuZero
+                };
+
+                Fast486FpuPush(State, Constants[ModRegRm.SecondRegister]);
+                break;
+            }
+
+            /* F2XM1 */
+            case 0x30:
+            {
+                // TODO: NOT IMPLEMENTED
+                UNIMPLEMENTED;
+
+                break;
+            }
+
+            /* FYL2X */
+            case 0x31:
+            {
+                // TODO: NOT IMPLEMENTED
+                UNIMPLEMENTED;
+
+                break;
+            }
+
+            /* FPTAN */
+            case 0x32:
+            {
+                // TODO: NOT IMPLEMENTED
+                UNIMPLEMENTED;
+
+                break;
+            }
+
+            /* FPATAN */
+            case 0x33:
+            {
+                // TODO: NOT IMPLEMENTED
+                UNIMPLEMENTED;
+
+                break;
+            }
+
+            /* FXTRACT */
+            case 0x34:
+            {
+                FAST486_FPU_DATA_REG Value = FPU_ST(0);
+
+                if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY) || FPU_IS_INDEFINITE(&Value))
+                {
+                    State->FpuStatus.Ie = TRUE;
+                    if (FPU_GET_TAG(0) == FPU_TAG_EMPTY) State->FpuStatus.Sf = TRUE;
+
+                    if (!State->FpuControl.Im) Fast486FpuException(State);
+                    break;
+                }
+
+                if (FPU_IS_ZERO(&Value))
+                {
+                    /* The exponent of zero is negative infinity */
+                    FPU_ST(0).Sign = TRUE;
+                    FPU_ST(0).Exponent = FPU_MAX_EXPONENT + 1;
+                    FPU_ST(0).Mantissa = FPU_MANTISSA_HIGH_BIT;
+                }
+                else if (FPU_IS_INFINITY(&Value))
+                {
+                    /* The exponent of infinity is positive infinity */
+                    FPU_ST(0).Sign = FALSE;
+                    FPU_ST(0).Exponent = FPU_MAX_EXPONENT + 1;
+                    FPU_ST(0).Mantissa = FPU_MANTISSA_HIGH_BIT;
+                }
+                else
+                {
+                    /* Store the unbiased exponent in ST0 */
+                    Fast486FpuFromInteger(State,
+                                          (LONGLONG)Value.Exponent - (LONGLONG)FPU_REAL10_BIAS,
+                                          &FPU_ST(0));
+                }
+
+                /* Now push the mantissa as a real number, with the original sign */
+                Value.Exponent = FPU_REAL10_BIAS;
+                Fast486FpuPush(State, &Value);
+
+                break;
+            }
+
+            /* FPREM1 */
+            case 0x35:
+            {
+                // TODO: NOT IMPLEMENTED
+                UNIMPLEMENTED;
+
+                break;
+            }
+
+            /* FDECSTP */
+            case 0x36:
+            {
+                State->FpuStatus.Top--;
+                break;
+            }
+
+            /* FINCSTP */
+            case 0x37:
+            {
+                State->FpuStatus.Top++;
+                break;
+            }
+
+            /* FPREM */
+            case 0x38:
+            {
+                // TODO: NOT IMPLEMENTED
+                UNIMPLEMENTED;
+
+                break;
+            }
+
+            /* FYL2XP1 */
+            case 0x39:
+            {
+                // TODO: NOT IMPLEMENTED
+                UNIMPLEMENTED;
+
+                break;
+            }
+
+            /* FSQRT */
+            case 0x3A:
+            {
+                // TODO: NOT IMPLEMENTED
+                UNIMPLEMENTED;
+
+                break;
+            }
+
+            /* FSINCOS */
+            case 0x3B:
+            {
+                // TODO: NOT IMPLEMENTED
+                UNIMPLEMENTED;
+
+                break;
+            }
+
+            /* FRNDINT */
+            case 0x3C:
+            {
+                INT Bits;
+                ULONGLONG Result = 0ULL;
+                ULONGLONG Remainder;
+
+                if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+                {
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (!State->FpuControl.Im) Fast486FpuException(State);
+                    break;
+                }
+
+                if (!FPU_IS_NORMALIZED(&FPU_ST(0)))
+                {
+                    State->FpuStatus.De = TRUE;
+
+                    if (!State->FpuControl.Dm)
+                    {
+                        Fast486FpuException(State);
+                        break;
+                    }
+                }
+
+                Bits = min(max(0, (INT)FPU_ST(0).Exponent - FPU_REAL10_BIAS + 1), 64);
+                if (Bits == 64) break;
+
+                if (Bits)
+                {
+                    Result = FPU_ST(0).Mantissa >> (64 - Bits);
+                    Remainder = FPU_ST(0).Mantissa & ((1 << (64 - Bits)) - 1);
+                }
+                else Remainder = FPU_ST(0).Mantissa;
+
+                /* Perform the rounding */
+                Fast486FpuRound(State, &Result, FPU_ST(0).Sign, Remainder, 63 - Bits);
+
+                State->FpuStatus.Pe = TRUE;
+                if (!State->FpuControl.Pm) Fast486FpuException(State);
+
+                break;
+            }
+
+            /* FSCALE */
+            case 0x3D:
+            {
+                // TODO: NOT IMPLEMENTED
+                UNIMPLEMENTED;
+
+                break;
+            }
+
+            /* FSIN */
+            case 0x3E:
+            {
+                // TODO: NOT IMPLEMENTED
+                UNIMPLEMENTED;
+
+                break;
+            }
+
+            /* FCOS */
+            case 0x3F:
+            {
+                // TODO: NOT IMPLEMENTED
+                UNIMPLEMENTED;
+
+                break;
+            }
+
+            /* Invalid */
+            default:
+            {
+                Fast486Exception(State, FAST486_EXCEPTION_UD);
+                return;
+            }
+        }
+    }
+
+#endif
+}
+
+FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA)
+{
+    FAST486_MOD_REG_RM ModRegRm;
+    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+    PFAST486_FPU_DATA_REG SourceOperand, DestOperand;
+    LONG Value;
+    FAST486_FPU_DATA_REG MemoryData;
+
+    TOGGLE_ADSIZE(AddressSize);
+
+    /* Get the operands */
+    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
+    {
+        /* Exception occurred */
+        return;
+    }
+
+    FPU_CHECK();
+
+#ifndef FAST486_NO_FPU
+
+    FPU_SAVE_LAST_INST();
+
+    if (!ModRegRm.Memory)
+    {
+        /* The only valid opcode in this case is FUCOMPP (0xDA 0xE9) */
+        if ((ModRegRm.Register != 5) && (ModRegRm.SecondRegister != 1))
+        {
+            Fast486Exception(State, FAST486_EXCEPTION_UD);
+            return;
+        }
+
+        if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY) || (FPU_GET_TAG(1) == FPU_TAG_EMPTY))
+        {
+            /* Raise the invalid operation exception*/
+            State->FpuStatus.Ie = TRUE;
+
+            if (!State->FpuControl.Im) Fast486FpuException(State);
+            return;
+        }
+
+        /* Compare */
+        Fast486FpuCompare(State, &FPU_ST(0), &FPU_ST(1));
+
+        /* Pop twice */
+        Fast486FpuPop(State);
+        Fast486FpuPop(State);
+
+        return;
+    }
+
+    FPU_SAVE_LAST_OPERAND();
+
+    /* Load the source operand from memory */
+    if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, (PULONG)&Value))
+    {
+        /* Exception occurred */
+        return;
+    }
+
+    /* The destination operand is always ST0 */
+    DestOperand = &FPU_ST(0);
+
+    if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+    {
+        /* Raise the invalid operation exception */
+        State->FpuStatus.Ie = TRUE;
+
+        if (State->FpuControl.Im)
+        {
+            /* Return the indefinite NaN */
+            DestOperand->Sign = TRUE;
+            DestOperand->Exponent = FPU_MAX_EXPONENT + 1;
+            DestOperand->Mantissa = FPU_INDEFINITE_MANTISSA;
+
+            FPU_SET_TAG(0, FPU_TAG_SPECIAL);
+        }
+        else Fast486FpuException(State);
+
+        return;
+    }
+
+    Fast486FpuFromInteger(State, (LONGLONG)Value, &MemoryData);
+    SourceOperand = &MemoryData;
+
+    /* Perform the requested operation */
+    Fast486FpuArithmeticOperation(State, ModRegRm.Register, SourceOperand, DestOperand);
+
+#endif
+}
+
+FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB)
+{
+    FAST486_MOD_REG_RM ModRegRm;
+    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+
+    TOGGLE_ADSIZE(AddressSize);
+
+    /* Get the operands */
+    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
+    {
+        /* Exception occurred */
+        return;
+    }
+
+    FPU_CHECK();
+
+#ifndef FAST486_NO_FPU
+
+    if (ModRegRm.Memory)
+    {
+        FPU_SAVE_LAST_INST();
+        FPU_SAVE_LAST_OPERAND();
+
+        switch (ModRegRm.Register)
+        {
+            /* FILD */
+            case 0:
+            {
+                LONG Value;
+                FAST486_FPU_DATA_REG Temp;
+
+                if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, (PULONG)&Value))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                Fast486FpuFromInteger(State, (LONGLONG)Value, &Temp);
+                Fast486FpuPush(State, &Temp);
+
+                break;
+            }
+
+            /* FIST */
+            case 2:
+            /* FISTP */
+            case 3:
+            {
+                LONGLONG Temp = 0;
+
+                if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY) || (FPU_GET_TAG(0) == FPU_TAG_SPECIAL))
+                {
+                    /* Raise the invalid operation exception */
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (!State->FpuControl.Im)
+                    {
+                        Fast486FpuException(State);
+                        return;
+                    }
+                }
+                else if (!Fast486FpuToInteger(State, &FPU_ST(0), &Temp))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                /* Check if it can fit in a signed 32-bit integer */
+                if ((((ULONGLONG)Temp >> 31) + 1ULL) > 1ULL)
+                {
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (State->FpuControl.Im) Temp = 0LL;
+                    else
+                    {
+                        Fast486FpuException(State);
+                        return;
+                    }
+                }
+
+                if (!Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, (ULONG)((LONG)Temp)))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                if (ModRegRm.Register == 3)
+                {
+                    /* Pop the FPU stack too */
+                    Fast486FpuPop(State);
+                }
+
+                break;
+            }
+
+            /* FLD */
+            case 5:
+            {
+                FAST486_FPU_DATA_REG Value;
+                UCHAR Buffer[10];
+
+                if (!Fast486ReadMemory(State,
+                                       (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                       ? State->SegmentOverride : FAST486_REG_DS,
+                                       ModRegRm.MemoryAddress,
+                                       FALSE,
+                                       Buffer,
+                                       sizeof(Buffer)))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                Value.Mantissa = *((PULONGLONG)Buffer);
+                Value.Exponent = *((PUSHORT)&Buffer[8]) & (FPU_MAX_EXPONENT + 1);
+                Value.Sign = *((PUCHAR)&Buffer[9]) >> 7;
+
+                Fast486FpuPush(State, &Value);
+                break;
+            }
+
+            /* FSTP */
+            case 7:
+            {
+                UCHAR Buffer[10];
+
+                if (FPU_GET_TAG(0) != FPU_TAG_EMPTY)
+                {
+                    *((PULONGLONG)Buffer) = FPU_ST(0).Mantissa;
+                    *((PUSHORT)&Buffer[sizeof(ULONGLONG)]) = FPU_ST(0).Exponent
+                                                             | (FPU_ST(0).Sign ? 0x8000 : 0);
+                }
+                else
+                {
+                    /* Raise the invalid operation exception */
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (State->FpuControl.Im)
+                    {
+                        *((PULONGLONG)Buffer) = FPU_INDEFINITE_MANTISSA;
+                        *((PUSHORT)&Buffer[sizeof(ULONGLONG)]) = 0x8000 | (FPU_MAX_EXPONENT + 1);
+                    }
+                    else
+                    {
+                        Fast486FpuException(State);
+                        return;
+                    }
+                }
+
+                if (!Fast486WriteMemory(State,
+                                        (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                        ? State->SegmentOverride : FAST486_REG_DS,
+                                        ModRegRm.MemoryAddress,
+                                        Buffer,
+                                        sizeof(Buffer)))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                Fast486FpuPop(State);
+                break;
+            }
+
+            /* Invalid */
+            default:
+            {
+                Fast486Exception(State, FAST486_EXCEPTION_UD);
+                return;
+            }
+        }
+    }
+    else
+    {
+        /* Only a few of these instructions have any meaning on a 487 */
+        switch ((ModRegRm.Register << 3) | ModRegRm.SecondRegister)
+        {
+            /* FCLEX */
+            case 0x22:
+            {
+                /* Clear exception data */
+                State->FpuStatus.Ie =
+                State->FpuStatus.De =
+                State->FpuStatus.Ze =
+                State->FpuStatus.Oe =
+                State->FpuStatus.Ue =
+                State->FpuStatus.Pe =
+                State->FpuStatus.Sf =
+                State->FpuStatus.Es =
+                State->FpuStatus.Busy = FALSE;
+
+                break;
+            }
+
+            /* FINIT */
+            case 0x23:
+            {
+                /* Restore the state */
+                State->FpuControl.Value = FAST486_FPU_DEFAULT_CONTROL;
+                State->FpuStatus.Value = 0;
+                State->FpuTag = 0xFFFF;
+
+                break;
+            }
+
+            /* FENI */
+            case 0x20:
+            /* FDISI */
+            case 0x21:
+            /* FSETPM */
+            case 0x24:
+            /* FRSTPM */
+            case 0x25:
+            {
+                /* These do nothing */
+                break;
+            }
+
+            /* Invalid */
+            default:
+            {
+                Fast486Exception(State, FAST486_EXCEPTION_UD);
+                return;
+            }
+        }
+    }
+
+#endif
+}
+
+FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDC)
+{
+    FAST486_MOD_REG_RM ModRegRm;
+    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+    PFAST486_FPU_DATA_REG SourceOperand, DestOperand;
+    FAST486_FPU_DATA_REG MemoryData;
+
+    TOGGLE_ADSIZE(AddressSize);
+
+    /* Get the operands */
+    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
+    {
+        /* Exception occurred */
+        return;
+    }
+
+    FPU_CHECK();
+
+#ifndef FAST486_NO_FPU
+
+    FPU_SAVE_LAST_INST();
+
+    if (ModRegRm.Memory)
+    {
+        ULONGLONG Value;
+
+        /* The destination operand is ST0 */
+        DestOperand = &FPU_ST(0);
+
+        if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+        {
+            /* Raise the invalid operation exception */
+            State->FpuStatus.Ie = TRUE;
+
+            if (State->FpuControl.Im)
+            {
+                /* Return the indefinite NaN */
+                DestOperand->Sign = TRUE;
+                DestOperand->Exponent = FPU_MAX_EXPONENT + 1;
+                DestOperand->Mantissa = FPU_INDEFINITE_MANTISSA;
+
+                FPU_SET_TAG(0, FPU_TAG_SPECIAL);
+            }
+            else Fast486FpuException(State);
+
+            return;
+        }
+
+        /* Load the source operand from memory */
+        if (!Fast486ReadMemory(State,
+                               (State->PrefixFlags & FAST486_PREFIX_SEG)
+                               ? State->SegmentOverride : FAST486_REG_DS,
+                               ModRegRm.MemoryAddress,
+                               FALSE,
+                               &Value,
+                               sizeof(ULONGLONG)))
+        {
+            /* Exception occurred */
+            return;
+        }
+
+        Fast486FpuFromDoubleReal(State, Value, &MemoryData);
+        SourceOperand = &MemoryData;
+
+        FPU_SAVE_LAST_OPERAND();
+    }
+    else
+    {
+        /* The source operand is ST0 */
+        SourceOperand = &FPU_ST(0);
+
+        /* Load the destination operand from an FPU register */
+        DestOperand = &FPU_ST(ModRegRm.SecondRegister);
+
+        if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+            || (FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY))
+        {
+            /* Raise the invalid operation exception */
+            State->FpuStatus.Ie = TRUE;
+
+            if (State->FpuControl.Im)
+            {
+                /* Return the indefinite NaN */
+                DestOperand->Sign = TRUE;
+                DestOperand->Exponent = FPU_MAX_EXPONENT + 1;
+                DestOperand->Mantissa = FPU_INDEFINITE_MANTISSA;
+
+                FPU_SET_TAG(ModRegRm.SecondRegister, FPU_TAG_SPECIAL);
+            }
+            else Fast486FpuException(State);
+
+            return;
+        }
+    }
+
+    /* Perform the requested operation */
+    Fast486FpuArithmeticOperation(State, ModRegRm.Register, SourceOperand, DestOperand);
+
+#endif
+}
+
+FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD)
+{
+    FAST486_MOD_REG_RM ModRegRm;
+    BOOLEAN OperandSize, AddressSize;
+
+    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+    TOGGLE_OPSIZE(OperandSize);
+    TOGGLE_ADSIZE(AddressSize);
+
+    /* Get the operands */
+    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
+    {
+        /* Exception occurred */
+        return;
+    }
+
+    FPU_CHECK();
+
+#ifndef FAST486_NO_FPU
+
+    if (ModRegRm.Memory)
+    {
+        switch (ModRegRm.Register)
+        {
+            /* FLD */
+            case 0:
+            {
+                ULONGLONG Value;
+                FAST486_FPU_DATA_REG MemoryData;
+
+                FPU_SAVE_LAST_INST();
+                FPU_SAVE_LAST_OPERAND();
+
+                if (!Fast486ReadMemory(State,
+                                       (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                       ? State->SegmentOverride : FAST486_REG_DS,
+                                       ModRegRm.MemoryAddress,
+                                       FALSE,
+                                       &Value,
+                                       sizeof(ULONGLONG)))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                Fast486FpuFromDoubleReal(State, Value, &MemoryData);
+                Fast486FpuPush(State, &MemoryData);
+
+                break;
+            }
+
+            /* FST */
+            case 2:
+            /* FSTP */
+            case 3:
+            {
+                ULONGLONG Value = FPU_REAL8_INDEFINITE;
+
+                FPU_SAVE_LAST_INST();
+                FPU_SAVE_LAST_OPERAND();
+
+                if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+                {
+                    /* Raise the invalid operation exception */
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (!State->FpuControl.Im)
+                    {
+                        Fast486FpuException(State);
+                        return;
+                    }
+                }
+                else if (!Fast486FpuToDoubleReal(State, &FPU_ST(0), &Value))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                if (!Fast486WriteMemory(State,
+                                        (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                        ? State->SegmentOverride : FAST486_REG_DS,
+                                        ModRegRm.MemoryAddress,
+                                        &Value,
+                                        sizeof(ULONGLONG)))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                if (ModRegRm.Register == 3) Fast486FpuPop(State);
+                break;
+            }
+
+            /* FRSTOR */
+            case 4:
+            {
+                INT i;
+                UCHAR AllRegs[80];
+
+                /* Save the environment */
+                if (!Fast486FpuLoadEnvironment(State,
+                                               (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                               ? FAST486_REG_DS : State->SegmentOverride,
+                                               ModRegRm.MemoryAddress,
+                                               OperandSize))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                /* Load the registers */
+                if (!Fast486ReadMemory(State,
+                                       (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                       ? FAST486_REG_DS : State->SegmentOverride,
+                                       ModRegRm.MemoryAddress + (OperandSize + 1) * 14,
+                                       FALSE,
+                                       AllRegs,
+                                       sizeof(AllRegs)))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                for (i = 0; i < FAST486_NUM_FPU_REGS; i++)
+                {
+                    State->FpuRegisters[i].Mantissa = *((PULONGLONG)&AllRegs[i * 10]);
+                    State->FpuRegisters[i].Exponent = *((PUSHORT)&AllRegs[(i * 10) + sizeof(ULONGLONG)]) & 0x7FFF;
+
+                    if (*((PUSHORT)&AllRegs[(i * 10) + sizeof(ULONGLONG)]) & 0x8000)
+                    {
+                        State->FpuRegisters[i].Sign = TRUE;
+                    }
+                    else
+                    {
+                        State->FpuRegisters[i].Sign = FALSE;
+                    }
+                }
+
+                break;
+            }
+
+            /* FSAVE */
+            case 6:
+            {
+                INT i;
+                UCHAR AllRegs[80];
+
+                /* Save the environment */
+                if (!Fast486FpuSaveEnvironment(State,
+                                               (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                               ? FAST486_REG_DS : State->SegmentOverride,
+                                               ModRegRm.MemoryAddress,
+                                               OperandSize))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                /* Save the registers */
+                for (i = 0; i < FAST486_NUM_FPU_REGS; i++)
+                {
+                    *((PULONGLONG)&AllRegs[i * 10]) = State->FpuRegisters[i].Mantissa;
+                    *((PUSHORT)&AllRegs[(i * 10) + sizeof(ULONGLONG)]) = State->FpuRegisters[i].Exponent;
+
+                    if (State->FpuRegisters[i].Sign)
+                    {
+                        *((PUSHORT)&AllRegs[(i * 10) + sizeof(ULONGLONG)]) |= 0x8000;
+                    }
+                }
+
+                Fast486WriteMemory(State,
+                                   (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                   ? FAST486_REG_DS : State->SegmentOverride,
+                                   ModRegRm.MemoryAddress + (OperandSize + 1) * 14,
+                                   AllRegs,
+                                   sizeof(AllRegs));
+
+                break;
+            }
+
+            /* FSTSW */
+            case 7:
+            {
+                Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, State->FpuStatus.Value);
+                break;
+            }
+
+            /* Invalid */
+            default:
+            {
+                Fast486Exception(State, FAST486_EXCEPTION_UD);
+            }
+        }
+    }
+    else
+    {
+        FPU_SAVE_LAST_INST();
+
+        switch (ModRegRm.Register)
+        {
+            /* FFREE */
+            case 0:
+            {
+                FPU_SET_TAG(ModRegRm.SecondRegister, FPU_TAG_EMPTY);
+                break;
+            }
+
+            /* FXCH */
+            case 1:
+            {
+                FAST486_FPU_DATA_REG Temp;
+
+                if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+                    || FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY)
+                {
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (!State->FpuControl.Im) Fast486FpuException(State);
+                    break;
+                }
+
+                /* Exchange */
+                Temp = FPU_ST(0);
+                FPU_ST(0) = FPU_ST(ModRegRm.SecondRegister);
+                FPU_ST(ModRegRm.SecondRegister) = Temp;
+
+                FPU_UPDATE_TAG(0);
+                FPU_UPDATE_TAG(ModRegRm.SecondRegister);
+
+                break;
+            }
+
+            /* FST */
+            case 2:
+            /* FSTP */
+            case 3:
+            {
+                FPU_ST(ModRegRm.SecondRegister) = FPU_ST(0);
+                FPU_UPDATE_TAG(ModRegRm.SecondRegister);
+
+                if (ModRegRm.Register == 3) Fast486FpuPop(State);
+                break;
+            }
+
+            /* FUCOM */
+            case 4:
+            /* FUCOMP */
+            case 5:
+            {
+                if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+                    || (FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY))
+                {
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (!State->FpuControl.Im) Fast486FpuException(State);
+                    return;
+                }
+
+                Fast486FpuCompare(State, &FPU_ST(0), &FPU_ST(ModRegRm.SecondRegister));
+                if (ModRegRm.Register == 5) Fast486FpuPop(State);
+
+                break;
+            }
+
+            /* Invalid */
+            default:
+            {
+                Fast486Exception(State, FAST486_EXCEPTION_UD);
+            }
+        }
+    }
+
+#endif
+}
+
+FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDE)
+{
+    FAST486_MOD_REG_RM ModRegRm;
+    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+    PFAST486_FPU_DATA_REG SourceOperand, DestOperand;
+
+    TOGGLE_ADSIZE(AddressSize);
+
+    /* Get the operands */
+    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
+    {
+        /* Exception occurred */
+        return;
+    }
+
+    FPU_CHECK();
+
+#ifndef FAST486_NO_FPU
+
+    FPU_SAVE_LAST_INST();
+
+    if (ModRegRm.Memory)
+    {
+        SHORT Value;
+        FAST486_FPU_DATA_REG MemoryData;
+
+        /* The destination operand is ST0 */
+        DestOperand = &FPU_ST(0);
+
+        if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+        {
+            /* Raise the invalid operation exception */
+            State->FpuStatus.Ie = TRUE;
+
+            if (State->FpuControl.Im)
+            {
+                /* Return the indefinite NaN */
+                DestOperand->Sign = TRUE;
+                DestOperand->Exponent = FPU_MAX_EXPONENT + 1;
+                DestOperand->Mantissa = FPU_INDEFINITE_MANTISSA;
+
+                FPU_SET_TAG(0, FPU_TAG_SPECIAL);
+            }
+            else Fast486FpuException(State);
+
+            return;
+        }
+
+        /* Load the source operand from memory */
+        if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, (PUSHORT)&Value))
+        {
+            /* Exception occurred */
+            return;
+        }
+
+        Fast486FpuFromInteger(State, (LONGLONG)Value, &MemoryData);
+        SourceOperand = &MemoryData;
+
+        FPU_SAVE_LAST_OPERAND();
+    }
+    else
+    {
+        /* FCOMPP check */
+        if ((ModRegRm.Register == 3) && (ModRegRm.SecondRegister != 1))
+        {
+            /* Invalid */
+            Fast486Exception(State, FAST486_EXCEPTION_UD);
+            return;
+        }
+
+        /* The source operand is ST0 */
+        SourceOperand = &FPU_ST(0);
+
+        /* Load the destination operand from a register */
+        DestOperand = &FPU_ST(ModRegRm.SecondRegister);
+
+        if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+            || (FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY))
+        {
+            /* Raise the invalid operation exception, if unmasked */
+            State->FpuStatus.Ie = TRUE;
+
+            if (!State->FpuControl.Im) Fast486FpuException(State);
+            return;
+        }
+    }
+
+    /* Perform the requested operation */
+    Fast486FpuArithmeticOperation(State, ModRegRm.Register, SourceOperand, DestOperand);
+    if (!ModRegRm.Memory) Fast486FpuPop(State);
+
+#endif
+}
+
+FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDF)
+{
+    FAST486_MOD_REG_RM ModRegRm;
+    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+
+    TOGGLE_ADSIZE(AddressSize);
+
+    /* Get the operands */
+    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
+    {
+        /* Exception occurred */
+        return;
+    }
+
+    FPU_CHECK();
+
+#ifndef FAST486_NO_FPU
+
+    FPU_SAVE_LAST_INST();
+
+    if (ModRegRm.Memory)
+    {
+        FPU_SAVE_LAST_OPERAND();
+
+        switch (ModRegRm.Register)
+        {
+            /* FILD */
+            case 0:
+            {
+                SHORT Value;
+                FAST486_FPU_DATA_REG Temp;
+
+                if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, (PUSHORT)&Value))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                Fast486FpuFromInteger(State, (LONGLONG)Value, &Temp);
+                Fast486FpuPush(State, &Temp);
+
+                break;
+            }
+
+            /* FIST */
+            case 2:
+            /* FISTP */
+            case 3:
+            {
+                LONGLONG Temp = 0LL;
+
+                if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY) || (FPU_GET_TAG(0) == FPU_TAG_SPECIAL))
+                {
+                    /* Raise the invalid operation exception */
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (!State->FpuControl.Im)
+                    {
+                        Fast486FpuException(State);
+                        return;
+                    }
+                }
+                else if (!Fast486FpuToInteger(State, &FPU_ST(0), &Temp))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                /* Check if it can fit in a signed 16-bit integer */
+                if ((((ULONGLONG)Temp >> 15) + 1ULL) > 1ULL)
+                {
+                    /* Raise the invalid operation exception */
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (State->FpuControl.Im) Temp = 0LL;
+                    else
+                    {
+                        Fast486FpuException(State);
+                        return;
+                    }
+                }
+
+                if (!Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, (USHORT)((SHORT)Temp)))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                if (ModRegRm.Register == 3)
+                {
+                    /* Pop the FPU stack too */
+                    Fast486FpuPop(State);
+                }
+
+                break;
+            }
+
+            /* FBLD */
+            case 4:
+            {
+                FAST486_FPU_DATA_REG Value;
+                UCHAR Buffer[10];
+
+                if (!Fast486ReadMemory(State,
+                                       (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                       ? State->SegmentOverride : FAST486_REG_DS,
+                                       ModRegRm.MemoryAddress,
+                                       FALSE,
+                                       Buffer,
+                                       sizeof(Buffer)))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                Fast486FpuFromPackedBcd(State, Buffer, &Value);
+                Fast486FpuPush(State, &Value);
+
+                break;
+            }
+
+            /* FILD (64-bit int) */
+            case 5:
+            {
+                LONGLONG Value;
+                FAST486_FPU_DATA_REG Temp;
+
+                if (!Fast486ReadMemory(State,
+                                       (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                       ? State->SegmentOverride : FAST486_REG_DS,
+                                       ModRegRm.MemoryAddress,
+                                       FALSE,
+                                       &Value,
+                                       sizeof(LONGLONG)))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                Fast486FpuFromInteger(State, (LONGLONG)Value, &Temp);
+                Fast486FpuPush(State, &Temp);
+
+                break;
+            }
+
+            /* FBSTP */
+            case 6:
+            {
+                UCHAR Buffer[10] = {0};
+
+                if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+                {
+                    /* Raise the invalid operation exception */
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (!State->FpuControl.Im)
+                    {
+                        Fast486FpuException(State);
+                        return;
+                    }
+                }
+                else if (!Fast486FpuToPackedBcd(State, &FPU_ST(0), Buffer))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                if (!Fast486WriteMemory(State,
+                                        (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                        ? State->SegmentOverride : FAST486_REG_DS,
+                                        ModRegRm.MemoryAddress,
+                                        Buffer,
+                                        sizeof(Buffer)))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                Fast486FpuPop(State);
+                break;
+            }
+
+            /* FISTP (64-bit int) */
+            case 7:
+            {
+                LONGLONG Temp = 0LL;
+
+                if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY) || (FPU_GET_TAG(0) == FPU_TAG_SPECIAL))
+                {
+                    /* Raise the invalid operation exception */
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (!State->FpuControl.Im)
+                    {
+                        Fast486FpuException(State);
+                        return;
+                    }
+                }
+                else if (!Fast486FpuToInteger(State, &FPU_ST(0), &Temp))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                if (!Fast486WriteMemory(State,
+                                        (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                        ? State->SegmentOverride : FAST486_REG_DS,
+                                        ModRegRm.MemoryAddress,
+                                        &Temp,
+                                        sizeof(LONGLONG)))
+                {
+                    /* Exception occurred */
+                    return;
+                }
+
+                /* Pop the FPU stack too */
+                Fast486FpuPop(State);
+
+                break;
+            }
+
+            /* Invalid */
+            default:
+            {
+                Fast486Exception(State, FAST486_EXCEPTION_UD);
+            }
+        }
+    }
+    else
+    {
+        switch (ModRegRm.Register)
+        {
+            /* FFREEP */
+            case 0:
+            {
+                FPU_SET_TAG(ModRegRm.SecondRegister, FPU_TAG_EMPTY);
+                Fast486FpuPop(State);
+
+                break;
+            }
+
+            /* FXCH */
+            case 1:
+            {
+                FAST486_FPU_DATA_REG Temp;
+
+                if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+                    || FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY)
+                {
+                    State->FpuStatus.Ie = TRUE;
+
+                    if (!State->FpuControl.Im) Fast486FpuException(State);
+                    break;
+                }
+
+                /* Exchange */
+                Temp = FPU_ST(0);
+                FPU_ST(0) = FPU_ST(ModRegRm.SecondRegister);
+                FPU_ST(ModRegRm.SecondRegister) = Temp;
+
+                FPU_UPDATE_TAG(0);
+                FPU_UPDATE_TAG(ModRegRm.SecondRegister);
+
+                break;
+            }
+
+            /* FSTP */
+            case 2:
+            case 3:
+            {
+                FPU_ST(ModRegRm.SecondRegister) = FPU_ST(0);
+                FPU_UPDATE_TAG(ModRegRm.SecondRegister);
+                Fast486FpuPop(State);
+
+                break;
+            }
+
+            /* FSTSW */
+            case 4:
+            {
+                if (ModRegRm.SecondRegister != 0)
+                {
+                    /* Invalid */
+                    Fast486Exception(State, FAST486_EXCEPTION_UD);
+                    return;
+                }
+
+                /* Store the status word in AX */
+                State->GeneralRegs[FAST486_REG_EAX].LowWord = State->FpuStatus.Value;
+
+                break;
+            }
+
+            /* Invalid */
+            default:
+            {
+                Fast486Exception(State, FAST486_EXCEPTION_UD);
+                return;
+            }
+        }
+    }
 
-#ifndef FAST486_NO_FPU
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
-#else
-    /* Do nothing */
 #endif
 }