486 lines
12 KiB
C
486 lines
12 KiB
C
/*++
|
|
|
|
Copyright (c) 1995-1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fpufprem.c
|
|
|
|
Abstract:
|
|
|
|
Floating point remainder fragments (FPREM, FPREM1)
|
|
|
|
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 "fragp.h"
|
|
#include "fpufrags.h"
|
|
#include "fpufragp.h"
|
|
|
|
//
|
|
// Forward references
|
|
//
|
|
NPXFUNC2(FPREM_VALID_VALID);
|
|
NPXFUNC2(FPREM_VALID_ZERO);
|
|
NPXFUNC2(FPREM_VALID_SPECIAL);
|
|
NPXFUNC2(FPREM_ZERO_VALIDORZERO);
|
|
NPXFUNC2(FPREM_ZERO_SPECIAL);
|
|
NPXFUNC2(FPREM_SPECIAL_VALIDORZERO);
|
|
NPXFUNC2(FPREM_SPECIAL_SPECIAL);
|
|
NPXFUNC2(FPREM_EMPTY_ANY);
|
|
NPXFUNC2(FPREM_ANY_EMPTY);
|
|
|
|
NPXFUNC2(FPREM1_VALID_VALID);
|
|
//NPXFUNC2(FPREM1_VALID_ZERO); // same as FPREM_VALID_ZERO
|
|
NPXFUNC2(FPREM1_VALID_SPECIAL);
|
|
//NPXFUNC2(FPREM1_ZERO_VALIDORZERO); // same as FPREM_ZERO_VALIDORZERO
|
|
NPXFUNC2(FPREM1_ZERO_SPECIAL);
|
|
NPXFUNC2(FPREM1_SPECIAL_VALIDORZERO);
|
|
NPXFUNC2(FPREM1_SPECIAL_SPECIAL);
|
|
NPXFUNC2(FPREM1_EMPTY_ANY);
|
|
NPXFUNC2(FPREM1_ANY_EMPTY);
|
|
|
|
|
|
//
|
|
// Jump tables
|
|
//
|
|
const NpxFunc2 FPREMTable[TAG_MAX][TAG_MAX] = {
|
|
// left is TAG_VALID, right is ...
|
|
{ FPREM_VALID_VALID, FPREM_VALID_ZERO, FPREM_VALID_SPECIAL, FPREM_ANY_EMPTY },
|
|
// left is TAG_ZERO, right is ...
|
|
{ FPREM_ZERO_VALIDORZERO, FPREM_ZERO_VALIDORZERO, FPREM_ZERO_SPECIAL, FPREM_ANY_EMPTY },
|
|
// left is TAG_SPECIAL, right is ...
|
|
{ FPREM_SPECIAL_VALIDORZERO, FPREM_SPECIAL_VALIDORZERO, FPREM_SPECIAL_SPECIAL, FPREM_ANY_EMPTY },
|
|
// left is TAG_EMPTY, right is ...
|
|
{ FPREM_EMPTY_ANY, FPREM_EMPTY_ANY, FPREM_EMPTY_ANY, FPREM_EMPTY_ANY }
|
|
};
|
|
|
|
const NpxFunc2 FPREM1Table[TAG_MAX][TAG_MAX] = {
|
|
// left is TAG_VALID, right is ...
|
|
{ FPREM1_VALID_VALID, FPREM_VALID_ZERO, FPREM1_VALID_SPECIAL, FPREM1_ANY_EMPTY },
|
|
// left is TAG_ZERO, right is ...
|
|
{ FPREM_ZERO_VALIDORZERO, FPREM_ZERO_VALIDORZERO, FPREM1_ZERO_SPECIAL, FPREM1_ANY_EMPTY },
|
|
// left is TAG_SPECIAL, right is ...
|
|
{ FPREM1_SPECIAL_VALIDORZERO, FPREM1_SPECIAL_VALIDORZERO, FPREM1_SPECIAL_SPECIAL, FPREM1_ANY_EMPTY },
|
|
// left is TAG_EMPTY, right is ...
|
|
{ FPREM1_EMPTY_ANY, FPREM1_EMPTY_ANY, FPREM1_EMPTY_ANY, FPREM1_EMPTY_ANY }
|
|
};
|
|
|
|
|
|
NPXFUNC2(FPREM_VALID_VALID)
|
|
{
|
|
int ExpL;
|
|
int ExpR;
|
|
int ExpDiff;
|
|
LONG Q;
|
|
double DQ;
|
|
|
|
ExpL = (int)((l->rdw[1] >> 20) & 0x7ff) - 1023;
|
|
ExpR = (int)((r->rdw[1] >> 20) & 0x7ff) - 1023;
|
|
ExpDiff = abs(ExpL-ExpR);
|
|
if (ExpDiff < 64) {
|
|
|
|
// Do the division and chop the integer result towards zero
|
|
DQ = r->r64 / l->r64;
|
|
if (DQ < 0) {
|
|
Q = (long)ceil(DQ);
|
|
} else {
|
|
Q = (long)floor(DQ);
|
|
}
|
|
|
|
// Store the remainder
|
|
r->r64 -= (DOUBLE)Q * l->r64;
|
|
SetTag(r);
|
|
|
|
// Store the status bits
|
|
if (Q < 0) {
|
|
//
|
|
// Take the absolute value of Q before returning the low 3 bits
|
|
// of the quotient.
|
|
//
|
|
Q = -Q;
|
|
}
|
|
cpu->FpStatusC2 = 0; // indicate the final remainder is ready
|
|
cpu->FpStatusC0 = (Q>>2) & 1;
|
|
cpu->FpStatusC3 = (Q>>1) & 1;
|
|
cpu->FpStatusC1 = Q & 1;
|
|
} else {
|
|
DOUBLE PowerOfTwo;
|
|
|
|
cpu->FpStatusC2 = 1; // indicate the app must loop more
|
|
PowerOfTwo = ldexp(1.0, ExpDiff-32); // get 2^(ExpDiff-32)
|
|
|
|
// get Q by chopping towards zero
|
|
DQ = (r->r64/PowerOfTwo) / (l->r64/PowerOfTwo);
|
|
if (DQ < 0) {
|
|
Q = (long)ceil(DQ);
|
|
} else {
|
|
Q = (long)floor(DQ);
|
|
}
|
|
r->r64 -= (DOUBLE)Q * l->r64 * PowerOfTwo;
|
|
SetTag(r);
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FPREM_VALID_ZERO)
|
|
{
|
|
// l is a number, but r is zero - return ST(0) unchanged
|
|
cpu->FpStatusC2 = 0; // indicate the final remainder is ready
|
|
// Q is 0, so store low 3 bits in the status word
|
|
cpu->FpStatusC0 = 0;
|
|
cpu->FpStatusC1 = 0;
|
|
cpu->FpStatusC3 = 0;
|
|
}
|
|
|
|
NPXFUNC2(FPREM_VALID_SPECIAL)
|
|
{
|
|
switch (l->TagSpecial) {
|
|
case TAG_SPECIAL_DENORM:
|
|
FPREM_VALID_VALID(cpu, l, r);
|
|
break;
|
|
|
|
case TAG_SPECIAL_INFINITY:
|
|
// Dividing infinity.
|
|
SetIndefinite(r);
|
|
break;
|
|
|
|
case TAG_SPECIAL_SNAN:
|
|
if (HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
// else fall into QNAN case
|
|
|
|
case TAG_SPECIAL_QNAN:
|
|
case TAG_SPECIAL_INDEF:
|
|
// r is the destination and it is a QNAN, while l is a VALID. Return
|
|
// the QNAN as the result of the operation
|
|
// x86 emulator leaves condition flags alone
|
|
break;
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FPREM_ZERO_VALIDORZERO)
|
|
{
|
|
// l is zero, and r is a number or zero - return INDEFINITE due to the
|
|
// division by zero.
|
|
if (!HandleInvalidOp(cpu)) {
|
|
SetIndefinite(r);
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FPREM_ZERO_SPECIAL)
|
|
{
|
|
if (r->TagSpecial == TAG_SPECIAL_INFINITY) {
|
|
SetIndefinite(r);
|
|
} else {
|
|
FPREM_VALID_SPECIAL(cpu, l, r);
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FPREM_SPECIAL_VALIDORZERO)
|
|
{
|
|
switch (l->TagSpecial) {
|
|
case TAG_SPECIAL_DENORM:
|
|
FPREM_VALID_VALID(cpu, l, r);
|
|
break;
|
|
|
|
case TAG_SPECIAL_INFINITY:
|
|
// number / infinity - quotient == 0
|
|
cpu->FpStatusC2 = 0;
|
|
cpu->FpStatusC0 = 0;
|
|
cpu->FpStatusC1 = 0;
|
|
cpu->FpStatusC3 = 0;
|
|
break;
|
|
|
|
case TAG_SPECIAL_SNAN:
|
|
if (HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
// else fall into QNAN case
|
|
|
|
case TAG_SPECIAL_QNAN:
|
|
case TAG_SPECIAL_INDEF:
|
|
// r is the destination and it is a VALID, while l is a NAN. Return
|
|
// the NAN as the result of the operation
|
|
r->r64 = l->r64;
|
|
r->Tag = l->Tag;
|
|
r->TagSpecial = l->TagSpecial;
|
|
// x86 emulator leaves condition flags alone
|
|
break;
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FPREM_SPECIAL_SPECIAL)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_DENORM) {
|
|
FPREM_VALID_SPECIAL(cpu, l, r);
|
|
return;
|
|
}
|
|
|
|
if (r->TagSpecial == TAG_SPECIAL_DENORM) {
|
|
FPREM_SPECIAL_VALIDORZERO(cpu, l, r);
|
|
}
|
|
|
|
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_INFINITY) {
|
|
if (r->TagSpecial == TAG_SPECIAL_INFINITY) {
|
|
SetIndefinite(r);
|
|
}
|
|
//
|
|
// r is a NAN of some sort, and l is infinity - return the NAN
|
|
// which is already in r.
|
|
//
|
|
} else {
|
|
//
|
|
// l is a NAN, and r is either a NAN or INFINITY. Have the native
|
|
// FPU return the largest NAN, and re-tag it as appropriate.
|
|
//
|
|
r->r64 = l->r64 + r->r64;
|
|
SetTag(r);
|
|
}
|
|
|
|
}
|
|
|
|
NPXFUNC2(FPREM_EMPTY_ANY)
|
|
{
|
|
if (HandleStackEmpty(cpu, l)) {
|
|
return;
|
|
}
|
|
(*FPREMTable[l->Tag][r->Tag])(cpu, l, r);
|
|
}
|
|
|
|
NPXFUNC2(FPREM_ANY_EMPTY)
|
|
{
|
|
if (HandleStackEmpty(cpu, l)) {
|
|
return;
|
|
}
|
|
(*FPREMTable[l->Tag][r->Tag])(cpu, l, r);
|
|
}
|
|
|
|
|
|
FRAG0(FPREM)
|
|
{
|
|
// get remainder of r/l
|
|
|
|
PFPREG l = &cpu->FpStack[ST(1)];
|
|
PFPREG r = cpu->FpST0;
|
|
|
|
FpArithPreamble(cpu);
|
|
(*FPREMTable[l->Tag][r->Tag])(cpu, l, r);
|
|
}
|
|
|
|
NPXFUNC2(FPREM1_VALID_VALID)
|
|
{
|
|
int ExpL;
|
|
int ExpR;
|
|
int ExpDiff;
|
|
LONG Q;
|
|
double DQ;
|
|
double FloorQ, CeilQ;
|
|
|
|
ExpL = (int)((l->rdw[1] >> 20) & 0x7ff) - 1023;
|
|
ExpR = (int)((r->rdw[1] >> 20) & 0x7ff) - 1023;
|
|
ExpDiff = abs(ExpL-ExpR);
|
|
if (ExpDiff < 64) {
|
|
|
|
// Do the division and get the integer nearest to the value
|
|
DQ = r->r64 / l->r64;
|
|
FloorQ = floor(DQ);
|
|
CeilQ = ceil(DQ);
|
|
if (DQ-FloorQ >= CeilQ-DQ) {
|
|
// CeilQ is closer - use it
|
|
Q = (long)CeilQ;
|
|
} else {
|
|
// FloorQ is closer - use it
|
|
Q = (long)FloorQ;
|
|
}
|
|
|
|
// Store the remainder
|
|
r->r64 -= (DOUBLE)Q * l->r64;
|
|
SetTag(r);
|
|
|
|
// Store the status bits
|
|
if (Q < 0) {
|
|
//
|
|
// Take the absolute value of Q before returning the low 3 bits
|
|
// of the quotient.
|
|
//
|
|
Q = -Q;
|
|
}
|
|
cpu->FpStatusC2 = 0; // indicate the final remainder is ready
|
|
cpu->FpStatusC0 = (Q>>2) & 1;
|
|
cpu->FpStatusC3 = (Q>>1) & 1;
|
|
cpu->FpStatusC1 = Q & 1;
|
|
} else {
|
|
DOUBLE PowerOfTwo;
|
|
|
|
cpu->FpStatusC2 = 1; // indicate the app must loop more
|
|
PowerOfTwo = ldexp(1.0, ExpDiff-32); // get 2^(ExpDiff-32)
|
|
|
|
// get Q by finding the integer nearest to the value
|
|
DQ = (r->r64/PowerOfTwo) / (l->r64/PowerOfTwo);
|
|
FloorQ = floor(DQ);
|
|
CeilQ = ceil(DQ);
|
|
if (DQ-FloorQ >= CeilQ-DQ) {
|
|
// CeilQ is closer - use it
|
|
Q = (long)CeilQ;
|
|
} else {
|
|
// FloorQ is closer - use it
|
|
Q = (long)FloorQ;
|
|
}
|
|
r->r64 -= (DOUBLE)Q * l->r64 * PowerOfTwo;
|
|
SetTag(r);
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FPREM1_VALID_SPECIAL)
|
|
{
|
|
switch (l->TagSpecial) {
|
|
case TAG_SPECIAL_DENORM:
|
|
FPREM1_VALID_VALID(cpu, l, r);
|
|
break;
|
|
|
|
case TAG_SPECIAL_INFINITY:
|
|
// dividing infinity
|
|
SetIndefinite(r);
|
|
break;
|
|
|
|
case TAG_SPECIAL_SNAN:
|
|
if (HandleSnan(cpu, r)) {
|
|
return;
|
|
}
|
|
// else fall into QNAN case
|
|
|
|
case TAG_SPECIAL_QNAN:
|
|
case TAG_SPECIAL_INDEF:
|
|
// r is the destination and it is a QNAN, while l is a VALID. Return
|
|
// the QNAN as the result of the operation
|
|
// x86 emulator leaves condition flags alone
|
|
break;
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FPREM1_ZERO_SPECIAL)
|
|
{
|
|
if (r->TagSpecial == TAG_SPECIAL_INFINITY) {
|
|
SetIndefinite(r);
|
|
} else {
|
|
FPREM1_VALID_SPECIAL(cpu, l, r);
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FPREM1_SPECIAL_VALIDORZERO)
|
|
{
|
|
switch (l->TagSpecial) {
|
|
case TAG_SPECIAL_DENORM:
|
|
FPREM1_VALID_VALID(cpu, l, r);
|
|
break;
|
|
|
|
case TAG_SPECIAL_INFINITY:
|
|
// number / infinity - quotient == 0
|
|
cpu->FpStatusC2 = 0;
|
|
cpu->FpStatusC0 = 0;
|
|
cpu->FpStatusC1 = 0;
|
|
cpu->FpStatusC3 = 0;
|
|
break;
|
|
|
|
case TAG_SPECIAL_SNAN:
|
|
if (HandleSnan(cpu, l)) {
|
|
return;
|
|
}
|
|
// else fall into QNAN case
|
|
|
|
case TAG_SPECIAL_QNAN:
|
|
case TAG_SPECIAL_INDEF:
|
|
// r is the destination and it is a VALID, while l is a NAN. Return
|
|
// the NAN as the result of the operation
|
|
r->r64 = l->r64;
|
|
r->Tag = l->Tag;
|
|
r->TagSpecial = l->TagSpecial;
|
|
break;
|
|
}
|
|
}
|
|
|
|
NPXFUNC2(FPREM1_SPECIAL_SPECIAL)
|
|
{
|
|
if (l->TagSpecial == TAG_SPECIAL_DENORM) {
|
|
FPREM1_VALID_SPECIAL(cpu, l, r);
|
|
return;
|
|
}
|
|
|
|
if (r->TagSpecial == TAG_SPECIAL_DENORM) {
|
|
FPREM1_SPECIAL_VALIDORZERO(cpu, l, r);
|
|
}
|
|
|
|
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_INFINITY) {
|
|
if (r->TagSpecial == TAG_SPECIAL_INFINITY) {
|
|
SetIndefinite(r);
|
|
}
|
|
//
|
|
// r is a NAN of some sort, and l is infinity - return the NAN
|
|
// which is already in r.
|
|
//
|
|
} else {
|
|
//
|
|
// l is a NAN, and r is either a NAN or INFINITY. Have the native
|
|
// FPU return the largest NAN, and re-tag it as appropriate.
|
|
//
|
|
r->r64 = l->r64 + r->r64;
|
|
SetTag(r);
|
|
}
|
|
|
|
}
|
|
|
|
NPXFUNC2(FPREM1_EMPTY_ANY)
|
|
{
|
|
if (HandleStackEmpty(cpu, l)) {
|
|
return;
|
|
}
|
|
(*FPREM1Table[l->Tag][r->Tag])(cpu, l, r);
|
|
}
|
|
|
|
NPXFUNC2(FPREM1_ANY_EMPTY)
|
|
{
|
|
if (HandleStackEmpty(cpu, l)) {
|
|
return;
|
|
}
|
|
(*FPREM1Table[l->Tag][r->Tag])(cpu, l, r);
|
|
}
|
|
FRAG0(FPREM1)
|
|
{
|
|
// get remainder of r/l
|
|
|
|
PFPREG l = &cpu->FpStack[ST(1)];
|
|
PFPREG r = cpu->FpST0;
|
|
|
|
FpArithPreamble(cpu);
|
|
(*FPREM1Table[l->Tag][r->Tag])(cpu, l, r);
|
|
}
|