[FAST486]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 10 Oct 2014 23:07:14 +0000 (23:07 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 10 Oct 2014 23:07:14 +0000 (23:07 +0000)
Update the copyright year.
Implement the first batch of FPU instructions. These are still experimental.

svn path=/trunk/; revision=64656

13 files changed:
reactos/include/reactos/libs/fast486/fast486.h
reactos/lib/fast486/common.c
reactos/lib/fast486/common.h
reactos/lib/fast486/common.inl
reactos/lib/fast486/extraops.c
reactos/lib/fast486/extraops.h
reactos/lib/fast486/fast486.c
reactos/lib/fast486/fpu.c
reactos/lib/fast486/fpu.h
reactos/lib/fast486/opcodes.c
reactos/lib/fast486/opcodes.h
reactos/lib/fast486/opgroups.c
reactos/lib/fast486/opgroups.h

index 434f06c..efd4235 100644 (file)
@@ -2,7 +2,7 @@
  * Fast486 386/486 CPU Emulation Library
  * fast486.h
  *
  * Fast486 386/486 CPU Emulation Library
  * fast486.h
  *
- * Copyright (C) 2013 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -91,6 +91,8 @@
 #define FAST486_PREFIX_REPNZ    (1 << 4)
 #define FAST486_PREFIX_REP      (1 << 5)
 
 #define FAST486_PREFIX_REPNZ    (1 << 4)
 #define FAST486_PREFIX_REP      (1 << 5)
 
+#define FAST486_FPU_DEFAULT_CONTROL 0x037F
+
 struct _FAST486_STATE;
 typedef struct _FAST486_STATE FAST486_STATE, *PFAST486_STATE;
 
 struct _FAST486_STATE;
 typedef struct _FAST486_STATE FAST486_STATE, *PFAST486_STATE;
 
@@ -420,6 +422,7 @@ typedef struct _FAST486_FPU_DATA_REG
 {
     ULONGLONG Mantissa;
     USHORT Exponent;
 {
     ULONGLONG Mantissa;
     USHORT Exponent;
+    UCHAR Sign;
 } FAST486_FPU_DATA_REG, *PFAST486_FPU_DATA_REG;
 
 typedef union _FAST486_FPU_STATUS_REG
 } FAST486_FPU_DATA_REG, *PFAST486_FPU_DATA_REG;
 
 typedef union _FAST486_FPU_STATUS_REG
@@ -490,10 +493,12 @@ struct _FAST486_STATE
     FAST486_INT_STATUS IntStatus;
     UCHAR PendingIntNum;
     PULONG Tlb;
     FAST486_INT_STATUS IntStatus;
     UCHAR PendingIntNum;
     PULONG Tlb;
+#ifndef FAST486_NO_FPU
     FAST486_FPU_DATA_REG FpuRegisters[FAST486_NUM_FPU_REGS];
     FAST486_FPU_STATUS_REG FpuStatus;
     FAST486_FPU_CONTROL_REG FpuControl;
     USHORT FpuTag;
     FAST486_FPU_DATA_REG FpuRegisters[FAST486_NUM_FPU_REGS];
     FAST486_FPU_STATUS_REG FpuStatus;
     FAST486_FPU_CONTROL_REG FpuControl;
     USHORT FpuTag;
+#endif
 };
 
 /* FUNCTIONS ******************************************************************/
 };
 
 /* FUNCTIONS ******************************************************************/
index 2105457..bccd9b8 100644 (file)
@@ -2,7 +2,7 @@
  * Fast486 386/486 CPU Emulation Library
  * common.c
  *
  * Fast486 386/486 CPU Emulation Library
  * common.c
  *
- * Copyright (C) 2013 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
index 677bd19..764dbbc 100644 (file)
@@ -2,7 +2,7 @@
  * Fast486 386/486 CPU Emulation Library
  * common.h
  *
  * Fast486 386/486 CPU Emulation Library
  * common.h
  *
- * Copyright (C) 2013 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
index a558ed4..7a28f9d 100644 (file)
@@ -2,7 +2,7 @@
  * Fast486 386/486 CPU Emulation Library
  * common.inl
  *
  * Fast486 386/486 CPU Emulation Library
  * common.inl
  *
- * Copyright (C) 2013 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -20,6 +20,7 @@
  */
 
 #include "common.h"
  */
 
 #include "common.h"
+#include "fpu.h"
 
 /* PUBLIC FUNCTIONS ***********************************************************/
 
 
 /* PUBLIC FUNCTIONS ***********************************************************/
 
@@ -1330,4 +1331,76 @@ Fast486WriteModrmDwordOperands(PFAST486_STATE State,
     return TRUE;
 }
 
     return TRUE;
 }
 
+#ifndef FAST486_NO_FPU
+
+FORCEINLINE
+VOID
+Fast486FpuNormalize(PFAST486_STATE State, PFAST486_FPU_DATA_REG Data)
+{
+    UINT LeadingZeros = 0;
+
+    if (FPU_IS_NORMALIZED(Data)) return;
+    if (FPU_IS_ZERO(Data))
+    {
+        Data->Exponent = 0;
+        return;
+    }
+
+    /* Count the leading zeros */
+    while (!(Data->Mantissa & (1 << (63 - LeadingZeros)))) LeadingZeros++;
+
+    if (LeadingZeros < Data->Exponent)
+    {
+        Data->Mantissa <<= LeadingZeros;
+        Data->Exponent -= LeadingZeros;
+    }
+    else
+    {
+        /* Make it denormalized */
+        Data->Mantissa <<= Data->Exponent - 1;
+        Data->Exponent = 1;
+
+        /* Underflow */
+        State->FpuStatus.Ue = TRUE;
+    }
+}
+
+FORCEINLINE
+USHORT
+Fast486GetValueTag(PFAST486_FPU_DATA_REG Data)
+{
+    if (FPU_IS_ZERO(Data)) return FPU_TAG_ZERO;
+    else if (FPU_IS_NAN(Data)) return FPU_TAG_SPECIAL;
+    else return FPU_TAG_VALID;
+}
+
+FORCEINLINE
+VOID
+Fast486FpuPush(PFAST486_STATE State,
+               PFAST486_FPU_DATA_REG Data)
+{
+    State->FpuStatus.Top--;
+
+    if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+    {
+        FPU_ST(0) = *Data;
+        FPU_SET_TAG(0, Fast486GetValueTag(Data));
+    }
+    else State->FpuStatus.Ie = TRUE;
+}
+
+FORCEINLINE
+VOID
+Fast486FpuPop(PFAST486_STATE State)
+{
+    if (FPU_GET_TAG(0) != FPU_TAG_EMPTY)
+    {
+        FPU_SET_TAG(0, FPU_TAG_EMPTY);
+        State->FpuStatus.Top++;
+    }
+    else State->FpuStatus.Ie = TRUE;
+}
+
+#endif
+
 /* EOF */
 /* EOF */
index f9fe386..7adac3b 100644 (file)
@@ -2,7 +2,7 @@
  * Fast486 386/486 CPU Emulation Library
  * extraops.c
  *
  * Fast486 386/486 CPU Emulation Library
  * extraops.c
  *
- * Copyright (C) 2013 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
index eab8e28..cc10e1d 100644 (file)
@@ -2,7 +2,7 @@
  * Fast486 386/486 CPU Emulation Library
  * extraops.h
  *
  * Fast486 386/486 CPU Emulation Library
  * extraops.h
  *
- * Copyright (C) 2013 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
index ce80a8c..a854a47 100644 (file)
@@ -2,7 +2,7 @@
  * Fast486 386/486 CPU Emulation Library
  * fast486.c
  *
  * Fast486 386/486 CPU Emulation Library
  * fast486.c
  *
- * Copyright (C) 2013 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -282,6 +282,11 @@ Fast486Reset(PFAST486_STATE State)
 #ifndef FAST486_NO_FPU
     /* Initialize CR0 */
     State->ControlRegisters[FAST486_REG_CR0] |= FAST486_CR0_ET;
 #ifndef FAST486_NO_FPU
     /* Initialize CR0 */
     State->ControlRegisters[FAST486_REG_CR0] |= FAST486_CR0_ET;
+
+    /* Initialize the FPU control and tag registers */
+    State->FpuControl.Value = FAST486_FPU_DEFAULT_CONTROL;
+    State->FpuStatus.Value = 0;
+    State->FpuTag = 0xFFFF;
 #endif
 
     /* Restore the callbacks and TLB */
 #endif
 
     /* Restore the callbacks and TLB */
index 18f4a89..26924a3 100644 (file)
@@ -2,7 +2,7 @@
  * Fast486 386/486 CPU Emulation Library
  * fpu.c
  *
  * Fast486 386/486 CPU Emulation Library
  * fpu.c
  *
- * Copyright (C) 2013 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 
 #include <fast486.h>
 #include "common.h"
 
 #include <fast486.h>
 #include "common.h"
-#include "opcodes.h"
 #include "fpu.h"
 
 #include "fpu.h"
 
-/* PUBLIC FUNCTIONS ***********************************************************/
+/* PRIVATE FUNCTIONS **********************************************************/
 
 
-FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8)
+static ULONGLONG UnsignedMult128(ULONGLONG Multiplicand,
+                                 ULONGLONG Multiplier,
+                                 ULONGLONG *HighProduct)
 {
 {
-    FAST486_MOD_REG_RM ModRegRm;
-    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+    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);
+    MultiplierHigh = (ULONG)(Multiplier >> 32);
+
+    LowProduct = (ULONGLONG)MultiplicandLow * (ULONGLONG)MultiplierLow;
+    Intermediate1 = (ULONGLONG)MultiplicandLow * (ULONGLONG)MultiplierHigh;
+    Intermediate2 = (ULONGLONG)MultiplicandHigh * (ULONGLONG)MultiplierLow;
+    *HighProduct = (ULONGLONG)MultiplicandHigh * (ULONGLONG)MultiplierHigh;
+
+    Intermediate = Intermediate1 + Intermediate2;
+    if (Intermediate < Intermediate1) *HighProduct += 1ULL << 32;
+
+    IntermediateLow = (ULONG)(Intermediate & 0xFFFFFFFFULL);
+    IntermediateHigh = (ULONG)(Intermediate >> 32);
+
+    LowProduct += (ULONGLONG)IntermediateLow << 32;
+    if ((ULONG)(LowProduct >> 32) < IntermediateLow) (*HighProduct)++;
+
+    *HighProduct += IntermediateHigh;
+    return LowProduct;
+}
 
 
-    /* Get the operands */
-    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
+static VOID Fast486FpuGetSingleReal(PFAST486_STATE State,
+                                    ULONG Value,
+                                    PFAST486_FPU_DATA_REG Result)
+{
+    /* Extract the sign, exponent and mantissa */
+    Result->Sign = (UCHAR)(Value >> 31);
+    Result->Exponent = (USHORT)((Value >> 23) & 0xFF);
+    Result->Mantissa = (((ULONGLONG)Value & 0x7FFFFFULL) | 0x800000ULL) << 40;
+
+    /* If this is a zero, we're done */
+    if (Value == 0) return;
+
+    if (Result->Exponent == 0xFF) Result->Exponent = FPU_MAX_EXPONENT + 1;
+    else
     {
     {
-        /* Exception occurred */
-        return FALSE;
+        /* Adjust the exponent bias */
+        Result->Exponent += (FPU_REAL10_BIAS - FPU_REAL4_BIAS);
     }
     }
+}
 
 
-    FPU_CHECK();
+static VOID Fast486FpuGetDoubleReal(PFAST486_STATE State,
+                                    ULONGLONG Value,
+                                    PFAST486_FPU_DATA_REG Result)
+{
+    /* Extract the sign, exponent and mantissa */
+    Result->Sign = (UCHAR)(Value >> 63);
+    Result->Exponent = (USHORT)((Value >> 52) & 0x7FF);
+    Result->Mantissa = (((ULONGLONG)Value & 0xFFFFFFFFFFFFFULL) | 0x10000000000000ULL) << 11;
+
+    /* If this is a zero, we're done */
+    if (Value == 0) return;
+
+    if (Result->Exponent == 0x3FF) Result->Exponent = FPU_MAX_EXPONENT + 1;
+    else
+    {
+        /* Adjust the exponent bias */
+        Result->Exponent += (FPU_REAL10_BIAS - FPU_REAL8_BIAS);
+    }
+}
+
+static VOID Fast486FpuAdd(PFAST486_STATE State,
+                          PFAST486_FPU_DATA_REG FirstOperand,
+                          PFAST486_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))
+    {
+        /* Denormalized */
+        State->FpuStatus.De = TRUE;
+    }
+
+    /* Find the largest exponent */
+    TempResult.Exponent = max(FirstOperand->Exponent, SecondOperand->Exponent);
+
+    /* Adjust the first operand to it */
+    if (FirstAdjusted.Exponent < TempResult.Exponent)
+    {
+        FirstAdjusted.Mantissa >>= (TempResult.Exponent - FirstAdjusted.Exponent);
+        FirstAdjusted.Exponent = TempResult.Exponent;
+    }
+
+    /* ... and the second one too */
+    if (SecondAdjusted.Exponent < TempResult.Exponent)
+    {
+        SecondAdjusted.Mantissa >>= (TempResult.Exponent - SecondAdjusted.Exponent);
+        SecondAdjusted.Exponent = TempResult.Exponent;
+    }
+
+    if (FirstAdjusted.Sign == SecondAdjusted.Sign)
+    {
+        /* Calculate the mantissa and sign of the result */
+        TempResult.Mantissa = FirstAdjusted.Mantissa + SecondAdjusted.Mantissa;
+        TempResult.Sign = FirstAdjusted.Sign;
+    }
+    else
+    {
+        /* Calculate the sign of the result */
+        if (FirstAdjusted.Mantissa > SecondAdjusted.Mantissa) TempResult.Sign = FirstAdjusted.Sign;
+        else if (FirstAdjusted.Mantissa < SecondAdjusted.Mantissa) TempResult.Sign = SecondAdjusted.Sign;
+        else TempResult.Sign = FALSE;
+
+        /* Invert the negative mantissa */
+        if (FirstAdjusted.Sign) FirstAdjusted.Mantissa = -FirstAdjusted.Mantissa;
+        if (SecondAdjusted.Sign) SecondAdjusted.Mantissa = -SecondAdjusted.Mantissa;
+
+        /* Calculate the mantissa of the result */
+        TempResult.Mantissa = FirstAdjusted.Mantissa + SecondAdjusted.Mantissa;
+    }
+
+    /* Did it overflow? */
+    if (FPU_IS_NORMALIZED(&FirstAdjusted) && FPU_IS_NORMALIZED(&SecondAdjusted))
+    {
+        if (TempResult.Exponent == FPU_MAX_EXPONENT)
+        {
+            /* Total overflow, return infinity */
+            TempResult.Mantissa = FPU_MANTISSA_HIGH_BIT;
+            TempResult.Exponent = FPU_MAX_EXPONENT + 1;
+
+            /* Update flags */
+            State->FpuStatus.Oe = TRUE;
+        }
+        else
+        {
+            /* Lose the LSB in favor of the carry */
+            TempResult.Mantissa >>= 1;
+            TempResult.Mantissa |= FPU_MANTISSA_HIGH_BIT;
+            TempResult.Exponent++;
+        }
+    }
+    
+    /* Normalize the result and return it */
+    Fast486FpuNormalize(State, &TempResult);
+    *Result = TempResult;
+}
+
+static VOID Fast486FpuSubtract(PFAST486_STATE State,
+                               PFAST486_FPU_DATA_REG FirstOperand,
+                               PFAST486_FPU_DATA_REG SecondOperand,
+                               PFAST486_FPU_DATA_REG Result)
+{
+    FAST486_FPU_DATA_REG NegativeSecondOperand = *SecondOperand;
+
+    /* Invert the sign */
+    NegativeSecondOperand.Sign = !NegativeSecondOperand.Sign;
+
+    /* And perform an addition instead */
+    Fast486FpuAdd(State, Result, FirstOperand, &NegativeSecondOperand);
+}
+
+static VOID Fast486FpuCompare(PFAST486_STATE State,
+                              PFAST486_FPU_DATA_REG FirstOperand,
+                              PFAST486_FPU_DATA_REG SecondOperand)
+{
+    if (FPU_IS_NAN(FirstOperand) || FPU_IS_NAN(SecondOperand))
+    {
+        if (FPU_IS_POS_INF(FirstOperand) && FPU_IS_NEG_INF(SecondOperand))
+        {
+            State->FpuStatus.Code0 = FALSE;
+            State->FpuStatus.Code2 = FALSE;
+            State->FpuStatus.Code3 = FALSE;
+        }
+        else if (FPU_IS_NEG_INF(FirstOperand) && FPU_IS_POS_INF(SecondOperand))
+        {
+            State->FpuStatus.Code0 = TRUE;
+            State->FpuStatus.Code2 = FALSE;
+            State->FpuStatus.Code3 = FALSE;
+        }
+        else
+        {
+            State->FpuStatus.Code0 = TRUE;
+            State->FpuStatus.Code2 = TRUE;
+            State->FpuStatus.Code3 = TRUE;
+        }
+    }
+    else
+    {
+        FAST486_FPU_DATA_REG TempResult;
+
+        Fast486FpuSubtract(State, FirstOperand, SecondOperand, &TempResult);
+
+        if (FPU_IS_ZERO(&TempResult))
+        {
+            State->FpuStatus.Code0 = FALSE;
+            State->FpuStatus.Code2 = FALSE;
+            State->FpuStatus.Code3 = TRUE;
+        }
+        else if (TempResult.Sign)
+        {
+            State->FpuStatus.Code0 = TRUE;
+            State->FpuStatus.Code2 = FALSE;
+            State->FpuStatus.Code3 = FALSE;
+        }
+        else
+        {
+            State->FpuStatus.Code0 = FALSE;
+            State->FpuStatus.Code2 = FALSE;
+            State->FpuStatus.Code3 = FALSE;
+        }
+    }
+}
+
+static VOID Fast486FpuMultiply(PFAST486_STATE State,
+                               PFAST486_FPU_DATA_REG FirstOperand,
+                               PFAST486_FPU_DATA_REG SecondOperand,
+                               PFAST486_FPU_DATA_REG Result)
+{
+    FAST486_FPU_DATA_REG TempResult;
+
+    if (!FPU_IS_NORMALIZED(FirstOperand) || !FPU_IS_NORMALIZED(SecondOperand))
+    {
+        /* Denormalized */
+        State->FpuStatus.De = TRUE;
+    }
+
+    UnsignedMult128(FirstOperand->Mantissa,
+                    SecondOperand->Mantissa,
+                    &TempResult.Mantissa);
+
+    TempResult.Exponent = FirstOperand->Exponent + SecondOperand->Exponent;
+    TempResult.Sign = FirstOperand->Sign ^ SecondOperand->Sign;
+
+    /* Normalize the result */
+    Fast486FpuNormalize(State, &TempResult);
+    *Result = TempResult;
+}
+
+static VOID Fast486FpuDivide(PFAST486_STATE State,
+                             PFAST486_FPU_DATA_REG FirstOperand,
+                             PFAST486_FPU_DATA_REG SecondOperand,
+                             PFAST486_FPU_DATA_REG Result)
+{
+    FAST486_FPU_DATA_REG TempResult;
+
+    if (FPU_IS_ZERO(SecondOperand))
+    {
+        /* Division by zero */
+        State->FpuStatus.Ze = TRUE;
+        return;
+    }
+
+    TempResult.Exponent = FirstOperand->Exponent - SecondOperand->Exponent;
+    TempResult.Sign = FirstOperand->Sign ^ SecondOperand->Sign;
 
 
-#ifndef FAST486_NO_FPU
     // TODO: NOT IMPLEMENTED
     // TODO: NOT IMPLEMENTED
+    UNREFERENCED_PARAMETER(TempResult);
     UNIMPLEMENTED;
     UNIMPLEMENTED;
-
-    return FALSE;
-#else
-    /* Do nothing */
-    return TRUE;
-#endif
 }
 
 }
 
-FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9)
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8DC)
 {
     FAST486_MOD_REG_RM ModRegRm;
     BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
 {
     FAST486_MOD_REG_RM ModRegRm;
     BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
+    PFAST486_FPU_DATA_REG SourceOperand, DestOperand;
+    FAST486_FPU_DATA_REG MemoryData;
 
     /* Get the operands */
     if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
 
     /* Get the operands */
     if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
@@ -73,17 +314,130 @@ FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9)
     FPU_CHECK();
 
 #ifndef FAST486_NO_FPU
     FPU_CHECK();
 
 #ifndef FAST486_NO_FPU
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
 
 
-    return FALSE;
-#else
-    /* Do nothing */
-    return TRUE;
+    if (ModRegRm.Memory)
+    {
+        /* Load the source operand from memory */
+
+        if (Opcode == 0xDC)
+        {
+            ULONGLONG Value;
+
+            if (!Fast486ReadMemory(State,
+                                   (State->PrefixFlags & FAST486_PREFIX_SEG)
+                                   ? State->SegmentOverride : FAST486_REG_DS,
+                                   ModRegRm.MemoryAddress,
+                                   FALSE,
+                                   &Value,
+                                   sizeof(ULONGLONG)))
+            {
+                /* Exception occurred */
+                return FALSE;
+            }
+
+            Fast486FpuGetDoubleReal(State, Value, &MemoryData);
+        }
+        else
+        {
+            ULONG Value;
+
+            if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value))
+            {
+                /* Exception occurred */
+                return FALSE;
+            }
+
+            Fast486FpuGetSingleReal(State, Value, &MemoryData);
+        }
+
+        SourceOperand = &MemoryData;
+    }
+    else
+    {
+        /* Load the source operand from an FPU register */
+        SourceOperand = &FPU_ST(ModRegRm.SecondRegister);
+
+        if (FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY)
+        {
+            /* Invalid operation */
+            State->FpuStatus.Ie = TRUE;
+            return FALSE;
+        }
+    }
+
+    /* The destination operand is always ST0 */
+    DestOperand = &FPU_ST(0);
+
+    if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
+    {
+        /* Invalid operation */
+        State->FpuStatus.Ie = TRUE;
+        return FALSE;
+    }
+
+    /* Check the operation */
+    switch (ModRegRm.Register)
+    {
+        /* FADD */
+        case 0:
+        {
+            Fast486FpuAdd(State, DestOperand, SourceOperand, DestOperand);
+            break;
+        }
+
+        /* FMUL */
+        case 1:
+        {
+            Fast486FpuMultiply(State, DestOperand, SourceOperand, DestOperand);
+            break;
+        }
+
+        /* FCOM */
+        case 2:
+        /* FCOMP */
+        case 3:
+        {
+            Fast486FpuCompare(State, DestOperand, SourceOperand);
+            if (ModRegRm.Register == 3) Fast486FpuPop(State);
+
+            break;
+        }
+
+        /* FSUB */
+        case 4:
+        {
+            Fast486FpuSubtract(State, DestOperand, SourceOperand, DestOperand);
+            break;
+        }
+
+        /* FSUBR */
+        case 5:
+        {
+            Fast486FpuSubtract(State, SourceOperand, DestOperand, DestOperand);
+            break;
+        }
+
+        /* FDIV */
+        case 6:
+        {
+            Fast486FpuDivide(State, DestOperand, SourceOperand, DestOperand);
+            break;
+        }
+
+        /* FDIVR */
+        case 7:
+        {
+            Fast486FpuDivide(State, SourceOperand, DestOperand, DestOperand);
+            break;
+        }
+    }
+
 #endif
 #endif
+
+    return TRUE;
 }
 
 }
 
-FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA)
+FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9)
 {
     FAST486_MOD_REG_RM ModRegRm;
     BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
 {
     FAST486_MOD_REG_RM ModRegRm;
     BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
@@ -108,7 +462,7 @@ FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA)
 #endif
 }
 
 #endif
 }
 
-FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB)
+FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA)
 {
     FAST486_MOD_REG_RM ModRegRm;
     BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
 {
     FAST486_MOD_REG_RM ModRegRm;
     BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
@@ -133,7 +487,7 @@ FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB)
 #endif
 }
 
 #endif
 }
 
-FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDC)
+FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB)
 {
     FAST486_MOD_REG_RM ModRegRm;
     BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
 {
     FAST486_MOD_REG_RM ModRegRm;
     BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
@@ -148,14 +502,66 @@ FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDC)
     FPU_CHECK();
 
 #ifndef FAST486_NO_FPU
     FPU_CHECK();
 
 #ifndef FAST486_NO_FPU
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
 
 
-    return FALSE;
-#else
-    /* Do nothing */
-    return TRUE;
+    if (ModRegRm.Memory)
+    {
+        // TODO: NOT IMPLEMENTED
+        UNIMPLEMENTED;
+    }
+    else
+    {
+        /* Only a few of these instructions have any meaning on a 487 */
+        switch ((ModRegRm.SecondRegister << 3) | ModRegRm.Register)
+        {
+            /* 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:
+            {
+                /* These do nothing */
+                break;
+            }
+
+            /* Invalid */
+            default:
+            {
+                Fast486Exception(State, FAST486_EXCEPTION_UD);
+                return FALSE;
+            }
+        }
+    }
+
 #endif
 #endif
+
+    return TRUE;
 }
 
 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD)
 }
 
 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD)
index 63128a4..00c2921 100644 (file)
@@ -2,7 +2,7 @@
  * Fast486 386/486 CPU Emulation Library
  * fpu.h
  *
  * Fast486 386/486 CPU Emulation Library
  * fpu.h
  *
- * Copyright (C) 2013 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -24,6 +24,8 @@
 
 #pragma once
 
 
 #pragma once
 
+#include "opcodes.h"
+
 /* DEFINES ********************************************************************/
 
 #define FPU_CHECK() if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_EM) \
 /* DEFINES ********************************************************************/
 
 #define FPU_CHECK() if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_EM) \
                         return FALSE; \
                     }
 #define FPU_ST(i) State->FpuRegisters[(State->FpuStatus.Top + (i)) % FAST486_NUM_FPU_REGS]
                         return FALSE; \
                     }
 #define FPU_ST(i) State->FpuRegisters[(State->FpuStatus.Top + (i)) % FAST486_NUM_FPU_REGS]
+#define FPU_GET_TAG(i)  ((State->FpuTag >> ((i) * 2)) & 3)
+#define FPU_SET_TAG(i, t)   { \
+                                State->FpuTag &= ~((1 << ((i) * 2)) | (1 << (((i) * 2) + 1))); \
+                                State->FpuTag |= ((t) & 3) << ((i) * 2); \
+                            }
+
+#define FPU_REAL4_BIAS 0x7F
+#define FPU_REAL8_BIAS 0x3FF
+#define FPU_REAL10_BIAS 0x3FFF
+#define FPU_MAX_EXPONENT 0x7FFE
+#define FPU_MANTISSA_HIGH_BIT 0x8000000000000000ULL
+
+#define FPU_IS_NORMALIZED(x) (!FPU_IS_ZERO(x) && (((x)->Mantissa & FPU_MANTISSA_HIGH_BIT) != 0ULL))
+#define FPU_IS_ZERO(x) ((x)->Mantissa == 0ULL)
+#define FPU_IS_NAN(x) ((x)->Exponent == (FPU_MAX_EXPONENT + 1))
+#define FPU_IS_INFINITY(x) (FPU_IS_NAN(x) && (x)->Mantissa & FPU_MANTISSA_HIGH_BIT)
+#define FPU_IS_POS_INF(x) (FPU_IS_INFINITY(x) && !(x)->Sign)
+#define FPU_IS_NEG_INF(x) (FPU_IS_INFINITY(x) && (x)->Sign)
 
 enum
 {
 
 enum
 {
@@ -48,11 +68,10 @@ enum
     FPU_TAG_EMPTY = 3
 };
 
     FPU_TAG_EMPTY = 3
 };
 
-FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8);
+FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8DC);
 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9);
 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA);
 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB);
 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9);
 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA);
 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB);
-FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDC);
 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD);
 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDE);
 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDF);
 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD);
 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDE);
 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDF);
index dd3a0cc..c4934c4 100644 (file)
@@ -2,7 +2,7 @@
  * Fast486 386/486 CPU Emulation Library
  * opcodes.c
  *
  * Fast486 386/486 CPU Emulation Library
  * opcodes.c
  *
- * Copyright (C) 2013 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -254,11 +254,11 @@ Fast486OpcodeHandlers[FAST486_NUM_OPCODE_HANDLERS] =
     Fast486OpcodeAad,
     Fast486OpcodeSalc,
     Fast486OpcodeXlat,
     Fast486OpcodeAad,
     Fast486OpcodeSalc,
     Fast486OpcodeXlat,
-    Fast486FpuOpcodeD8,
+    Fast486FpuOpcodeD8DC,
     Fast486FpuOpcodeD9,
     Fast486FpuOpcodeDA,
     Fast486FpuOpcodeDB,
     Fast486FpuOpcodeD9,
     Fast486FpuOpcodeDA,
     Fast486FpuOpcodeDB,
-    Fast486FpuOpcodeDC,
+    Fast486FpuOpcodeD8DC,
     Fast486FpuOpcodeDD,
     Fast486FpuOpcodeDE,
     Fast486FpuOpcodeDF,
     Fast486FpuOpcodeDD,
     Fast486FpuOpcodeDE,
     Fast486FpuOpcodeDF,
index 3d29e47..f4a3727 100644 (file)
@@ -2,7 +2,7 @@
  * Fast486 386/486 CPU Emulation Library
  * opcodes.h
  *
  * Fast486 386/486 CPU Emulation Library
  * opcodes.h
  *
- * Copyright (C) 2013 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
index efcaa16..fa01acc 100644 (file)
@@ -2,7 +2,7 @@
  * Fast486 386/486 CPU Emulation Library
  * opgroups.c
  *
  * Fast486 386/486 CPU Emulation Library
  * opgroups.c
  *
- * Copyright (C) 2013 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
index bf55cce..1bfb091 100644 (file)
@@ -2,7 +2,7 @@
  * Fast486 386/486 CPU Emulation Library
  * opgroups.h
  *
  * Fast486 386/486 CPU Emulation Library
  * opgroups.h
  *
- * Copyright (C) 2013 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License