/*++

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