/*
JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine
Release Version 2.4
A project from the Physics Dept, The University of Oxford
Copyright (C) 2007-2010 The University of Oxford
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as published by
the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Details (including contact information) can be found at:
jpc.sourceforge.net
or the developer website
sourceforge.net/projects/jpc/
Conceived and Developed by:
Rhys Newman, Ian Preston, Chris Dennis
End of licence header
*/
package org.jpc.emulator.processor.fpu64;
import org.jpc.emulator.processor.*;
import org.jpc.j2se.*;
import java.io.*;
import java.util.logging.*;
public class FpuState64 extends FpuState
{
private static final Logger LOGGING = Logger.getLogger(FpuState64.class.getName());
public final static int FPU_SPECIAL_TAG_NONE = 0;
public final static int FPU_SPECIAL_TAG_NAN = 1;
public final static int FPU_SPECIAL_TAG_UNSUPPORTED = 2;
public final static int FPU_SPECIAL_TAG_INFINITY = 3;
public final static int FPU_SPECIAL_TAG_DENORMAL = 4;
public final static int FPU_SPECIAL_TAG_SNAN = 5;
public final static double UNDERFLOW_THRESHOLD = Math.pow(2.0, -1022.0);
public static final double DEFAULT_NAN = -1.5; // Double.longBitsToDouble(0xFFF8000000000000L);// to match Bochs' floatx80_default_nan truncation
private final Processor cpu;
double[] data;
int[] tag;
int[] specialTag;
// status word
private int statusWord;
private boolean invalidOperation;
private boolean denormalizedOperand;
private boolean zeroDivide;
private boolean overflow;
private boolean underflow;
private boolean precision;
private boolean stackFault;
public void saveState(DataOutput output) throws IOException
{
output.writeInt(statusWord);
output.writeInt(maskWord);
output.writeInt(precisionControl);
output.writeInt(roundingControl);
output.writeBoolean(invalidOperation);
output.writeBoolean(denormalizedOperand);
output.writeBoolean(zeroDivide);
output.writeBoolean(overflow);
output.writeBoolean(underflow);
output.writeBoolean(precision);
output.writeBoolean(stackFault);
output.writeInt(data.length);
for (int i=0; i< data.length; i++)
output.writeDouble(data[i]);
output.writeInt(tag.length);
for (int i=0; i< tag.length; i++)
output.writeInt(tag[i]);
output.writeInt(specialTag.length);
for (int i=0; i< specialTag.length; i++)
output.writeInt(specialTag[i]);
}
public void loadState(DataInput input) throws IOException
{
statusWord = input.readInt();
maskWord = input.readInt();
precisionControl = input.readInt();
roundingControl = input.readInt();
invalidOperation = input.readBoolean();
denormalizedOperand = input.readBoolean();
zeroDivide = input.readBoolean();
overflow = input.readBoolean();
underflow = input.readBoolean();
precision = input.readBoolean();
stackFault = input.readBoolean();
int len = input.readInt();
data = new double[len];
for (int i=0; i< data.length; i++)
data[i] = input.readDouble();
len = input.readInt();
tag = new int[len];
for (int i=0; i< tag.length; i++)
tag[i] = input.readInt();
len = input.readInt();
specialTag = new int[len];
for (int i=0; i< specialTag.length; i++)
specialTag[i] = input.readInt();
}
public boolean getInvalidOperation() { return ((statusWord & 0x01) != 0); }
public boolean getDenormalizedOperand() { return ((statusWord&0x02) != 0); }
public boolean getZeroDivide() { return ((statusWord & 0x04) != 0); }
public boolean getOverflow() { return ((statusWord & 0x08) != 0); }
public boolean getUnderflow() { return ((statusWord & 0x10) != 0); }
public boolean getPrecision() { return ((statusWord & 0x20) != 0); }
public boolean getStackFault() { return ((statusWord & 0x40) != 0); }
public void setInvalidOperation() { statusWord |= 0x01;}
public void setDenormalizedOperand() { statusWord |= 0x02;}
public void setZeroDivide() { statusWord |= 0x04;}
public void setOverflow() { statusWord |= 0x08;}
public void setUnderflow() {statusWord |= 0x10;}
public void setPrecision() { statusWord |= 0x20;}
public void setStackFault() { statusWord |= 0x40;}
private static final boolean checkPendingExceptions = true;
public void prepareFPU(Processor cpu, boolean checkExceptions)
{
if (((cpu.getCR0() & Processor.CR0_FPU_EMULATION) != 0) || (((cpu.getCR0() & Processor.CR0_MONITOR_COPROCESSOR) != 0) && ((cpu.getCR0() & Processor.CR0_TASK_SWITCHED) != 0)))
throw ProcessorException.NO_FPU;
if (checkExceptions)
checkExceptions();
}
public void setC0(boolean val)
{
if (val)
conditionCode |= 1;
else
conditionCode &= ~0x1;
}
public void setC1(boolean val)
{
if (val)
conditionCode |= 2;
else
conditionCode &= ~2;
}
public void setC2(boolean val)
{
if (val)
conditionCode |= 4;
else
conditionCode &= ~4;
}
public void setC3(boolean val)
{
if (val)
conditionCode |= 8;
else
conditionCode &= ~8;
}
public boolean getBusy() { return getErrorSummaryStatus(); }
public boolean getErrorSummaryStatus()
{
// (note stack fault is a subset of invalid operation)
return (((statusWord & 0x3f) & ~maskWord) != 0);
}
public void checkExceptions() throws ProcessorException
{
if (getErrorSummaryStatus())
cpu.reportFPUException();
}
public void clearExceptions() { statusWord = 0; }
// control word
private int maskWord;
private int precisionControl;
private int roundingControl;
public boolean getInvalidOperationMask() { return ((maskWord & 1) != 0); }
public boolean getDenormalizedOperandMask() { return ((maskWord & 2) != 0);}
public boolean getZeroDivideMask() { return ((maskWord & 4) != 0); }
public boolean getOverflowMask() { return ((maskWord & 8) != 0); }
public boolean getUnderflowMask() { return ((maskWord & 0x10) != 0); }
public boolean getPrecisionMask() { return ((maskWord & 0x20) != 0); }
public int getPrecisionControl() { return precisionControl; }
public int getRoundingControl() { return roundingControl; }
public void setInvalidOperationMask(boolean value)
{
if (value) maskWord |= 1;
else maskWord &= ~1;
}
public void setDenormalizedOperandMask(boolean value)
{
if (value) maskWord |= 2;
else maskWord &= ~2;
}
public void setZeroDivideMask(boolean value)
{
if (value) maskWord |= 4;
else maskWord &= ~4;
}
public void setOverflowMask(boolean value)
{
if (value) maskWord |= 8;
else maskWord &= ~8;
}
public void setUnderflowMask(boolean value)
{
if (value) maskWord |= 0x10;
else maskWord &= ~0x10;
}
public void setPrecisionMask(boolean value)
{
if (value) maskWord |= 0x20;
else maskWord &= ~0x20;
}
public void setAllMasks(boolean value)
{
if (value) maskWord |= 0x3f;
else maskWord = 0;
}
public void setPrecisionControl(int value)
{
precisionControl = value & 3;
}
public void setRoundingControl(int value)
{
roundingControl = value & 3;
}
public FpuState64(Processor owner)
{
cpu = owner;
data = new double[STACK_DEPTH];
tag = new int[STACK_DEPTH];
specialTag = new int[STACK_DEPTH];
}
public void init()
{
//Bochs does these checks, but they stop many things booting!
if (Option.useBochs.isSet())
prepareFPU(cpu, !checkPendingExceptions);
for (int i = 0; i < tag.length; ++i)
tag[i] = FPU_TAG_EMPTY;
for (int i = 0; i < specialTag.length; ++i)
specialTag[i] = FPU_SPECIAL_TAG_NONE;
// status word
clearExceptions();
conditionCode = 0;
top = 0;
// control word
setAllMasks(true);
infinityControl = false;
setPrecisionControl(FPU_PRECISION_CONTROL_EXTENDED);
setRoundingControl(FPU_ROUNDING_CONTROL_EVEN); // default
lastIP = lastData = lastOpcode = 0;
}
public int tagCode(double x)
{
if (x == 0.0) return FPU_TAG_ZERO;
else if (Double.isNaN(x)||Double.isInfinite(x)) return FPU_TAG_SPECIAL;
else return FPU_TAG_VALID;
}
public static boolean isDenormal(double x)
{
long n = Double.doubleToRawLongBits(x);
int exponent = (int)((n >> 52) & 0x7ff);
if (exponent != 0) return false;
long fraction = (n & ~(0xfffL << 52));
if (fraction == 0L) return false;
return true;
}
public static boolean isSNaN(long n)
{
// have to determine this based on 64-bit bit pattern,
// since reassignment might cause Java to rationalize it to infinity
int exponent = (int)((n >> 52) & 0x7ff);
if (exponent != 0x7ff) return false;
long fraction = (n & ~(0xfffL << 52));
if ((fraction & (1L << 51)) != 0) return false;
return (fraction != 0L);
}
// SNaN's aren't generated internally by x87. Instead, they are
// detected when they are read in from memory. So if you push()
// from memory, find out before whether it's an SNaN, then push(),
// then set the tag word accordingly.
public static int specialTagCode(double x)
{
// decode special: NaN, unsupported, infinity, or denormal
if (Double.isNaN(x)) return FPU_SPECIAL_TAG_NAN; // QNaN by default
else if (Double.isInfinite(x)) return FPU_SPECIAL_TAG_INFINITY;
//else if (Math.abs(x) < UNDERFLOW_THRESHOLD)
else if (isDenormal(x))
return FPU_SPECIAL_TAG_DENORMAL;
else return FPU_SPECIAL_TAG_NONE;
}
public void push(double x) throws ProcessorException
{
if (--top < 0) top = STACK_DEPTH - 1;
if (tag[top] != FPU_TAG_EMPTY)
{
setInvalidOperation();
setStackFault();
conditionCode |= 2; // C1 set to indicate stack overflow
checkExceptions();
x = DEFAULT_NAN;
}
data[top] = x;
tag[top] = tagCode(x);
specialTag[top] = specialTagCode(x);
}
// public void pushBig(BigDecimal x) throws ProcessorException
// {
// push(x.doubleValue());
// }
public double pop() throws ProcessorException
{
if (tag[top] == FPU_TAG_EMPTY)
{
setInvalidOperation();
setStackFault();
conditionCode &= ~2; // C1 cleared to indicate stack underflow
checkExceptions();
// TODO: if IE masked, do we just return whatever
// random contents there are? That's what it seems
// from the reference.
}
else if (specialTag[top] == FPU_SPECIAL_TAG_SNAN)
{
setInvalidOperation();
checkExceptions();
return Double.NaN; // QNaN if masked
}
double x = data[top];
tag[top] = FPU_TAG_EMPTY;
if (++top >= STACK_DEPTH) top = 0;
return x;
}
// public BigDecimal popBig() throws ProcessorException
// {
// return new BigDecimal(pop());
// }
public double[] getStack()
{
double[] res = new double[8];
for (int i=0; i < 8; i++)
res[i] = data[(i+top)&7];
return res;
}
public void setStack(double[] s)
{
for (int i=0; i < 8; i++)
data[(i+top)&7] = s[i];
}
public double ST(int index) throws ProcessorException
{
int i = ((top + index) & 0x7);
if (tag[i] == FPU_TAG_EMPTY)
{
// an attempt to read an empty register is technically
// a "stack underflow"
setInvalidOperation();
setStackFault();
conditionCode &= ~2; // C1 cleared to indicate stack underflow
checkExceptions();
}
else if (specialTag[i] == FPU_SPECIAL_TAG_SNAN)
{
setInvalidOperation();
checkExceptions();
return Double.NaN; // QNaN if masked
}
return data[i];
}
// public BigDecimal bigST(int index) throws ProcessorException
// {
// return new BigDecimal(ST(index));
// }
public int getTag(int index)
{
int i = ((top + index) & 0x7);
return tag[i];
}
public int getSpecialTag(int index)
{
int i = ((top + index) & 0x7);
return specialTag[i];
}
public void setTagEmpty(int index)
{
// used by FFREE
int i = ((top + index) & 0x7);
tag[i] = FpuState.FPU_TAG_EMPTY;
}
public void setST(int index, double value)
{
int i = ((top + index) & 0x7);
data[i] = value;
tag[i] = tagCode(value);
specialTag[i] = specialTagCode(value);
}
public int getStatus()
{
int w = statusWord;
if (getErrorSummaryStatus()) w |= 0x80;
if (getBusy()) w |= 0x8000;
w |= (top << 11);
w |= ((conditionCode & 0x7) << 8);
w |= ((conditionCode & 0x8) << 11);
return w;
}
public void setStatus(int w)
{
statusWord &= ~0x7f;
statusWord |= (w & 0x7f);
top = ((w >> 11) & 0x7);
conditionCode = ((w >> 8) & 0x7);
conditionCode |= ((w >>> 14) & 1);
}
public int getControl()
{
int w = maskWord;
w |= ((precisionControl & 0x3) << 8);
w |= ((roundingControl & 0x3) << 10);
w |= 0x40; // reserved bit
if (infinityControl) w |= 0x1000;
return w;
}
public void setControl(int w)
{
maskWord &= ~0x3f;
maskWord |= (w & 0x3f);
infinityControl = ((w & 0x1000) != 0);
setPrecisionControl((w >> 8) & 3);
setRoundingControl((w >> 10) & 3);
}
public int getTagWord()
{
int w = 0;
for (int i = STACK_DEPTH - 1; i >= 0; --i)
w = ((w << 2) | (tag[i] & 0x3));
return w;
}
public void setTagWord(int w)
{
for (int i = 0; i < tag.length; ++i)
{
int t = (w & 0x3);
if (t == FPU_TAG_EMPTY)
{
tag[i] = FPU_TAG_EMPTY;
}
else
{
tag[i] = tagCode(data[i]);
if (specialTag[i] != FPU_SPECIAL_TAG_SNAN)
specialTag[i] = specialTagCode(data[i]);
// SNaN is sticky, and Java doesn't preserve the bit pattern.
}
w >>= 2;
}
}
public double round(double in)
{
if (!Double.isInfinite(in)) // preserve infinities
{
switch(getRoundingControl())
{
case FpuState.FPU_ROUNDING_CONTROL_EVEN:
return Math.rint(in);
case FpuState.FPU_ROUNDING_CONTROL_DOWN:
return Math.floor(in);
case FpuState.FPU_ROUNDING_CONTROL_UP:
return Math.ceil(in);
case FpuState.FPU_ROUNDING_CONTROL_TRUNCATE:
return Math.signum(in) * Math.floor(Math.abs(in));
default:
throw new IllegalStateException("Invalid rounding control value");
}
}
return in;
}
public static byte[] doubleToExtended(double x, boolean isSignalNaN)
{
byte[] b = new byte[10];
long fraction;
int iexp = 0;
// other special forms?
if (isSignalNaN)
{
fraction = 0xc000000000000000L;
}
else
{
long n = Double.doubleToRawLongBits(x);
fraction = (n & ~(0xfffL << 52));
iexp = ((int)(n >> 52) & 0x7ff);
boolean sgn = ((n & (1L << 63)) != 0);
// insert implicit 1
fraction |= (1L << 52);
fraction <<= 11;
// re-bias exponent
iexp += (16383 - 1023);
if (sgn) iexp |= 0x8000;
}
for (int i = 0; i < 8; ++i)
{
b[i] = (byte)fraction;
fraction >>>= 8;
}
b[8] = (byte)iexp;
b[9] = (byte)(iexp >> 8);
return b;
}
public static int specialTagCode(byte[] b)
{
long fraction = 0;
for (int i = 7; i >= 0; --i)
{
long w = ((long)b[i] & 0xff);
fraction |= w;
fraction <<= 8;
}
int iexp = (((int)b[8] & 0xff) | (((int)b[9] & 0x7f) << 8));
boolean sgn = ((b[9] & 0x80) != 0);
boolean integ = ((b[7] & 0x80) != 0); // explicit integer bit
if (iexp == 0)
{
if (integ)
{
// "pseudo-denormals" - treated like a normal denormal
return FPU_SPECIAL_TAG_DENORMAL;
}
else
{
// normal denormals
return FPU_SPECIAL_TAG_DENORMAL;
}
}
else if (iexp == 0x7fff)
{
if (fraction == 0L)
{
// "pseudo-infinity"
return FPU_SPECIAL_TAG_UNSUPPORTED;
}
else if (integ)
{
if ((fraction << 1) == 0)
{
// infinity
return FPU_SPECIAL_TAG_INFINITY;
}
else
{
// NaN's
if ((fraction >>> 62) == 0) return FPU_SPECIAL_TAG_SNAN;
else return FPU_SPECIAL_TAG_NAN;
}
}
else
{
// pseudo-NaN
return FPU_SPECIAL_TAG_UNSUPPORTED;
}
}
else
{
if (integ)
{
// normal float
return FPU_SPECIAL_TAG_NONE;
}
else
{
// "unnormal"
return FPU_SPECIAL_TAG_UNSUPPORTED;
}
}
}
public static double extendedToDouble(byte[] b)
{
long fraction = 0;
for (int i = 7; i >= 0; --i)
{
long w = ((long)b[i] & 0xff);
fraction |= w;
fraction <<= 8;
}
int iexp = (((int)b[8] & 0xff) | (((int)b[9] & 0x7f) << 8));
boolean sgn = ((b[9] & 0x80) != 0);
boolean integ = ((b[7] & 0x80) != 0); // explicit integer bit
if (iexp == 0)
{
if (integ)
{
// "pseudo-denormals" - treat exponent as value 1 and
// mantissa as the same
// (http://www.ragestorm.net/downloads/387intel.txt)
iexp = 1;
}
// now treat as a normal denormal (from denormal).
// actually, given that min unbiased exponent is -16383 for
// extended, and only -1023 for double, a denormalized
// extended is pretty much zero in double!
return 0.0;
}
else if (iexp == 0x7fff)
{
if (fraction == 0L)
{
// "pseudo-infinity": if #IA masked, return QNaN
// more technically, sign bit should be set to indicate
// "QNaN floating-point indefinite"
return Double.NaN;
}
else if (integ)
{
if ((fraction << 1) == 0)
{
return (sgn) ? Double.NEGATIVE_INFINITY :
Double.POSITIVE_INFINITY;
}
else
{
// a conventional NaN
return Double.NaN;
}
}
else
{
// pseudo-NaN
return Double.NaN;
}
}
else
{
if (integ)
{
// normal float: decode
iexp += 1023 - 16383; // rebias for double format
fraction >>>= 11; // truncate rounding (is this the right way?)
if (iexp > 0x7ff)
{
// too big an exponent
return (sgn) ? Double.NEGATIVE_INFINITY :
Double.POSITIVE_INFINITY;
}
else if (iexp < 0)
{
// denormal (from normal)
fraction >>>= (- iexp);
iexp = 0;
}
fraction &= ~(0xfffL << 52); // this cuts off explicit 1
fraction |= (((long)iexp & 0x7ff) << 52);
if (sgn) fraction |= (1L << 63);
return Double.longBitsToDouble(fraction);
}
else
{
// "unnormal": if #IA masked, return QNaN FP indefinite
return Double.NaN;
}
}
}
}