windows-nt/Source/XPSP1/NT/base/wow64/mscpu/fraglib/fpuarith.c
2020-09-26 16:20:57 +08:00

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;
}