You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
973 lines
21 KiB
973 lines
21 KiB
/*++
|
|
|
|
Copyright (c) 1995-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fputrig.c
|
|
|
|
Abstract:
|
|
|
|
Floating point trig and transcendental functions
|
|
|
|
Author:
|
|
|
|
05-Oct-1995 BarryBo
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <float.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include "wx86.h"
|
|
#include "cpuassrt.h"
|
|
#include "fragp.h"
|
|
#include "fpufragp.h"
|
|
|
|
ASSERTNAME;
|
|
|
|
//
|
|
// Forward declarations
|
|
//
|
|
NPXFUNC1(FCOS_VALID);
|
|
NPXFUNC1(FCOS_ZERO);
|
|
NPXFUNC1(FCOS_SPECIAL);
|
|
NPXFUNC1(FCOS_EMPTY);
|
|
NPXFUNC2(FPATAN_VALID_VALID);
|
|
NPXFUNC2(FPATAN_VALID_SPECIAL);
|
|
NPXFUNC2(FPATAN_SPECIAL_VALID);
|
|
NPXFUNC2(FPATAN_SPECIAL_SPECIAL);
|
|
NPXFUNC2(FPATAN_EMPTY_ALL);
|
|
NPXFUNC2(FPATAN_ALL_EMPTY);
|
|
NPXFUNC0(FPTAN_VALID);
|
|
NPXFUNC0(FPTAN_ZERO);
|
|
NPXFUNC0(FPTAN_SPECIAL);
|
|
NPXFUNC0(FSIN_VALID);
|
|
NPXFUNC0(FSIN_ZERO);
|
|
NPXFUNC0(FSIN_SPECIAL);
|
|
NPXFUNC0(FSIN_EMPTY);
|
|
NPXFUNC0(FSINCOS_VALID);
|
|
NPXFUNC0(FSINCOS_ZERO);
|
|
NPXFUNC0(FSINCOS_SPECIAL);
|
|
NPXFUNC2(FYL2X_VALID_VALID);
|
|
NPXFUNC2(FYL2X_VALID_ZERO);
|
|
NPXFUNC2(FYL2X_ZERO_VALID);
|
|
NPXFUNC2(FYL2X_ZERO_ZERO);
|
|
NPXFUNC2(FYL2X_SPECIAL_VALIDORZERO);
|
|
NPXFUNC2(FYL2X_VALIDORZERO_SPECIAL);
|
|
NPXFUNC2(FYL2X_SPECIAL_SPECIAL);
|
|
NPXFUNC2(FYL2X_ANY_EMPTY);
|
|
NPXFUNC2(FYL2X_EMPTY_ANY);
|
|
NPXFUNC2(FYL2XP1_VALIDORZERO_ZERO);
|
|
NPXFUNC2(FYL2XP1_VALIDORZERO_VALID);
|
|
NPXFUNC2(FYL2XP1_SPECIAL_VALIDORZERO);
|
|
NPXFUNC2(FYL2XP1_VALIDORZERO_SPECIAL);
|
|
NPXFUNC2(FYL2XP1_SPECIAL_SPECIAL);
|
|
NPXFUNC2(FYL2XP1_ANY_EMPTY);
|
|
NPXFUNC2(FYL2XP1_EMPTY_ANY);
|
|
NPXFUNC1(F2XM1_VALID);
|
|
NPXFUNC1(F2XM1_ZERO);
|
|
NPXFUNC1(F2XM1_SPECIAL);
|
|
NPXFUNC1(F2XM1_EMPTY);
|
|
|
|
|
|
//
|
|
// Jump tables
|
|
//
|
|
const NpxFunc1 FCOSTable[TAG_MAX] = {
|
|
FCOS_VALID,
|
|
FCOS_ZERO,
|
|
FCOS_SPECIAL,
|
|
FCOS_EMPTY
|
|
};
|
|
|
|
const NpxFunc2 FPATANTable[TAG_MAX][TAG_MAX] = {
|
|
// left = TAG_VALID, right is ...
|
|
{ FPATAN_VALID_VALID, FPATAN_VALID_VALID, FPATAN_VALID_SPECIAL, FPATAN_ALL_EMPTY },
|
|
// left = TAG_ZERO, right is ...
|
|
{ FPATAN_VALID_VALID, FPATAN_VALID_VALID, FPATAN_VALID_SPECIAL, FPATAN_ALL_EMPTY },
|
|
// left = TAG_SPECIAL, right is ...
|
|
{ FPATAN_SPECIAL_VALID, FPATAN_SPECIAL_VALID, FPATAN_SPECIAL_SPECIAL, FPATAN_ALL_EMPTY },
|
|
// left = TAG_EMPTY, right is ...
|
|
{ FPATAN_EMPTY_ALL, FPATAN_EMPTY_ALL, FPATAN_EMPTY_ALL, FPATAN_EMPTY_ALL }
|
|
};
|
|
|
|
const NpxFunc0 FPTANTable[TAG_MAX-1] = {
|
|
FPTAN_VALID,
|
|
FPTAN_ZERO,
|
|
FPTAN_SPECIAL
|
|
};
|
|
|
|
const NpxFunc0 FSINTable[TAG_MAX] = {
|
|
FSIN_VALID,
|
|
FSIN_ZERO,
|
|
FSIN_SPECIAL,
|
|
FSIN_EMPTY
|
|
};
|
|
|
|
const NpxFunc0 FSINCOSTable[TAG_MAX-1] = {
|
|
FSINCOS_VALID,
|
|
FSINCOS_ZERO,
|
|
FSINCOS_SPECIAL
|
|
};
|
|
|
|
// In the functions, l == ST(0), r = ST(1)
|
|
// r = r*log(l), l must be > 0
|
|
const NpxFunc2 FYL2XTable[TAG_MAX][TAG_MAX] = {
|
|
// left is TAG_VALID, right is ...
|
|
{ FYL2X_VALID_VALID, FYL2X_VALID_ZERO, FYL2X_VALIDORZERO_SPECIAL, FYL2X_ANY_EMPTY },
|
|
// left is TAG_ZERO, right is ...
|
|
{ FYL2X_ZERO_VALID, FYL2X_ZERO_ZERO, FYL2X_VALIDORZERO_SPECIAL, FYL2X_ANY_EMPTY },
|
|
// left is TAG_SPECIAL, right is ...
|
|
{ FYL2X_SPECIAL_VALIDORZERO, FYL2X_SPECIAL_VALIDORZERO, FYL2X_SPECIAL_SPECIAL, FYL2X_ANY_EMPTY },
|
|
// left is TAG_EMPTY, right is ...
|
|
{ FYL2X_EMPTY_ANY, FYL2X_EMPTY_ANY, FYL2X_EMPTY_ANY, FYL2X_EMPTY_ANY}
|
|
};
|
|
|
|
// In the functions, l == ST(0), r = ST(1)
|
|
// r = r*(logl+1), l must be > 1
|
|
const NpxFunc2 FYL2XP1Table[TAG_MAX][TAG_MAX] = {
|
|
// left is TAG_VALID, right is ...
|
|
{ FYL2XP1_VALIDORZERO_VALID, FYL2XP1_VALIDORZERO_ZERO, FYL2XP1_VALIDORZERO_SPECIAL, FYL2XP1_ANY_EMPTY },
|
|
// left is TAG_ZERO, right is ...
|
|
{ FYL2XP1_VALIDORZERO_VALID, FYL2XP1_VALIDORZERO_ZERO, FYL2XP1_VALIDORZERO_SPECIAL, FYL2X_ANY_EMPTY },
|
|
// left is TAG_SPECIAL, right is ...
|
|
{ FYL2XP1_SPECIAL_VALIDORZERO, FYL2XP1_SPECIAL_VALIDORZERO, FYL2XP1_SPECIAL_SPECIAL, FYL2XP1_ANY_EMPTY },
|
|
// left is TAG_EMPTY, right is ...
|
|
{ FYL2XP1_EMPTY_ANY, FYL2XP1_EMPTY_ANY, FYL2XP1_EMPTY_ANY, FYL2XP1_EMPTY_ANY}
|
|
};
|
|
|
|
const NpxFunc1 F2XM1Table[TAG_MAX] = {
|
|
F2XM1_VALID,
|
|
F2XM1_ZERO,
|
|
F2XM1_SPECIAL,
|
|
F2XM1_EMPTY
|
|
};
|
|
|
|
|
|
|
|
NPXFUNC1(FCOS_VALID)
|
|
{
|
|
Fp->r64 = cos(Fp->r64);
|
|
SetTag(Fp);
|
|
}
|
|
|
|
NPXFUNC1(FCOS_ZERO)
|
|
{
|
|
Fp->Tag = TAG_VALID;
|
|
Fp->r64 = 1.0;
|
|
}
|
|
|
|
NPXFUNC1(FCOS_SPECIAL)
|
|
{
|
|
switch (Fp->TagSpecial) {
|
|
case TAG_SPECIAL_DENORM:
|
|
FCOS_VALID(cpu, Fp);
|
|
break;
|
|
|
|
case TAG_SPECIAL_INFINITY:
|
|
cpu->FpStatusC2 = 1;
|
|
SetIndefinite(Fp);
|
|
break;
|
|
|
|
case TAG_SPECIAL_SNAN:
|
|
HandleSnan(cpu, Fp);
|
|
break;
|
|
|
|
case TAG_SPECIAL_QNAN:
|
|
case TAG_SPECIAL_INDEF:
|
|
HandleInvalidOp(cpu);
|
|
break;
|
|
}
|
|
}
|
|
|
|
NPXFUNC1(FCOS_EMPTY)
|
|
{
|
|
HandleStackEmpty(cpu, Fp);
|
|
}
|
|
|
|
FRAG0(FCOS)
|
|
{
|
|
PFPREG ST0;
|
|
FpArithPreamble(cpu);
|
|
|
|
cpu->FpStatusC2 = 0;
|
|
ST0 = cpu->FpST0;
|
|
(*FCOSTable[ST0->Tag])(cpu, ST0);
|
|
}
|
|
|
|
NPXFUNC2(FPATAN_VALID_VALID)
|
|
{
|
|
l->r64 = Proxyatan2(l->r64, r->r64);
|
|
SetTag(l);
|
|
POPFLT;
|
|
}
|
|
|
|
NPXFUNC2(FPATAN_VALID_SPECIAL)
|
|
{
|
|
switch (r->TagSpecial) {
|
|
case TAG_SPECIAL_DENORM:
|
|
case TAG_SPECIAL_INFINITY:
|
|
FPATAN_VALID_VALID(cpu, l, r);
|
|
break;
|
|
|
|
case TAG_SPECIAL_SNAN:
|
|
if (HandleSnan(cpu, r)) {
|
|
break;
|
|
}
|
|
// else fall into QNAN
|
|
|
|
case TAG_SPECIAL_QNAN:
|
|
case TAG_SPECIAL_INDEF:
|
|
// return the QNAN as the result
|
|
l->r64 = r->r64;
|
|
l->Tag = TAG_SPECIAL;
|
|
l->TagSpecial = r->TagSpecial;
|
|
POPFLT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FPATAN_SPECIAL_VALID)
|
|
{
|
|
switch (l->TagSpecial) {
|
|
case TAG_SPECIAL_DENORM:
|
|
case TAG_SPECIAL_INFINITY:
|
|
FPATAN_VALID_VALID(cpu, l, r);
|
|
break;
|
|
|
|
case TAG_SPECIAL_SNAN:
|
|
if (HandleSnan(cpu, l)) {
|
|
break;
|
|
}
|
|
// else fall into QNAN
|
|
|
|
case TAG_SPECIAL_QNAN:
|
|
case TAG_SPECIAL_INDEF:
|
|
// The QNAN is already in l, so nothing to do.
|
|
POPFLT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FPATAN_SPECIAL_SPECIAL)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
if (IS_TAG_NAN(l)) {
|
|
if (IS_TAG_NAN(r)) {
|
|
//
|
|
// Return the larger of the two NANs
|
|
//
|
|
l->r64 = l->r64 + r->r64;
|
|
SetTag(l);
|
|
}
|
|
//
|
|
// else l is a NAN and r isn't - return the NAN in l
|
|
//
|
|
POPFLT;
|
|
return;
|
|
}
|
|
if (IS_TAG_NAN(r)) {
|
|
// r is a NAN and l isn't - return the NAN in l
|
|
l->r64 = r->r64;
|
|
l->Tag = TAG_SPECIAL;
|
|
l->TagSpecial = r->TagSpecial;
|
|
POPFLT;
|
|
}
|
|
|
|
// Otherwise, l and r are both INFINITY. Return INDEFINITE
|
|
CPUASSERT(l->TagSpecial == TAG_SPECIAL_INFINITY &&
|
|
r->TagSpecial == TAG_SPECIAL_INFINITY);
|
|
SetIndefinite(l);
|
|
POPFLT;
|
|
}
|
|
|
|
NPXFUNC2(FPATAN_EMPTY_ALL)
|
|
{
|
|
if (!HandleStackEmpty(cpu, l)) {
|
|
(*FPATANTable[TAG_SPECIAL][r->Tag])(cpu, l, r);
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FPATAN_ALL_EMPTY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, r)) {
|
|
(*FPATANTable[l->Tag][TAG_SPECIAL])(cpu, l, r);
|
|
}
|
|
}
|
|
|
|
FRAG0(FPATAN)
|
|
{
|
|
PFPREG l = &cpu->FpStack[ST(1)];
|
|
PFPREG r = cpu->FpST0;
|
|
|
|
FpArithPreamble(cpu);
|
|
(*FPATANTable[l->Tag][r->Tag])(cpu, l, r);
|
|
}
|
|
|
|
|
|
NPXFUNC0(FPTAN_VALID)
|
|
{
|
|
int Exponent;
|
|
PFPREG ST0;
|
|
|
|
// get the exponent and make sure it is < 63
|
|
ST0 = cpu->FpST0;
|
|
Exponent = (int)((ST0->rdw[1] >> 20) & 0x7ff) - 1023;
|
|
if (Exponent >= 63) {
|
|
cpu->FpStatusC2 = 1;
|
|
return;
|
|
}
|
|
ST0->r64 = tan(ST0->r64);
|
|
SetTag(ST0);
|
|
PUSHFLT(ST0);
|
|
ST0->Tag = TAG_VALID;
|
|
ST0->r64 = 1.0;
|
|
}
|
|
|
|
NPXFUNC0(FPTAN_ZERO)
|
|
{
|
|
PFPREG ST0;
|
|
|
|
ST0=cpu->FpST0;
|
|
ST0->r64 = 0.0;
|
|
ST0->Tag = TAG_ZERO;
|
|
PUSHFLT(ST0);
|
|
ST0->r64 = 1.0;
|
|
ST0->Tag = TAG_VALID;
|
|
}
|
|
|
|
NPXFUNC0(FPTAN_SPECIAL)
|
|
{
|
|
if (cpu->FpST0->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, cpu->FpST0)) {
|
|
return;
|
|
} else if (cpu->FpST0->TagSpecial == TAG_SPECIAL_DENORM) {
|
|
FPTAN_VALID(cpu);
|
|
}
|
|
cpu->FpStatusC2 = 1;
|
|
}
|
|
|
|
FRAG0(FPTAN)
|
|
{
|
|
PFPREG ST0;
|
|
|
|
FpArithPreamble(cpu);
|
|
|
|
ST0 = cpu->FpST0;
|
|
|
|
//
|
|
// TAG_EMPTY is handled first so that we can check ST(7) before
|
|
// anything else has a chance to raise an exception.
|
|
//
|
|
if (ST0->Tag == TAG_EMPTY && HandleStackEmpty(cpu, ST0)) {
|
|
return;
|
|
}
|
|
|
|
if (cpu->FpStack[ST(7)].Tag != TAG_EMPTY) {
|
|
HandleStackFull(cpu, &cpu->FpStack[ST(7)]);
|
|
return;
|
|
}
|
|
|
|
// assume no error
|
|
cpu->FpStatusC2 = 0;
|
|
|
|
// calculate the value
|
|
CPUASSERT(ST0->Tag < TAG_EMPTY); // EMPTY was already handled
|
|
(*FPTANTable[ST0->Tag])(cpu);
|
|
}
|
|
|
|
NPXFUNC0(FSIN_VALID)
|
|
{
|
|
PFPREG ST0;
|
|
|
|
ST0 = cpu->FpST0;
|
|
ST0->r64 = sin(ST0->r64);
|
|
SetTag(ST0);
|
|
}
|
|
|
|
NPXFUNC0(FSIN_ZERO)
|
|
{
|
|
// sin(0.0) == 0.0, so there is nothing to do
|
|
}
|
|
|
|
NPXFUNC0(FSIN_SPECIAL)
|
|
{
|
|
if (cpu->FpST0->TagSpecial == TAG_SPECIAL_SNAN && HandleSnan(cpu, cpu->FpST0)) {
|
|
return;
|
|
} else if (cpu->FpST0->TagSpecial == TAG_SPECIAL_DENORM) {
|
|
FSIN_VALID(cpu);
|
|
}
|
|
cpu->FpStatusC2 = 1;
|
|
}
|
|
|
|
NPXFUNC0(FSIN_EMPTY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, cpu->FpST0)) {
|
|
cpu->FpStatusC2 = 1;
|
|
}
|
|
}
|
|
|
|
FRAG0(FSIN)
|
|
{
|
|
FpArithPreamble(cpu);
|
|
|
|
// assume no error
|
|
cpu->FpStatusC2 = 0;
|
|
|
|
// calculate the value
|
|
(*FSINTable[cpu->FpST0->Tag])(cpu);
|
|
}
|
|
|
|
NPXFUNC0(FSINCOS_VALID)
|
|
{
|
|
DOUBLE Val;
|
|
PFPREG ST0;
|
|
|
|
ST0 = cpu->FpST0;
|
|
Val = ST0->r64;
|
|
ST0->r64 = sin(Val);
|
|
SetTag(ST0);
|
|
PUSHFLT(ST0);
|
|
ST0->r64 = cos(Val);
|
|
SetTag(ST0);
|
|
}
|
|
|
|
NPXFUNC0(FSINCOS_ZERO)
|
|
{
|
|
PFPREG ST0;
|
|
|
|
ST0=cpu->FpST0;
|
|
ST0->r64 = 0.0;
|
|
ST0->Tag = TAG_ZERO;
|
|
PUSHFLT(ST0);
|
|
ST0->r64 = 1.0;
|
|
ST0->Tag = TAG_VALID;
|
|
}
|
|
|
|
NPXFUNC0(FSINCOS_SPECIAL)
|
|
{
|
|
switch (cpu->FpST0->TagSpecial) {
|
|
case TAG_SPECIAL_DENORM:
|
|
FSINCOS_VALID(cpu);
|
|
break;
|
|
|
|
case TAG_SPECIAL_INFINITY:
|
|
cpu->FpStatusC2 = 1;
|
|
SetIndefinite(cpu->FpST0);
|
|
break;
|
|
|
|
case TAG_SPECIAL_SNAN:
|
|
if (HandleSnan(cpu, cpu->FpST0)) {
|
|
return;
|
|
}
|
|
// else fall into TAG_SPECIAL_QNAN
|
|
|
|
case TAG_SPECIAL_QNAN:
|
|
case TAG_SPECIAL_INDEF:
|
|
cpu->FpStatusC2 = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
FRAG0(FSINCOS)
|
|
{
|
|
PFPREG ST0;
|
|
|
|
FpArithPreamble(cpu);
|
|
|
|
// assume no errors
|
|
cpu->FpStatusC2 = 0;
|
|
|
|
ST0 = cpu->FpST0;
|
|
if (ST0->Tag == TAG_EMPTY && HandleStackEmpty(cpu, ST0)) {
|
|
return;
|
|
}
|
|
|
|
if (cpu->FpStack[ST(7)].Tag != TAG_EMPTY) {
|
|
HandleStackFull(cpu, &cpu->FpStack[ST(7)]);
|
|
return;
|
|
}
|
|
|
|
CPUASSERT(ST0->Tag < TAG_EMPTY); // EMPTY was already handled
|
|
(*FSINCOSTable[ST0->Tag])(cpu);
|
|
}
|
|
|
|
NPXFUNC2(FYL2X_VALID_VALID)
|
|
{
|
|
if (l->r64 < 0.0) {
|
|
// ST0 is negative - invalid operation
|
|
if (!HandleInvalidOp(cpu)) {
|
|
SetIndefinite(r);
|
|
POPFLT;
|
|
}
|
|
return;
|
|
}
|
|
// r = r * log10(l->r64) / log10(2)
|
|
//
|
|
r->r64 *= Proxylog10(l->r64) / (0.301029995664);
|
|
SetTag(r);
|
|
POPFLT;
|
|
}
|
|
|
|
NPXFUNC2(FYL2X_VALID_ZERO)
|
|
{
|
|
if (l->r64 < 0.0) {
|
|
// ST0 is negative - invalid operation
|
|
if (!HandleInvalidOp(cpu)) {
|
|
SetIndefinite(r);
|
|
POPFLT;
|
|
}
|
|
return;
|
|
}
|
|
// r = r*log2(l), but r=0, so the answer is 0.
|
|
r->r64 = 0;
|
|
r->Tag = TAG_ZERO;
|
|
POPFLT;
|
|
}
|
|
|
|
NPXFUNC2(FYL2X_ZERO_VALID)
|
|
{
|
|
// Divide-by-zero error
|
|
cpu->FpStatusExceptions |= FPCONTROL_ZM;
|
|
if (cpu->FpControlMask & FPCONTROL_ZM) {
|
|
// Zero-divide exception is masked - return -INFINITY
|
|
r->r64 = R8NegativeInfinity;
|
|
r->Tag = TAG_SPECIAL;
|
|
r->TagSpecial = TAG_SPECIAL_INFINITY;
|
|
POPFLT;
|
|
} else {
|
|
cpu->FpStatusES = 1;
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FYL2X_ZERO_ZERO)
|
|
{
|
|
if (!HandleInvalidOp(cpu)) {
|
|
SetIndefinite(r);
|
|
POPFLT;
|
|
}
|
|
}
|
|
|
|
|
|
NPXFUNC2(FYL2X_SPECIAL_VALIDORZERO)
|
|
{
|
|
switch (l->TagSpecial) {
|
|
case TAG_SPECIAL_DENORM:
|
|
(*FYL2XTable[TAG_VALID][r->Tag])(cpu, l, r);
|
|
break;
|
|
|
|
case TAG_SPECIAL_INFINITY:
|
|
if (r->Tag == TAG_ZERO || r->rb[7] & 0x80) {
|
|
// 0*infinity, or anything*-infinity
|
|
SetIndefinite(r);
|
|
} else {
|
|
// return -infinity
|
|
r->rb[7] |= 0x80;
|
|
}
|
|
POPFLT;
|
|
break;
|
|
|
|
case TAG_SPECIAL_SNAN:
|
|
if (HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
// else fall into TAG_SPECIAL_QNAN
|
|
|
|
case TAG_SPECIAL_QNAN:
|
|
case TAG_SPECIAL_INDEF:
|
|
// l is a NAN and r is VALID or ZERO - return the NAN
|
|
r->r64 = l->r64;
|
|
r->Tag = l->Tag;
|
|
r->TagSpecial = r->TagSpecial;
|
|
POPFLT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FYL2X_VALIDORZERO_SPECIAL)
|
|
{
|
|
switch (r->TagSpecial) {
|
|
case TAG_SPECIAL_DENORM:
|
|
(*FYL2XTable[l->Tag][TAG_VALID])(cpu, l, r);
|
|
break;
|
|
|
|
case TAG_SPECIAL_INFINITY:
|
|
// log(x)*infinity
|
|
if (l->r64 > 1.0) {
|
|
// return the original infinity - nothing to do
|
|
} else if (l->r64 < 0.0 || l->r64 == 1.0) {
|
|
if (HandleInvalidOp(cpu)) {
|
|
return;
|
|
}
|
|
SetIndefinite(r);
|
|
} else {
|
|
// return infinity with sign flipped
|
|
r->rb[7] ^= 0x80;
|
|
}
|
|
|
|
POPFLT;
|
|
break;
|
|
|
|
case TAG_SPECIAL_SNAN:
|
|
if (HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
// else fall into TAG_SPECIAL_QNAN
|
|
|
|
case TAG_SPECIAL_QNAN:
|
|
case TAG_SPECIAL_INDEF:
|
|
// r is a NAN and l is VALID or ZERO - return the NAN
|
|
POPFLT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FYL2X_SPECIAL_SPECIAL)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleStackEmpty(cpu, l)) {
|
|
return;
|
|
}
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleStackEmpty(cpu, r)) {
|
|
return;
|
|
}
|
|
|
|
if (l->TagSpecial == TAG_SPECIAL_DENORM) {
|
|
(*FYL2XTable[TAG_VALID][r->Tag])(cpu, l, r);
|
|
return;
|
|
}
|
|
if (r->TagSpecial == TAG_SPECIAL_DENORM) {
|
|
(*FYL2XTable[l->Tag][TAG_VALID])(cpu, l, r);
|
|
return;
|
|
}
|
|
|
|
if (l->Tag == TAG_SPECIAL_INFINITY) {
|
|
|
|
if (r->Tag == TAG_SPECIAL_INFINITY) {
|
|
|
|
// two infinities - return INDEFINITE
|
|
SetIndefinite(r);
|
|
|
|
} else {
|
|
CPUASSERT(IS_TAG_NAN(r));
|
|
|
|
// r already has the NAN to return
|
|
}
|
|
} else {
|
|
CPUASSERT(IS_TAG_NAN(l));
|
|
|
|
if (r->Tag == TAG_SPECIAL_INFINITY) {
|
|
//
|
|
// Return the NAN from l
|
|
//
|
|
r->r64 = l->r64;
|
|
r->TagSpecial = l->TagSpecial;
|
|
} else {
|
|
//
|
|
// Return the largest of the two NANs
|
|
//
|
|
r->r64 = l->r64 + r->r64;
|
|
SetTag(r);
|
|
}
|
|
}
|
|
POPFLT;
|
|
}
|
|
|
|
|
|
NPXFUNC2(FYL2X_ANY_EMPTY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, r)) {
|
|
(*FYL2XTable[l->Tag][TAG_SPECIAL])(cpu, l, r);
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FYL2X_EMPTY_ANY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, l)) {
|
|
(*FYL2XTable[TAG_SPECIAL][r->Tag])(cpu, l, r);
|
|
}
|
|
}
|
|
|
|
FRAG0(FYL2X)
|
|
{
|
|
PFPREG l, r;
|
|
|
|
FpArithPreamble(cpu);
|
|
|
|
l = cpu->FpST0;
|
|
r = &cpu->FpStack[ST(1)];
|
|
|
|
// In the functions, l == ST(0), r = ST(1)
|
|
(*FYL2XTable[l->Tag][r->Tag])(cpu, l, r);
|
|
}
|
|
|
|
|
|
|
|
NPXFUNC2(FYL2XP1_VALIDORZERO_VALID)
|
|
{
|
|
if (l->r64 < -1.0) {
|
|
if (!HandleInvalidOp(cpu)) {
|
|
SetIndefinite(r);
|
|
POPFLT;
|
|
}
|
|
return;
|
|
} else if (l->r64 == -1.0) {
|
|
// Divide-by-zero error
|
|
cpu->FpStatusExceptions |= FPCONTROL_ZM;
|
|
if (cpu->FpControlMask & FPCONTROL_ZM) {
|
|
// Zero-divide exception is masked - return -INFINITY
|
|
r->r64 = R8NegativeInfinity;
|
|
r->Tag = TAG_SPECIAL;
|
|
r->TagSpecial = TAG_SPECIAL_INFINITY;
|
|
POPFLT;
|
|
} else {
|
|
cpu->FpStatusES = 1;
|
|
}
|
|
return;
|
|
}
|
|
// r = r * log10(l+1) / log10(2)
|
|
//
|
|
r->r64 *= Proxylog10(l->r64 + 1.0) / (0.301029995664);
|
|
SetTag(r);
|
|
POPFLT;
|
|
}
|
|
|
|
NPXFUNC2(FYL2XP1_VALIDORZERO_ZERO)
|
|
{
|
|
if (l->r64 < -1.0) {
|
|
if (!HandleInvalidOp(cpu)) {
|
|
SetIndefinite(r);
|
|
POPFLT;
|
|
}
|
|
return;
|
|
}
|
|
// r = r*log2(l), but r=0, so the answer is 0.
|
|
r->r64 = 0;
|
|
r->Tag = TAG_ZERO;
|
|
POPFLT;
|
|
}
|
|
|
|
NPXFUNC2(FYL2XP1_SPECIAL_VALIDORZERO)
|
|
{
|
|
switch (l->TagSpecial) {
|
|
case TAG_SPECIAL_DENORM:
|
|
(*FYL2XP1Table[TAG_VALID][r->Tag])(cpu, l, r);
|
|
break;
|
|
|
|
case TAG_SPECIAL_INFINITY:
|
|
if (r->Tag == TAG_ZERO || r->rb[7] & 0x80) {
|
|
if (HandleInvalidOp(cpu)) {
|
|
return;
|
|
}
|
|
|
|
// 0*infinity, or anything*-infinity
|
|
SetIndefinite(r);
|
|
} else {
|
|
// return -infinity
|
|
r->rb[7] |= 0x80;
|
|
}
|
|
POPFLT;
|
|
break;
|
|
|
|
case TAG_SPECIAL_SNAN:
|
|
if (HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
// else fall into TAG_SPECIAL_QNAN
|
|
|
|
case TAG_SPECIAL_QNAN:
|
|
case TAG_SPECIAL_INDEF:
|
|
// l is a NAN and r is VALID or ZERO - return the NAN
|
|
r->r64 = l->r64;
|
|
r->Tag = l->Tag;
|
|
r->TagSpecial = r->TagSpecial;
|
|
POPFLT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FYL2XP1_VALIDORZERO_SPECIAL)
|
|
{
|
|
switch (r->TagSpecial) {
|
|
case TAG_SPECIAL_DENORM:
|
|
(*FYL2XP1Table[l->Tag][TAG_VALID])(cpu, l, r);
|
|
break;
|
|
|
|
case TAG_SPECIAL_INFINITY:
|
|
// log(x)*infinity
|
|
if (l->r64 > 1.0) {
|
|
// return the original infinity - nothing to do
|
|
} else if (l->r64 < 0.0 || l->r64 == 1.0) {
|
|
if (HandleInvalidOp(cpu)) {
|
|
return;
|
|
}
|
|
SetIndefinite(r);
|
|
} else {
|
|
// return infinity with sign flipped
|
|
r->rb[7] ^= 0x80;
|
|
}
|
|
|
|
POPFLT;
|
|
break;
|
|
|
|
case TAG_SPECIAL_SNAN:
|
|
if (HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
// else fall into TAG_SPECIAL_QNAN
|
|
|
|
case TAG_SPECIAL_QNAN:
|
|
case TAG_SPECIAL_INDEF:
|
|
// r is a NAN and l is VALID or ZERO - return the NAN
|
|
POPFLT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FYL2XP1_SPECIAL_SPECIAL)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_SNAN && HandleStackEmpty(cpu, l)) {
|
|
return;
|
|
}
|
|
if (r->TagSpecial == TAG_SPECIAL_SNAN && HandleStackEmpty(cpu, r)) {
|
|
return;
|
|
}
|
|
|
|
if (l->TagSpecial == TAG_SPECIAL_DENORM) {
|
|
(*FYL2XP1Table[TAG_VALID][r->Tag])(cpu, l, r);
|
|
return;
|
|
}
|
|
if (r->TagSpecial == TAG_SPECIAL_DENORM) {
|
|
(*FYL2XP1Table[l->Tag][TAG_VALID])(cpu, l, r);
|
|
return;
|
|
}
|
|
|
|
if (l->Tag == TAG_SPECIAL_INFINITY) {
|
|
|
|
if (r->Tag == TAG_SPECIAL_INFINITY) {
|
|
|
|
if (l->rb[7] & 0x80) {
|
|
// l is negative infinity. Invalid op
|
|
if (HandleInvalidOp(cpu)) {
|
|
return;
|
|
}
|
|
SetIndefinite(r);
|
|
}
|
|
|
|
|
|
} else {
|
|
CPUASSERT(IS_TAG_NAN(r));
|
|
|
|
// r already has the NAN to return
|
|
}
|
|
} else {
|
|
CPUASSERT(IS_TAG_NAN(l));
|
|
|
|
if (r->Tag == TAG_SPECIAL_INFINITY) {
|
|
//
|
|
// Return the NAN from l
|
|
//
|
|
r->r64 = l->r64;
|
|
r->TagSpecial = l->TagSpecial;
|
|
} else {
|
|
//
|
|
// Return the largest of the two NANs
|
|
//
|
|
r->r64 = l->r64 + r->r64;
|
|
SetTag(r);
|
|
}
|
|
}
|
|
POPFLT;
|
|
}
|
|
|
|
|
|
NPXFUNC2(FYL2XP1_ANY_EMPTY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, r)) {
|
|
(*FYL2XP1Table[l->Tag][TAG_SPECIAL])(cpu, l, r);
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FYL2XP1_EMPTY_ANY)
|
|
{
|
|
if (!HandleStackEmpty(cpu, l)) {
|
|
(*FYL2XP1Table[TAG_SPECIAL][r->Tag])(cpu, l, r);
|
|
}
|
|
}
|
|
|
|
FRAG0(FYL2XP1)
|
|
{
|
|
PFPREG l, r;
|
|
|
|
FpArithPreamble(cpu);
|
|
|
|
l = cpu->FpST0;
|
|
r = &cpu->FpStack[ST(1)];
|
|
|
|
// In the functions, l == ST(0), r = ST(1)
|
|
(*FYL2XP1Table[l->Tag][r->Tag])(cpu, l, r);
|
|
}
|
|
|
|
|
|
|
|
NPXFUNC1(F2XM1_VALID)
|
|
{
|
|
Fp->r64 = pow(2.0, Fp->r64) - 1.0;
|
|
SetTag(Fp);
|
|
}
|
|
|
|
NPXFUNC1(F2XM1_ZERO)
|
|
{
|
|
// nothing to do - return the same zero
|
|
}
|
|
|
|
NPXFUNC1(F2XM1_SPECIAL)
|
|
{
|
|
switch (Fp->TagSpecial) {
|
|
case TAG_SPECIAL_DENORM:
|
|
F2XM1_VALID(cpu, Fp);
|
|
break;
|
|
|
|
case TAG_SPECIAL_INFINITY:
|
|
if (Fp->rb[7] & 0x80) {
|
|
// -infinity - return 1
|
|
Fp->r64 = 1.0;
|
|
Fp->Tag = TAG_VALID;
|
|
}
|
|
// else +infinity - return +infinity
|
|
break;
|
|
|
|
case TAG_SPECIAL_SNAN:
|
|
HandleSnan(cpu, Fp);
|
|
// fall into TAG_SPECIAL_QNAN
|
|
|
|
case TAG_SPECIAL_QNAN:
|
|
case TAG_SPECIAL_INDEF:
|
|
// return the NAN
|
|
break;
|
|
}
|
|
}
|
|
|
|
NPXFUNC1(F2XM1_EMPTY)
|
|
{
|
|
HandleStackEmpty(cpu, Fp);
|
|
}
|
|
|
|
FRAG0(F2XM1)
|
|
{
|
|
PFPREG ST0;
|
|
|
|
FpArithPreamble(cpu);
|
|
ST0 = cpu->FpST0;
|
|
(*F2XM1Table[ST0->Tag])(cpu, ST0);
|
|
}
|