From 7a0147c122eea686ca3aabf47c605df002bbe8f5 Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Tue, 1 Dec 2015 09:43:11 -0800 Subject: [PATCH] In-line documentation updates --- modules/pcjs/lib/debugger.js | 16 +- modules/pcjs/lib/x86fpu.js | 3286 +++++++++++++++++----------------- 2 files changed, 1699 insertions(+), 1603 deletions(-) diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index 4b010281c..15ffbc2af 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -576,11 +576,11 @@ if (DEBUGGER) { Debugger.TYPE_ST = 0x0009; // FPU ST (implicit stack top) Debugger.TYPE_STREG = 0x000A; // FPU ST (explicit stack register, relative to top) Debugger.TYPE_SINT = 0x000B; // FPU SI (short-integer; 32-bit); displayed as "INT32" - Debugger.TYPE_SREAL = 0x000B; // FPU SR (short-real; 32-bit) + Debugger.TYPE_SREAL = 0x000B; // FPU SR (short-real; 32-bit); displayed as "REAL32" Debugger.TYPE_LINT = 0x000C; // FPU LI (long-integer; 64-bit); displayed as "INT64" - Debugger.TYPE_LREAL = 0x000C; // FPU LR (long-real; 64-bit) - Debugger.TYPE_TREAL = 0x000D; // FPU TR (temp-real; 80-bit) - Debugger.TYPE_DEC18 = 0x000E; // FPU PD (packed-decimal, 18 digits; 80-bit) + Debugger.TYPE_LREAL = 0x000C; // FPU LR (long-real; 64-bit); displayed as "REAL64" + Debugger.TYPE_TREAL = 0x000D; // FPU TR (temp-real; 80-bit); displayed as "REAL80" + Debugger.TYPE_BCD80 = 0x000E; // FPU PD (packed-decimal; 18 BCD digits in 80 bits, bits 72-78 unused, sign in bit 79); displayed as "BCD80" Debugger.TYPE_ENV = 0x000F; // FPU ENV (environment; 14 bytes in real-mode, 28 bytes in protected-mode) Debugger.TYPE_FPU = 0x000F; // FPU SAVE (save/restore; 94 bytes in real-mode, 108 bytes in protected-mode) @@ -1229,9 +1229,9 @@ if (DEBUGGER) { 0x00: [Debugger.FINS.FILD, Debugger.TYPE_MODRM | Debugger.TYPE_SHORT | Debugger.TYPE_IN], 0x02: [Debugger.FINS.FIST, Debugger.TYPE_MODRM | Debugger.TYPE_SHORT | Debugger.TYPE_OUT], 0x03: [Debugger.FINS.FISTP, Debugger.TYPE_MODRM | Debugger.TYPE_SHORT | Debugger.TYPE_OUT], - 0x04: [Debugger.FINS.FBLD, Debugger.TYPE_MODRM | Debugger.TYPE_DEC18 | Debugger.TYPE_IN], + 0x04: [Debugger.FINS.FBLD, Debugger.TYPE_MODRM | Debugger.TYPE_BCD80 | Debugger.TYPE_IN], 0x05: [Debugger.FINS.FILD, Debugger.TYPE_MODRM | Debugger.TYPE_LINT | Debugger.TYPE_IN], - 0x06: [Debugger.FINS.FBSTP, Debugger.TYPE_MODRM | Debugger.TYPE_DEC18 | Debugger.TYPE_OUT], + 0x06: [Debugger.FINS.FBSTP, Debugger.TYPE_MODRM | Debugger.TYPE_BCD80 | Debugger.TYPE_OUT], 0x07: [Debugger.FINS.FISTP, Debugger.TYPE_MODRM | Debugger.TYPE_LINT | Debugger.TYPE_OUT], 0x30: [Debugger.FINS.FFREEP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], // Obsolete encoding 0x31: [Debugger.FINS.FXCH, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT], // Obsolete encoding @@ -5180,8 +5180,8 @@ if (DEBUGGER) { case Debugger.TYPE_TREAL: sPrefix = "REAL80"; break; - case Debugger.TYPE_DEC18: - sPrefix = "DEC18"; + case Debugger.TYPE_BCD80: + sPrefix = "BCD80"; break; } if (sPrefix) sOperand = sPrefix + ' ' + sOperand; diff --git a/modules/pcjs/lib/x86fpu.js b/modules/pcjs/lib/x86fpu.js index 7af02a4b1..d434acc23 100644 --- a/modules/pcjs/lib/x86fpu.js +++ b/modules/pcjs/lib/x86fpu.js @@ -6,6 +6,9 @@ * * Copyright © 2012-2015 Jeff Parsons * + * 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). + * * This file is part of PCjs, which is part of the JavaScript Machines Project (aka JSMachines) * at and . * @@ -44,7 +47,7 @@ if (NODE) { /* * Operand Type Reference * - * ST stack top; the register currently at the top of the stack + * 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) @@ -154,1591 +157,6 @@ function X86FPU(parmsFPU) Component.subclass(X86FPU); -/* - * Class constants - */ - -/** @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() - * - * NOTE: 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. - * - * @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() -{ - var v = this.roundInteger(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() - * - * From PC Magazine's "Programmer's Technical Reference: The Processor and Coprocessor", p.647: - * - * 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() - * - * @this {X86FPU} - */ -X86FPU.FLD1 = function() -{ - this.pushValue(1.0); -}; - -/** - * FLDL2T() - * - * @this {X86FPU} - */ -X86FPU.FLDL2T = function() -{ - this.pushValue(X86FPU.regL2T); -}; - -/** - * FLDL2E() - * - * @this {X86FPU} - */ -X86FPU.FLDL2E = function() -{ - this.pushValue(X86FPU.regL2E); -}; - -/** - * FLDPI() - * - * @this {X86FPU} - */ -X86FPU.FLDPI = function() -{ - this.pushValue(X86FPU.regPI); -}; - -/** - * FLDLG2() - * - * @this {X86FPU} - */ -X86FPU.FLDLG2 = function() -{ - this.pushValue(X86FPU.regLG2); -}; - -/** - * FLDLN2() - * - * @this {X86FPU} - */ -X86FPU.FLDLN2 = function() -{ - this.pushValue(X86FPU.regLN2); -}; - -/** - * FLDZ() - * - * @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.roundInteger(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(); -}; - /** * initBus(cmp, bus, cpu, dbg) * @@ -2758,7 +1176,7 @@ X86FPU.prototype.getLRFromTR = function(a) if (expLR == 0x7fff) { /* - * Convert an TR NaN to a LR Nan. + * Convert an TR NaN to a LR NaN. */ expLR = 0x7ff; } @@ -2802,7 +1220,7 @@ X86FPU.prototype.getTRFromLR = function(loLR, hiLR) if (expTR == 0x07ff) { /* - * Convert an LR NaN to a TR Nan. NaNs encompass +/- infinity, which in the LR + * 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; @@ -3025,9 +1443,9 @@ X86FPU.prototype.opFPU = function(bOpcode, bModRM, dst, src) /* * 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 - * have to 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's the best we can do for now. + * 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++; @@ -3098,6 +1516,1681 @@ if (DEBUGGER) { }; } +/* + * 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() +{ + var v = this.roundInteger(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.roundInteger(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). * @@ -3130,22 +3223,25 @@ if (DEBUGGER) { * * 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 */