pcjs/modules/pcx86/lib/x86fpu.js
2016-05-17 00:04:51 -07:00

3379 lines
91 KiB
JavaScript

/**
* @fileoverview Implements PCx86 8087 FPU logic.
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
* @version 1.0
* Created 2015-Nov-09
*
* Copyright © 2012-2016 Jeff Parsons <Jeff@pcjs.org>
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* PCjs 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 PCjs. If not,
* see <http://www.gnu.org/licenses/gpl.html>.
*
* You are required to include the above copyright notice in every source code file of every
* copy or modified version of this work, and to display that copyright notice on every screen
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
*
* Some PCjs files also attempt to load external resource files, such as character-image files,
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
* for purposes of the GNU General Public License, and the author does not claim any copyright
* as to their contents.
*
* FPU instruction description excerpts from the PC Magazine "Programmer's Technical Reference:
* The Processor and Coprocessor," Copyright 1992 by Ziff-Davis Press (ISBN 1-56276-016-5).
*/
"use strict";
if (NODE) {
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var State = require("./state");
var X86 = require("./x86");
}
/*
* Operand Type Reference
*
* ST(0), ST stack top; the register currently at the top of the stack
* ST(i) register in the stack i (0<=i<=7) stack elements from the top
* SR (short-real) short real (32 bits) number in memory; exponent bias is 127 (0x7f)
* LR (long-real) long real (64 bits) number in memory; exponent bias is 1023 (0x3ff)
* TR (temp-real) temporary real (80 bits) number in memory; exponent bias is 16383 (0x3fff)
* PD (packed-decimal) packed decimal integer (18 digits, 10 bytes) in memory
* WI (word-integer) word binary integer (16 bits) in memory
* SI (short-integer) short binary integer (32 bits) in memory
* LI (long-integer) long binary integer (64 bits) in memory
* NN (nn-bytes) memory area nn bytes long
*
* FPU Coprocessor Trivia
*
* Microsoft C 4.00 libraries executed software interrupts in the range 0x34-0x3B immediately after
* FPU operations, to assist with floating-point emulation when no coprocessor was present, since
* processors prior to the 80286 had no mechanism for generating a fault when an unsupported FPU
* instruction was executed.
*
* In short, INT 0x34 through INT 0x3B was used after ESC opcodes 0xD8 through 0xDF, INT 0x3C was
* used for FPU instructions containing a segment override, and INT 0x3D was used for FWAIT.
*
* A sample piece of code is available in x86ops.js, because it also highlights the Microsoft C 4.00
* library's dependency on the 8086/8088 behavior of "PUSH SP" (see the opPUSHSP_8086() function).
*/
/**
* X86FPU(parmsFPU)
*
* The X86FPU class uses the following (parmsFPU) properties:
*
* model: a number (eg, 8087) that should match one of the X86.FPU.MODEL values (default is 8087)
* stepping: a string (eg, "B1") that should match one of the X86.FPU.STEPPING values (default is "")
*
* @constructor
* @extends Component
* @param {Object} parmsFPU
*/
function X86FPU(parmsFPU)
{
Component.call(this, "FPU", parmsFPU, X86FPU);
this.model = parmsFPU['model'] || X86.FPU.MODEL_8087;
/*
* We take the 'stepping' value, convert it to a hex value, and then add that to the model to provide
* a single value that's unique for any given CPU stepping. If no stepping is provided, then stepping
* is equal to model.
*/
var stepping = parmsFPU['stepping'];
this.stepping = this.model + (stepping? str.parseInt(stepping, 16) : 0);
/*
* Perform a one-time allocation of all floating-point registers.
* NOTE: The FPU's internal registers are supposed to be 80-bit, but JavaScript gives us only 64-bit floats.
*/
this.regStack = new Float64Array(8);
this.intStack = new Int32Array(this.regStack.buffer);
/*
* Used for "short-real" (SR) 32-bit floating-point operations.
*/
this.regTmpSR = new Float32Array(1);
this.intTmpSR = new Int32Array(this.regTmpSR.buffer);
/*
* Used for "long-real" (LR) 64-bit floating-point operations. We also use intTmpLR as temporary storage
* for all "word-integer" (WI or INT16), "short-integer" (SI or INT32) and "long-integer" (LI or INT64) values,
* since it's just large enough to accommodate all three integer sizes.
*/
this.regTmpLR = new Float64Array(1);
this.intTmpLR = new Int32Array(this.regTmpLR.buffer);
/*
* Used for conversion to/from the 80-bit "temp-real" (TR) format; used as three 32-bit integers,
* where [0] contains TR bits 0-31, [1] contains TR bits 32-63, and [2] contains TR bits 64-79; the
* upper 16 bits of [2] are not used and should remain zero.
*/
this.intTmpTR = new Array(3);
/*
* Initialize other (non-floating-point) coprocessor registers that resetFPU() doesn't touch,
* such as the "exception" registers: regCodeSel, regCodeOff, regDataSel, regDataOff, and regOpcode.
*
* Note that regCodeSel and regDataSel are NEVER set in real-mode and are ALWAYS set in protected-mode,
* so we set them to -1 in their "unset" state; if those values ever show up in an exception block,
* something may have gone amiss (it's not impossible though, because if an exception occurs before any
* memory operands have been used, regDataSel may still be "unset").
*
* NOTE: iStack is the low 3 bits of the bModRM byte, for instructions that have an explicit stack operand.
*/
this.regCodeSel = this.regDataSel = -1;
this.regCodeOff = this.regDataOff = this.regOpcode = this.iStack = 0;
/*
* Initialize special floating-point constants, as if they were internal read-only registers;
* all other simple (non-special) constants are "statically" initialized below, as class constants.
*/
this.regIndefinite = new Float64Array(1);
this.intIndefinite = new Int32Array(this.regIndefinite.buffer);
this.intIndefinite[0] = 0x00000000; this.intIndefinite[1] = 0xFFF8000;
/*
* Initialize all other coprocessor registers (control word, tag word, status word, etc) by resetting them.
*/
this.resetFPU();
}
Component.subclass(X86FPU);
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {X86FPU}
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
*/
X86FPU.prototype.initBus = function(cmp, bus, cpu, dbg)
{
this.cpu = cpu;
this.chipset = cmp.getMachineComponent("ChipSet");
this.setReady();
};
/**
* clearBusy()
*
* The ChipSet calls us whenever an I/O operation that clears the coprocessor's "busy" state is performed.
*
* @this {X86FPU}
*/
X86FPU.prototype.clearBusy = function()
{
/*
* We're never "busy" as far as other components are concerned, because we perform all FPU operations
* synchronously, so there's nothing to do here.
*/
};
/**
* powerUp(data, fRepower)
*
* @this {X86FPU}
* @param {Object|null} data
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
X86FPU.prototype.powerUp = function(data, fRepower)
{
if (!fRepower) {
if (!data || !this.restore) {
this.resetFPU();
} else {
if (!this.restore(data)) return false;
}
}
return true;
};
/**
* powerDown(fSave, fShutdown)
*
* @this {X86FPU}
* @param {boolean} [fSave]
* @param {boolean} [fShutdown]
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
X86FPU.prototype.powerDown = function(fSave, fShutdown)
{
return fSave? this.save() : true;
};
/**
* save()
*
* This implements save support for the X86FPU component.
*
* @this {X86FPU}
* @return {Object}
*/
X86FPU.prototype.save = function()
{
var state = new State(this);
var a = [], i = 0;
a[i++] = this.regControl;
a[i++] = this.getStatus();
a[i++] = this.getTags();
/*
* Note that, unlike the FSAVE() and FRSTOR() operations, we save the registers in regStack in their physical
* order (0-7) rather than their logical order (ST0-ST7). Moreover, FSAVE() and FRSTOR() use the "temp-real" (TR)
* format, whereas we use the current native format -- which, sadly, is only a 64-bit "long-real" (LR) format.
*/
for (var iReg = 0; iReg < this.regStack.length; iReg++) {
a[i++] = this.regStack[iReg];
}
state.set(0, a);
return state.data();
};
/**
* restore(data)
*
* This implements restore support for the X86FPU component.
*
* @this {X86FPU}
* @param {Object} data
* @return {boolean} true if successful, false if failure
*/
X86FPU.prototype.restore = function(data)
{
var a = data[0], i = 0;
this.setControl(a[i++]);
this.setStatus(a[i++]);
this.setTags(a[i++]);
for (var iReg = 0; iReg < this.regStack.length; iReg++) {
this.regStack[iReg] = a[i++];
}
return true;
};
/**
* resetFPU()
*
* Aside from calling this internally (eg, during initialization and FINIT operations), the ChipSet may also call
* us whenever an I/O operation that resets the coprocessor is performed. Only 80487 coprocessors and higher will
* also clear the "exception" registers, but the 80487 is currently beyond my planned level of support.
*
* TODO: Add support for X86.FPU.CONTROL.PC (Precision Control) and X86.FPU.CONTROL.IC (Infinity Control)
*
* @this {X86FPU}
*/
X86FPU.prototype.resetFPU = function()
{
this.regUsed = 0; // bits 0-7 are set as regs 0-7 are used
this.regControl = X86.FPU.CONTROL.INIT;
this.regStatus = 0; // contains all status register bits EXCEPT for ST
this.iST = 0; // the ST bits for regStatus are actually stored here
if (DEBUG) {
/*
* All the registers were tagged "unused" above, which is all that would normally happen, but debugging is
* a little easier if we zero all the registers as well.
*/
for (var iReg = 0; iReg < this.regStack.length; iReg++) {
this.regStack[iReg] = 0.0;
}
}
if (this.chipset) this.chipset.clearFPUInterrupt();
};
/**
* isModel(model)
*
* If the current model is equal to the specified model, then it's assumed the current operation
* is supported, and we return true.
*
* @this {X86FPU}
* @param {number} model
* @return {boolean}
*/
X86FPU.prototype.isModel = function(model)
{
return this.model == model;
};
/**
* isAtLeastModel(model)
*
* If the current model is greater than or equal to the specified model, then it's assumed that the
* current operation is supported, and we return true.
*
* @this {X86FPU}
* @param {number} model
* @return {boolean}
*/
X86FPU.prototype.isAtLeastModel = function(model)
{
return this.model >= model;
};
/**
* getRandomInt(min, max)
*
* Used with old test code to verify that any randomly-constructed "long-real" (REAL64) could be converted
* to a "temp-real" (REAL80) and back again losslessly, otherwise a bug in either getTRFromLR() or getLRFromTR()
* might exist. That test code can be resurrected from the repo; this code is being retained for future tests.
*
* NOTE: If either min or max is a value containing 32 or more significant bits AND bit 31 is set AND it has passed
* through some bit-wise operation(s), then that value may end up being negative, so you may end up with an inverted
* range, or a range that's smaller or larger than intended.
*
* @this {X86FPU}
* @param {number} min (inclusive)
* @param {number} max (inclusive)
* @return {number}
*
X86FPU.prototype.getRandomInt = function(min, max)
{
max -= min;
if (max < 0) { // compensate for inverted ranges (ie, where min > max)
min += max;
max = -max;
}
return Math.floor(Math.random() * (max + 1)) + min;
};
*/
/**
* opStop(fError)
*
* Place this inside any opcode handler to stop the CPU from running the current instruction; eg:
*
* if (this.opStop()) return;
*
* You can still use the Debugger to single-step over the instruction; opStop() will return false in that case.
*
* @this {X86FPU}
* @param {boolean} [fError]
* @return {boolean} (true if there was an error or the CPU was running, false if not)
*/
X86FPU.prototype.opStop = function(fError)
{
if (DEBUG) {
var cpu = this.cpu;
if (fError || cpu.isRunning()) {
cpu.setIP(cpu.opLIP - cpu.segCS.base);
cpu.stopCPU();
return true;
}
}
return false;
};
/**
* opNone()
*
* Used for any coprocessor opcode that has no known operation for the given model.
*
* @this {X86FPU}
*/
X86FPU.prototype.opNone = function()
{
if (DEBUG) this.println(this.idComponent + ".opNone(" + str.toHexByte(this.cpu.bOpcode) + "," + str.toHexByte(this.cpu.bModRM) + ")");
this.opStop(true);
};
/**
* opObsolete()
*
* Used for any coprocessor opcodes that are redundant and potentially obsolete.
*
* @this {X86FPU}
*/
X86FPU.prototype.opObsolete = function()
{
if (DEBUG) this.println(this.idComponent + ".opObsolete(" + str.toHexByte(this.cpu.bOpcode) + "," + str.toHexByte(this.cpu.bModRM) + ")");
this.opStop(true);
};
/**
* opUnimplemented()
*
* Used for any coprocessor opcode that DOES have a known operation, we just haven't implemented it yet.
*
* @this {X86FPU}
*/
X86FPU.prototype.opUnimplemented = function()
{
if (DEBUG) this.println(this.idComponent + ".opUnimplemented(" + str.toHexByte(this.cpu.bOpcode) + "," + str.toHexByte(this.cpu.bModRM) + ")");
this.opStop(true);
};
/**
* checkException()
*
* @this {X86FPU}
* @return {boolean} (true if unmasked exception exists, false if not)
*/
X86FPU.prototype.checkException = function()
{
this.regStatus &= ~X86.FPU.STATUS.ES;
/*
* NOTE: The "Stack Fault" (SF) status bit wasn't introduced until the 80387, so it triggers the pre-existing
* "Invalid Operation" (IE) exception; there is no corresponding "Stack Fault" (SE) exception, and the matching
* control bit is still reserved. Consequently, X86.FPU.CONTROL.EXC is a *subset* of X86.FPU.STATUS.EXC (0x3F
* instead of 0x7F).
*
* However, we shouldn't have to do anything special when SF is set, because any setException() call that sets
* SF should ALSO set IE.
*/
if (this.regStatus & (~this.regControl & X86.FPU.CONTROL.EXC)) {
this.regStatus |= X86.FPU.STATUS.ES; // set ES whenever one or more unmasked EXC bits are set
}
if ((this.regStatus & X86.FPU.STATUS.ES) && !(this.regControl & X86.FPU.CONTROL.IEM)) {
this.chipset.setFPUInterrupt();
return true;
}
this.chipset.clearFPUInterrupt();
return false;
};
/**
* setException(n)
*
* Sets one or more of the FPU.STATUS.ECX bits; ie:
*
* IE (0x0001 bit 0: Invalid Operation)
* DE (0x0002 bit 1: Denormalized Operand)
* ZE (0x0004 bit 2: Zero Divide)
* OE (0x0008 bit 3: Overflow)
* UE (0x0010 bit 4: Underflow)
* PE (0x0020 bit 5: Precision)
* SF (0x0040 bit 6: Stack Fault; 80387 and later)
*
* Also, as noted in checkException(), any time you set the SF bit, you should also set the IE bit, because
* Stack Fault is a subset of Invalid Operation. TODO: We should include a test for that in the assertion below.
*
* @this {X86FPU}
* @param {number} n (one or more of the above error status bits)
* @return {boolean} (true if unmasked exception exists, false if not)
*/
X86FPU.prototype.setException = function(n)
{
if (DEBUG) this.println(this.idComponent + ".setException(" + str.toHexWord(n) + ")");
if (!this.isAtLeastModel(X86.FPU.MODEL_80387)) {
n &= ~X86.FPU.STATUS.SF; // the SF bit didn't exist on pre-80387 coprocessors
}
this.assert(!(n & ~X86.FPU.STATUS.EXC)); // make sure the caller isn't setting any non-EXC bits
this.regStatus |= n;
return this.checkException();
};
/**
* getControl()
*
* @this {X86FPU}
* @return {number}
*/
X86FPU.prototype.getControl = function()
{
return this.regControl;
};
/**
* setControl(n)
*
* NOTE: Be sure to use this function for all "wholesale" regControl updates, because it ensures that
* unused bits cannot be set -- including bit 6, which could otherwise inadvertently mask the SF error
* condition on 80387 and newer coprocessors.
*
* @this {X86FPU}
* @param {number} n
*/
X86FPU.prototype.setControl = function(n)
{
this.regControl = n & ~X86.FPU.CONTROL.UNUSED;
};
/**
* clearStatus(n)
*
* @this {X86FPU}
* @param {number} n
*/
X86FPU.prototype.clearStatus = function(n)
{
this.regStatus &= ~n;
this.checkException();
};
/**
* getStatus()
*
* @this {X86FPU}
* @return {number} regStatus merged with iST
*/
X86FPU.prototype.getStatus = function()
{
/*
* As long as we never store any ST bits in regStatus, they should always be zero, so in
* order to return the complete regStatus, all we need to do is shift and "or" the bits from iST.
*/
return this.regStatus | (this.iST << X86.FPU.STATUS.ST_SHIFT);
};
/**
* setStatus(n)
*
* NOTE: Be sure to use this function for all "wholesale" regStatus updates, because it ensures that
* the ST bits get propagated to the internal iST register. Setting individual EXC bits should be done
* through the fault() interface, and clearing individual EXC or BUSY bits should be done through
* clearStatus(). Both functions, including this function, call checkException() after updating regStatus.
*
* @this {X86FPU}
* @param {number} n
*/
X86FPU.prototype.setStatus = function(n)
{
this.regStatus = n & ~X86.FPU.STATUS.ST;
this.iST = (n & X86.FPU.STATUS.ST) >> X86.FPU.STATUS.ST_SHIFT;
this.checkException();
};
/**
* checkOperand(v)
*
* @this {X86FPU}
* @param {number|null} v
* @return {boolean} (true if no exception, false otherwise)
*/
X86FPU.prototype.checkOperand = function(v)
{
return isNaN(v)? !this.setException(X86.FPU.STATUS.IE) : true;
};
/**
* checkResult(v)
*
* @this {X86FPU}
* @param {number} v
* @return {boolean} (true if no exception, false otherwise)
*/
X86FPU.prototype.checkResult = function(v)
{
return !isFinite(v)? !this.setException(v === Infinity? X86.FPU.STATUS.OE : X86.FPU.STATUS.UE) : true;
};
/**
* doAdd(operand1, operand2)
*
* @this {X86FPU}
* @param {number|null} operand1
* @param {number|null} operand2
* @return {number|null}
*/
X86FPU.prototype.doAdd = function(operand1, operand2)
{
var result = null;
if (operand1 != null && operand2 != null) {
result = operand1 + operand2;
if (!this.checkResult(result)) result = null;
}
return result;
};
/**
* doSubtract(operand1, operand2)
*
* @this {X86FPU}
* @param {number|null} operand1
* @param {number|null} operand2
* @return {number|null}
*/
X86FPU.prototype.doSubtract = function(operand1, operand2)
{
var result = null;
if (operand1 != null && operand2 != null) {
result = operand1 - operand2;
if (!this.checkResult(result)) result = null;
}
return result;
};
/**
* doMultiply(operand1, operand2)
*
* @this {X86FPU}
* @param {number|null} operand1
* @param {number|null} operand2
* @return {number|null}
*/
X86FPU.prototype.doMultiply = function(operand1, operand2)
{
var result = null;
if (operand1 != null && operand2 != null) {
result = operand1 * operand2;
if (!this.checkResult(result)) result = null;
}
return result;
};
/**
* doDivide(dividend, divisor)
*
* TODO: IE exceptions: infinity / infinity, 0 / 0, 0 / pseudo-zero, or divisor is denormal or unnormal.
*
* @this {X86FPU}
* @param {number|null} dividend
* @param {number|null} divisor
* @return {number|null}
*/
X86FPU.prototype.doDivide = function(dividend, divisor)
{
var quotient = null;
if (dividend != null && divisor != null) {
if (divisor || !this.setException(X86.FPU.STATUS.DE)) {
quotient = dividend / divisor;
if (!this.checkResult(quotient)) quotient = null;
}
}
return quotient;
};
/**
* doCompare(operand1, operand2)
*
* @this {X86FPU}
* @param {number|null} operand1
* @param {number|null} operand2
* @return {boolean}
*/
X86FPU.prototype.doCompare = function(operand1, operand2)
{
if (operand1 != null && operand2 != null) {
var cc = 0; // default value used when result > 0
if (!isNaN(operand1) && !isNaN(operand2)) {
var result = operand1 - operand2;
if (result < 0) {
cc = X86.FPU.STATUS.C0;
} else if (result === 0) {
cc = X86.FPU.STATUS.C3;
}
} else {
cc = X86.FPU.STATUS.C0 | X86.FPU.STATUS.C2 | X86.FPU.STATUS.C3;
}
this.regStatus = (this.regStatus & ~X86.FPU.STATUS.CC) | cc;
return true;
}
return false;
};
/**
* doSquareRoot(operand)
*
* @this {X86FPU}
* @param {number|null} operand
* @return {number|null}
*/
X86FPU.prototype.doSquareRoot = function(operand)
{
var result = null;
/*
* Happily, -0 is ALSO >= 0. Also happily, Math.sqrt(-0) returns -0.
*/
if (operand >= 0 || !this.setException(X86.FPU.STATUS.IE)) {
result = Math.sqrt(operand);
if (!this.checkResult(result)) result = null;
}
return result;
};
/**
* roundValue(operand, max)
*
* NOTE: The max parameter is EXCLUSIVE, not inclusive (ie, the maximum positive value is < max).
*
* Also, callers that expect intTmpLR[] to be loaded with the result *must* also specify a max parameter;
* callers performing internal rounding and using just the return value may omit max to skip loading intTmpLR[].
*
* @this {X86FPU}
* @param {number|null} operand
* @param {number} [max] (ie, 0x8000, 0x80000000, or 0x8000000000000000)
* @return {number|null} (rounded result, or null if there was an unmasked exception)
*/
X86FPU.prototype.roundValue = function(operand, max)
{
if (operand == null) return null;
var rc = (this.regControl & X86.FPU.CONTROL.RC.MASK), result;
if (rc == X86.FPU.CONTROL.RC.NEAR) {
result = Math.round(operand);
if (result - operand === 0.5 && (result % 2)) result--;
}
else if (rc == X86.FPU.CONTROL.RC.DOWN || rc == X86.FPU.CONTROL.RC.CHOP && operand > 0) {
result = Math.floor(operand);
}
else { // X86.FPU.CONTROL.RC.UP or X86.FPU.CONTROL.RC.CHOP && operand <= 0
result = Math.ceil(operand);
}
if (max) {
if (result >= max) {
if (this.setException(X86.FPU.STATUS.IE)) return null;
result = -max; // apparently, the masked response is to return the most negative integer (not max - 1)
}
else if (result < -max) {
if (this.setException(X86.FPU.STATUS.IE)) return null;
result = -max;
}
this.intTmpLR[0] = result|0;
if (max > X86FPU.MAX_INT32) {
this.intTmpLR[1] = (result / 0x100000000)|0;
if (!this.intTmpLR[1] && result < 0) this.intTmpLR[1] = -1;
}
}
return result;
};
/**
* truncateValue(v)
*
* @this {X86FPU}
* @param {number} v
* @return {number}
*/
X86FPU.prototype.truncateValue = function(v)
{
return v > 0? Math.floor(v) : Math.ceil(v);
};
/**
* getTag(iReg)
*
* @this {X86FPU}
* @param {number} iReg (register index)
* @return {number} tag value for register
*/
X86FPU.prototype.getTag = function(iReg)
{
var bitUsed = (1 << iReg);
var tag = X86.FPU.TAGS.EMPTY;
if (this.regUsed & bitUsed) {
var f = this.regStack[iReg];
tag = X86.FPU.TAGS.VALID;
if (f === 0.0) {
tag = X86.FPU.TAGS.ZERO;
}
else if (!isFinite(f)) {
tag = X86.FPU.TAGS.SPECIAL;
}
}
return tag;
};
/**
* getTags()
*
* @this {X86FPU}
* @return {number} tag values for all registers
*/
X86FPU.prototype.getTags = function()
{
var tags = 0;
for (var iReg = this.regStack.length - 1; iReg >= 0; iReg--) {
tags <<= 2;
tags |= this.getTag(iReg);
}
return tags;
};
/**
* setTag(iReg, tag)
*
* @this {X86FPU}
* @param {number} iReg (register index)
* @param {number} tag value for register (EMPTY is the only supported value)
*/
X86FPU.prototype.setTag = function(iReg, tag)
{
this.assert(!(iReg & ~0x7) && tag == X86.FPU.TAGS.EMPTY);
this.regUsed &= ~(1 << iReg);
};
/**
* setTags(n)
*
* All we need to update here are which physical registers are marked "empty"; the rest of the tags
* are generated on the fly based on actual values in the registers.
*
* @this {X86FPU}
* @param {number} n (16-bit tag word, containing 8 2-bit tags)
*/
X86FPU.prototype.setTags = function(n)
{
this.regUsed = 0;
for (var bitUsed = 0x1; bitUsed <= 0x80; bitUsed <<= 1) {
var tag = n & X86.FPU.TAGS.MASK;
if (tag != X86.FPU.TAGS.EMPTY) {
this.regUsed |= bitUsed;
}
n >>= 2;
}
};
/**
* getWI(i)
*
* Gets a "word-integer" (WI aka INT16) from ST(i)
*
* @this {X86FPU}
* @param {number} i (eg, 0 for top-of-stack)
* @return {boolean} true if intTmpLR was loaded, false if not
*/
X86FPU.prototype.getWI = function(i)
{
return this.roundValue(this.getST(i), X86FPU.MAX_INT16) != null;
};
/**
* getSI(i)
*
* Gets a "short-integer" (SI aka INT32) from ST(i)
*
* @this {X86FPU}
* @param {number} i (eg, 0 for top-of-stack)
* @return {boolean} true if intTmpLR was loaded, false if not
*/
X86FPU.prototype.getSI = function(i)
{
return this.roundValue(this.getST(i), X86FPU.MAX_INT32) != null;
};
/**
* getLI(i)
*
* Gets a "long-integer" (LI aka INT64) from ST(i)
*
* @this {X86FPU}
* @param {number} i (eg, 0 for top-of-stack)
* @return {boolean} true if intTmpLR was loaded, false if not
*/
X86FPU.prototype.getLI = function(i)
{
return this.roundValue(this.getST(i), X86FPU.MAX_INT64) != null;
};
/**
* getSR(i)
*
* @this {X86FPU}
* @param {number} i (eg, 0 for top-of-stack)
* @return {boolean} true if regTmpSR was loaded, false if not
*/
X86FPU.prototype.getSR = function(i)
{
var iReg = (this.iST + i) & 7;
if (this.regUsed & (1 << iReg)) {
this.regTmpSR[0] = this.regStack[iReg];
return true;
} else if (!this.setException(X86.FPU.STATUS.IE)) {
this.regTmpSR[0] = this.regIndefinite[0];
return true;
}
return false;
};
/**
* getLR(i)
*
* @this {X86FPU}
* @param {number} i (eg, 0 for top-of-stack)
* @return {boolean} true if regTmpLR was loaded, false if not
*/
X86FPU.prototype.getLR = function(i)
{
var iReg = (this.iST + i) & 7;
if (this.regUsed & (1 << iReg)) {
this.regTmpLR[0] = this.regStack[iReg];
return true;
} else if (!this.setException(X86.FPU.STATUS.IE)) {
this.regTmpLR[0] = this.regIndefinite[0];
return true;
}
return false;
};
/**
* getST(i)
*
* @this {X86FPU}
* @param {number} i (eg, 0 for top-of-stack)
* @return {number|null} v
*/
X86FPU.prototype.getST = function(i)
{
var v = null;
var iReg = (this.iST + i) & 7;
if (this.regUsed & (1 << iReg)) {
v = this.regStack[iReg];
} else if (!this.setException(X86.FPU.STATUS.IE)) {
v = this.regIndefinite[0];
}
return v;
};
/**
* getSTSign(i)
*
* Returns zero if sign bit clear, and non-zero (negative) if sign bit set. This is safer
* than comparing getST() to zero, because JavaScript comparisons involving NaNs are meaningless.
*
* For internal use only; ignores whether the register is empty, and performs no exception checks.
*
* @this {X86FPU}
* @param {number} i (eg, 0 for top-of-stack)
* @return {number}
*/
X86FPU.prototype.getSTSign = function(i)
{
var iInt = ((this.iST + i) & 7) << 1;
return this.intStack[iInt + 1] & (0x80000000|0);
};
/**
* setST(i, v)
*
* @this {X86FPU}
* @param {number} i (eg, 0 for top-of-stack)
* @param {number|null} v
* @return {boolean}
*/
X86FPU.prototype.setST = function(i, v)
{
if (v != null && this.checkOperand(v)) {
var iReg = (this.iST + i) & 7;
this.regStack[iReg] = v;
this.regUsed |= (1 << iReg);
return true;
}
return false;
};
/**
* getTR(i, fSafe)
*
* @this {X86FPU}
* @param {number} i (stack index, 0-7)
* @param {boolean} [fSafe] (true to ignore all exception criteria; used by FSAVE)
* @return {Array.<number>|null} ("temp-real" aka TR, as an array of three 32-bit integers)
*/
X86FPU.prototype.getTR = function(i, fSafe)
{
var a = null;
var iReg = (this.iST + i) & 7;
if (fSafe || this.regUsed & (1 << iReg) || !this.setException(X86.FPU.STATUS.IE)) {
var iInt = iReg << 1;
a = this.getTRFromLR(this.intStack[iInt], this.intStack[iInt + 1]);
}
return a;
};
/**
* setTR(i, a)
*
* Sets ST(i) to the TR ("long-real") in a[].
*
* @this {X86FPU}
* @param {number} i (stack index, 0-7)
* @param {Array.<number>|null} a
*/
X86FPU.prototype.setTR = function(i, a)
{
if (a) this.setST(i, this.getLRFromTR(a));
};
/**
* getWIFromEA()
*
* Returns the (16-bit) "word-integer" value located at regEA.
*
* @this {X86FPU}
* @return {number} v
*/
X86FPU.prototype.getWIFromEA = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
return (this.cpu.getShort(this.cpu.regEA) << 16) >> 16;
};
/**
* getSIFromEA()
*
* Returns the (32-bit) "short-integer" value located at regEA.
*
* @this {X86FPU}
* @return {number} v
*/
X86FPU.prototype.getSIFromEA = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
return this.cpu.getLong(this.cpu.regEA);
};
/**
* getLIFromEA()
*
* Returns the (64-bit) "long-integer" value located at regEA.
*
* @this {X86FPU}
* @return {number} v
*/
X86FPU.prototype.getLIFromEA = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
var lo = this.cpu.getLong(this.cpu.regEA);
var hi = this.cpu.getLong(this.cpu.regEA + 4);
return (hi * 0x100000000) + (lo >>> 0);
};
/**
* getSRFromEA()
*
* Sets the internal regTmpSR register to the (32-bit) "short-real" value located at regEA.
*
* @this {X86FPU}
* @return {number} v
*/
X86FPU.prototype.getSRFromEA = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
this.intTmpSR[0] = this.cpu.getLong(this.cpu.regEA);
return this.regTmpSR[0];
};
/**
* getLRFromEA()
*
* Sets the internal regTmpLR register to the (64-bit) "long-real" value located at regEA.
*
* @this {X86FPU}
* @return {number} v
*/
X86FPU.prototype.getLRFromEA = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
this.intTmpLR[0] = this.cpu.getLong(this.cpu.regEA);
this.intTmpLR[1] = this.cpu.getLong(this.cpu.regEA + 4);
return this.regTmpLR[0];
};
/**
* getTRFromEA()
*
* Sets the internal intTmpTR register to the (80-bit) "temp-real" value located at regEA.
*
* @this {X86FPU}
* @return {Array.<number>} intTmpTR
*/
X86FPU.prototype.getTRFromEA = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
this.intTmpTR[0] = this.cpu.getLong(this.cpu.regEA);
this.intTmpTR[1] = this.cpu.getLong(this.cpu.regEA + 4);
this.intTmpTR[2] = this.cpu.getShort(this.cpu.regEA + 8);
return this.intTmpTR;
};
/**
* setEAFromWI()
*
* Stores the (16-bit) "word-integer" value in the internal intTmpLR register to the address in regEA.
*
* @this {X86FPU}
*/
X86FPU.prototype.setEAFromWI = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
this.cpu.setShort(this.cpu.regEA, this.intTmpLR[0]);
};
/**
* setEAFromSI()
*
* Stores the (32-bit) "short-integer" value in the internal intTmpLR register to the address in regEA.
*
* @this {X86FPU}
*/
X86FPU.prototype.setEAFromSI = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
this.cpu.setLong(this.cpu.regEA, this.intTmpLR[0]);
};
/**
* setEAFromLI()
*
* Stores the (64-bit) "long-integer" value in the internal intTmpLR register to the address in regEA.
*
* @this {X86FPU}
*/
X86FPU.prototype.setEAFromLI = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
this.cpu.setLong(this.cpu.regEA, this.intTmpLR[0]);
this.cpu.setLong(this.cpu.regEA + 4, this.intTmpLR[1]);
};
/**
* setEAFromSR()
*
* Stores the (32-bit) "short-real" value in the internal regTmpSR register to the address in regEA.
*
* @this {X86FPU}
*/
X86FPU.prototype.setEAFromSR = X86FPU.prototype.setEAFromSI;
/**
* setEAFromLR()
*
* Stores the (64-bit) "long-real" value in the internal regTmpLR register to the address in regEA.
*
* @this {X86FPU}
*/
X86FPU.prototype.setEAFromLR = X86FPU.prototype.setEAFromLI;
/**
* setEAFromTR()
*
* Stores the (80-bit) "temp-real" value in the internal intTmpTR register to the address in regEA.
*
* @this {X86FPU}
*/
X86FPU.prototype.setEAFromTR = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
this.cpu.setLong(this.cpu.regEA, this.intTmpTR[0]);
this.cpu.setLong(this.cpu.regEA + 4, this.intTmpTR[1]);
this.cpu.setShort(this.cpu.regEA + 8, this.intTmpTR[2]);
};
/**
* getLRFromTR(a)
*
* Since we must use the "long-real" (64-bit) format internally, rather than the "temp-real" (80-bit) format,
* this function converts a 64-bit value to an 80-bit value. The major differences: 1) the former uses a 52-bit
* fraction and 11-bit exponent, while the latter uses a 64-bit fraction and 15-bit exponent; 2) the former
* does NOT store a leading 1 with the fraction, whereas the latter does.
*
* @this {X86FPU}
* @param {Array.<number>} a (eg, intTmpTR)
* @return {number} v
*/
X86FPU.prototype.getLRFromTR = function(a)
{
var loTR = a[0], hiTR = a[1];
var signLR = (a[2] & 0x8000) >> 4, expLR = a[2] & 0x7fff;
/*
* We have no choice but to chop off the bottom 11 TR bits in order to fit in an LR....
*/
var loLR = (loTR >>> 11) | (hiTR << 21), hiLR = (hiTR >> 11) & 0xfffff;
if (expLR == 0x7fff) {
/*
* Convert an TR NaN to a LR NaN.
*/
expLR = 0x7ff;
}
else if (expLR) {
/*
* We have a normal (biased) TR exponent which we must now convert to a (biased) LR exponent;
* subtract the TR bias (0x3fff) and add the LR bias (0x3ff); additionally, we have a problem
* that getTRFromLR() did not: if the TR exponent is too large to fit in an LR exponent, then we
* have convert the result to +/- infinity.
*/
expLR += 0x3ff - 0x3fff;
if (expLR <= 0) {
expLR = 0x7ff;
loLR = hiLR = 0;
}
}
this.intTmpLR[0] = loLR;
this.intTmpLR[1] = hiLR | ((signLR | expLR) << 20);
return this.regTmpLR[0];
};
/**
* getTRFromLR(loLR, hiLR)
*
* Since we must use the "long-real" (64-bit) format internally, rather than the "temp-real" (80-bit) format,
* this function converts a 64-bit value to an 80-bit value. The major differences: 1) the former uses a 52-bit
* fraction and 11-bit exponent, while the latter uses a 64-bit fraction and 15-bit exponent; 2) the former
* does NOT store a leading 1 with the fraction, whereas the latter does.
*
* @this {X86FPU}
* @param {number} loLR
* @param {number} hiLR
* @return {Array.<number>} (intTmpTR)
*/
X86FPU.prototype.getTRFromLR = function(loLR, hiLR)
{
var expTR = (hiLR >> 20) & 0x07ff;
var signTR = (hiLR >> 16) & 0x8000;
var loTR = loLR << 11, hiTR = 0x80000000 | ((hiLR & 0x000fffff) << 11) | (loLR >>> 21);
if (expTR == 0x07ff) {
/*
* Convert an LR NaN to a TR NaN. NaNs encompass +/- infinity, which in the LR
* world are fractions of all zeros. NaNs also encompass indefinite, which in the LR
* world are negative numbers with only the high fraction bit set. So, in both cases,
* our default TR value (ie, with zeros shifted into the bottom 11 bits) should be fine;
* we simply need to change the exponent to the maximum TR value.
*/
expTR = 0x7fff;
}
else if (!expTR) {
/*
* An LR with an exponent of zero could be an actual +/- zero, if the fraction is zero,
* or it could be a denormal, if the fraction is non-zero. In both cases, the only
* change we need to make the TR form is clearing the leading 1 bit.
*/
hiTR &= 0x7fffffff;
}
else {
/*
* We have a normal (biased) LR exponent which we must now convert to a (biased) TR exponent;
* subtract the LR bias (0x3ff) and add the TR bias (0x3fff).
*/
expTR += 0x3fff - 0x3ff;
}
this.intTmpTR[0] = loTR;
this.intTmpTR[1] = hiTR;
this.intTmpTR[2] = signTR | expTR;
return this.intTmpTR;
};
/**
* decodeBCD()
*
* @this {X86FPU}
* @param {number} i (32-bit integer containing n BCD digits)
* @param {number} n (number of BCD digits to decode)
* @return {number} (binary value representing the specified number of BCD digits)
*/
X86FPU.prototype.decodeBCD = function(i, n)
{
var v = 0, m = 1;
this.assert(n > 0 && n <= 8);
while (n--) {
var d = i & 0xf;
this.assert(d <= 9);
v += d * m;
m *= 10;
i >>= 4;
}
return v;
};
/**
* encodeBCD()
*
* @this {X86FPU}
* @param {number} v (binary value from which to extract n BCD digits)
* @param {number} n (number of BCD digits to extract)
* @return {number} (integer containing the requested number of BCD digits)
*/
X86FPU.prototype.encodeBCD = function(v, n)
{
var i = 0, s = 0;
this.assert(n > 0 && n <= 8);
while (n--) {
i |= (v % 10) << s;
v /= 10;
s += 4;
}
return i;
};
/**
* popValue()
*
* @this {X86FPU}
* @return {number|null} v
*/
X86FPU.prototype.popValue = function()
{
var v = null;
var bitUsed = (1 << this.iST);
if (!(this.regUsed & bitUsed)) {
this.regStatus &= ~X86.FPU.STATUS.C1; // clear C1 to indicate stack underflow (80287XL and up)
if (this.setException(X86.FPU.STATUS.SF | X86.FPU.STATUS.IE)) return v;
}
this.regUsed &= ~bitUsed;
v = this.regStack[this.iST];
this.iST = (this.iST + 1) & 7;
return v;
};
/**
* pushValue(v)
*
* @this {X86FPU}
* @param {number|null} v
*/
X86FPU.prototype.pushValue = function(v)
{
if (v == null) return;
var iReg = (this.iST - 1) & 7;
var bitUsed = (1 << iReg);
if (this.regUsed & bitUsed) {
this.regStatus |= X86.FPU.STATUS.C1; // set C1 to indicate stack overflow (80287XL and up)
if (this.setException(X86.FPU.STATUS.SF | X86.FPU.STATUS.IE)) return;
}
if (!this.checkOperand(v)) {
if (this.setException(X86.FPU.STATUS.IE)) return;
v = NaN;
}
this.regStack[this.iST = iReg] = v;
this.regUsed |= bitUsed;
};
/**
* loadEnv(addr)
*
* @this {X86FPU}
* @param {number} addr
* @return {number} updated addr
*/
X86FPU.prototype.loadEnv = function(addr)
{
var w;
var cpu = this.cpu;
this.setControl(cpu.getWord(addr));
this.setStatus(cpu.getWord(addr += cpu.sizeData));
this.setTags(cpu.getWord(addr += cpu.sizeData));
if (!(cpu.regCR0 & X86.CR0.MSW.PE) || (cpu.regPS & X86.PS.VM)) {
this.regCodeOff = cpu.getWord(addr += cpu.sizeData);
w = cpu.getWord(addr += cpu.sizeData);
this.regOpcode = w & 0x7ff;
this.regCodeOff |= (w & ~0xfff) << 4;
this.regCodeSel = -1;
this.regDataOff = cpu.getWord(addr += cpu.sizeData);
this.regDataOff |= (cpu.getWord(addr += cpu.sizeData) & ~0xfff) << 4;
this.regDataSel = -1;
} else {
this.regCodeOff = cpu.getWord(addr += cpu.sizeData);
w = cpu.getWord(addr += cpu.sizeData);
this.regCodeSel = w & 0xffff;
this.regOpcode = (w >> 16) & 0x7ff;
this.regDataOff = cpu.getWord(addr += cpu.sizeData);
this.regDataSel = cpu.getWord(addr += cpu.sizeData) & 0xffff;
}
return addr + cpu.sizeData;
};
/**
* saveEnv(addr)
*
* @this {X86FPU}
* @param {number} addr
* @return {number} updated addr
*/
X86FPU.prototype.saveEnv = function(addr)
{
var cpu = this.cpu;
cpu.setWord(addr, this.regControl);
cpu.setWord(addr += cpu.sizeData, this.getStatus());
cpu.setWord(addr += cpu.sizeData, this.getTags());
if (!(cpu.regCR0 & X86.CR0.MSW.PE) || (cpu.regPS & X86.PS.VM)) {
var off = (this.regCodeSel << 4) + this.regCodeOff;
cpu.setWord(addr += cpu.sizeData, off);
cpu.setWord(addr += cpu.sizeData, ((off >> 4) & ~0xfff) | this.regOpcode);
off = (this.regDataSel << 4) + this.regDataOff;
cpu.setWord(addr += cpu.sizeData, off);
cpu.setWord(addr += cpu.sizeData, ((off >> 4) & ~0xfff));
} else {
cpu.setWord(addr += cpu.sizeData, this.regCodeOff);
cpu.setWord(addr += cpu.sizeData, this.regCodeSel | (this.regOpcode << 16));
cpu.setWord(addr += cpu.sizeData, this.regDataOff);
cpu.setWord(addr += cpu.sizeData, this.regDataSel);
}
return addr + cpu.sizeData;
};
/**
* opFPU(bOpcode, bModRM, dst, src)
*
* This is called by the CPU's ESC opcode handlers, after each instruction has been fully decoded.
*
* @this {X86FPU}
* @param {number} bOpcode (0xD8-0xDF)
* @param {number} bModRM
* @param {number} dst
* @param {number} src
*/
X86FPU.prototype.opFPU = function(bOpcode, bModRM, dst, src)
{
var mod = (bModRM >> 6) & 3;
var reg = (bModRM >> 3) & 7;
this.iStack = (bModRM & 7);
/*
* Combine mod and reg into one decodable value: put mod in the high nibble
* and reg in the low nibble, after first collapsing all mod values < 3 to zero.
*/
var modReg = (mod < 3? 0 : 0x30) + reg;
/*
* All values >= 0x34 imply mod == 3 and reg >= 4, so now we shift reg into the high
* nibble and iStack into the low, yielding values >= 0x40.
*/
if ((bOpcode == X86.OPCODE.ESC1 || bOpcode == X86.OPCODE.ESC3) && modReg >= 0x34) {
modReg = (reg << 4) | this.iStack;
}
var fnOp = X86FPU.aaOps[bOpcode][modReg];
if (fnOp) {
/*
* A handful of FPU instructions must preserve (at least some of) the "exception" registers,
* so if the current function is NOT one of those, then update all the "exception" registers.
*/
if (X86FPU.afnPreserveExceptions.indexOf(fnOp) < 0) {
var cpu = this.cpu;
var off = cpu.opLIP;
/*
* WARNING: opLIP points to any prefixes preceding the ESC instruction, but the 8087 always
* points to the ESC instruction. Technically, that's a bug, but it's also a reality, so we
* check for preceding prefixes and bump the instruction pointer accordingly. This isn't a
* perfect solution, because it doesn't account for multiple (redundant) prefixes, but it
* should be adequate.
*/
if (this.isModel(X86.FPU.MODEL_8087)) {
if (cpu.opPrefixes & X86.OPFLAG.SEG) off++;
if (cpu.opPrefixes & X86.OPFLAG.LOCK) off++;
}
this.regCodeSel = cpu.segCS.sel;
this.regCodeOff = off - cpu.segCS.base;
if (cpu.regEA !== X86.ADDR_INVALID) {
this.regDataSel = cpu.segEA.sel;
this.regDataOff = cpu.regEA - cpu.segEA.base;
}
this.regOpcode = ((bOpcode & 7) << 8) | bModRM;
}
/*
* Finally, perform the FPU operation.
*/
fnOp.call(this);
}
else {
/*
* This is a gray area, at least until aaOps has been filled in for all supported coprocessors;
* but for now, we'll treat all unrecognized operations as "no operation", as opposed to unimplemented.
*/
this.opNone();
}
};
/**
* opWAIT()
*
* This is called by the CPU's WAIT opcode handler, giving us the opportunity to synchronize the FPU with the CPU,
* charge an appropriate number of cycles, and return true. In this context, it's considered an FWAIT instruction,
* but technically, it's the same opcode.
*
* If we choose to do nothing, then we must return false, so that the CPU can charge a default number of cycles.
*
* @this {X86FPU}
* @return {boolean} true if implemented, false if not
*/
X86FPU.prototype.opWAIT = function()
{
return false;
};
if (DEBUGGER) {
/**
* readFPUStack(i)
*
* Returns the following information for the requested FPU stack element, relative to ST:
*
* a[0]: physical stack position (0-7)
* a[1]: corresponding tag value
* a[2]: 64-bit "long-real" (LR) value
* a[3]: bits 0-31 of 64-bit "long-real" (LR)
* a[4]: bits 32-63 of 64-bit "long-real" (LR)
* a[5]: bits 0-31 of 80-bit "temp-real" (TR)
* a[6]: bits 32-63 of 80-bit "temp-real" (TR)
* a[7]: bits 64-79 of 80-bit "temp-real" (TR) (in bits 0-15)
*
* Used by the Debugger for its floating-point register ("rfp") command. For other FPU registers,
* the Debugger calls getStatus() and getControl() directly.
*
* NOTE: The "temp-real" values are fake; we manufacture them on demand from 64-bit "long-real" values
* actually stored in the stack; see getTRFromLR().
*
* @this {X86FPU}
* @param {number} i (stack index, relative to ST)
* @return {Array.<number>|null} (an array of information as described above, or null if invalid element)
*/
X86FPU.prototype.readFPUStack = function(i) {
var a = null;
if (i < this.regStack.length) {
a = [];
var iReg = (this.iST + i) & 7;
a[0] = iReg;
a[1] = this.getTag(iReg);
a[2] = this.regStack[iReg];
var iInt = iReg << 1;
a[3] = this.intStack[iInt];
a[4] = this.intStack[iInt + 1];
var aTR = this.getTRFromLR(a[3], a[4]);
a[5] = aTR[0]; a[6] = aTR[1]; a[7] = aTR[2];
}
return a;
};
}
/*
* Class constants
*
* TODO: When loading any of the following 5 constants, the 80287XL and newer coprocessors apply rounding control.
*/
/** @const */
X86FPU.regL2T = Math.log(10) / Math.LN2; // log2(10) (use Math.log2() if we ever switch to ES6)
/** @const */
X86FPU.regL2E = Math.LOG2E; // log2(e)
/** @const */
X86FPU.regPI = Math.PI; // pi
/** @const */
X86FPU.regLG2 = Math.log(2) / Math.LN10; // log10(2) (use Math.log10() if we ever switch to ES6)
/** @const */
X86FPU.regLN2 = Math.LN2; // log(2)
/** @const */
X86FPU.MAX_INT16 = 0x8000;
/** @const */
X86FPU.MAX_INT32 = 0x80000000;
/** @const */
X86FPU.MAX_INT64 = Math.pow(2, 63);
/**
* F2XM1()
*
* F2XM1 (2 to the x minus 1) calculates the function 2^x - 1 and returns the result to ST(0).
*
* On the 8087 and 80287, the value in ST(0) must satisfy the inequality 0 <= ST(0) <= 0.5. On the 80287XL and
* later coprocessors, the permissible range is greater, and ST(0) must satisfy the inequality -1 <= ST(0) <= 1.
* If ST(0) is out of range, the result is undefined, even though no exception is raised.
*
* The F2XM1 instruction is designed to provide an accurate result even when x is close to zero. To obtain 2^x,
* simply add 1.0 to the result returned by F2XM1.
*
* This instruction is useful in performing exponentiation of values other than 2 as shown in the following formulas:
*
* 10^x = 2^(x * log2(10))
* e^x = 2^(x * log2(e))
* y^x = 2^(x * log2(y))
*
* Note that the NPX has dedicated instructions for loading the constants log2(10) and log2(e). The FYL2X instruction
* may be used to calculate x * log2(y).
*
* See also: FYL2X, FLDL2T, FLDL2E.
*
* @this {X86FPU}
*/
X86FPU.F2XM1 = function()
{
this.setST(0, Math.pow(2, this.getST(0)) - 1);
};
/**
* FABS()
*
* @this {X86FPU}
*/
X86FPU.FABS = function()
{
/*
* TODO: This could be implemented more efficiently by simply clearing the sign bit of ST(0).
*/
this.setST(0, Math.abs(this.getST(0)));
};
/**
* FADDlr()
*
* @this {X86FPU}
*/
X86FPU.FADDlr = function()
{
this.setST(0, this.doAdd(this.getST(0), this.getLRFromEA()));
};
/**
* FADDsr()
*
* Encoding 0xD8,reg=0x00 ("FADD short-real"): ST(0) <- ST(0) + REAL32
*
* @this {X86FPU}
*/
X86FPU.FADDsr = function()
{
this.setST(0, this.doAdd(this.getST(0), this.getSRFromEA()));
};
/**
* FADDst()
*
* @this {X86FPU}
*/
X86FPU.FADDst = function()
{
this.setST(0, this.doAdd(this.getST(0), this.getST(this.iStack)));
};
/**
* FADDsti()
*
* @this {X86FPU}
*/
X86FPU.FADDsti = function()
{
this.setST(this.iStack, this.doAdd(this.getST(this.iStack), this.getST(0)));
};
/**
* FADDPsti()
*
* @this {X86FPU}
*/
X86FPU.FADDPsti = function()
{
if (this.setST(this.iStack, this.doAdd(this.getST(this.iStack), this.getST(0)))) this.popValue();
};
/**
* FBLDpd()
*
* @this {X86FPU}
*/
X86FPU.FBLDpd = function()
{
var a = this.getTRFromEA();
/*
* a[0] contains the 8 least-significant BCD digits, a[1] contains the next 8, and a[2] contains
* the next 2 (bit 15 of a[2] is the sign bit, and bits 8-14 of a[2] are unused).
*/
var v = this.decodeBCD(a[0], 8) + this.decodeBCD(a[1], 8) * 100000000 + this.decodeBCD(a[2], 2) * 10000000000000000;
if (a[2] & 0x8000) v = -v;
this.pushValue(v);
};
/**
* FBSTPpd()
*
* @this {X86FPU}
*/
X86FPU.FBSTPpd = function()
{
/*
* TODO: Verify the operation of FBSTP (eg, does it signal an exception if abs(value) >= 1000000000000000000?)
*/
var v = this.roundValue(this.popValue());
if (v != null) {
/*
* intTmpTR[0] will contain the 8 least-significant BCD digits, intTmpTR[1] will contain the next 8,
* and intTmpTR[2] will contain the next 2 (bit 15 of intTmpTR[2] will be the sign bit, and bits 8-14 of
* intTmpTR[2] will be unused).
*/
this.intTmpTR[0] = this.encodeBCD(v, 8);
this.intTmpTR[1] = this.encodeBCD(v / 100000000, 8);
this.intTmpTR[2] = this.encodeBCD(v / 10000000000000000, 2);
if (v < 0) this.intTmpTR[2] |= 0x8000;
this.setEAFromTR();
}
};
/**
* FCHS()
*
* @this {X86FPU}
*/
X86FPU.FCHS = function()
{
/*
* TODO: This could be implemented more efficiently by simply inverting the sign bit of ST(0).
*/
this.setST(0, -this.getST(0));
};
/**
* FCLEX()
*
* NOTE: Although we explicitly clear the BUSY bit, there shouldn't be any code setting it, because
* we're never "busy" (all floating-point operations are performed synchronously). Conversely, there's
* no need to explicitly clear the ES bit, because clearStatus() will call checkException(), which
* updates ES and clears/sets FPU interrupt status as appropriate.
*
* @this {X86FPU}
*/
X86FPU.FCLEX = function()
{
this.clearStatus(X86.FPU.STATUS.EXC | X86.FPU.STATUS.BUSY);
};
/**
* FCOMlr()
*
* Encoding 0xDC,mod<3,reg=2 ("FCOM long-real"): Evaluate ST(0) - REAL64
*
* @this {X86FPU}
*/
X86FPU.FCOMlr = function()
{
this.doCompare(this.getST(0), this.getLRFromEA());
};
/**
* FCOMsr()
*
* Encoding 0xD8,mod<3,reg=2 ("FCOM short-real"): Evaluate ST(0) - REAL32
*
* @this {X86FPU}
*/
X86FPU.FCOMsr = function()
{
this.doCompare(this.getST(0), this.getSRFromEA());
};
/**
* FCOMst()
*
* Encoding 0xD8,mod=3,reg=2 ("FCOM ST(i)"): Evaluate ST(0) - ST(i)
*
* @this {X86FPU}
*/
X86FPU.FCOMst = function()
{
this.doCompare(this.getST(0), this.getST(this.iStack));
};
/**
* FCOM8087()
*
* NOTE: This is used with encoding(s) (0xDC,0xD0-0xD7) that were valid for the 8087 and 80287
* but may no longer be valid as of the 80387.
*
* TODO: Determine if this form subtracted the operands in the same order, or if it requires an FCOMsti(),
* which, like the other *sti() functions, uses ST(0) as the second operand rather than the first.
*
* @this {X86FPU}
*/
X86FPU.FCOM8087 = function()
{
this.opObsolete();
X86FPU.FCOMst.call(this);
};
/**
* FCOMPlr()
*
* Encoding 0xDC,mod<3,reg=3 ("FCOM long-real"): Evaluate ST(0) - REAL64, POP
*
* @this {X86FPU}
*/
X86FPU.FCOMPlr = function()
{
if (this.doCompare(this.getST(0), this.getLRFromEA())) this.popValue();
};
/**
* FCOMPsr()
*
* Encoding 0xD8,mod<3,reg=3 ("FCOM short-real"): Evaluate ST(0) - REAL32, POP
*
* @this {X86FPU}
*/
X86FPU.FCOMPsr = function()
{
if (this.doCompare(this.getST(0), this.getSRFromEA())) this.popValue();
};
/**
* FCOMPst()
*
* Encoding 0xD8,mod=3,reg=3 ("FCOMP ST(i)"): Evaluate ST(0) - ST(i), POP
*
* @this {X86FPU}
*/
X86FPU.FCOMPst = function()
{
if (this.doCompare(this.getST(0), this.getST(this.iStack))) this.popValue();
};
/**
* FCOMP8087()
*
* NOTE: This is used with encodings (0xDC,0xD8-0xDF and 0xDE,0xD0-0xD7) that were valid for the 8087
* and 80287 but may no longer be valid as of the 80387.
*
* TODO: Determine if this form subtracted the operands in the same order, or if it requires an FCOMPsti(),
* which, like the other *sti() functions, uses ST(0) as the second operand rather than the first.
*
* @this {X86FPU}
*/
X86FPU.FCOMP8087 = function()
{
this.opObsolete();
X86FPU.FCOMPst.call(this);
};
/**
* FCOMPP()
*
* @this {X86FPU}
*/
X86FPU.FCOMPP = function()
{
if (this.doCompare(this.getST(0), this.getST(1)) && this.popValue() != null) this.popValue();
};
/**
* FDECSTP()
*
* @this {X86FPU}
*/
X86FPU.FDECSTP = function()
{
this.iST = (this.iST - 1) & 0x7;
this.regStatus &= ~X86.FPU.STATUS.C1;
};
/**
* FDISI8087()
*
* @this {X86FPU}
*/
X86FPU.FDISI8087 = function()
{
if (this.isModel(X86.FPU.MODEL_8087)) {
this.regControl |= X86.FPU.CONTROL.IEM;
}
};
/**
* FDIVlr()
*
* @this {X86FPU}
*/
X86FPU.FDIVlr = function()
{
this.setST(0, this.doDivide(this.getST(0), this.getLRFromEA()));
};
/**
* FDIVsr()
*
* @this {X86FPU}
*/
X86FPU.FDIVsr = function()
{
this.setST(0, this.doDivide(this.getST(0), this.getSRFromEA()));
};
/**
* FDIVst()
*
* Encoding 0xD8,0xF0-0xF7 ("FDIV ST,ST(i)"): ST(0) <- ST(0) / ST(i)
*
* @this {X86FPU}
*/
X86FPU.FDIVst = function()
{
this.setST(0, this.doDivide(this.getST(0), this.getST(this.iStack)));
};
/**
* FDIVsti()
*
* Encoding 0xDC,0xF8-0xFF ("FDIV ST(i),ST"): ST(i) <- ST(i) / ST(0)
*
* @this {X86FPU}
*/
X86FPU.FDIVsti = function()
{
this.setST(this.iStack, this.doDivide(this.getST(this.iStack), this.getST(0)));
};
/**
* FDIVPsti()
*
* Encoding 0xDE,0xF8-0xFF ("FDIVP ST(i),ST"): ST(i) <- ST(i) / ST(0), POP
*
* @this {X86FPU}
*/
X86FPU.FDIVPsti = function()
{
if (this.setST(this.iStack, this.doDivide(this.getST(this.iStack), this.getST(0)))) this.popValue();
};
/**
* FDIVRlr()
*
* @this {X86FPU}
*/
X86FPU.FDIVRlr = function()
{
this.setST(0, this.doDivide(this.getLRFromEA(), this.getST(0)));
};
/**
* FDIVRsr()
*
* @this {X86FPU}
*/
X86FPU.FDIVRsr = function()
{
this.setST(0, this.doDivide(this.getSRFromEA(), this.getST(0)));
};
/**
* FDIVRst()
*
* Encoding 0xD8,0xF8-0xFF ("FDIVR ST,ST(i)"): ST(0) <- ST(i) / ST(0)
*
* @this {X86FPU}
*/
X86FPU.FDIVRst = function()
{
this.setST(0, this.doDivide(this.getST(this.iStack), this.getST(0)));
};
/**
* FDIVRsti()
*
* Encoding 0xDC,0xF0-0xF7 ("FDIVR ST(i),ST"): ST(i) <- ST(0) / ST(i)
*
* @this {X86FPU}
*/
X86FPU.FDIVRsti = function()
{
this.setST(this.iStack, this.doDivide(this.getST(0), this.getST(this.iStack)));
};
/**
* FDIVRPsti()
*
* Encoding 0xDE,0xF0-0xE7 ("FDIVRP ST(i),ST"): ST(i) <- ST(0) / ST(i), POP
*
* @this {X86FPU}
*/
X86FPU.FDIVRPsti = function()
{
if (this.setST(this.iStack, this.doDivide(this.getST(0), this.getST(this.iStack)))) this.popValue();
};
/**
* FENI8087()
*
* @this {X86FPU}
*/
X86FPU.FENI8087 = function()
{
if (this.isModel(X86.FPU.MODEL_8087)) {
this.regControl &= ~X86.FPU.CONTROL.IEM;
}
};
/**
* FFREEsti()
*
* @this {X86FPU}
*/
X86FPU.FFREEsti = function()
{
this.setTag(this.iST, X86.FPU.TAGS.EMPTY);
};
/**
* FFREEP8087()
*
* NOTE: This is used with an encoding (0xDF,0xC0-0xC7) that was valid for the 8087 and 80287
* but may no longer be valid as of the 80387. Also, if the older documentation is to be believed,
* this instruction has no modern counterpart, as FFREE doesn't pop the stack.
*
* @this {X86FPU}
*/
X86FPU.FFREEP8087 = function()
{
this.opObsolete();
X86FPU.FFREEsti.call(this);
this.popValue();
};
/**
* FIADD16()
*
* @this {X86FPU}
*/
X86FPU.FIADD16 = function()
{
this.setST(0, this.doAdd(this.getST(0), this.getWIFromEA()));
};
/**
* FIADD32()
*
* @this {X86FPU}
*/
X86FPU.FIADD32 = function()
{
this.setST(0, this.doAdd(this.getST(0), this.getSIFromEA()));
};
/**
* FICOM16()
*
* @this {X86FPU}
*/
X86FPU.FICOM16 = function()
{
this.doCompare(this.getST(0), this.getWIFromEA());
};
/**
* FICOM32()
*
* @this {X86FPU}
*/
X86FPU.FICOM32 = function()
{
this.doCompare(this.getST(0), this.getSIFromEA());
};
/**
* FICOMP16()
*
* @this {X86FPU}
*/
X86FPU.FICOMP16 = function()
{
if (this.doCompare(this.getST(0), this.getWIFromEA())) this.popValue();
};
/**
* FICOMP32()
*
* @this {X86FPU}
*/
X86FPU.FICOMP32 = function()
{
if (this.doCompare(this.getST(0), this.getSIFromEA())) this.popValue();
};
/**
* FIDIV16()
*
* @this {X86FPU}
*/
X86FPU.FIDIV16 = function()
{
this.setST(0, this.doDivide(this.getST(0), this.getWIFromEA()));
};
/**
* FIDIV32()
*
* @this {X86FPU}
*/
X86FPU.FIDIV32 = function()
{
this.setST(0, this.doDivide(this.getST(0), this.getSIFromEA()));
};
/**
* FIDIVR16()
*
* @this {X86FPU}
*/
X86FPU.FIDIVR16 = function()
{
this.setST(0, this.doDivide(this.getWIFromEA(), this.getST(0)));
};
/**
* FIDIVR32()
*
* @this {X86FPU}
*/
X86FPU.FIDIVR32 = function()
{
this.setST(0, this.doDivide(this.getSIFromEA(), this.getST(0)));
};
/**
* FILD16()
*
* @this {X86FPU}
*/
X86FPU.FILD16 = function()
{
this.pushValue(this.getWIFromEA());
};
/**
* FILD32()
*
* @this {X86FPU}
*/
X86FPU.FILD32 = function()
{
this.pushValue(this.getSIFromEA());
};
/**
* FILD64()
*
* @this {X86FPU}
*/
X86FPU.FILD64 = function()
{
this.pushValue(this.getLIFromEA());
};
/**
* FIMUL16()
*
* @this {X86FPU}
*/
X86FPU.FIMUL16 = function()
{
this.setST(0, this.doMultiply(this.getST(0), this.getWIFromEA()));
};
/**
* FIMUL32()
*
* @this {X86FPU}
*/
X86FPU.FIMUL32 = function()
{
this.setST(0, this.doMultiply(this.getST(0), this.getSIFromEA()));
};
/**
* FINCSTP()
*
* @this {X86FPU}
*/
X86FPU.FINCSTP = function()
{
this.iST = (this.iST + 1) & 0x7;
this.regStatus &= ~X86.FPU.STATUS.C1;
};
/**
* FINIT()
*
* @this {X86FPU}
*/
X86FPU.FINIT = function()
{
this.resetFPU();
};
/**
* FIST16()
*
* @this {X86FPU}
*/
X86FPU.FIST16 = function()
{
if (this.getWI(0)) this.setEAFromWI();
};
/**
* FIST32()
*
* @this {X86FPU}
*/
X86FPU.FIST32 = function()
{
if (this.getSI(0)) this.setEAFromSI();
};
/**
* FISTP16()
*
* @this {X86FPU}
*/
X86FPU.FISTP16 = function()
{
if (this.getWI(0)) {
this.setEAFromWI();
this.popValue();
}
};
/**
* FISTP32()
*
* @this {X86FPU}
*/
X86FPU.FISTP32 = function()
{
if (this.getSI(0)) {
this.setEAFromSI();
this.popValue();
}
};
/**
* FISTP64()
*
* @this {X86FPU}
*/
X86FPU.FISTP64 = function()
{
if (this.getLI(0)) {
this.setEAFromLI();
this.popValue();
}
};
/**
* FISUB16()
*
* @this {X86FPU}
*/
X86FPU.FISUB16 = function()
{
this.setST(0, this.doSubtract(this.getST(0), this.getWIFromEA()));
};
/**
* FISUB32()
*
* @this {X86FPU}
*/
X86FPU.FISUB32 = function()
{
this.setST(0, this.doSubtract(this.getST(0), this.getSIFromEA()));
};
/**
* FISUBR16()
*
* @this {X86FPU}
*/
X86FPU.FISUBR16 = function()
{
this.setST(0, this.doSubtract(this.getWIFromEA(), this.getST(0)));
};
/**
* FISUBR32()
*
* @this {X86FPU}
*/
X86FPU.FISUBR32 = function()
{
this.setST(0, this.doSubtract(this.getSIFromEA(), this.getST(0)));
};
/**
* FLDlr()
*
* The FLD instruction loads the source operand, converts it to temporary real format (if required),
* and pushes the resulting value onto the floating-point stack.
*
* The load operation is accomplished by decrementing the top-of-stack pointer (TOP) and copying the
* source operand to the new stack top. If the source operand is a float ing-point register, the index of
* the register is taken before TOP is changed. The source operand may also be a short real, long real,
* or temporary real memory operand. Short real and long real operands are converted automatically.
*
* Note that coding the instruction FLD ST(0) duplicates the value at the stack top.
*
* On the 8087 and 80287, the FLD real80 instruction will raise the denormal exception if the memory
* operand is a denormal. The 80287XL and later coprocessors will not, since the operation is not arithmetic.
*
* On the 8087 and 80287, a denormal will be converted to an unnormal by FLD; on the 80287XL and later
* coprocessors, the number will be converted to temporary real. If the next instruction is an FXTRACT or FXAM,
* the 8087/80827 and 80287XL/80387/ 80486 results will be different.
*
* On the 8087 and 80287, the FLD real32 and FLD real64 instructions will not raise an exception when loading
* a signaling NaN; on the 80287XL and later coprocessors, loading a signaling NaN raises the invalid operation
* exception.
*
* @this {X86FPU}
*/
X86FPU.FLDlr = function()
{
this.pushValue(this.getLRFromEA());
};
/**
* FLDsr()
*
* @this {X86FPU}
*/
X86FPU.FLDsr = function()
{
this.pushValue(this.getSRFromEA());
};
/**
* FLDsti()
*
* @this {X86FPU}
*/
X86FPU.FLDsti = function()
{
this.pushValue(this.getST(this.iStack));
};
/**
* FLDtr()
*
* @this {X86FPU}
*/
X86FPU.FLDtr = function()
{
this.pushValue(this.getLRFromTR(this.getTRFromEA()));
};
/**
* FLDCW()
*
* @this {X86FPU}
*/
X86FPU.FLDCW = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
this.setControl(this.cpu.getShort(this.cpu.regEA));
};
/**
* FLDENV()
*
* @this {X86FPU}
*/
X86FPU.FLDENV = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
this.loadEnv(this.cpu.regEA);
};
/**
* FLD1()
*
* The FLD1 instruction loads the constant +1.0 from the NPX's constant ROM and pushes the value onto the
* floating-point stack.
*
* The constant is stored internally in temporary real format and is simply moved to the stack.
*
* See also: FLDLG2, FLDLN2, FLDL2E, FLDL2T, FLDPI, and FLD1.
*
* @this {X86FPU}
*/
X86FPU.FLD1 = function()
{
this.pushValue(1.0);
};
/**
* FLDL2T()
*
* The FLDL2T instruction loads the constant log2(10) from the NPX's constant ROM and pushes the value onto the
* floating-point stack.
*
* The constant is stored internally in temporary real format and is simply moved to the stack.
*
* On the 8087 and 80287, rounding control is not in effect for the loading of this constant. On the 80287XL and
* later coprocessors, rounding control is in effect. If RC is set for chop (round toward 0), round down (toward
* -infinity), or round to nearest or even, the result will be the same as on the 8087 and 80287. If RC is set for
* round up (toward +infinity), the result will differ by one in the least significant bit of the mantissa.
*
* See also: FLDLG2, FLDLN2, FLDL2E, FLDPI, FLD1, and FLDZ.
*
* @this {X86FPU}
*/
X86FPU.FLDL2T = function()
{
this.pushValue(X86FPU.regL2T);
};
/**
* FLDL2E()
*
* The FLDL2E instruction loads the constant log2(e) from the NPX's constant ROM and pushes the value onto the
* floating-point stack.
*
* The constant is stored internally in temporary real format and is simply moved to the stack.
*
* On the 8087 and 80287, rounding control is not in effect for the loading of this constant. On the 80287XL and
* later coprocessors, rounding control is in effect. If RC is set for chop (round toward 0) or round down (toward
* -infinity), the result is the same as on the 8087 and 80827. If RC is set for round to nearest or even, or round
* up (toward +infinity), the result will differ by one in the least significant bit of the mantissa.
*
* See also: FLDLG2, FLDLN2, FLDL2T, FLDPI, FLD1, and FLDZ.
*
* @this {X86FPU}
*/
X86FPU.FLDL2E = function()
{
this.pushValue(X86FPU.regL2E);
};
/**
* FLDPI()
*
* The FLDPI instruction loads the constant Pi from the NPX's constant ROM and pushes the value onto the
* floating-point stack.
*
* The constant is stored internally in temporary real format and is simply moved to the stack.
*
* On the 8087 and 80287, rounding control is not in effect for the loading of these constants. On the 80287XL and
* later coprocessors, rounding control is in effect. If RC is set for chop (round toward 0) or round down (toward
* -infinity), the result is the same as on the 8087 and 80827. If RC is set for round to nearest or even, or round
* up (toward +infinity), the result will differ by one in the least significant bit of the mantissa.
*
* See also: FLDLG2, FLDLN2, FLDL2E, FLDL2T, FLD1, and FLDZ.
*
* @this {X86FPU}
*/
X86FPU.FLDPI = function()
{
this.pushValue(X86FPU.regPI);
};
/**
* FLDLG2()
*
* The FLDLG2 instruction loads the constant log10(2) from the NPX's constant ROM and pushes the value onto the
* floating-point stack.
*
* The constant is stored internally in temporary real format and is simply moved to the stack.
*
* On the 8087 and 80287, rounding control is not in effect for the loading of this constant. On the 80287XL and
* later coprocessors, rounding control is in effect. If RC is set for chop (round toward 0) or round down (toward
* -infinity), the result is the same as on the 8087 and 80827. If RC is set for round to nearest or even, or round
* up (toward +infinity), the result will differ by one in the least significant bit of the mantissa.
*
* See also: FLDLN2, FLDL2E, FLDL2T, FLDPI, FLD1, and FLDZ.
*
* @this {X86FPU}
*/
X86FPU.FLDLG2 = function()
{
this.pushValue(X86FPU.regLG2);
};
/**
* FLDLN2()
*
* The FLDLN2 instruction loads the constant loge(2) from the NPX's constant ROM and pushes the value onto the
* floating-point stack.
*
* The constant is stored internally in temporary real format and is simply moved to the stack.
*
* On the 8087 and 80287, rounding control is not in effect for the loading of this constant. On the 80287XL and
* later coprocessors, rounding control is in effect. If RC is set for chop (round toward 0) or round down (toward
* -infinity), the result will be the same as on the 8087 and 80827. If RC is set for round to nearest or even, or
* round up (toward +infinity), the result will differ by one in the least significant bit of the mantissa.
*
* See also: FLDLG2, FLDL2E, FLDL2T, FLDPI, FLD1, and FLDZ.
*
* @this {X86FPU}
*/
X86FPU.FLDLN2 = function()
{
this.pushValue(X86FPU.regLN2);
};
/**
* FLDZ()
*
* The FLDZ instruction loads the constant +0.0 from the NPX's constant ROM and pushes the value onto the
* floating-point stack.
*
* The constant is stored internally in temporary real format and is simply moved to the stack.
*
* See also: FLDLG2, FLDLN2, FLDL2E, FLDL2T, FLDPI, and FLD1.
*
* @this {X86FPU}
*/
X86FPU.FLDZ = function()
{
this.pushValue(0.0);
};
/**
* FMULlr()
*
* @this {X86FPU}
*/
X86FPU.FMULlr = function()
{
this.setST(0, this.doMultiply(this.getST(0), this.getLRFromEA()));
};
/**
* FMULsr()
*
* Encoding 0xD8,reg=0x01 ("FMUL short-real"): ST(0) <- ST(0) * REAL32
*
* @this {X86FPU}
*/
X86FPU.FMULsr = function()
{
this.setST(0, this.doMultiply(this.getST(0), this.getSRFromEA()));
};
/**
* FMULst()
*
* @this {X86FPU}
*/
X86FPU.FMULst = function()
{
this.setST(0, this.doMultiply(this.getST(0), this.getST(this.iStack)));
};
/**
* FMULsti()
*
* @this {X86FPU}
*/
X86FPU.FMULsti = function()
{
this.setST(this.iStack, this.doMultiply(this.getST(this.iStack), this.getST(0)));
};
/**
* FMULPsti()
*
* @this {X86FPU}
*/
X86FPU.FMULPsti = function()
{
if (this.setST(this.iStack, this.doMultiply(this.getST(this.iStack), this.getST(0)))) this.popValue();
};
/**
* FNOP()
*
* @this {X86FPU}
*/
X86FPU.FNOP = function()
{
};
/**
* FPATAN()
*
* FPATAN calculates the partial arctangent of ST(0) divided by ST(1):
*
* ST(1) = tan^-1( ST(1) / ST(0) )
*
* On the 8087 and 80287, the arguments must satisfy the inequality 0 <= ST(1) < ST(0) < +infinity.
* On the 80287XL and later coprocessors, the range of the operands is unrestricted. The result is
* returned to ST(1), and the stack is popped, destroying both operands and leaving the result in ST(0).
*
* @this {X86FPU}
*/
X86FPU.FPATAN = function()
{
if (this.setST(1, Math.atan2(this.getST(1), this.getST(0)))) this.popValue();
};
/**
* FPTAN()
*
* FPTAN calculates the partial tangent of ST(0):
*
* y / x = tan( ST(0) )
*
* The result of the operation is a ratio. y replaces the argument on the stack, and x is pushed onto the stack,
* where it becomes the new ST(0).
*
* On the 8087 and 80287, the FPTAN function assumes that its argument is valid and in-range. No argument checking
* is performed. The value of ST(0) must satisfy the inequality -pi/4 <= ST(0) <= pi/4. In the case of an invalid
* argument, the result is undefined and no error is signaled.
*
* On the 80287XL and later coprocessors, if value of ST(0) satisfies the condition -2^63 < ST(0) < 2^63, it will
* automatically be reduced to within range. If the operand is outside this range, however, C2 is set to 1 to indicate
* that the function is incomplete, and ST(0) is left unchanged.
*
* The 80287XL, 80387, and 80486 always push a value of +1.0 for x. The value of x pushed by the 8087 and 80287 may be
* any real number. In either case, the ratio is the same. The cotangent can be calculated by executing FDIVR immediately
* after FPTAN. The following code will leave the 8087 and 80287 in the same state as the later coprocessors:
*
* FDIV
* FLD1
*
* ST(7) must be empty before this instruction is executed to avoid an invalid operation exception. If the invalid
* operation exception is masked, the 8087 and 80287 leave the original operand unchanged, but push it to ST(1). On the
* 80287XL and later coprocessors, both ST(0) and ST(1) will contain quiet NaNs. On the 80287XL and later coprocessors,
* if condition code bit C2 is 0 and the precision exception is raised, then C1=1 if the last bit was rounded up. C1 is
* undefined for the 8087 and 80287.
*
* @this {X86FPU}
*/
X86FPU.FPTAN = function()
{
if (this.setST(0, Math.tan(this.getST(0)))) this.pushValue(1.0);
};
/**
* FPREM()
*
* FPREM performs modulo division of ST(0) by ST(1) and returns the result to ST(0).
*
* The FPREM instruction is used to reduce the real operand in ST(0) to a value whose magnitude is less than the
* magnitude of ST(1). FPREM produces an exact result, so the precision exception is never raised and the rounding
* control has no effect. The sign of the remainder is the same as the sign of the original operand.
*
* The remaindering operation is performed by iterative scaled subtractions and can reduce the exponent of ST(0) by
* no more than 63 in one execution. If the remainder is less than ST(1) (the modulus), the function is complete and
* C2 in the status word is cleared.
*
* If the modulo function is incomplete, C2 is set to 1, and the result in ST(0) is termed the partial remainder.
* C2 can be inspected by storing the status word and re-executing the instruction until C2 is clear. Alternately,
* ST(0) can be compared to ST(1). If ST(0) > ST(1), then FPREM must be executed again. If ST(0) = ST(1), then the
* remainder is 0.
*
* FPREM is important for reducing arguments to the periodic transcendental functions such as FPTAN. Because FPREM
* produces an exact result, no round-off error is introduced into the calculation.
*
* When reduction is complete, the three least-significant bits of the quotient are stored in the condition code bits
* C3, C1, and C0, respectively. When arguments to the tangent function are reduced by pi/4, this result can be used
* to identify the octant that contained the original angle.
*
* The FPREM function operates differently than specified by the IEEE 754 standard when rounding the quotient to form
* a partial remainder (see the algorithm). The FPREM1 function (80287XL and up) is provided for compatibility with
* that standard.
*
* The FPREM instruction can also be used to normalize ST(0). If ST(0) is unnormal and ST(1) is greater than ST(0),
* FPREM will normalize ST(0). On the 8087 and 80287, operation on a denormal operand raises the invalid operation
* exception. Underflow is not possible. On the 80287XL and later coprocessors, operation on a denormal is supported
* and an underflow exception can occur.
*
* ALGORITHM:
*
* t = EXPONENT(ST) - EXPONENT(ST(1))
* IF (t < 64) THEN
* q = R0UND(ST(0) / ST(1), CHOP)
* ST(0) = ST(0) - (ST(1) * q)
* C2 = 0
* C0 = BIT 2 of q
* C1 = BIT 1 of q
* C3 = BIT 0 of q
* ELSE
* n = a number between 32 and 63
* q = ROUND((ST(0) / ST(1)) / 2^(t-n), CHOP)
* ST(0) = ST(0) - (ST(1) * q * 2^(t-n))
* C2 = 1
* ENDIF
*
* TODO: Determine the extent to which the JavaScript MOD operator differs from the above algorithm.
*
* ERRATA: On the 8087 and 80287, the condition code bits C3, C1, and C0 are incorrect when performing a reduction of
* 64^n + m, where n >= 1, and m=1 or m=2. A bug fix should be implemented in software.
*
* @this {X86FPU}
*/
X86FPU.FPREM = function()
{
this.setST(0, this.getST(0) % this.getST(1));
};
/**
* FRSTOR()
*
* @this {X86FPU}
*/
X86FPU.FRSTOR = function()
{
var cpu = this.cpu;
var addr = this.loadEnv(cpu.regEA);
var a = this.intTmpTR;
for (var i = 0; i < this.regStack.length; i++) {
a[0] = cpu.getLong(addr);
a[1] = cpu.getLong(addr += 4);
a[2] = cpu.getShort(addr += 4);
this.setTR(i, a);
addr += 2;
}
};
/**
* FRNDINT()
*
* @this {X86FPU}
*/
X86FPU.FRNDINT = function()
{
this.setST(0, this.roundValue(this.getST(0), X86FPU.MAX_INT64));
};
/**
* FSAVE()
*
* @this {X86FPU}
*/
X86FPU.FSAVE = function()
{
var cpu = this.cpu;
var addr = this.saveEnv(cpu.regEA);
for (var i = 0; i < this.regStack.length; i++) {
var a = this.getTR(i, true);
cpu.setLong(addr, a[0]);
cpu.setLong(addr += 4, a[1]);
cpu.setShort(addr += 4, a[2]);
addr += 2;
}
this.resetFPU();
};
/**
* FSCALE()
*
* FSCALE interprets the value in ST(1) as an integer and adds this number to the exponent of the number in ST(0).
*
* The FSCALE instruction provides a means of quickly performing multiplication or division by powers of two.
* This operation is often required when scaling array indexes.
*
* On the 8087 and 80287, FSCALE assumes that the scale factor in ST(1) is an integer that satisfies the inequality
* -2^15 <= ST(1) < +2^15. If ST(1) is not an integer value, the value is chopped to the next smallest integer in
* magnitude (chopped toward zero). If the value is out of range or 0 < ST(1) < 1, FSCALE produces an undefined
* result and doesn't signal an exception. Typically, the value in ST(0) is unchanged but should not be depended on.
*
* On the 80287XL and later coprocessors, there is no limit on the range of the scale factor in ST(1). The value in
* ST(1) is still chopped toward zero. If ST(1) is 0, ST(0) is unchanged.
*
* @this {X86FPU}
*/
X86FPU.FSCALE = function()
{
var x = this.getST(0);
var y = this.getST(1);
if (x != null && y != null) this.setST(0, x * Math.pow(2, this.truncateValue(y)));
};
/**
* FSETPM287()
*
* @this {X86FPU}
*/
X86FPU.FSETPM287 = function()
{
if (this.isModel(X86.FPU.MODEL_80287)) {
this.opUnimplemented();
}
};
/**
* FSINCOS387()
*
* @this {X86FPU}
*/
X86FPU.FSINCOS387 = function()
{
if (this.isAtLeastModel(X86.FPU.MODEL_80287XL)) {
this.opUnimplemented();
}
};
/**
* FSQRT()
*
* @this {X86FPU}
*/
X86FPU.FSQRT = function()
{
this.setST(0, this.doSquareRoot(this.getST(0)));
};
/**
* FSTlr()
*
* @this {X86FPU}
*/
X86FPU.FSTlr = function()
{
if (this.getLR(0)) this.setEAFromLR();
};
/**
* FSTsr()
*
* @this {X86FPU}
*/
X86FPU.FSTsr = function()
{
if (this.getSR(0)) this.setEAFromSR();
};
/**
* FSTsti()
*
* @this {X86FPU}
*/
X86FPU.FSTsti = function()
{
this.setST(this.iStack, this.getST(0));
};
/**
* FSTENV()
*
* @this {X86FPU}
*/
X86FPU.FSTENV = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
this.saveEnv(this.cpu.regEA);
this.regControl |= X86.FPU.CONTROL.EXC; // mask all exceptions (but do not set IEM)
};
/**
* FSTPlr()
*
* @this {X86FPU}
*/
X86FPU.FSTPlr = function()
{
if (this.getLR(0)) {
this.setEAFromLR();
this.popValue();
}
};
/**
* FSTPsr()
*
* @this {X86FPU}
*/
X86FPU.FSTPsr = function()
{
if (this.getSR(0)) {
this.setEAFromSR();
this.popValue();
}
};
/**
* FSTPsti()
*
* @this {X86FPU}
*/
X86FPU.FSTPsti = function()
{
if (this.setST(this.iStack, this.getST(0))) this.popValue();
};
/**
* FSTP8087()
*
* NOTE: This is used with encodings (0xD9,0xD8-0xDF and 0xDF,0xD0-0xDF) that were valid for the 8087 and 80287
* but may no longer be valid as of the 80387.
*
* @this {X86FPU}
*/
X86FPU.FSTP8087 = function()
{
this.opObsolete();
X86FPU.FSTPsti.call(this);
};
/**
* FSTPtr()
*
* @this {X86FPU}
*/
X86FPU.FSTPtr = function()
{
if (this.getTR(0)) {
this.setEAFromTR();
this.popValue();
}
};
/**
* FSTCW()
*
* @this {X86FPU}
*/
X86FPU.FSTCW = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
this.cpu.setShort(this.cpu.regEA, this.regControl);
};
/**
* FSTSW()
*
* @this {X86FPU}
*/
X86FPU.FSTSW = function()
{
this.assert(this.cpu.regEA !== X86.ADDR_INVALID);
this.cpu.setShort(this.cpu.regEA, this.getStatus());
};
/**
* FSTSWAX287()
*
* @this {X86FPU}
*/
X86FPU.FSTSWAX287 = function()
{
if (this.isAtLeastModel(X86.FPU.MODEL_80287)) {
this.cpu.regEAX = (this.cpu.regEAX & ~0xffff) | this.getStatus();
}
};
/**
* FSUBlr()
*
* @this {X86FPU}
*/
X86FPU.FSUBlr = function()
{
this.setST(0, this.doSubtract(this.getST(0), this.getLRFromEA()));
};
/**
* FSUBsr()
*
* @this {X86FPU}
*/
X86FPU.FSUBsr = function()
{
this.setST(0, this.doSubtract(this.getST(0), this.getSRFromEA()));
};
/**
* FSUBst()
*
* Encoding 0xD8,0xE0-0xE7 ("FSUB ST,ST(i)"): ST(0) <- ST(0) - ST(i)
*
* @this {X86FPU}
*/
X86FPU.FSUBst = function()
{
this.setST(0, this.doSubtract(this.getST(0), this.getST(this.iStack)));
};
/**
* FSUBsti()
*
* Encoding 0xDC,0xE8-0xEF ("FSUB ST(i),ST"): ST(i) <- ST(i) - ST(0)
*
* @this {X86FPU}
*/
X86FPU.FSUBsti = function()
{
this.setST(this.iStack, this.doSubtract(this.getST(this.iStack), this.getST(0)));
};
/**
* FSUBPsti()
*
* Encoding 0xDE,0xE8-0xEF ("FSUBP ST(i),ST"): ST(i) <- ST(i) - ST(0), POP
*
* @this {X86FPU}
*/
X86FPU.FSUBPsti = function()
{
if (this.setST(this.iStack, this.doSubtract(this.getST(this.iStack), this.getST(0)))) this.popValue();
};
/**
* FSUBRlr()
*
* @this {X86FPU}
*/
X86FPU.FSUBRlr = function()
{
this.setST(0, this.doSubtract(this.getLRFromEA(), this.getST(0)));
};
/**
* FSUBRsr()
*
* @this {X86FPU}
*/
X86FPU.FSUBRsr = function()
{
this.setST(0, this.doSubtract(this.getSRFromEA(), this.getST(0)));
};
/**
* FSUBRst()
*
* Encoding 0xD8,0xE8-0xEF ("FSUBR ST,ST(i)"): ST(0) <- ST(i) - ST(0)
*
* @this {X86FPU}
*/
X86FPU.FSUBRst = function()
{
this.setST(0, this.doSubtract(this.getST(this.iStack), this.getST(0)));
};
/**
* FSUBRsti()
*
* Encoding 0xDC,0xE0-0xE7 ("FSUBR ST(i),ST"): ST(i) <- ST(0) - ST(i)
*
* @this {X86FPU}
*/
X86FPU.FSUBRsti = function()
{
this.setST(this.iStack, this.doSubtract(this.getST(0), this.getST(this.iStack)));
};
/**
* FSUBRPsti()
*
* Encoding 0xDE,0xE0-0xE7 ("FSUBRP ST(i),ST"): ST(i) <- ST(0) - ST(i), POP
*
* @this {X86FPU}
*/
X86FPU.FSUBRPsti = function()
{
if (this.setST(this.iStack, this.doSubtract(this.getST(0), this.getST(this.iStack)))) this.popValue();
};
/**
* FTST()
*
* @this {X86FPU}
*/
X86FPU.FTST = function()
{
this.doCompare(this.getST(0), 0);
};
/**
* FXAM()
*
* @this {X86FPU}
*/
X86FPU.FXAM = function()
{
this.regStatus &= ~X86.FPU.STATUS.CC;
if (this.getSTSign(0)) {
this.regStatus |= X86.FPU.STATUS.C1;
}
if (this.getTag(this.iST) == X86.FPU.TAGS.EMPTY) {
this.regStatus |= X86.FPU.STATUS.C0 | X86.FPU.STATUS.C3;
}
else {
var v = this.getST(0);
if (isNaN(v)) {
this.regStatus |= X86.FPU.STATUS.C0;
}
else if (v === 0) { // this equals -0, too (WTF, strict equality?)
this.regStatus |= X86.FPU.STATUS.C3;
}
else if (v === Infinity || v === -Infinity) { // these are so divergent that even non-strict equality doesn't consider them equal
this.regStatus |= X86.FPU.STATUS.C0 | X86.FPU.STATUS.C2;
}
else {
this.regStatus |= X86.FPU.STATUS.C2;
}
}
};
/**
* FXCHsti()
*
* @this {X86FPU}
*/
X86FPU.FXCHsti = function()
{
var tmp = this.getST(0);
this.setST(0, this.getST(this.iStack));
this.setST(this.iStack, tmp);
};
/**
* FXCH8087()
*
* NOTE: This is used with encodings (0xDD,0xC8-0xCF and 0xDF,0xC8-0xCF) that were valid for the 8087 and 80287
* but may no longer be valid as of the 80387.
*
* @this {X86FPU}
*/
X86FPU.FXCH8087 = function()
{
this.opObsolete();
X86FPU.FXCHsti.call(this);
};
/**
* FXTRACT()
*
* FXTRACT splits the value encoded in ST(0) into two separate numbers representing the actual value of the
* fraction (mantissa) and exponent fields.
*
* The FXTRACT instruction is used to decompose the two fields of the temporary real number in ST(0). The exponent
* replaces the value in ST(0), then the fraction is pushed onto the stack. When execution is complete, ST(0)
* contains the original fraction, expressed as a real number with a true exponent of 0 (0x3FFF in biased form),
* and ST(1) contains the value of the original operand's true (unbiased) exponent expressed as a real number.
*
* If ST(0) is 0, the 8087 and 80287 will leave zeros in both ST(0) and ST(1); both zeros will have the same sign as
* the original operand. If ST(0) is +infinity, the invalid operation exception is raised.
*
* On the 80287XL and later coprocessors, if ST(0) is 0, the zero-divide exception is reported and ST(1) is set to
* -infinity. If ST(0) is +infinity, no exception is reported.
*
* The FXTRACT instruction may be thought of as the complement to the FSCALE instruction, which combines a separate
* fraction and exponent into a single value.
*
* ALGORITHM:
*
* IF (ST(0) = 0) THEN
* DEC TOP
* ST(0) = ST(1)
* ELSE
* temp = ST(0)
* ST(0) = EXPONENT(ST(0)) ; stored as true exponent
* DEC TOP
* ST(0) = FRACTION(ST(0))
* ENDIF
*
* @this {X86FPU}
*/
X86FPU.FXTRACT = function()
{
var v = this.getST(0);
if (v != null) {
this.regTmpLR[0] = v;
this.setST(0, ((this.intTmpLR[1] >> 20) & 0x7ff) - 0x3ff);
this.intTmpLR[1] = (this.intTmpLR[1] | 0x3ff00000) & ~0x40000000;
this.pushValue(this.regTmpLR[0]);
}
};
/**
* FYL2X()
*
* FYL2X (y log base 2 of x) calculates:
*
* ST(1) = ST(1) * log2(ST(0))
*
* The operands must satisfy the inequalities 0 < ST(0) < +infinity and -infinity < ST(1) < +infinity. FYL2X pops
* the stack and returns the result to the new ST(0). Both original operands are destroyed.
*
* The FYL2X function is designed to optimize the calculation of a log to a base, n, other than two. In such a case,
* the following multiplication is required; ie:
*
* logn(x) = logn(2) * log2(x)
*
* @this {X86FPU}
*/
X86FPU.FYL2X = function()
{
if (this.setST(1, this.getST(1) * Math.log(this.getST(0)) / Math.LN2)) this.popValue();
};
/**
* FYL2XP1()
*
* FYL2XP1 (y log base 2 of x plus 1) calculates:
*
* ST(1) = ST(1) * log2(ST(0) + 1)
*
* The operands must satisfy the inequalities -(1-sqrt(2)/2) < ST(0) < (1-sqrt(2)/2) and -infinity < ST(1) < +infinity.
* FYL2XP1 pops the stack and returns the result to the new ST(0). Both original operands are destroyed.
*
* The FYL2XP1 function provides greater accuracy than FYL2X in computing the log of a number that is very close to 1.
*
* FYL2XP1 is typically used when computing compound interest, for example, which requires the calculation of a logarithm
* of 1.0 + n where 0 < n < 0.29. If 1.0 was added to n, significant digits might be lost. By using FYL2XP1, the result
* will be as accurate as n to within three units of temporary real precision.
*
* @this {X86FPU}
*/
X86FPU.FYL2XP1 = function()
{
if (this.setST(1, this.getST(1) * Math.log(this.getST(0) + 1.0) / Math.LN2)) this.popValue();
};
/*
* FPU operation lookup table (be sure to keep the following table in sync with Debugger.aaaOpFPUDescs).
*
* The second lookup value corresponds to bits in the ModRegRM byte that follows the ESC byte (0xD8-0xDF).
*
* Here's a little cheat-sheet for how the 2nd lookup values relate to ModRegRM values; see opFPU() for details.
*
* Lookup ModRegRM value(s)
* ------ -------------------------------
* 0x00: 0x00-0x07, 0x40-0x47, 0x80-0x87
* 0x01: 0x08-0x0F, 0x48-0x4F, 0x88-0x8F
* 0x02: 0x10-0x17, 0x50-0x57, 0x90-0x97
* 0x03: 0x18-0x1F, 0x58-0x5F, 0x98-0x9F
* 0x04: 0x20-0x27, 0x60-0x67, 0xA0-0xA7
* 0x05: 0x28-0x2F, 0x68-0x6F, 0xA8-0xAF
* 0x06: 0x30-0x37, 0x70-0x77, 0xB0-0xB7
* 0x07: 0x38-0x3F, 0x78-0x7F, 0xB8-0xBF
* 0x30: 0xC0-0xC7
* 0x31: 0xC8-0xCF
* 0x32: 0xD0-0xD7
* 0x33: 0xD8-0xDF
* 0x34: 0xE0-0xE7
* 0x35: 0xE8-0xEF
* 0x36: 0xF0-0xF7
* 0x37: 0xF8-0xFF
*
* ESC bytes 0xD9 and 0xDB use the RM field to further describe the operation when the ModRegRM value >= 0xE0.
* In those cases, we shift the Reg value into the high nibble and the RM value into the low nibble, resulting in
* the following lookup values (which look a lot like hex-encoded octal):
*
* 0x40: 0xE0
* 0x41: 0xE1
* ... ...
* 0x46: 0xE6
* 0x47: 0xE7
*
* 0x50: 0xE8
* 0x51: 0xE9
* ... ...
* 0x56: 0xEE
* 0x57: 0xEF
*
* 0x60: 0xF0
* 0x61: 0xF1
* ... ...
* 0x66: 0xF6
* 0x67: 0xF7
*
* 0x70: 0xF8
* 0x71: 0xF9
* ... ...
* 0x76: 0xFE
* 0x77: 0xFF
*/
X86FPU.aaOps = {
0xD8: {
0x00: X86FPU.FADDsr, 0x01: X86FPU.FMULsr, 0x02: X86FPU.FCOMsr, 0x03: X86FPU.FCOMPsr,
0x04: X86FPU.FSUBsr, 0x05: X86FPU.FSUBRsr, 0x06: X86FPU.FDIVsr, 0x07: X86FPU.FDIVsr,
0x30: X86FPU.FADDst, 0x31: X86FPU.FMULst, 0x32: X86FPU.FCOMst, 0x33: X86FPU.FCOMPst,
0x34: X86FPU.FSUBst, 0x35: X86FPU.FSUBRst, 0x36: X86FPU.FDIVst, 0x37: X86FPU.FDIVRst
},
0xD9: {
0x00: X86FPU.FLDsr, 0x02: X86FPU.FSTsr, 0x03: X86FPU.FSTPsr,
0x04: X86FPU.FLDENV, 0x05: X86FPU.FLDCW, 0x06: X86FPU.FSTENV, 0x07: X86FPU.FSTCW,
0x30: X86FPU.FLDsti, 0x31: X86FPU.FXCHsti, 0x32: X86FPU.FNOP, 0x33: X86FPU.FSTP8087,
0x40: X86FPU.FCHS, 0x41: X86FPU.FABS,
0x44: X86FPU.FTST, 0x45: X86FPU.FXAM,
0x50: X86FPU.FLD1, 0x51: X86FPU.FLDL2T, 0x52: X86FPU.FLDL2E, 0x53: X86FPU.FLDPI,
0x54: X86FPU.FLDLG2, 0x55: X86FPU.FLDLN2, 0x56: X86FPU.FLDZ,
0x60: X86FPU.F2XM1, 0x61: X86FPU.FYL2X, 0x62: X86FPU.FPTAN, 0x63: X86FPU.FPATAN,
0x64: X86FPU.FXTRACT, 0x66: X86FPU.FDECSTP, 0x67: X86FPU.FINCSTP,
0x70: X86FPU.FPREM, 0x71: X86FPU.FYL2XP1, 0x72: X86FPU.FSQRT,
0x74: X86FPU.FRNDINT, 0x75: X86FPU.FSCALE
},
0xDA: {
0x00: X86FPU.FIADD32, 0x01: X86FPU.FIMUL32, 0x02: X86FPU.FICOM32, 0x03: X86FPU.FICOMP32,
0x04: X86FPU.FISUB32, 0x05: X86FPU.FISUBR32, 0x06: X86FPU.FIDIV32, 0x07: X86FPU.FIDIVR32
},
0xDB: {
0x00: X86FPU.FILD32, 0x02: X86FPU.FIST32, 0x03: X86FPU.FISTP32,
0x05: X86FPU.FLDtr, 0x07: X86FPU.FSTPtr,
0x40: X86FPU.FENI8087, 0x41: X86FPU.FDISI8087, 0x42: X86FPU.FCLEX, 0x43: X86FPU.FINIT,
0x44: X86FPU.FSETPM287,
0x73: X86FPU.FSINCOS387
},
0xDC: {
0x00: X86FPU.FADDlr, 0x01: X86FPU.FMULlr, 0x02: X86FPU.FCOMlr, 0x03: X86FPU.FCOMPlr,
0x04: X86FPU.FSUBlr, 0x05: X86FPU.FSUBRlr, 0x06: X86FPU.FDIVlr, 0x07: X86FPU.FDIVRlr,
0x30: X86FPU.FADDsti, 0x31: X86FPU.FMULsti, 0x32: X86FPU.FCOM8087, 0x33: X86FPU.FCOMP8087,
/*
* Intel's original 8087 datasheet had these forms of SUB and SUBR (and DIV and DIVR) swapped.
*/
0x34: X86FPU.FSUBRsti, 0x35: X86FPU.FSUBsti, 0x36: X86FPU.FDIVRsti, 0x37: X86FPU.FDIVsti
},
0xDD: {
0x00: X86FPU.FLDlr, 0x02: X86FPU.FSTlr, 0x03: X86FPU.FSTPlr,
0x04: X86FPU.FRSTOR, 0x06: X86FPU.FSAVE, 0x07: X86FPU.FSTSW,
0x30: X86FPU.FFREEsti, 0x31: X86FPU.FXCH8087, 0x32: X86FPU.FSTsti, 0x33: X86FPU.FSTPsti
},
0xDE: {
0x00: X86FPU.FIADD16, 0x01: X86FPU.FIMUL16, 0x02: X86FPU.FICOM16, 0x03: X86FPU.FICOMP16,
0x04: X86FPU.FISUB16, 0x05: X86FPU.FISUBR16, 0x06: X86FPU.FIDIV16, 0x07: X86FPU.FIDIVR16,
0x30: X86FPU.FADDPsti, 0x31: X86FPU.FMULPsti, 0x32: X86FPU.FCOMP8087, 0x33: X86FPU.FCOMPP,
/*
* Intel's original 8087 datasheet had these forms of SUBP and SUBRP (and DIVP and DIVRP) swapped.
*/
0x34: X86FPU.FSUBRPsti, 0x35: X86FPU.FSUBPsti, 0x36: X86FPU.FDIVRPsti, 0x37: X86FPU.FDIVPsti
},
0xDF: {
0x00: X86FPU.FILD16, 0x02: X86FPU.FIST16, 0x03: X86FPU.FISTP16,
0x04: X86FPU.FBLDpd, 0x05: X86FPU.FILD64, 0x06: X86FPU.FBSTPpd, 0x07: X86FPU.FISTP64,
0x30: X86FPU.FFREEP8087,0x31: X86FPU.FXCH8087, 0x32: X86FPU.FSTP8087, 0x33: X86FPU.FSTP8087,
0x34: X86FPU.FSTSWAX287
}
};
/*
* An array of X86FPU functions documented as preserving the "exception" registers.
*/
X86FPU.afnPreserveExceptions = [
X86FPU.FCLEX, X86FPU.FINIT, X86FPU.FLDCW, X86FPU.FLDENV, X86FPU.FRSTOR,
X86FPU.FSAVE, X86FPU.FSTCW, X86FPU.FSTENV, X86FPU.FSTSW, X86FPU.FSTSWAX287
];
/**
* X86FPU.init()
*
* This function operates on every HTML element of class "fpu", extracting the
* JSON-encoded parameters for the X86FPU constructor from the element's "data-value"
* attribute, invoking the constructor to create an X86FPU component, and then binding
* any associated HTML controls to the new component.
*/
X86FPU.init = function()
{
var aeFPUs = Component.getElementsByClass(document, PCX86.APPCLASS, "fpu");
for (var iFPU = 0; iFPU < aeFPUs.length; iFPU++) {
var eFPU = aeFPUs[iFPU];
var parmsFPU = Component.getComponentParms(eFPU);
var fpu = new X86FPU(parmsFPU);
Component.bindComponentControls(fpu, eFPU, PCX86.APPCLASS);
}
};
/*
* Initialize every FPU module on the page
*/
web.onInit(X86FPU.init);
if (NODE) module.exports = X86FPU;