mirror of
https://github.com/reactos/reactos.git
synced 2025-04-21 12:40:33 +00:00
[FAST486]
Update the copyright year. Implement the first batch of FPU instructions. These are still experimental. svn path=/trunk/; revision=64656
This commit is contained in:
parent
8b43551c8c
commit
1618cfa1f2
13 changed files with 561 additions and 53 deletions
|
@ -2,7 +2,7 @@
|
||||||
* Fast486 386/486 CPU Emulation Library
|
* Fast486 386/486 CPU Emulation Library
|
||||||
* fast486.h
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -91,6 +91,8 @@
|
||||||
#define FAST486_PREFIX_REPNZ (1 << 4)
|
#define FAST486_PREFIX_REPNZ (1 << 4)
|
||||||
#define FAST486_PREFIX_REP (1 << 5)
|
#define FAST486_PREFIX_REP (1 << 5)
|
||||||
|
|
||||||
|
#define FAST486_FPU_DEFAULT_CONTROL 0x037F
|
||||||
|
|
||||||
struct _FAST486_STATE;
|
struct _FAST486_STATE;
|
||||||
typedef struct _FAST486_STATE FAST486_STATE, *PFAST486_STATE;
|
typedef struct _FAST486_STATE FAST486_STATE, *PFAST486_STATE;
|
||||||
|
|
||||||
|
@ -420,6 +422,7 @@ typedef struct _FAST486_FPU_DATA_REG
|
||||||
{
|
{
|
||||||
ULONGLONG Mantissa;
|
ULONGLONG Mantissa;
|
||||||
USHORT Exponent;
|
USHORT Exponent;
|
||||||
|
UCHAR Sign;
|
||||||
} FAST486_FPU_DATA_REG, *PFAST486_FPU_DATA_REG;
|
} FAST486_FPU_DATA_REG, *PFAST486_FPU_DATA_REG;
|
||||||
|
|
||||||
typedef union _FAST486_FPU_STATUS_REG
|
typedef union _FAST486_FPU_STATUS_REG
|
||||||
|
@ -490,10 +493,12 @@ struct _FAST486_STATE
|
||||||
FAST486_INT_STATUS IntStatus;
|
FAST486_INT_STATUS IntStatus;
|
||||||
UCHAR PendingIntNum;
|
UCHAR PendingIntNum;
|
||||||
PULONG Tlb;
|
PULONG Tlb;
|
||||||
|
#ifndef FAST486_NO_FPU
|
||||||
FAST486_FPU_DATA_REG FpuRegisters[FAST486_NUM_FPU_REGS];
|
FAST486_FPU_DATA_REG FpuRegisters[FAST486_NUM_FPU_REGS];
|
||||||
FAST486_FPU_STATUS_REG FpuStatus;
|
FAST486_FPU_STATUS_REG FpuStatus;
|
||||||
FAST486_FPU_CONTROL_REG FpuControl;
|
FAST486_FPU_CONTROL_REG FpuControl;
|
||||||
USHORT FpuTag;
|
USHORT FpuTag;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/* FUNCTIONS ******************************************************************/
|
/* FUNCTIONS ******************************************************************/
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Fast486 386/486 CPU Emulation Library
|
* Fast486 386/486 CPU Emulation Library
|
||||||
* common.c
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Fast486 386/486 CPU Emulation Library
|
* Fast486 386/486 CPU Emulation Library
|
||||||
* common.h
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Fast486 386/486 CPU Emulation Library
|
* Fast486 386/486 CPU Emulation Library
|
||||||
* common.inl
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* 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 */
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Fast486 386/486 CPU Emulation Library
|
* Fast486 386/486 CPU Emulation Library
|
||||||
* extraops.c
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Fast486 386/486 CPU Emulation Library
|
* Fast486 386/486 CPU Emulation Library
|
||||||
* extraops.h
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Fast486 386/486 CPU Emulation Library
|
* Fast486 386/486 CPU Emulation Library
|
||||||
* fast486.c
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -282,6 +282,11 @@ Fast486Reset(PFAST486_STATE State)
|
||||||
#ifndef FAST486_NO_FPU
|
#ifndef FAST486_NO_FPU
|
||||||
/* Initialize CR0 */
|
/* Initialize CR0 */
|
||||||
State->ControlRegisters[FAST486_REG_CR0] |= FAST486_CR0_ET;
|
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
|
#endif
|
||||||
|
|
||||||
/* Restore the callbacks and TLB */
|
/* Restore the callbacks and TLB */
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Fast486 386/486 CPU Emulation Library
|
* Fast486 386/486 CPU Emulation Library
|
||||||
* fpu.c
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -28,15 +28,281 @@
|
||||||
|
|
||||||
#include <fast486.h>
|
#include <fast486.h>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "opcodes.h"
|
|
||||||
#include "fpu.h"
|
#include "fpu.h"
|
||||||
|
|
||||||
|
/* PRIVATE FUNCTIONS **********************************************************/
|
||||||
|
|
||||||
|
static ULONGLONG UnsignedMult128(ULONGLONG Multiplicand,
|
||||||
|
ULONGLONG Multiplier,
|
||||||
|
ULONGLONG *HighProduct)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
/* Adjust the exponent bias */
|
||||||
|
Result->Exponent += (FPU_REAL10_BIAS - FPU_REAL4_BIAS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// TODO: NOT IMPLEMENTED
|
||||||
|
UNREFERENCED_PARAMETER(TempResult);
|
||||||
|
UNIMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
/* PUBLIC FUNCTIONS ***********************************************************/
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
||||||
|
|
||||||
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8)
|
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8DC)
|
||||||
{
|
{
|
||||||
FAST486_MOD_REG_RM ModRegRm;
|
FAST486_MOD_REG_RM ModRegRm;
|
||||||
BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
|
BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
|
||||||
|
PFAST486_FPU_DATA_REG SourceOperand, DestOperand;
|
||||||
|
FAST486_FPU_DATA_REG MemoryData;
|
||||||
|
|
||||||
/* Get the operands */
|
/* Get the operands */
|
||||||
if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
|
if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
|
||||||
|
@ -48,14 +314,127 @@ FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8)
|
||||||
FPU_CHECK();
|
FPU_CHECK();
|
||||||
|
|
||||||
#ifndef FAST486_NO_FPU
|
#ifndef FAST486_NO_FPU
|
||||||
// TODO: NOT IMPLEMENTED
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
|
|
||||||
|
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;
|
return FALSE;
|
||||||
#else
|
}
|
||||||
/* Do nothing */
|
|
||||||
return TRUE;
|
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(Fast486FpuOpcodeD9)
|
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9)
|
||||||
|
@ -123,39 +502,66 @@ FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB)
|
||||||
FPU_CHECK();
|
FPU_CHECK();
|
||||||
|
|
||||||
#ifndef FAST486_NO_FPU
|
#ifndef FAST486_NO_FPU
|
||||||
|
|
||||||
|
if (ModRegRm.Memory)
|
||||||
|
{
|
||||||
// TODO: NOT IMPLEMENTED
|
// TODO: NOT IMPLEMENTED
|
||||||
UNIMPLEMENTED;
|
UNIMPLEMENTED;
|
||||||
|
}
|
||||||
return FALSE;
|
else
|
||||||
#else
|
|
||||||
/* Do nothing */
|
|
||||||
return TRUE;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDC)
|
|
||||||
{
|
|
||||||
FAST486_MOD_REG_RM ModRegRm;
|
|
||||||
BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
|
|
||||||
|
|
||||||
/* Get the operands */
|
|
||||||
if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
|
|
||||||
{
|
{
|
||||||
/* Exception occurred */
|
/* Only a few of these instructions have any meaning on a 487 */
|
||||||
return FALSE;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
FPU_CHECK();
|
/* FINIT */
|
||||||
|
case 0x23:
|
||||||
|
{
|
||||||
|
/* Restore the state */
|
||||||
|
State->FpuControl.Value = FAST486_FPU_DEFAULT_CONTROL;
|
||||||
|
State->FpuStatus.Value = 0;
|
||||||
|
State->FpuTag = 0xFFFF;
|
||||||
|
|
||||||
#ifndef FAST486_NO_FPU
|
break;
|
||||||
// TODO: NOT IMPLEMENTED
|
}
|
||||||
UNIMPLEMENTED;
|
|
||||||
|
|
||||||
|
/* FENI */
|
||||||
|
case 0x20:
|
||||||
|
/* FDISI */
|
||||||
|
case 0x21:
|
||||||
|
{
|
||||||
|
/* These do nothing */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invalid */
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
Fast486Exception(State, FAST486_EXCEPTION_UD);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
#else
|
}
|
||||||
/* Do nothing */
|
}
|
||||||
return TRUE;
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD)
|
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Fast486 386/486 CPU Emulation Library
|
* Fast486 386/486 CPU Emulation Library
|
||||||
* fpu.h
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -24,6 +24,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "opcodes.h"
|
||||||
|
|
||||||
/* DEFINES ********************************************************************/
|
/* DEFINES ********************************************************************/
|
||||||
|
|
||||||
#define FPU_CHECK() if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_EM) \
|
#define FPU_CHECK() if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_EM) \
|
||||||
|
@ -32,6 +34,24 @@
|
||||||
return FALSE; \
|
return FALSE; \
|
||||||
}
|
}
|
||||||
#define FPU_ST(i) State->FpuRegisters[(State->FpuStatus.Top + (i)) % FAST486_NUM_FPU_REGS]
|
#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(Fast486FpuOpcodeD9);
|
||||||
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA);
|
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA);
|
||||||
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB);
|
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB);
|
||||||
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDC);
|
|
||||||
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD);
|
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD);
|
||||||
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDE);
|
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDE);
|
||||||
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDF);
|
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDF);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Fast486 386/486 CPU Emulation Library
|
* Fast486 386/486 CPU Emulation Library
|
||||||
* opcodes.c
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -254,11 +254,11 @@ Fast486OpcodeHandlers[FAST486_NUM_OPCODE_HANDLERS] =
|
||||||
Fast486OpcodeAad,
|
Fast486OpcodeAad,
|
||||||
Fast486OpcodeSalc,
|
Fast486OpcodeSalc,
|
||||||
Fast486OpcodeXlat,
|
Fast486OpcodeXlat,
|
||||||
Fast486FpuOpcodeD8,
|
Fast486FpuOpcodeD8DC,
|
||||||
Fast486FpuOpcodeD9,
|
Fast486FpuOpcodeD9,
|
||||||
Fast486FpuOpcodeDA,
|
Fast486FpuOpcodeDA,
|
||||||
Fast486FpuOpcodeDB,
|
Fast486FpuOpcodeDB,
|
||||||
Fast486FpuOpcodeDC,
|
Fast486FpuOpcodeD8DC,
|
||||||
Fast486FpuOpcodeDD,
|
Fast486FpuOpcodeDD,
|
||||||
Fast486FpuOpcodeDE,
|
Fast486FpuOpcodeDE,
|
||||||
Fast486FpuOpcodeDF,
|
Fast486FpuOpcodeDF,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Fast486 386/486 CPU Emulation Library
|
* Fast486 386/486 CPU Emulation Library
|
||||||
* opcodes.h
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Fast486 386/486 CPU Emulation Library
|
* Fast486 386/486 CPU Emulation Library
|
||||||
* opgroups.c
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Fast486 386/486 CPU Emulation Library
|
* Fast486 386/486 CPU Emulation Library
|
||||||
* opgroups.h
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
Loading…
Reference in a new issue