1762 lines
44 KiB
C
1762 lines
44 KiB
C
/*++
|
|
|
|
Copyright (c) 1995-1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fpuarith.c
|
|
|
|
Abstract:
|
|
|
|
Floating point arithmetic fragments (Add/Sub/Mul/Div/Com/Tst)
|
|
|
|
Author:
|
|
|
|
04-Oct-1995 BarryBo
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <float.h>
|
|
#include <math.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include "wx86.h"
|
|
#include "cpuassrt.h"
|
|
#include "fragp.h"
|
|
#include "fpufrags.h"
|
|
#include "fpufragp.h"
|
|
#include "fpuarith.h"
|
|
|
|
ASSERTNAME;
|
|
|
|
#define CALLNPXFUNC2(table, lTag, rTag, l, r) { \
|
|
NpxFunc2 *p = (NpxFunc2 *)(table); \
|
|
(*(p + (lTag)*TAG_MAX + (rTag)))(cpu, l, r); \
|
|
}
|
|
|
|
#define CALLNPXFUNC3(table, lTag, rTag, d, l, r) { \
|
|
NpxFunc3 *p = (NpxFunc3 *)(table); \
|
|
(*(p + (lTag)*TAG_MAX + (rTag)))(cpu, d, l, r); \
|
|
}
|
|
|
|
|
|
//
|
|
// Forward declarations
|
|
//
|
|
NPXFUNC2(FpAdd32_VALID_VALID);
|
|
NPXFUNC2(FpAdd32_VALID_SPECIAL);
|
|
NPXFUNC2(FpAdd32_SPECIAL_VALID);
|
|
NPXFUNC2(FpAdd32_SPECIAL_SPECIAL);
|
|
NPXFUNC2(FpAdd_ANY_EMPTY);
|
|
NPXFUNC2(FpAdd_EMPTY_ANY);
|
|
NPXFUNC2(FpAdd64_VALID_VALID);
|
|
NPXFUNC2(FpAdd64_VALID_SPECIAL);
|
|
NPXFUNC2(FpAdd64_SPECIAL_VALID);
|
|
NPXFUNC2(FpAdd64_SPECIAL_SPECIAL);
|
|
NPXFUNC3(FpDiv32_VALID_VALID);
|
|
NPXFUNC3(FpDiv32_VALID_SPECIAL);
|
|
NPXFUNC3(FpDiv32_SPECIAL_VALID);
|
|
NPXFUNC3(FpDiv32_SPECIAL_SPECIAL);
|
|
NPXFUNC3(FpDiv_ANY_EMPTY);
|
|
NPXFUNC3(FpDiv_EMPTY_ANY);
|
|
NPXFUNC3(FpDiv64_VALID_VALID);
|
|
NPXFUNC3(FpDiv64_VALID_SPECIAL);
|
|
NPXFUNC3(FpDiv64_SPECIAL_VALID);
|
|
NPXFUNC3(FpDiv64_SPECIAL_SPECIAL);
|
|
NPXFUNC2(FpMul32_VALID_VALID);
|
|
NPXFUNC2(FpMul32_VALID_SPECIAL);
|
|
NPXFUNC2(FpMul32_SPECIAL_VALID);
|
|
NPXFUNC2(FpMul32_SPECIAL_SPECIAL);
|
|
NPXFUNC2(FpMul_ANY_EMPTY);
|
|
NPXFUNC2(FpMul_EMPTY_ANY);
|
|
NPXFUNC2(FpMul64_VALID_VALID);
|
|
NPXFUNC2(FpMul64_VALID_SPECIAL);
|
|
NPXFUNC2(FpMul64_SPECIAL_VALID);
|
|
NPXFUNC2(FpMul64_SPECIAL_SPECIAL);
|
|
NPXFUNC3(FpSub32_VALID_VALID);
|
|
NPXFUNC3(FpSub32_VALID_SPECIAL);
|
|
NPXFUNC3(FpSub32_SPECIAL_VALID);
|
|
NPXFUNC3(FpSub32_SPECIAL_SPECIAL);
|
|
NPXFUNC3(FpSub_ANY_EMPTY);
|
|
NPXFUNC3(FpSub_EMPTY_ANY);
|
|
NPXFUNC3(FpSub64_VALID_VALID);
|
|
NPXFUNC3(FpSub64_VALID_SPECIAL);
|
|
NPXFUNC3(FpSub64_SPECIAL_VALID);
|
|
NPXFUNC3(FpSub64_SPECIAL_SPECIAL);
|
|
NPXCOMFUNC(FpCom_VALID_VALID);
|
|
NPXCOMFUNC(FpCom_VALID_SPECIAL);
|
|
NPXCOMFUNC(FpCom_SPECIAL_VALID);
|
|
NPXCOMFUNC(FpCom_SPECIAL_SPECIAL);
|
|
NPXCOMFUNC(FpCom_VALID_EMPTY);
|
|
NPXCOMFUNC(FpCom_EMPTY_VALID);
|
|
NPXCOMFUNC(FpCom_EMPTY_SPECIAL);
|
|
NPXCOMFUNC(FpCom_SPECIAL_EMPTY);
|
|
NPXCOMFUNC(FpCom_EMPTY_EMPTY);
|
|
|
|
//
|
|
// Jump tables
|
|
//
|
|
const NpxFunc2 FpAdd32Table[TAG_MAX][TAG_MAX] = {
|
|
// Left is TAG_VALID, Right is...
|
|
{FpAdd32_VALID_VALID, FpAdd32_VALID_VALID, FpAdd32_VALID_SPECIAL, FpAdd_ANY_EMPTY},
|
|
// Left is TAG_ZERO, Right is...
|
|
{FpAdd32_VALID_VALID, FpAdd32_VALID_VALID, FpAdd32_VALID_SPECIAL, FpAdd_ANY_EMPTY},
|
|
// Left is TAG_SPECIAL, Right is...
|
|
{FpAdd32_SPECIAL_VALID, FpAdd32_SPECIAL_VALID, FpAdd32_SPECIAL_SPECIAL, FpAdd_ANY_EMPTY},
|
|
// Left is TAG_EMPTY, Right is...
|
|
{FpAdd_EMPTY_ANY, FpAdd_EMPTY_ANY, FpAdd_EMPTY_ANY, FpAdd_EMPTY_ANY}
|
|
};
|
|
const NpxFunc2 FpAdd64Table[TAG_MAX][TAG_MAX] = {
|
|
// Left is TAG_VALID, Right is...
|
|
{FpAdd64_VALID_VALID, FpAdd64_VALID_VALID, FpAdd64_VALID_SPECIAL, FpAdd_ANY_EMPTY},
|
|
// Left is TAG_ZERO, Right is...
|
|
{FpAdd64_VALID_VALID, FpAdd64_VALID_VALID, FpAdd64_VALID_SPECIAL, FpAdd_ANY_EMPTY},
|
|
// Left is TAG_SPECIAL, Right is...
|
|
{FpAdd64_SPECIAL_VALID, FpAdd64_SPECIAL_VALID, FpAdd64_SPECIAL_SPECIAL, FpAdd_ANY_EMPTY},
|
|
// Left is TAG_EMPTY, Right is...
|
|
{FpAdd_EMPTY_ANY, FpAdd_EMPTY_ANY, FpAdd_EMPTY_ANY, FpAdd_EMPTY_ANY}
|
|
};
|
|
|
|
const NpxFunc3 FpDiv32Table[TAG_MAX][TAG_MAX] = {
|
|
// Left is TAG_VALID, Right is...
|
|
{FpDiv32_VALID_VALID, FpDiv32_VALID_VALID, FpDiv32_VALID_SPECIAL, FpDiv_ANY_EMPTY},
|
|
// Left is TAG_ZERO, Right is...
|
|
{FpDiv32_VALID_VALID, FpDiv32_VALID_VALID, FpDiv32_VALID_SPECIAL, FpDiv_ANY_EMPTY},
|
|
// Left is TAG_SPECIAL, Right is...
|
|
{FpDiv32_SPECIAL_VALID, FpDiv32_SPECIAL_VALID, FpDiv32_SPECIAL_SPECIAL, FpDiv_ANY_EMPTY},
|
|
// Left is TAG_EMPTY, Right is...
|
|
{FpDiv_EMPTY_ANY, FpDiv_EMPTY_ANY, FpDiv_EMPTY_ANY, FpDiv_EMPTY_ANY}
|
|
};
|
|
const NpxFunc3 FpDiv64Table[TAG_MAX][TAG_MAX] = {
|
|
// Left is TAG_VALID, Right is...
|
|
{FpDiv64_VALID_VALID, FpDiv64_VALID_VALID, FpDiv64_VALID_SPECIAL, FpDiv_ANY_EMPTY},
|
|
// Left is TAG_ZERO, Right is...
|
|
{FpDiv64_VALID_VALID, FpDiv64_VALID_VALID, FpDiv64_VALID_SPECIAL, FpDiv_ANY_EMPTY},
|
|
// Left is TAG_SPECIAL, Right is...
|
|
{FpDiv64_SPECIAL_VALID, FpDiv64_SPECIAL_VALID, FpDiv64_SPECIAL_SPECIAL, FpDiv_ANY_EMPTY},
|
|
// Left is TAG_EMPTY, Right is...
|
|
{FpDiv_EMPTY_ANY, FpDiv_EMPTY_ANY, FpDiv_EMPTY_ANY, FpDiv_EMPTY_ANY}
|
|
};
|
|
|
|
const NpxFunc2 FpMul32Table[TAG_MAX][TAG_MAX] = {
|
|
// Left is TAG_VALID, Right is...
|
|
{FpMul32_VALID_VALID, FpMul32_VALID_VALID, FpMul32_VALID_SPECIAL, FpMul_ANY_EMPTY},
|
|
// Left is TAG_ZERO, Right is...
|
|
{FpMul32_VALID_VALID, FpMul32_VALID_VALID, FpMul32_VALID_SPECIAL, FpMul_ANY_EMPTY},
|
|
// Left is TAG_SPECIAL, Right is...
|
|
{FpMul32_SPECIAL_VALID, FpMul32_SPECIAL_VALID, FpMul32_SPECIAL_SPECIAL, FpMul_ANY_EMPTY},
|
|
// Left is TAG_EMPTY, Right is...
|
|
{FpMul_EMPTY_ANY, FpMul_EMPTY_ANY, FpMul_EMPTY_ANY, FpMul_EMPTY_ANY}
|
|
};
|
|
const NpxFunc2 FpMul64Table[TAG_MAX][TAG_MAX] = {
|
|
// Left is TAG_VALID, Right is...
|
|
{FpMul64_VALID_VALID, FpMul64_VALID_VALID, FpMul64_VALID_SPECIAL, FpMul_ANY_EMPTY},
|
|
// Left is TAG_ZERO, Right is...
|
|
{FpMul64_VALID_VALID, FpMul64_VALID_VALID, FpMul64_VALID_SPECIAL, FpMul_ANY_EMPTY},
|
|
// Left is TAG_SPECIAL, Right is...
|
|
{FpMul64_SPECIAL_VALID, FpMul64_SPECIAL_VALID, FpMul64_SPECIAL_SPECIAL, FpMul_ANY_EMPTY},
|
|
// Left is TAG_EMPTY, Right is...
|
|
{FpMul_EMPTY_ANY, FpMul_EMPTY_ANY, FpMul_EMPTY_ANY, FpMul_EMPTY_ANY}
|
|
};
|
|
|
|
const NpxFunc3 FpSub32Table[TAG_MAX][TAG_MAX] = {
|
|
// Left is TAG_VALID, Right is...
|
|
{FpSub32_VALID_VALID, FpSub32_VALID_VALID, FpSub32_VALID_SPECIAL, FpSub_ANY_EMPTY},
|
|
// Left is TAG_ZERO, Right is...
|
|
{FpSub32_VALID_VALID, FpSub32_VALID_VALID, FpSub32_VALID_SPECIAL, FpSub_ANY_EMPTY},
|
|
// Left is TAG_SPECIAL, Right is...
|
|
{FpSub32_SPECIAL_VALID, FpSub32_SPECIAL_VALID, FpSub32_SPECIAL_SPECIAL, FpSub_ANY_EMPTY},
|
|
// Left is TAG_EMPTY, Right is...
|
|
{FpSub_EMPTY_ANY, FpSub_EMPTY_ANY, FpSub_EMPTY_ANY, FpSub_EMPTY_ANY}
|
|
};
|
|
const NpxFunc3 FpSub64Table[TAG_MAX][TAG_MAX] = {
|
|
// Left is TAG_VALID, Right is...
|
|
{FpSub64_VALID_VALID, FpSub64_VALID_VALID, FpSub64_VALID_SPECIAL, FpSub_ANY_EMPTY},
|
|
// Left is TAG_ZERO, Right is...
|
|
{FpSub64_VALID_VALID, FpSub64_VALID_VALID, FpSub64_VALID_SPECIAL, FpSub_ANY_EMPTY},
|
|
// Left is TAG_SPECIAL, Right is...
|
|
{FpSub64_SPECIAL_VALID, FpSub64_SPECIAL_VALID, FpSub64_SPECIAL_SPECIAL, FpSub_ANY_EMPTY},
|
|
// Left is TAG_EMPTY, Right is...
|
|
{FpSub_EMPTY_ANY, FpSub_EMPTY_ANY, FpSub_EMPTY_ANY, FpSub_EMPTY_ANY}
|
|
};
|
|
|
|
const NpxComFunc FpComTable[TAG_MAX][TAG_MAX] = {
|
|
// Left is TAG_VALID, Right is...
|
|
{FpCom_VALID_VALID, FpCom_VALID_VALID, FpCom_VALID_SPECIAL, FpCom_VALID_EMPTY},
|
|
// Left is TAG_ZERO, Right is...
|
|
{FpCom_VALID_VALID, FpCom_VALID_VALID, FpCom_VALID_SPECIAL, FpCom_VALID_EMPTY},
|
|
// Left is TAG_SPECIAL, Right is...
|
|
{FpCom_SPECIAL_VALID, FpCom_SPECIAL_VALID, FpCom_SPECIAL_SPECIAL, FpCom_SPECIAL_EMPTY},
|
|
// Left is TAG_EMPTY, Right is...
|
|
{FpCom_EMPTY_VALID, FpCom_EMPTY_VALID, FpCom_EMPTY_SPECIAL, FpCom_EMPTY_EMPTY}
|
|
};
|
|
|
|
|
|
VOID
|
|
ChangeFpPrecision(
|
|
PCPUDATA cpu,
|
|
INT NewPrecision
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to modify the floating-point precision. It modifies per-thread
|
|
jump tables used by instructions which are sensitive to the FP
|
|
precision bits.
|
|
|
|
Arguments:
|
|
|
|
cpu - per-thread data
|
|
NewPrecision - new precision value
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
cpu->FpControlPrecision = NewPrecision;
|
|
|
|
if (NewPrecision == 0) {
|
|
//
|
|
// New precision is 32-bit
|
|
//
|
|
cpu->FpAddTable = FpAdd32Table;
|
|
cpu->FpSubTable = FpSub32Table;
|
|
cpu->FpMulTable = FpMul32Table;
|
|
cpu->FpDivTable = FpDiv32Table;
|
|
} else {
|
|
//
|
|
// New precision is 24, 64, or 80-bit - treat all as 64-bit
|
|
//
|
|
cpu->FpAddTable = FpAdd64Table;
|
|
cpu->FpSubTable = FpSub64Table;
|
|
cpu->FpMulTable = FpMul64Table;
|
|
cpu->FpDivTable = FpDiv64Table;
|
|
}
|
|
}
|
|
|
|
|
|
NPXFUNC2(FpAdd32_VALID_VALID)
|
|
{
|
|
//UNDONE: If 487 overflow exceptions are unmasked and an overflow occurs,
|
|
//UNDONE: a different value is written to 'l' than if the exception
|
|
//UNDONE: was masked. To get this right, we need to install an
|
|
//UNDONE: exception handler around the addition and run the native FPU
|
|
//UNDONE: with overflow exceptions unmasked. The trap handler must then
|
|
//UNDONE: map the exception back into FpStatus->ES so the next Intel
|
|
//UNDONE: FP instruction can get the Intel exception as expected. Gross!
|
|
//UNDONE: Read Intel 16-24 for the gory details.
|
|
|
|
l->r64 = (DOUBLE)( (FLOAT)l->r64 + (FLOAT)r->r64 );
|
|
|
|
// Compute the new tag value
|
|
SetTag(l);
|
|
}
|
|
|
|
NPXFUNC2(FpAdd64_VALID_VALID)
|
|
{
|
|
//UNDONE: If 487 overflow exceptions are unmasked and an overflow occurs,
|
|
//UNDONE: a different value is written to 'l' than if the exception
|
|
//UNDONE: was masked. To get this right, we need to install an
|
|
//UNDONE: exception handler around the addition and run the native FPU
|
|
//UNDONE: with overflow exceptions unmasked. The trap handler must then
|
|
//UNDONE: map the exception back into FpStatus->ES so the next Intel
|
|
//UNDONE: FP instruction can get the Intel exception as expected. Gross!
|
|
//UNDONE: Read Intel 16-24 for the gory details.
|
|
|
|
l->r64 = l->r64 + r->r64;
|
|
|
|
// Compute the new tag value
|
|
SetTag(l);
|
|
}
|
|
|
|
NPXFUNC2(FpAdd32_VALID_SPECIAL)
|
|
{
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpAdd32_VALID_VALID(cpu, l, r);
|
|
}
|
|
|
|
NPXFUNC2(FpAdd64_VALID_SPECIAL)
|
|
{
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpAdd64_VALID_VALID(cpu, l, r);
|
|
}
|
|
|
|
NPXFUNC2(FpAdd32_SPECIAL_VALID)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpAdd32_VALID_VALID(cpu, l, r);
|
|
}
|
|
|
|
NPXFUNC2(FpAdd64_SPECIAL_VALID)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpAdd64_VALID_VALID(cpu, l, r);
|
|
}
|
|
|
|
NPXFUNC2(FpAdd32_SPECIAL_SPECIAL)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpAdd32_VALID_VALID(cpu, l, r);
|
|
}
|
|
|
|
NPXFUNC2(FpAdd64_SPECIAL_SPECIAL)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpAdd64_VALID_VALID(cpu, l, r);
|
|
}
|
|
|
|
NPXFUNC2(FpAdd_ANY_EMPTY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, r)) {
|
|
CALLNPXFUNC2(cpu->FpAddTable, l->Tag, TAG_SPECIAL, l, r);
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FpAdd_EMPTY_ANY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, l)) {
|
|
CALLNPXFUNC2(cpu->FpAddTable, TAG_SPECIAL, r->Tag, l, r);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FpAddCommon(
|
|
PCPUDATA cpu,
|
|
PFPREG l,
|
|
PFPREG r
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Implements l += r.
|
|
|
|
Arguments:
|
|
|
|
cpu - per-thread data
|
|
l - left-hand FP register
|
|
r - right-hand FP register
|
|
|
|
Return Value:
|
|
|
|
None. l is updated to contain the value of l+r.
|
|
|
|
--*/
|
|
|
|
{
|
|
CALLNPXFUNC2(cpu->FpAddTable, l->Tag, r->Tag, l, r);
|
|
}
|
|
|
|
|
|
|
|
NPXFUNC3(FpDiv32_VALID_VALID)
|
|
{
|
|
//UNDONE: If 487 overflow exceptions are unmasked and an overflow occurs,
|
|
//UNDONE: a different value is written to 'l' than if the exception
|
|
//UNDONE: was masked. To get this right, we need to install an
|
|
//UNDONE: exception handler around the addition and run the native FPU
|
|
//UNDONE: with overflow exceptions unmasked. The trap handler must then
|
|
//UNDONE: map the exception back into FpStatus->ES so the next Intel
|
|
//UNDONE: FP instruction can get the Intel exception as expected. Gross!
|
|
//UNDONE: Read Intel 16-24 for the gory details.
|
|
|
|
dest->r64 = (DOUBLE)( (FLOAT)l->r64 / (FLOAT)r->r64 );
|
|
|
|
// Compute the new tag value
|
|
SetTag(dest);
|
|
}
|
|
|
|
NPXFUNC3(FpDiv64_VALID_VALID)
|
|
{
|
|
//UNDONE: If 487 overflow exceptions are unmasked and an overflow occurs,
|
|
//UNDONE: a different value is written to 'l' than if the exception
|
|
//UNDONE: was masked. To get this right, we need to install an
|
|
//UNDONE: exception handler around the addition and run the native FPU
|
|
//UNDONE: with overflow exceptions unmasked. The trap handler must then
|
|
//UNDONE: map the exception back into FpStatus->ES so the next Intel
|
|
//UNDONE: FP instruction can get the Intel exception as expected. Gross!
|
|
//UNDONE: Read Intel 16-24 for the gory details.
|
|
|
|
dest->r64 = l->r64 / r->r64;
|
|
|
|
// Compute the new tag value
|
|
SetTag(dest);
|
|
}
|
|
|
|
NPXFUNC3(FpDiv32_VALID_SPECIAL)
|
|
{
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpDiv32_VALID_VALID(cpu, dest, l, r);
|
|
}
|
|
|
|
NPXFUNC3(FpDiv64_VALID_SPECIAL)
|
|
{
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpDiv64_VALID_VALID(cpu, dest, l, r);
|
|
}
|
|
|
|
NPXFUNC3(FpDiv32_SPECIAL_VALID)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpDiv32_VALID_VALID(cpu, dest, l, r);
|
|
}
|
|
|
|
NPXFUNC3(FpDiv64_SPECIAL_VALID)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpDiv64_VALID_VALID(cpu, dest, l, r);
|
|
}
|
|
|
|
NPXFUNC3(FpDiv32_SPECIAL_SPECIAL)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpDiv32_VALID_VALID(cpu, dest, l, r);
|
|
}
|
|
|
|
NPXFUNC3(FpDiv64_SPECIAL_SPECIAL)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpDiv64_VALID_VALID(cpu, dest, l, r);
|
|
}
|
|
|
|
NPXFUNC3(FpDiv_ANY_EMPTY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, r)) {
|
|
CALLNPXFUNC3(cpu->FpDivTable, l->Tag, TAG_SPECIAL, dest, l, r);
|
|
}
|
|
}
|
|
|
|
NPXFUNC3(FpDiv_EMPTY_ANY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, l)) {
|
|
CALLNPXFUNC3(cpu->FpDivTable, TAG_SPECIAL, r->Tag, dest, l, r);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FpDivCommon(
|
|
PCPUDATA cpu,
|
|
PFPREG dest,
|
|
PFPREG l,
|
|
PFPREG r
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Implements dest = l/r
|
|
|
|
Arguments:
|
|
|
|
cpu - per-thread data
|
|
l - left-hand FP register
|
|
r - right-hand FP register
|
|
|
|
Return Value:
|
|
|
|
None. l is updated to contain the value of l+r.
|
|
|
|
--*/
|
|
|
|
{
|
|
CALLNPXFUNC3(cpu->FpDivTable, l->Tag, r->Tag, dest, l, r);
|
|
}
|
|
|
|
|
|
NPXFUNC2(FpMul32_VALID_VALID)
|
|
{
|
|
//UNDONE: If 487 overflow exceptions are unmasked and an overflow occurs,
|
|
//UNDONE: a different value is written to 'l' than if the exception
|
|
//UNDONE: was masked. To get this right, we need to install an
|
|
//UNDONE: exception handler around the addition and run the native FPU
|
|
//UNDONE: with overflow exceptions unmasked. The trap handler must then
|
|
//UNDONE: map the exception back into FpStatus->ES so the next Intel
|
|
//UNDONE: FP instruction can get the Intel exception as expected. Gross!
|
|
//UNDONE: Read Intel 16-24 for the gory details.
|
|
|
|
l->r64 = (DOUBLE)( (FLOAT)l->r64 * (FLOAT)r->r64 );
|
|
|
|
// Compute the new tag value
|
|
SetTag(l);
|
|
}
|
|
|
|
NPXFUNC2(FpMul64_VALID_VALID)
|
|
{
|
|
//UNDONE: If 487 overflow exceptions are unmasked and an overflow occurs,
|
|
//UNDONE: a different value is written to 'l' than if the exception
|
|
//UNDONE: was masked. To get this right, we need to install an
|
|
//UNDONE: exception handler around the addition and run the native FPU
|
|
//UNDONE: with overflow exceptions unmasked. The trap handler must then
|
|
//UNDONE: map the exception back into FpStatus->ES so the next Intel
|
|
//UNDONE: FP instruction can get the Intel exception as expected. Gross!
|
|
//UNDONE: Read Intel 16-24 for the gory details.
|
|
|
|
l->r64 = l->r64 * r->r64;
|
|
|
|
// Compute the new tag value
|
|
SetTag(l);
|
|
}
|
|
|
|
NPXFUNC2(FpMul32_VALID_SPECIAL)
|
|
{
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpMul32_VALID_VALID(cpu, l, r);
|
|
}
|
|
|
|
NPXFUNC2(FpMul64_VALID_SPECIAL)
|
|
{
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpMul64_VALID_VALID(cpu, l, r);
|
|
}
|
|
|
|
NPXFUNC2(FpMul32_SPECIAL_VALID)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpMul32_VALID_VALID(cpu, l, r);
|
|
}
|
|
|
|
NPXFUNC2(FpMul64_SPECIAL_VALID)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpMul64_VALID_VALID(cpu, l, r);
|
|
}
|
|
|
|
NPXFUNC2(FpMul32_SPECIAL_SPECIAL)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpMul32_VALID_VALID(cpu, l, r);
|
|
}
|
|
NPXFUNC2(FpMul64_SPECIAL_SPECIAL)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpMul64_VALID_VALID(cpu, l, r);
|
|
}
|
|
|
|
NPXFUNC2(FpMul_ANY_EMPTY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, r)) {
|
|
CALLNPXFUNC2(cpu->FpMulTable, l->Tag, TAG_SPECIAL, l, r);
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FpMul_EMPTY_ANY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, l)) {
|
|
CALLNPXFUNC2(cpu->FpMulTable, TAG_SPECIAL, r->Tag, l, r);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FpMulCommon(
|
|
PCPUDATA cpu,
|
|
PFPREG l,
|
|
PFPREG r
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Implements l += r.
|
|
|
|
Arguments:
|
|
|
|
cpu - per-thread data
|
|
l - left-hand FP register
|
|
r - right-hand FP register
|
|
|
|
Return Value:
|
|
|
|
None. l is updated to contain the value of l+r.
|
|
|
|
--*/
|
|
|
|
{
|
|
CALLNPXFUNC2(cpu->FpMulTable, l->Tag, r->Tag, l, r);
|
|
}
|
|
|
|
|
|
|
|
NPXFUNC3(FpSub32_VALID_VALID)
|
|
{
|
|
//UNDONE: If 487 overflow exceptions are unmasked and an overflow occurs,
|
|
//UNDONE: a different value is written to 'l' than if the exception
|
|
//UNDONE: was masked. To get this right, we need to install an
|
|
//UNDONE: exception handler around the addition and run the native FPU
|
|
//UNDONE: with overflow exceptions unmasked. The trap handler must then
|
|
//UNDONE: map the exception back into FpStatus->ES so the next Intel
|
|
//UNDONE: FP instruction can get the Intel exception as expected. Gross!
|
|
//UNDONE: Read Intel 16-24 for the gory details.
|
|
|
|
dest->r64 = (DOUBLE)( (FLOAT)l->r64 - (FLOAT)r->r64 );
|
|
|
|
// Compute the new tag value
|
|
SetTag(dest);
|
|
}
|
|
|
|
NPXFUNC3(FpSub64_VALID_VALID)
|
|
{
|
|
//UNDONE: If 487 overflow exceptions are unmasked and an overflow occurs,
|
|
//UNDONE: a different value is written to 'l' than if the exception
|
|
//UNDONE: was masked. To get this right, we need to install an
|
|
//UNDONE: exception handler around the addition and run the native FPU
|
|
//UNDONE: with overflow exceptions unmasked. The trap handler must then
|
|
//UNDONE: map the exception back into FpStatus->ES so the next Intel
|
|
//UNDONE: FP instruction can get the Intel exception as expected. Gross!
|
|
//UNDONE: Read Intel 16-24 for the gory details.
|
|
|
|
dest->r64 = l->r64 - r->r64;
|
|
|
|
// Compute the new tag value
|
|
SetTag(dest);
|
|
}
|
|
|
|
NPXFUNC3(FpSub32_VALID_SPECIAL)
|
|
{
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpSub32_VALID_VALID(cpu, dest, l, r);
|
|
}
|
|
|
|
NPXFUNC3(FpSub64_VALID_SPECIAL)
|
|
{
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpSub64_VALID_VALID(cpu, dest, l, r);
|
|
}
|
|
|
|
NPXFUNC3(FpSub32_SPECIAL_VALID)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpSub32_VALID_VALID(cpu, dest, l, r);
|
|
}
|
|
|
|
NPXFUNC3(FpSub64_SPECIAL_VALID)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpSub64_VALID_VALID(cpu, dest, l, r);
|
|
}
|
|
|
|
NPXFUNC3(FpSub32_SPECIAL_SPECIAL)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpSub32_VALID_VALID(cpu, dest, l, r);
|
|
}
|
|
|
|
NPXFUNC3(FpSub64_SPECIAL_SPECIAL)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
//
|
|
// TAG_SPECIAL_INDEF, TAG_SPECIAL_QNAN, TAG_SPECIAL_DENORM, and
|
|
// TAG_SPECIAL_INFINITY have no special handling - just use the
|
|
// VALID_VALID code.
|
|
FpSub64_VALID_VALID(cpu, dest, l, r);
|
|
}
|
|
|
|
NPXFUNC3(FpSub_ANY_EMPTY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, r)) {
|
|
CALLNPXFUNC3(cpu->FpSubTable, l->Tag, TAG_SPECIAL, dest, l, r);
|
|
}
|
|
}
|
|
|
|
NPXFUNC3(FpSub_EMPTY_ANY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, l)) {
|
|
CALLNPXFUNC3(cpu->FpSubTable, TAG_SPECIAL, r->Tag, dest, l, r);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FpSubCommon(
|
|
PCPUDATA cpu,
|
|
PFPREG dest,
|
|
PFPREG l,
|
|
PFPREG r
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Implements dest = l-r
|
|
|
|
Arguments:
|
|
|
|
cpu - per-thread data
|
|
dest - destination FP register
|
|
l - left-hand FP register
|
|
r - right-hand FP register
|
|
|
|
Return Value:
|
|
|
|
None. l is updated to contain the value of l+r.
|
|
|
|
--*/
|
|
|
|
{
|
|
CALLNPXFUNC3(cpu->FpSubTable, l->Tag, r->Tag, dest, l, r);
|
|
}
|
|
|
|
|
|
|
|
NPXCOMFUNC(FpCom_VALID_VALID)
|
|
{
|
|
//
|
|
// Note that this function is called when one or both of the values
|
|
// is zero - the sign of 0.0 is ignored in the comparison, so the
|
|
// C language '==' and '<' operators do the Right Thing.
|
|
//
|
|
|
|
if (l->r64 == r->r64) {
|
|
cpu->FpStatusC3 = 1;
|
|
cpu->FpStatusC2 = 0;
|
|
cpu->FpStatusC0 = 0;
|
|
} else if (l->r64 < r->r64) {
|
|
cpu->FpStatusC3 = 0;
|
|
cpu->FpStatusC2 = 0;
|
|
cpu->FpStatusC0 = 1;
|
|
} else {
|
|
cpu->FpStatusC3 = 0;
|
|
cpu->FpStatusC2 = 0;
|
|
cpu->FpStatusC0 = 0;
|
|
}
|
|
}
|
|
|
|
NPXCOMFUNC(FpCom_VALID_SPECIAL)
|
|
{
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
|
|
if (r->TagSpecial == TAG_SPECIAL_QNAN || r->TagSpecial == TAG_SPECIAL_INDEF) {
|
|
//
|
|
// Cannot compare a VALID to a QNAN/INDEF
|
|
//
|
|
if (!fUnordered && HandleInvalidOp(cpu)) {
|
|
// abort the FCOM/FTST instruction - Illegal opcode is unmasked
|
|
return;
|
|
}
|
|
|
|
// Otherwise, FCOM's illegal opcode is masked, or the instruction
|
|
// is FUCOM, for which QNANs are uncomparable. Return "Not comparable"
|
|
cpu->FpStatusC3 = 1;
|
|
cpu->FpStatusC2 = 1;
|
|
cpu->FpStatusC0 = 1;
|
|
return;
|
|
}
|
|
|
|
CPUASSERT(r->TagSpecial == TAG_SPECIAL_DENORM || r->TagSpecial == TAG_SPECIAL_INFINITY);
|
|
FpCom_VALID_VALID(cpu, l, r, FALSE);
|
|
}
|
|
|
|
NPXCOMFUNC(FpCom_SPECIAL_VALID)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
|
|
if (l->TagSpecial == TAG_SPECIAL_QNAN || l->TagSpecial == TAG_SPECIAL_INDEF) {
|
|
//
|
|
// Cannot compare a VALID to a QNAN/INDEF
|
|
//
|
|
if (!fUnordered && HandleInvalidOp(cpu)) {
|
|
// abort the FCOM/FTST instruction - Illegal opcode is unmasked
|
|
return;
|
|
}
|
|
|
|
// Otherwise, FCOM's illegal opcode is masked, or the instruction
|
|
// is FUCOM, for which QNANs are uncomparable. Return "Not comparable"
|
|
cpu->FpStatusC3 = 1;
|
|
cpu->FpStatusC2 = 1;
|
|
cpu->FpStatusC0 = 1;
|
|
return;
|
|
}
|
|
|
|
CPUASSERT(l->TagSpecial == TAG_SPECIAL_DENORM || l->TagSpecial == TAG_SPECIAL_INFINITY);
|
|
FpCom_VALID_VALID(cpu, l, r, FALSE);
|
|
}
|
|
|
|
NPXCOMFUNC(FpCom_SPECIAL_SPECIAL)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
|
|
if (l->TagSpecial == TAG_SPECIAL_QNAN || l->TagSpecial == TAG_SPECIAL_INDEF ||
|
|
r->TagSpecial == TAG_SPECIAL_QNAN || r->TagSpecial == TAG_SPECIAL_INDEF) {
|
|
//
|
|
// Cannot compare a VALID to a QNAN/INDEF
|
|
//
|
|
if (!fUnordered && HandleInvalidOp(cpu)) {
|
|
// abort the FCOM/FTST instruction - Illegal opcode is unmasked
|
|
return;
|
|
}
|
|
|
|
// Otherwise, FCOM's illegal opcode is masked, or the instruction
|
|
// is FUCOM, for which QNANs are uncomparable. Return "Not comparable"
|
|
cpu->FpStatusC3 = 1;
|
|
cpu->FpStatusC2 = 1;
|
|
cpu->FpStatusC0 = 1;
|
|
return;
|
|
}
|
|
|
|
CPUASSERT((l->TagSpecial == TAG_SPECIAL_DENORM || l->TagSpecial == TAG_SPECIAL_INFINITY) &&
|
|
(r->TagSpecial == TAG_SPECIAL_DENORM || r->TagSpecial == TAG_SPECIAL_INFINITY));
|
|
FpCom_VALID_VALID(cpu, l, r, FALSE);
|
|
}
|
|
|
|
NPXCOMFUNC(FpCom_VALID_EMPTY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, r)) {
|
|
|
|
//
|
|
// r is now Indefinite, which can't be compared.
|
|
//
|
|
CPUASSERT(r->Tag == TAG_SPECIAL && r->TagSpecial == TAG_SPECIAL_INDEF);
|
|
if (!fUnordered && HandleInvalidOp(cpu)) {
|
|
// abort the FCOM/FTST instruction - Illegal opcode is unmasked
|
|
return;
|
|
}
|
|
|
|
// Otherwise, FCOM's illegal opcode is masked, or the instruction
|
|
// is FUCOM, for which QNANs are uncomparable. Return "Not comparable"
|
|
cpu->FpStatusC3 = 1;
|
|
cpu->FpStatusC2 = 1;
|
|
cpu->FpStatusC0 = 1;
|
|
}
|
|
}
|
|
|
|
NPXCOMFUNC(FpCom_EMPTY_VALID)
|
|
{
|
|
if (!HandleStackEmpty(cpu, l)) {
|
|
|
|
//
|
|
// l is now Indefinite, which can't be compared.
|
|
//
|
|
CPUASSERT(l->Tag == TAG_SPECIAL && l->TagSpecial == TAG_SPECIAL_INDEF);
|
|
if (!fUnordered && HandleInvalidOp(cpu)) {
|
|
// abort the FCOM/FTST instruction - Illegal opcode is unmasked
|
|
return;
|
|
}
|
|
|
|
// Otherwise, FCOM's illegal opcode is masked, or the instruction
|
|
// is FUCOM, for which QNANs are uncomparable. Return "Not comparable"
|
|
cpu->FpStatusC3 = 1;
|
|
cpu->FpStatusC2 = 1;
|
|
cpu->FpStatusC0 = 1;
|
|
}
|
|
}
|
|
|
|
NPXCOMFUNC(FpCom_EMPTY_SPECIAL)
|
|
{
|
|
if (!HandleStackEmpty(cpu, l)) {
|
|
(*FpComTable[TAG_SPECIAL][r->Tag])(cpu, l, r, fUnordered);
|
|
}
|
|
}
|
|
|
|
NPXCOMFUNC(FpCom_SPECIAL_EMPTY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, r)) {
|
|
(*FpComTable[r->Tag][TAG_SPECIAL])(cpu, l, r, fUnordered);
|
|
}
|
|
}
|
|
|
|
NPXCOMFUNC(FpCom_EMPTY_EMPTY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, l) && !HandleStackEmpty(cpu, r)) {
|
|
|
|
//
|
|
// l and r are both now Indefinite, which can't be compared.
|
|
//
|
|
CPUASSERT(l->Tag == TAG_SPECIAL && l->TagSpecial == TAG_SPECIAL_INDEF);
|
|
CPUASSERT(r->Tag == TAG_SPECIAL && r->TagSpecial == TAG_SPECIAL_INDEF);
|
|
if (!fUnordered && HandleInvalidOp(cpu)) {
|
|
// abort the FCOM/FTST instruction - Illegal opcode is unmasked
|
|
return;
|
|
}
|
|
|
|
// Otherwise, FCOM's illegal opcode is masked, or the instruction
|
|
// is FUCOM, for which QNANs are uncomparable. Return "Not comparable"
|
|
cpu->FpStatusC3 = 1;
|
|
cpu->FpStatusC2 = 1;
|
|
cpu->FpStatusC0 = 1;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FpComCommon(
|
|
PCPUDATA cpu,
|
|
PFPREG l,
|
|
PFPREG r,
|
|
BOOL fUnordered
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Implements l += r.
|
|
|
|
Arguments:
|
|
|
|
cpu - per-thread data
|
|
l - left-hand FP register
|
|
r - right-hand FP register
|
|
fUnordered - TRUE for unordered compares
|
|
|
|
Return Value:
|
|
|
|
None. l is updated to contain the value of l+r.
|
|
|
|
--*/
|
|
|
|
{
|
|
cpu->FpStatusC1 = 0; // assume no error
|
|
(*FpComTable[l->Tag][r->Tag])(cpu, l, r, fUnordered);
|
|
}
|
|
|
|
|
|
FRAG1(FADD32, FLOAT) // FADD m32real
|
|
{
|
|
FPREG m32real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR4(&m32real, pop1);
|
|
FpAddCommon(cpu, cpu->FpST0, &m32real);
|
|
}
|
|
|
|
FRAG1(FADD64, DOUBLE) // FADD m64real
|
|
{
|
|
FPREG m64real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR8(&m64real, pop1);
|
|
FpAddCommon(cpu, cpu->FpST0, &m64real);
|
|
}
|
|
|
|
FRAG1IMM(FADD_STi_ST, INT) // FADD ST(i), ST = add ST to ST(i)
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpAddCommon(cpu, &cpu->FpStack[ST(op1)], cpu->FpST0);
|
|
}
|
|
|
|
FRAG1IMM(FADD_ST_STi, INT) // FADD ST, ST(i) = add ST(i) to ST
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpAddCommon(cpu, cpu->FpST0, &cpu->FpStack[ST(op1)]);
|
|
}
|
|
|
|
FRAG1IMM(FADDP_STi_ST, INT) // FADDP ST(i), ST = add ST to ST(i) and pop ST
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpAddCommon(cpu, &cpu->FpStack[ST(op1)], cpu->FpST0);
|
|
POPFLT;
|
|
}
|
|
|
|
FRAG1(FIADD16, USHORT) // FIADD m16int
|
|
{
|
|
FPREG m16int;
|
|
short s;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
s = (short)GET_SHORT(pop1);
|
|
if (s) {
|
|
m16int.r64 = (DOUBLE)s;
|
|
m16int.Tag = TAG_VALID;
|
|
} else {
|
|
m16int.r64 = 0.0;
|
|
m16int.Tag = TAG_ZERO;
|
|
}
|
|
FpAddCommon(cpu, cpu->FpST0, &m16int);
|
|
}
|
|
|
|
FRAG1(FIADD32, ULONG) // FIADD m32int
|
|
{
|
|
FPREG m32int;
|
|
long l;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
l = (long)GET_LONG(pop1);
|
|
if (l) {
|
|
m32int.r64 = (DOUBLE)l;
|
|
m32int.Tag = TAG_VALID;
|
|
} else {
|
|
m32int.r64 = 0.0;
|
|
m32int.Tag = TAG_ZERO;
|
|
}
|
|
FpAddCommon(cpu, cpu->FpST0, &m32int);
|
|
}
|
|
|
|
|
|
FRAG1(FCOM32, FLOAT) // FCOM m32real
|
|
{
|
|
FPREG m32real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR4(&m32real, pop1);
|
|
FpComCommon(cpu, cpu->FpST0, &m32real, FALSE);
|
|
}
|
|
|
|
FRAG1(FCOM64, DOUBLE) // FCOM m64real
|
|
{
|
|
FPREG m64real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR8(&m64real, pop1);
|
|
FpComCommon(cpu, cpu->FpST0, &m64real, FALSE);
|
|
}
|
|
|
|
FRAG1IMM(FCOM_STi, INT) // FCOM ST(i)
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpComCommon(cpu, cpu->FpST0, &cpu->FpStack[ST(op1)], FALSE);
|
|
}
|
|
|
|
FRAG1(FCOMP32, FLOAT) // FCOMP m32real
|
|
{
|
|
FPREG m32real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR4(&m32real, pop1);
|
|
FpComCommon(cpu, cpu->FpST0, &m32real, FALSE);
|
|
POPFLT;
|
|
}
|
|
|
|
FRAG1(FCOMP64, DOUBLE) // FCOMP m64real
|
|
{
|
|
FPREG m64real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR8(&m64real, pop1);
|
|
FpComCommon(cpu, cpu->FpST0, &m64real, FALSE);
|
|
POPFLT;
|
|
}
|
|
|
|
FRAG1IMM(FCOMP_STi, INT) // FCOMP ST(i)
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpComCommon(cpu, cpu->FpST0, &cpu->FpStack[ST(op1)], FALSE);
|
|
POPFLT;
|
|
}
|
|
|
|
FRAG0(FCOMPP)
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpComCommon(cpu, cpu->FpST0, &cpu->FpStack[ST(1)], FALSE);
|
|
POPFLT;
|
|
POPFLT;
|
|
}
|
|
|
|
|
|
FRAG1(FDIV32, FLOAT) // FDIV m32real
|
|
{
|
|
FPREG m32real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR4(&m32real, pop1);
|
|
FpDivCommon(cpu, cpu->FpST0, cpu->FpST0, &m32real);
|
|
}
|
|
|
|
FRAG1(FDIV64, DOUBLE) // FDIV m64real
|
|
{
|
|
FPREG m64real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR8(&m64real, pop1);
|
|
FpDivCommon(cpu, cpu->FpST0, cpu->FpST0, &m64real);
|
|
}
|
|
|
|
FRAG1IMM(FDIV_ST_STi, INT) // FDIV ST, ST(i)
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpDivCommon(cpu, cpu->FpST0, cpu->FpST0, &cpu->FpStack[ST(op1)]);
|
|
}
|
|
|
|
FRAG1IMM(FDIV_STi_ST, INT) // FDIV ST(i), ST
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpDivCommon(cpu, &cpu->FpStack[ST(op1)], cpu->FpST0, &cpu->FpStack[ST(op1)]);
|
|
}
|
|
|
|
FRAG1(FIDIV16, USHORT) // FIDIV m16int
|
|
{
|
|
FPREG m16int;
|
|
short s;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
s = (short)GET_SHORT(pop1);
|
|
if (s) {
|
|
m16int.r64 = (DOUBLE)s;
|
|
m16int.Tag = TAG_VALID;
|
|
} else {
|
|
m16int.r64 = 0.0;
|
|
m16int.Tag = TAG_ZERO;
|
|
}
|
|
FpDivCommon(cpu, cpu->FpST0, cpu->FpST0, &m16int);
|
|
}
|
|
|
|
FRAG1(FIDIV32, ULONG) // FIDIV m32int
|
|
{
|
|
FPREG m32int;
|
|
long l;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
l = (long)GET_LONG(pop1);
|
|
if (l) {
|
|
m32int.r64 = (DOUBLE)l;
|
|
m32int.Tag = TAG_VALID;
|
|
} else {
|
|
m32int.r64 = 0.0;
|
|
m32int.Tag = TAG_ZERO;
|
|
}
|
|
FpDivCommon(cpu, cpu->FpST0, cpu->FpST0, &m32int);
|
|
}
|
|
|
|
FRAG1IMM(FDIVP_STi_ST, INT) // FDIVP ST(i), ST
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpDivCommon(cpu, &cpu->FpStack[ST(op1)], cpu->FpST0, &cpu->FpStack[ST(op1)]);
|
|
POPFLT;
|
|
}
|
|
|
|
FRAG1(FDIVR32, FLOAT) // FDIVR m32real
|
|
{
|
|
FPREG m32real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR4(&m32real, pop1);
|
|
FpDivCommon(cpu, cpu->FpST0, &m32real, cpu->FpST0);
|
|
}
|
|
|
|
FRAG1(FDIVR64, DOUBLE) // FDIVR m64real
|
|
{
|
|
FPREG m64real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR8(&m64real, pop1);
|
|
FpDivCommon(cpu, cpu->FpST0, &m64real, cpu->FpST0);
|
|
}
|
|
|
|
FRAG1IMM(FDIVR_ST_STi, INT) // FDIVR ST, ST(i)
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpDivCommon(cpu, cpu->FpST0, &cpu->FpStack[ST(op1)], cpu->FpST0);
|
|
}
|
|
|
|
FRAG1IMM(FDIVR_STi_ST, INT) // FDIVR ST(i), ST
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpDivCommon(cpu, &cpu->FpStack[ST(op1)], &cpu->FpStack[ST(op1)], cpu->FpST0);
|
|
}
|
|
|
|
FRAG1IMM(FDIVRP_STi_ST, INT) // FDIVRP ST(i)
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpDivCommon(cpu, &cpu->FpStack[ST(op1)], &cpu->FpStack[ST(op1)], cpu->FpST0);
|
|
POPFLT;
|
|
}
|
|
|
|
FRAG1(FIDIVR16, USHORT) // FIDIVR m16int
|
|
{
|
|
FPREG m16int;
|
|
short s;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
s = (short)GET_SHORT(pop1);
|
|
if (s) {
|
|
m16int.r64 = (DOUBLE)s;
|
|
m16int.Tag = TAG_VALID;
|
|
} else {
|
|
m16int.r64 = 0.0;
|
|
m16int.Tag = TAG_ZERO;
|
|
}
|
|
FpDivCommon(cpu, cpu->FpST0, &m16int, cpu->FpST0);
|
|
}
|
|
|
|
FRAG1(FIDIVR32, ULONG) // FIDIVR m32int
|
|
{
|
|
FPREG m32int;
|
|
long l;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
l = (long)GET_LONG(pop1);
|
|
if (l) {
|
|
m32int.r64 = (DOUBLE)l;
|
|
m32int.Tag = TAG_VALID;
|
|
} else {
|
|
m32int.r64 = 0.0;
|
|
m32int.Tag = TAG_ZERO;
|
|
}
|
|
FpDivCommon(cpu, cpu->FpST0, &m32int, cpu->FpST0);
|
|
}
|
|
|
|
FRAG1(FICOM16, USHORT) // FICOM m16int (Intel docs say m16real)
|
|
{
|
|
FPREG m16int;
|
|
short s;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
s = (short)GET_SHORT(pop1);
|
|
if (s) {
|
|
m16int.r64 = (DOUBLE)s;
|
|
m16int.Tag = TAG_VALID;
|
|
} else {
|
|
m16int.r64 = 0.0;
|
|
m16int.Tag = TAG_ZERO;
|
|
}
|
|
FpComCommon(cpu, cpu->FpST0, &m16int, FALSE);
|
|
}
|
|
|
|
FRAG1(FICOM32, ULONG) // FICOM m32int (Intel docs say m32real)
|
|
{
|
|
FPREG m32int;
|
|
long l;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
l = (long)GET_LONG(pop1);
|
|
if (l) {
|
|
m32int.r64 = (DOUBLE)l;
|
|
m32int.Tag = TAG_VALID;
|
|
} else {
|
|
m32int.r64 = 0.0;
|
|
m32int.Tag = TAG_ZERO;
|
|
}
|
|
FpComCommon(cpu, cpu->FpST0, &m32int, FALSE);
|
|
}
|
|
|
|
FRAG1(FICOMP16, USHORT) // FICOMP m16int
|
|
{
|
|
FPREG m16int;
|
|
short s;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
s = (short)GET_SHORT(pop1);
|
|
if (s) {
|
|
m16int.r64 = (DOUBLE)s;
|
|
m16int.Tag = TAG_VALID;
|
|
} else {
|
|
m16int.r64 = 0.0;
|
|
m16int.Tag = TAG_ZERO;
|
|
}
|
|
FpComCommon(cpu, cpu->FpST0, &m16int, FALSE);
|
|
POPFLT;
|
|
}
|
|
|
|
FRAG1(FICOMP32, ULONG) // FICOMP m32int
|
|
{
|
|
FPREG m32int;
|
|
long l;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
l = (long)GET_LONG(pop1);
|
|
if (l) {
|
|
m32int.r64 = (DOUBLE)l;
|
|
m32int.Tag = TAG_VALID;
|
|
} else {
|
|
m32int.r64 = 0.0;
|
|
m32int.Tag = TAG_ZERO;
|
|
}
|
|
FpComCommon(cpu, cpu->FpST0, &m32int, FALSE);
|
|
POPFLT;
|
|
}
|
|
|
|
FRAG1(FMUL32, FLOAT) // FMUL m32real
|
|
{
|
|
FPREG m32real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR4(&m32real, pop1);
|
|
FpMulCommon(cpu, cpu->FpST0, &m32real);
|
|
}
|
|
|
|
FRAG2(FMUL64, DOUBLE) // FMUL m64real
|
|
{
|
|
FPREG m64real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR8(&m64real, pop1);
|
|
FpMulCommon(cpu, cpu->FpST0, &m64real);
|
|
}
|
|
|
|
FRAG1IMM(FMUL_STi_ST, INT) // FMUL ST(i), ST
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpMulCommon(cpu, &cpu->FpStack[ST(op1)], cpu->FpST0);
|
|
}
|
|
|
|
FRAG1IMM(FMUL_ST_STi, INT) // FMUL ST, ST(i)
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpMulCommon(cpu, cpu->FpST0, &cpu->FpStack[ST(op1)]);
|
|
}
|
|
|
|
FRAG1IMM(FMULP_STi_ST, INT) // FMULP ST(i), ST
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpMulCommon(cpu, &cpu->FpStack[ST(op1)], cpu->FpST0);
|
|
POPFLT;
|
|
}
|
|
|
|
FRAG1(FIMUL16, USHORT) // FIMUL m16int
|
|
{
|
|
FPREG m16int;
|
|
short s;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
s = (short)GET_SHORT(pop1);
|
|
if (s) {
|
|
m16int.r64 = (DOUBLE)s;
|
|
m16int.Tag = TAG_VALID;
|
|
} else {
|
|
m16int.r64 = 0.0;
|
|
m16int.Tag = TAG_ZERO;
|
|
}
|
|
FpMulCommon(cpu, cpu->FpST0, &m16int);
|
|
}
|
|
|
|
FRAG1(FIMUL32, ULONG) // FIMUL m32int
|
|
{
|
|
FPREG m32int;
|
|
long l;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
l = (long)GET_LONG(pop1);
|
|
if (l) {
|
|
m32int.r64 = (DOUBLE)l;
|
|
m32int.Tag = TAG_VALID;
|
|
} else {
|
|
m32int.r64 = 0.0;
|
|
m32int.Tag = TAG_ZERO;
|
|
}
|
|
FpMulCommon(cpu, cpu->FpST0, &m32int);
|
|
}
|
|
|
|
FRAG1(FSUB32, FLOAT) // FSUB m32real
|
|
{
|
|
FPREG m32real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR4(&m32real, pop1);
|
|
FpSubCommon(cpu, cpu->FpST0, cpu->FpST0, &m32real);
|
|
}
|
|
|
|
FRAG1(FSUBP32, FLOAT) // FSUBP m32real
|
|
{
|
|
FPREG m32real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR4(&m32real, pop1);
|
|
FpSubCommon(cpu, cpu->FpST0, cpu->FpST0, &m32real);
|
|
POPFLT;
|
|
}
|
|
|
|
FRAG1(FSUB64, DOUBLE) // FSUB m64real
|
|
{
|
|
FPREG m64real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR8(&m64real, pop1);
|
|
FpSubCommon(cpu, cpu->FpST0, cpu->FpST0, &m64real);
|
|
}
|
|
|
|
FRAG1(FSUBP64, DOUBLE) // FSUBP m64real
|
|
{
|
|
FPREG m64real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR8(&m64real, pop1);
|
|
FpSubCommon(cpu, cpu->FpST0, cpu->FpST0, &m64real);
|
|
POPFLT;
|
|
}
|
|
|
|
FRAG1IMM(FSUB_ST_STi, INT) // FSUB ST, ST(i)
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpSubCommon(cpu, cpu->FpST0, cpu->FpST0, &cpu->FpStack[ST(op1)]);
|
|
}
|
|
|
|
FRAG1IMM(FSUB_STi_ST, INT) // FSUB ST(i), ST
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpSubCommon(cpu, &cpu->FpStack[ST(op1)], cpu->FpST0, &cpu->FpStack[ST(op1)]);
|
|
}
|
|
|
|
FRAG1IMM(FSUBP_STi_ST, INT) // FSUBP ST(i), ST
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpSubCommon(cpu, &cpu->FpStack[ST(op1)], cpu->FpST0, &cpu->FpStack[ST(op1)]);
|
|
POPFLT;
|
|
}
|
|
|
|
FRAG1(FISUB16, USHORT) // FISUB m16int
|
|
{
|
|
FPREG m16int;
|
|
short s;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
s = (short)GET_SHORT(pop1);
|
|
if (s) {
|
|
m16int.r64 = (DOUBLE)s;
|
|
m16int.Tag = TAG_VALID;
|
|
} else {
|
|
m16int.r64 = 0.0;
|
|
m16int.Tag = TAG_ZERO;
|
|
}
|
|
FpSubCommon(cpu, cpu->FpST0, cpu->FpST0, &m16int);
|
|
}
|
|
|
|
FRAG1(FISUB32, ULONG) // FISUB m32int
|
|
{
|
|
FPREG m32int;
|
|
long l;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
l = (long)GET_LONG(pop1);
|
|
if (l) {
|
|
m32int.r64 = (DOUBLE)l;
|
|
m32int.Tag = TAG_VALID;
|
|
} else {
|
|
m32int.r64 = 0.0;
|
|
m32int.Tag = TAG_ZERO;
|
|
}
|
|
FpSubCommon(cpu, cpu->FpST0, cpu->FpST0, &m32int);
|
|
}
|
|
|
|
FRAG1(FSUBR32, FLOAT) // FSUBR m32real
|
|
{
|
|
FPREG m32real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR4(&m32real, pop1);
|
|
FpSubCommon(cpu, cpu->FpST0, &m32real, cpu->FpST0);
|
|
}
|
|
|
|
FRAG1(FSUBR64, DOUBLE) // FSUBR m64real
|
|
{
|
|
FPREG m64real;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
GetIntelR8(&m64real, pop1);
|
|
FpSubCommon(cpu, cpu->FpST0, &m64real, cpu->FpST0);
|
|
}
|
|
|
|
FRAG1IMM(FSUBR_ST_STi, INT) // FSUBR ST, ST(i)
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpSubCommon(cpu, cpu->FpST0, &cpu->FpStack[ST(op1)], cpu->FpST0);
|
|
}
|
|
|
|
FRAG1IMM(FSUBR_STi_ST, INT) // FSUBR ST(i), ST
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpSubCommon(cpu, &cpu->FpStack[ST(op1)], &cpu->FpStack[ST(op1)], cpu->FpST0);
|
|
}
|
|
|
|
FRAG1IMM(FSUBRP_STi_ST, INT) // FSUBRP ST(i)
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpSubCommon(cpu, &cpu->FpStack[ST(op1)], &cpu->FpStack[ST(op1)], cpu->FpST0);
|
|
POPFLT;
|
|
}
|
|
|
|
FRAG1(FISUBR16, USHORT)
|
|
{
|
|
FPREG m16int;
|
|
short s;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
s = (short)GET_SHORT(pop1);
|
|
if (s) {
|
|
m16int.r64 = (DOUBLE)s;
|
|
m16int.Tag = TAG_VALID;
|
|
} else {
|
|
m16int.r64 = 0.0;
|
|
m16int.Tag = TAG_ZERO;
|
|
}
|
|
FpSubCommon(cpu, cpu->FpST0, &m16int, cpu->FpST0);
|
|
}
|
|
|
|
FRAG1(FISUBR32, ULONG)
|
|
{
|
|
FPREG m32int;
|
|
long l;
|
|
|
|
FpArithDataPreamble(cpu, pop1);
|
|
|
|
l = (long)GET_LONG(pop1);
|
|
if (l) {
|
|
m32int.r64 = (DOUBLE)l;
|
|
m32int.Tag = TAG_VALID;
|
|
} else {
|
|
m32int.r64 = 0.0;
|
|
m32int.Tag = TAG_ZERO;
|
|
}
|
|
FpSubCommon(cpu, cpu->FpST0, &m32int, cpu->FpST0);
|
|
}
|
|
|
|
FRAG0(FTST)
|
|
{
|
|
FPREG Zero;
|
|
|
|
FpArithPreamble(cpu);
|
|
|
|
Zero.r64 = 0.0;
|
|
Zero.Tag = TAG_ZERO;
|
|
FpComCommon(cpu, cpu->FpST0, &Zero, FALSE);
|
|
}
|
|
|
|
FRAG1IMM(FUCOM, INT) // FUCOM ST(i) / FUCOM
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpComCommon(cpu, cpu->FpST0, &cpu->FpStack[ST(op1)], TRUE);
|
|
}
|
|
|
|
FRAG1IMM(FUCOMP, INT) // FUCOMP ST(i) / FUCOMP
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpComCommon(cpu, cpu->FpST0, &cpu->FpStack[ST(op1)], TRUE);
|
|
POPFLT;
|
|
}
|
|
|
|
FRAG0(FUCOMPP)
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
FpComCommon(cpu, cpu->FpST0, &cpu->FpStack[ST(1)], TRUE);
|
|
POPFLT;
|
|
POPFLT;
|
|
}
|