From 2b1b62d5658cc9a13908c0ce965dcf5c5d78fa79 Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Fri, 13 Nov 2015 18:20:20 -0800 Subject: [PATCH] New skeleton for x86fpu.js --- modules/pcjs/lib/chipset.js | 56 +- modules/pcjs/lib/cpu.js | 4 +- modules/pcjs/lib/debugger.js | 55 +- modules/pcjs/lib/x86.js | 17 +- modules/pcjs/lib/x86cpu.js | 4 +- modules/pcjs/lib/x86fpu.js | 1457 +++++++++++++++++++++++++++++++--- modules/pcjs/lib/x86func.js | 20 +- 7 files changed, 1443 insertions(+), 170 deletions(-) diff --git a/modules/pcjs/lib/chipset.js b/modules/pcjs/lib/chipset.js index d0606d984..4dd82b0c6 100644 --- a/modules/pcjs/lib/chipset.js +++ b/modules/pcjs/lib/chipset.js @@ -40,6 +40,7 @@ if (NODE) { var Interrupts = require("./interrupts"); var Messages = require("./messages"); var State = require("./state"); + var X86 = require("./x86"); } /** @@ -985,7 +986,7 @@ ChipSet.NMI = { // this.bNMI /* * Coprocessor Control Registers (MODEL_5170) */ -ChipSet.COPROC = { // TODO: Define a variable for this +ChipSet.COPROC = { // TODO: Define a variable for this? PORT_CLEAR: 0xF0, // clear the coprocessor's "busy" state PORT_RESET: 0xF1 // reset the coprocessor }; @@ -1058,7 +1059,12 @@ ChipSet.prototype.initBus = function(cmp, bus, cpu, dbg) this.cpu = cpu; this.dbg = dbg; this.cmp = cmp; + + this.fpu = cmp.getMachineComponent("FPU"); + if (this.fpu) this.sw1Init |= ChipSet.PPI_SW.COPROC; + this.kbd = cmp.getMachineComponent("Keyboard"); + /* * This divisor is invariant, so we calculate it as soon as we're able to query the CPU's base speed. */ @@ -1091,9 +1097,6 @@ ChipSet.prototype.initBus = function(cmp, bus, cpu, dbg) } cpu.addIntNotify(Interrupts.RTC, this.intBIOSRTC.bind(this)); } - if (cpu.fpu) { - this.sw1Init |= ChipSet.PPI_SW.COPROC; - } }; /** @@ -3513,6 +3516,43 @@ ChipSet.prototype.getIRRVector = function(iPIC) return nIDT; }; +/** + * setFPUInterrupt() + * + * @this {ChipSet} + */ +ChipSet.prototype.setFPUInterrupt = function() +{ + if (this.model >= ChipSet.MODEL_5170) { + this.setIRR(ChipSet.IRQ.COPROC); + } else { + /* + * TODO: Determine whether we need to maintain an "Active NMI" state; ie, if NMI.DISABLE is cleared + * later, and the coprocessor is still indicating an error condition, should we then generate an NMI? + */ + if (!(this.bNMI & ChipSet.NMI.DISABLE)) { + X86.fnInterrupt.call(this.cpu, X86.EXCEPTION.NMI); + } + } +}; + +/** + * clearFPUInterrupt(fSet) + * + * @this {ChipSet} + */ +ChipSet.prototype.clearFPUInterrupt = function() +{ + if (this.model >= ChipSet.MODEL_5170) { + this.clearIRR(ChipSet.IRQ.COPROC); + } else { + /* + * TODO: If we maintain an "Active NMI" state, then we will need code here to clear that state, as well + * as code in outNMI() to clear that state and generate an NMI as needed. + */ + } +}; + /** * inTimer(iTimer, addrFrom) * @@ -4959,11 +4999,9 @@ ChipSet.prototype.outNMI = function(port, bOut, addrFrom) */ ChipSet.prototype.outCoprocClear = function(port, bOut, addrFrom) { - /* - * TODO: Implement - */ this.printMessageIO(port, bOut, addrFrom, "COPROC.CLEAR"); this.assert(!bOut); + if (this.fpu) this.fpu.clearBusy(); }; /** @@ -4978,11 +5016,9 @@ ChipSet.prototype.outCoprocClear = function(port, bOut, addrFrom) */ ChipSet.prototype.outCoprocReset = function(port, bOut, addrFrom) { - /* - * TODO: Implement - */ this.printMessageIO(port, bOut, addrFrom, "COPROC.RESET"); this.assert(!bOut); + if (this.fpu) this.fpu.resetFPU(); }; /** diff --git a/modules/pcjs/lib/cpu.js b/modules/pcjs/lib/cpu.js index 7afe0bf06..3ec001ad6 100644 --- a/modules/pcjs/lib/cpu.js +++ b/modules/pcjs/lib/cpu.js @@ -181,6 +181,7 @@ CPU.prototype.initBus = function(cmp, bus, cpu, dbg) this.cmp = cmp; this.bus = bus; this.dbg = dbg; + this.fpu = cmp.getMachineComponent("FPU"); /* * Attach the Video component to the CPU, so that the CPU can periodically update @@ -197,9 +198,6 @@ CPU.prototype.initBus = function(cmp, bus, cpu, dbg) * We must also call chipset.updateAllTimers() periodically; stepCPU() takes care of that. */ this.chipset = cmp.getMachineComponent("ChipSet"); - - this.fpu = cmp.getMachineComponent("FPU"); - this.setReady(); }; diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index de96f0282..b58ce5baa 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -534,7 +534,8 @@ if (DEBUGGER) { Debugger.TYPE_OTHER = 0xF000; // "other" field /* - * TYPE_SIZE values. + * TYPE_SIZE values. Some definitions use duplicate values when the operands are the + * same size and the Debugger doesn't need to make a distinction. */ Debugger.TYPE_NONE = 0x0000; // (all other TYPE fields ignored) Debugger.TYPE_BYTE = 0x0001; // (b) byte, regardless of operand size @@ -544,18 +545,16 @@ if (DEBUGGER) { Debugger.TYPE_LONG = 0x0005; // (d) 32-bit value Debugger.TYPE_SEGP = 0x0006; // (p) 32-bit or 48-bit pointer Debugger.TYPE_FARP = 0x0007; // (p) 32-bit or 48-bit pointer for JMP/CALL - Debugger.TYPE_2WORD = 0x0008; // (a) two memory operands (BOUND only) - Debugger.TYPE_DESC = 0x0009; // (s) 6 byte pseudo-descriptor - Debugger.TYPE_PREFIX = 0x000A; // (treat similarly to TYPE_NONE) - Debugger.TYPE_ST = 0x000B; // FPU ST - Debugger.TYPE_STREG = 0x000C; // FPU ST register - Debugger.TYPE_SI = 0x000D; // FPU SI (short-integer; 32-bit) - Debugger.TYPE_SR = 0x000D; // FPU SR (short-real; 32-bit) - Debugger.TYPE_LI = 0x000E; // FPU LI (long-integer; 64-bit) - Debugger.TYPE_LR = 0x000E; // FPU LR (long-real; 64-bit) - Debugger.TYPE_TR = 0x000F; // FPU TR (temp-real; 80-bit) - Debugger.TYPE_PD = 0x000F; // FPU PD (packed-decimal, 18 digits; 80-bit) - Debugger.TYPE_ENV = 0x000F; // FPU ENV (environment; 14 bytes) + Debugger.TYPE_PREFIX = 0x0008; // (treat similarly to TYPE_NONE) + Debugger.TYPE_ST = 0x0009; // FPU ST (implicit top) + Debugger.TYPE_STREG = 0x000A; // FPU ST (explicit register) + Debugger.TYPE_SI = 0x000B; // FPU SI (short-integer; 32-bit) + Debugger.TYPE_SR = 0x000B; // FPU SR (short-real; 32-bit) + Debugger.TYPE_LI = 0x000C; // FPU LI (long-integer; 64-bit) + Debugger.TYPE_LR = 0x000C; // FPU LR (long-real; 64-bit) + Debugger.TYPE_TR = 0x000D; // FPU TR (temp-real; 80-zbit) + Debugger.TYPE_PD = 0x000D; // FPU PD (packed-decimal, 18 digits; 80-bit) + Debugger.TYPE_ENV = 0x000E; // FPU ENV (environment; 14 bytes) Debugger.TYPE_FPU = 0x000F; // FPU SAVE (save/restore; 94 bytes) /* @@ -816,7 +815,7 @@ if (DEBUGGER) { /* 0x60 */ [Debugger.INS.PUSHA, Debugger.TYPE_NONE | Debugger.TYPE_80286], /* 0x61 */ [Debugger.INS.POPA, Debugger.TYPE_NONE | Debugger.TYPE_80286], - /* 0x62 */ [Debugger.INS.BOUND, Debugger.TYPE_REG | Debugger.TYPE_WORD | Debugger.TYPE_IN | Debugger.TYPE_80286, Debugger.TYPE_MODRM | Debugger.TYPE_2WORD | Debugger.TYPE_IN], + /* 0x62 */ [Debugger.INS.BOUND, Debugger.TYPE_REG | Debugger.TYPE_WORD | Debugger.TYPE_IN | Debugger.TYPE_80286, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_IN], /* 0x63 */ [Debugger.INS.ARPL, Debugger.TYPE_MODRM | Debugger.TYPE_SHORT | Debugger.TYPE_OUT, Debugger.TYPE_REG | Debugger.TYPE_SHORT | Debugger.TYPE_IN], /* 0x64 */ [Debugger.INS.FS, Debugger.TYPE_PREFIX | Debugger.TYPE_80386], /* 0x65 */ [Debugger.INS.GS, Debugger.TYPE_PREFIX | Debugger.TYPE_80386], @@ -1067,6 +1066,9 @@ if (DEBUGGER) { 0xBF: [Debugger.INS.MOVSX, Debugger.TYPE_REG | Debugger.TYPE_LONG | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_MODRM | Debugger.TYPE_SHORT | Debugger.TYPE_IN] }; + /* + * Be sure to keep the following table in sync with X86FPU.aaOps + */ Debugger.aaaOpFPUDescs = { 0xD8: { 0x00: [Debugger.FINS.FADD, Debugger.TYPE_MODRM | Debugger.TYPE_SR | Debugger.TYPE_IN], @@ -1097,7 +1099,7 @@ if (DEBUGGER) { 0x30: [Debugger.FINS.FLD, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT], 0x31: [Debugger.FINS.FXCH, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT], 0x32: [Debugger.FINS.FNOP], - 0x33: [Debugger.FINS.FSTP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT], + 0x33: [Debugger.FINS.FSTP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT], 0x40: [Debugger.FINS.FCHS], 0x41: [Debugger.FINS.FABS], 0x44: [Debugger.FINS.FTST], @@ -1144,14 +1146,14 @@ if (DEBUGGER) { 0x43: [Debugger.FINS.FINIT] }, 0xDC: { - 0x00: [Debugger.FINS.FADD, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], - 0x01: [Debugger.FINS.FMUL, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], - 0x02: [Debugger.FINS.FCOM, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], - 0x03: [Debugger.FINS.FCOMP, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], - 0x04: [Debugger.FINS.FSUB, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], - 0x05: [Debugger.FINS.FSUBR, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], - 0x06: [Debugger.FINS.FDIV, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], - 0x07: [Debugger.FINS.FDIVR, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], + 0x00: [Debugger.FINS.FADD, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], + 0x01: [Debugger.FINS.FMUL, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], + 0x02: [Debugger.FINS.FCOM, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], + 0x03: [Debugger.FINS.FCOMP, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], + 0x04: [Debugger.FINS.FSUB, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], + 0x05: [Debugger.FINS.FSUBR, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], + 0x06: [Debugger.FINS.FDIV, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], + 0x07: [Debugger.FINS.FDIVR, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], 0x30: [Debugger.FINS.FADD, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_IN], 0x31: [Debugger.FINS.FMUL, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_IN], 0x32: [Debugger.FINS.FCOM, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], @@ -4902,14 +4904,14 @@ if (DEBUGGER) { var r_m = (bModRM & 0x7); /* - * Similar to how fnFPU() decodes FPU instructions, we now combine mod and reg into - * one decodable value: put mod in the high nibble and reg in the low nibble, after first + * Similar to how fnFPU() decodes FPU instructions, we 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; /* - * Use values >= 0x40 to indicate mod == 0x3, with reg in the high nibble, and r_m in the low. + * Use values >= 0x40 to indicate mod == 0x3, with reg in the high nibble and r_m in the low. */ if ((bOpcode == X86.OPCODE.ESC1 || bOpcode == X86.OPCODE.ESC3) && modReg >= 0x34) { modReg = (reg << 4) | r_m; @@ -4949,7 +4951,6 @@ if (DEBUGGER) { sOperand = str.toHex((this.getByte(dbgAddr, 1) << 24) >> 24, dbgAddr.fData32? 8: 4); break; case Debugger.TYPE_WORD: - case Debugger.TYPE_2WORD: if (dbgAddr.fData32) { sOperand = str.toHex(this.getLong(dbgAddr, 4)); break; diff --git a/modules/pcjs/lib/x86.js b/modules/pcjs/lib/x86.js index 78a246713..b49c5dcc0 100644 --- a/modules/pcjs/lib/x86.js +++ b/modules/pcjs/lib/x86.js @@ -532,10 +532,13 @@ var X86 = { OM: 0x0008, // bit 3: Overflow Mask UM: 0x0010, // bit 4: Underflow Mask PM: 0x0020, // bit 5: Precision Mask + // bit 6: unused IEM: 0x0080, // bit 7: Interrupt Enable Mask (0 enables interrupts, 1 masks them; 8087 only) PC: 0x0300, // bits 8-9: Precision Control RC: 0x0C00, // bits 10-11: Rounding Control - IC: 0x1000 // bit 12: Infinity Control (0 for Projective, 1 for Affine) + IC: 0x1000, // bit 12: Infinity Control (0 for Projective, 1 for Affine) + // bits 13-15: unused + INIT: 0x03BF // X86.FPU.CONTROL.IM | X86.FPU.CONTROL.DM | X86.FPU.CONTROL.ZM | X86.FPU.CONTROL.OM | X86.FPU.CONTROL.UM | X86.FPU.CONTROL.PM | X86.FPU.CONTROL.IEM | X86.FPU.CONTROL.PC }, STATUS: { // FPU Status Word IE: 0x0001, // bit 0: Invalid Operation @@ -545,6 +548,7 @@ var X86 = { UE: 0x0010, // bit 4: Underflow PE: 0x0020, // bit 5: Precision SF: 0x0040, // bit 6: Stack Fault (80387 and later) + EXC: 0x007F, // all of the above exceptions ES: 0x0080, // bit 7: Exception Summary (Interrupt Request on 8087) C0: 0x0100, // bit 8: Condition Code 0 C1: 0x0200, // bit 9: Condition Code 1 @@ -553,11 +557,12 @@ var X86 = { C3: 0x4000, // bit 14: Condition Code 3 BUSY: 0x8000 // bit 15: Busy }, - TAG: { - VALID: 0x0, - ZERO: 0x1, - SPECIAL: 0x2, - EMPTY: 0x3 + TAGS: { + VALID: 0x0, + ZERO: 0x1, + SPECIAL:0x2, + EMPTY: 0x3, + MASK: 0x3 } /* C3 C2 C1 C0 Condition Code (CC) values following an Examine diff --git a/modules/pcjs/lib/x86cpu.js b/modules/pcjs/lib/x86cpu.js index d2aff5abe..cf4fd18e5 100644 --- a/modules/pcjs/lib/x86cpu.js +++ b/modules/pcjs/lib/x86cpu.js @@ -3937,7 +3937,7 @@ X86CPU.prototype.checkINTR = function() this.intFlags &= ~X86.INTFLAG.INTR; if (nIDT >= 0) { this.intFlags &= ~X86.INTFLAG.HALT; - X86.fnINT.call(this, this.nFault = nIDT, null, 11); + X86.fnInterrupt.call(this, nIDT); return true; } } @@ -3947,7 +3947,7 @@ X86CPU.prototype.checkINTR = function() if ((this.intFlags & X86.INTFLAG.TRAP)) { this.intFlags &= ~X86.INTFLAG.TRAP; if (I386 && this.model >= X86.MODEL_80386) this.regDR[6] |= X86.DR6.BS; - X86.fnINT.call(this, this.nFault = X86.EXCEPTION.DB_EXC, null, 11); + X86.fnInterrupt.call(this, X86.EXCEPTION.DB_EXC); return true; } break; diff --git a/modules/pcjs/lib/x86fpu.js b/modules/pcjs/lib/x86fpu.js index d8131353c..35b5324ff 100644 --- a/modules/pcjs/lib/x86fpu.js +++ b/modules/pcjs/lib/x86fpu.js @@ -66,28 +66,46 @@ function X86FPU(parmsFPU) this.stepping = this.model + (stepping? str.parseInt(stepping, 16) : 0); /* - * Initialize the coprocessor to match the requested model. - * - * NOTE: Technically, the FPU's internal registers are 80-bit, but all JavaScript gives us are 64-bit floats. + * Perform a one-time allocation of all floating-point registers. + * NOTE: Technically, the FPU's internal registers are 80-bit, but JavaScript gives us only 64-bit floats. */ - this.aFPUEmpty = new Array(8); - this.aFPURegs = new Float64Array(8); + this.aRegs = new Float64Array(8); - this.regFPUTmpSR = new Float32Array(1); - this.intFPUTmpSR = new Int32Array(this.regFPUTmpSR.buffer); + this.regTmpSR = new Float32Array(1); + this.intTmpSR = new Int32Array(this.regTmpSR.buffer); - this.regFPUTmpLR = new Float64Array(1); - this.intFPUTmpLR = new Int32Array(this.regFPUTmpLR.buffer); + this.regTmpLR = new Float64Array(1); + this.intTmpLR = new Int32Array(this.regTmpLR.buffer); - this.regFPUInsAddr = this.regFPUDataAddr = this.regFPUOpcode = 0; + /* + * Initialize any other (non-floating-point) coprocessor registers that resetFPU() doesn't touch. + * NOTE: iOperand is the low 3 bits of the bModRM byte, used by instructions that allow an explicit operand. + */ + this.regInsAddr = this.regDataAddr = this.regOpcode = this.iOperand = 0; + /* + * Initialize floating-point constants, as if they were internal read-only registers. + */ + this.regIndefinite = new Float64Array(1); + this.intIndefinite = new Int32Array(this.regIndefinite.buffer); + this.intIndefinite[0] = 0xFFF8000; this.intIndefinite[1] = 0x00000000; + + this.regL2T = Math.log(10) / Math.LN2; // log2(10) (use Math.log2() if we ever switch to ES6) + this.regL2E = Math.LOG2E; // log2(e) + this.regPI = Math.PI; // pi + this.regLG2 = Math.log(2) / Math.LN10; // log10(2) (use Math.log10() if we ever switch to ES6) + this.regLN2 = Math.LN2; // log(2) + + /* + * Reset all other coprocessor registers (control word, tag word, status word, etc). + */ this.resetFPU(); } Component.subclass(X86FPU); /* - * Operand types: + * Operand Type Reference * * 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; ST(1) is the next-on-stack register, ST(2) is below ST(1), etc @@ -102,6 +120,1135 @@ Component.subclass(X86FPU); * */ +/** + * F2XM1() + * + * @this {X86FPU} + */ +X86FPU.F2XM1 = function() +{ + this.opUnimplemented(); +}; + +/** + * FABS() + * + * @this {X86FPU} + */ +X86FPU.FABS = function() +{ + this.opUnimplemented(); +}; + +/** + * FADDlr() + * + * @this {X86FPU} + */ +X86FPU.FADDlr = function() +{ + this.opUnimplemented(); +}; + +/** + * FADDsr() + * + * @this {X86FPU} + */ +X86FPU.FADDsr = function() +{ + this.check(this.aRegs[this.iST] += this.getSRFromEA()); +}; + +/** + * FADDst() + * + * @this {X86FPU} + */ +X86FPU.FADDst = function() +{ + this.check(this.aRegs[this.iST] += this.aRegs[this.iOperand]); +}; + +/** + * FADDsti() + * + * @this {X86FPU} + */ +X86FPU.FADDsti = function() +{ + this.check(this.aRegs[this.iOperand] += this.aRegs[this.iST]); +}; + +/** + * FADDPsti() + * + * @this {X86FPU} + */ +X86FPU.FADDPsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FBLDpd() + * + * @this {X86FPU} + */ +X86FPU.FBLDpd = function() +{ + this.opUnimplemented(); +}; + +/** + * FBSTPpd() + * + * @this {X86FPU} + */ +X86FPU.FBSTPpd = function() +{ + this.opUnimplemented(); +}; + +/** + * FCHS() + * + * @this {X86FPU} + */ +X86FPU.FCHS = function() +{ + this.opUnimplemented(); +}; + +/** + * FCLEX() + * + * @this {X86FPU} + */ +X86FPU.FCLEX = function() +{ + this.opUnimplemented(); +}; + +/** + * FCOMlr() + * + * @this {X86FPU} + */ +X86FPU.FCOMlr = function() +{ + this.opUnimplemented(); +}; + +/** + * FCOMsr() + * + * @this {X86FPU} + */ +X86FPU.FCOMsr = function() +{ + this.opUnimplemented(); +}; + +/** + * FCOMst() + * + * @this {X86FPU} + */ +X86FPU.FCOMst = function() +{ + this.opUnimplemented(); +}; + +/** + * FCOMsti() + * + * @this {X86FPU} + */ +X86FPU.FCOMsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FCOMPlr() + * + * @this {X86FPU} + */ +X86FPU.FCOMPlr = function() +{ + this.opUnimplemented(); +}; + +/** + * FCOMPsr() + * + * @this {X86FPU} + */ +X86FPU.FCOMPsr = function() +{ + this.opUnimplemented(); +}; + +/** + * FCOMPst() + * + * @this {X86FPU} + */ +X86FPU.FCOMPst = function() +{ + this.opUnimplemented(); +}; + +/** + * FCOMPsti() + * + * @this {X86FPU} + */ +X86FPU.FCOMPsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FCOMPPsti() + * + * @this {X86FPU} + */ +X86FPU.FCOMPPsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FDECSTP() + * + * @this {X86FPU} + */ +X86FPU.FDECSTP = function() +{ + this.opUnimplemented(); +}; + +/** + * FDISI() + * + * @this {X86FPU} + */ +X86FPU.FDISI = function() +{ + this.opUnimplemented(); +}; + +/** + * FDIVlr() + * + * @this {X86FPU} + */ +X86FPU.FDIVlr = function() +{ + this.opUnimplemented(); +}; + +/** + * FDIVsr() + * + * @this {X86FPU} + */ +X86FPU.FDIVsr = function() +{ + this.opUnimplemented(); +}; + +/** + * FDIVst() + * + * @this {X86FPU} + */ +X86FPU.FDIVst = function() +{ + this.opUnimplemented(); +}; + +/** + * FDIVsti() + * + * @this {X86FPU} + */ +X86FPU.FDIVsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FDIVPsti() + * + * @this {X86FPU} + */ +X86FPU.FDIVPsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FDIVRlr() + * + * @this {X86FPU} + */ +X86FPU.FDIVRlr = function() +{ + this.opUnimplemented(); +}; + +/** + * FDIVRsr() + * + * @this {X86FPU} + */ +X86FPU.FDIVRsr = function() +{ + this.opUnimplemented(); +}; + +/** + * FDIVRst() + * + * @this {X86FPU} + */ +X86FPU.FDIVRst = function() +{ + this.opUnimplemented(); +}; + +/** + * FDIVRsti() + * + * @this {X86FPU} + */ +X86FPU.FDIVRsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FDIVRPsti() + * + * @this {X86FPU} + */ +X86FPU.FDIVRPsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FENI() + * + * @this {X86FPU} + */ +X86FPU.FENI = function() +{ + this.opUnimplemented(); +}; + +/** + * FFREEsti() + * + * @this {X86FPU} + */ +X86FPU.FFREEsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FIADD16() + * + * @this {X86FPU} + */ +X86FPU.FIADD16 = function() +{ + this.opUnimplemented(); +}; + +/** + * FIADD32() + * + * @this {X86FPU} + */ +X86FPU.FIADD32 = function() +{ + this.opUnimplemented(); +}; + +/** + * FICOM16() + * + * @this {X86FPU} + */ +X86FPU.FICOM16 = function() +{ + this.opUnimplemented(); +}; + +/** + * FICOM32() + * + * @this {X86FPU} + */ +X86FPU.FICOM32 = function() +{ + this.opUnimplemented(); +}; + +/** + * FICOMP16() + * + * @this {X86FPU} + */ +X86FPU.FICOMP16 = function() +{ + this.opUnimplemented(); +}; + +/** + * FICOMP32() + * + * @this {X86FPU} + */ +X86FPU.FICOMP32 = function() +{ + this.opUnimplemented(); +}; + +/** + * FIDIV16() + * + * @this {X86FPU} + */ +X86FPU.FIDIV16 = function() +{ + this.opUnimplemented(); +}; + +/** + * FIDIV32() + * + * @this {X86FPU} + */ +X86FPU.FIDIV32 = function() +{ + this.opUnimplemented(); +}; + +/** + * FIDIVR16() + * + * @this {X86FPU} + */ +X86FPU.FIDIVR16 = function() +{ + this.opUnimplemented(); +}; + +/** + * FIDIVR32() + * + * @this {X86FPU} + */ +X86FPU.FIDIVR32 = function() +{ + this.opUnimplemented(); +}; + +/** + * FILD16() + * + * @this {X86FPU} + */ +X86FPU.FILD16 = function() +{ + this.opUnimplemented(); +}; + +/** + * FILD32() + * + * @this {X86FPU} + */ +X86FPU.FILD32 = function() +{ + this.opUnimplemented(); +}; + +/** + * FILD64() + * + * @this {X86FPU} + */ +X86FPU.FILD64 = function() +{ + this.opUnimplemented(); +}; + +/** + * FIMUL16() + * + * @this {X86FPU} + */ +X86FPU.FIMUL16 = function() +{ + this.opUnimplemented(); +}; + +/** + * FIMUL32() + * + * @this {X86FPU} + */ +X86FPU.FIMUL32 = function() +{ + this.opUnimplemented(); +}; + +/** + * FINCSTP() + * + * @this {X86FPU} + */ +X86FPU.FINCSTP = function() +{ + this.opUnimplemented(); +}; + +/** + * FINIT() + * + * @this {X86FPU} + */ +X86FPU.FINIT = function() +{ + this.resetFPU(); +}; + +/** + * FIST16() + * + * @this {X86FPU} + */ +X86FPU.FIST16 = function() +{ + this.opUnimplemented(); +}; + +/** + * FIST32() + * + * @this {X86FPU} + */ +X86FPU.FIST32 = function() +{ + this.opUnimplemented(); +}; + +/** + * FISTP16() + * + * @this {X86FPU} + */ +X86FPU.FISTP16 = function() +{ + this.opUnimplemented(); +}; + +/** + * FISTP32() + * + * @this {X86FPU} + */ +X86FPU.FISTP32 = function() +{ + this.opUnimplemented(); +}; + +/** + * FISTP64() + * + * @this {X86FPU} + */ +X86FPU.FISTP64 = function() +{ + this.opUnimplemented(); +}; + +/** + * FISUB16() + * + * @this {X86FPU} + */ +X86FPU.FISUB16 = function() +{ + this.opUnimplemented(); +}; + +/** + * FISUB32() + * + * @this {X86FPU} + */ +X86FPU.FISUB32 = function() +{ + this.opUnimplemented(); +}; + +/** + * FISUBR16() + * + * @this {X86FPU} + */ +X86FPU.FISUBR16 = function() +{ + this.opUnimplemented(); +}; + +/** + * FISUBR32() + * + * @this {X86FPU} + */ +X86FPU.FISUBR32 = function() +{ + this.opUnimplemented(); +}; + +/** + * FLDlr() + * + * @this {X86FPU} + */ +X86FPU.FLDlr = function() +{ + this.opUnimplemented(); +}; + +/** + * FLDsr() + * + * @this {X86FPU} + */ +X86FPU.FLDsr = function() +{ + this.opUnimplemented(); +}; + +/** + * FLDsti() + * + * @this {X86FPU} + */ +X86FPU.FLDsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FLDtr() + * + * @this {X86FPU} + */ +X86FPU.FLDtr = function() +{ + this.opUnimplemented(); +}; + +/** + * FLDCW() + * + * @this {X86FPU} + */ +X86FPU.FLDCW = function() +{ + this.opUnimplemented(); +}; + +/** + * FLDENV() + * + * @this {X86FPU} + */ +X86FPU.FLDENV = function() +{ + this.opUnimplemented(); +}; + +/** + * FLD1() + * + * @this {X86FPU} + */ +X86FPU.FLD1 = function() +{ + this.push(1.0); +}; + +/** + * FLDL2T() + * + * @this {X86FPU} + */ +X86FPU.FLDL2T = function() +{ + this.push(this.regL2T); +}; + +/** + * FLDL2E() + * + * @this {X86FPU} + */ +X86FPU.FLDL2E = function() +{ + this.push(this.regL2E); +}; + +/** + * FLDPI() + * + * @this {X86FPU} + */ +X86FPU.FLDPI = function() +{ + this.push(Math.PI); +}; + +/** + * FLDLG2() + * + * @this {X86FPU} + */ +X86FPU.FLDLG2 = function() +{ + this.push(this.regLG2); +}; + +/** + * FLDLN2() + * + * @this {X86FPU} + */ +X86FPU.FLDLN2 = function() +{ + this.push(this.regLN2); +}; + +/** + * FLDZ() + * + * @this {X86FPU} + */ +X86FPU.FLDZ = function() +{ + this.push(0.0); +}; + +/** + * FMULlr() + * + * @this {X86FPU} + */ +X86FPU.FMULlr = function() +{ + this.opUnimplemented(); +}; + +/** + * FMULsr() + * + * @this {X86FPU} + */ +X86FPU.FMULsr = function() +{ + this.check(this.aRegs[this.iST] *= this.getSRFromEA()); +}; + +/** + * FMULst() + * + * @this {X86FPU} + */ +X86FPU.FMULst = function() +{ + this.check(this.aRegs[this.iST] *= this.aRegs[this.iOperand]); +}; + +/** + * FMULsti() + * + * @this {X86FPU} + */ +X86FPU.FMULsti = function() +{ + this.check(this.aRegs[this.iOperand] *= this.aRegs[this.iST]); +}; + +/** + * FMULPsti() + * + * @this {X86FPU} + */ +X86FPU.FMULPsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FNOP() + * + * @this {X86FPU} + */ +X86FPU.FNOP = function() +{ +}; + +/** + * FPATAN() + * + * @this {X86FPU} + */ +X86FPU.FPATAN = function() +{ + this.opUnimplemented(); +}; + +/** + * FPTAN() + * + * @this {X86FPU} + */ +X86FPU.FPTAN = function() +{ + this.opUnimplemented(); +}; + +/** + * FPREM() + * + * @this {X86FPU} + */ +X86FPU.FPREM = function() +{ + this.opUnimplemented(); +}; + +/** + * FRSTOR() + * + * @this {X86FPU} + */ +X86FPU.FRSTOR = function() +{ + this.opUnimplemented(); +}; + +/** + * FRNDINT() + * + * @this {X86FPU} + */ +X86FPU.FRNDINT = function() +{ + this.opUnimplemented(); +}; + +/** + * FSAVE() + * + * @this {X86FPU} + */ +X86FPU.FSAVE = function() +{ + this.opUnimplemented(); +}; + +/** + * FSCALE() + * + * @this {X86FPU} + */ +X86FPU.FSCALE = function() +{ + this.opUnimplemented(); +}; + +/** + * FSQRT() + * + * @this {X86FPU} + */ +X86FPU.FSQRT = function() +{ + this.opUnimplemented(); +}; + +/** + * FSTlr() + * + * @this {X86FPU} + */ +X86FPU.FSTlr = function() +{ + this.opUnimplemented(); +}; + +/** + * FSTsr() + * + * @this {X86FPU} + */ +X86FPU.FSTsr = function() +{ + this.opUnimplemented(); +}; + +/** + * FSTsti() + * + * @this {X86FPU} + */ +X86FPU.FSTsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FSTENV() + * + * @this {X86FPU} + */ +X86FPU.FSTENV = function() +{ + this.opUnimplemented(); +}; + +/** + * FSTPlr() + * + * @this {X86FPU} + */ +X86FPU.FSTPlr = function() +{ + this.opUnimplemented(); +}; + +/** + * FSTPsr() + * + * @this {X86FPU} + */ +X86FPU.FSTPsr = function() +{ + this.opUnimplemented(); +}; + +/** + * FSTPsti() + * + * @this {X86FPU} + */ +X86FPU.FSTPsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FSTPtr() + * + * @this {X86FPU} + */ +X86FPU.FSTPtr = function() +{ + this.opUnimplemented(); +}; + +/** + * FSTCW() + * + * @this {X86FPU} + */ +X86FPU.FSTCW = function() +{ + this.cpu.setShort(this.cpu.regEA, this.regControl); +}; + +/** + * FSTSW() + * + * @this {X86FPU} + */ +X86FPU.FSTSW = function() +{ + this.cpu.setShort(this.cpu.regEA, this.regControl); +}; + +/** + * FSUBlr() + * + * @this {X86FPU} + */ +X86FPU.FSUBlr = function() +{ + this.opUnimplemented(); +}; + +/** + * FSUBsr() + * + * @this {X86FPU} + */ +X86FPU.FSUBsr = function() +{ + this.opUnimplemented(); +}; + +/** + * FSUBst() + * + * @this {X86FPU} + */ +X86FPU.FSUBst = function() +{ + this.opUnimplemented(); +}; + +/** + * FSUBsti() + * + * @this {X86FPU} + */ +X86FPU.FSUBsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FSUBPsti() + * + * @this {X86FPU} + */ +X86FPU.FSUBPsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FSUBRlr() + * + * @this {X86FPU} + */ +X86FPU.FSUBRlr = function() +{ + this.opUnimplemented(); +}; + +/** + * FSUBRsr() + * + * @this {X86FPU} + */ +X86FPU.FSUBRsr = function() +{ + this.opUnimplemented(); +}; + +/** + * FSUBRst() + * + * @this {X86FPU} + */ +X86FPU.FSUBRst = function() +{ + this.opUnimplemented(); +}; + +/** + * FSUBRsti() + * + * @this {X86FPU} + */ +X86FPU.FSUBRsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FSUBRPsti() + * + * @this {X86FPU} + */ +X86FPU.FSUBRPsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FTST() + * + * @this {X86FPU} + */ +X86FPU.FTST = function() +{ + this.opUnimplemented(); +}; + +/** + * FXAM() + * + * @this {X86FPU} + */ +X86FPU.FXAM = function() +{ + this.opUnimplemented(); +}; + +/** + * FXCHsti() + * + * @this {X86FPU} + */ +X86FPU.FXCHsti = function() +{ + this.opUnimplemented(); +}; + +/** + * FXTRACT() + * + * @this {X86FPU} + */ +X86FPU.FXTRACT = function() +{ + this.opUnimplemented(); +}; + +/** + * FYL2X() + * + * @this {X86FPU} + */ +X86FPU.FYL2X = function() +{ + this.opUnimplemented(); +}; + +/** + * FYL2XP1() + * + * @this {X86FPU} + */ +X86FPU.FYL2XP1 = function() +{ + this.opUnimplemented(); +}; + /** * initBus(cmp, bus, cpu, dbg) * @@ -114,36 +1261,134 @@ Component.subclass(X86FPU); 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 to clear the coprocessor's "busy" state is performed. + * + * @this {X86FPU} + */ +X86FPU.prototype.clearBusy = function() +{ + /* + * We're never "busy" because we perform all FPU operations synchronously, so there's nothing to do. + */ +}; + /** * resetFPU() * + * Aside from calling this internally (eg, during initialization and FINIT operations), the ChipSet may also call + * us whenever an I/O operation to reset the coprocessor is performed. + * * @this {X86FPU} */ X86FPU.prototype.resetFPU = function() { - for (var i = 0; i < this.aFPUEmpty.length; i++) this.aFPUEmpty[i] = true; - this.regFPUControl = X86.FPU.CONTROL.IM | X86.FPU.CONTROL.DM | X86.FPU.CONTROL.ZM | X86.FPU.CONTROL.OM | X86.FPU.CONTROL.UM | X86.FPU.CONTROL.PM | X86.FPU.CONTROL.IEM | X86.FPU.CONTROL.PC; - this.regFPUStatus = 0; - this.iFPUReg = 0; // copy of the ST bits in regFPUStatus + this.regUsed = 0; // bits 0-7 are set as regs 0-7 are used + this.regTags = 0xffff; // this updated only as needed by getTags() + this.regControl = X86.FPU.CONTROL.INIT; + this.regStatus = 0; + this.iST = 0; // copy of the ST bits in regStatus + if (this.chipset) this.chipset.clearFPUInterrupt(); }; /** - * setFPUTmpSRFromEA() - * - * Sets the internal regFPUTmpSR register to the (32-bit) short-real value located at regEA. + * opUnimplemented() * * @this {X86FPU} */ -X86FPU.prototype.setFPUTmpSRFromEA = function() +X86FPU.prototype.opUnimplemented = function() { - this.intFPUTmpSR[0] = this.cpu.getLong(this.cpu.regEA); + this.println(this.idComponent + ".opUnimplemented(" + str.toHexByte(this.cpu.bOpcode) + "," + str.toHexByte(this.cpu.bModRM) + ")"); + this.cpu.stopCPU(); }; /** - * fnFPU(bOpcode, bModRM, dst, src) + * getSRFromEA() + * + * Sets the internal regTmpSR register to the (32-bit) short-real value located at regEA. + * + * @this {X86FPU} + * @return {number} + */ +X86FPU.prototype.getSRFromEA = function() +{ + this.intTmpSR[0] = this.cpu.getLong(this.cpu.regEA); + return this.regTmpSR[0]; +}; + +/** + * check(f) + * + * @this {X86FPU} + * @param {number} f + */ +X86FPU.prototype.check = function(f) +{ + if (!isFinite(f)) { + this.fault(f === Infinity? X86.FPU.STATUS.OE : X86.FPU.STATUS.UE); + } +}; + +/** + * fault(n) + * + * 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) + * + * @this {X86FPU} + * @param {number} n (one or more of the above error indicators) + */ +X86FPU.prototype.fault = function(n) +{ + if (this.model < X86.FPU.MODEL_80387) { + n &= ~X86.FPU.STATUS.SF; // this status bit didn't exist on pre-80387 coprocessors + } + this.regStatus |= n; + if (!(this.regControl & X86.FPU.CONTROL.IEM)) { + /* + * TODO: Make sure that "unused" bit 6 of regControl can never be set, otherwise it could inadvertently + * mask the SF error condition on 80387 and newer coprocessors. + */ + if ((this.regStatus & X86.FPU.STATUS.EXC) & ~this.regControl) { + this.chipset.setFPUInterrupt(); + } + } +}; + +/** + * push(f) + * + * @this {X86FPU} + * @param {number} f + */ +X86FPU.prototype.push = function(f) +{ + this.iST = (this.iST-1) & 7; + var bitUsed = (1 << this.iST); + + if (this.regUsed & bitUsed) { + this.regStatus |= X86.FPU.STATUS.C1; // C1 set indicates stack overflow + this.aRegs[this.iST] = this.regIndefinite[0]; + this.fault(X86.FPU.STATUS.SF | X86.FPU.STATUS.IE); + } else { + this.aRegs[this.iST] = f; + this.regUsed |= bitUsed; + } +}; + +/** + * opFPU(bOpcode, bModRM, dst, src) * * @this {X86FPU} * @param {number} bOpcode (0xD8-0xDF) @@ -151,17 +1396,17 @@ X86FPU.prototype.setFPUTmpSRFromEA = function() * @param {number} dst * @param {number} src */ -X86FPU.prototype.fnFPU = function(bOpcode, bModRM, dst, src) +X86FPU.prototype.opFPU = function(bOpcode, bModRM, dst, src) { - this.regFPUOpcode = bOpcode; - this.regFPUInsAddr = this.cpu.regLIP; - this.regFPUDataAddr = this.cpu.regEA; + this.regOpcode = bOpcode; + this.regInsAddr = this.cpu.regLIP; + this.regDataAddr = this.cpu.regEA; - this.println("FPU(" + str.toHexByte(bOpcode) + "," + str.toHexByte(bModRM) + ")"); + this.println(this.idComponent + ".opFPU(" + str.toHexByte(bOpcode) + "," + str.toHexByte(bModRM) + ")"); var mod = (bModRM >> 6) & 0x3; var reg = (bModRM >> 3) & 0x7; - var r_m = (bModRM & 0x7); + this.iOperand = (bModRM & 0x7); /* * Combine mod and reg into one decodable value: put mod in the high nibble @@ -169,104 +1414,76 @@ X86FPU.prototype.fnFPU = function(bOpcode, bModRM, dst, src) */ var modReg = (mod < 3? 0 : 0x30) + reg; - switch(bOpcode) { + /* + * Use values >= 0x40 to indicate mod == 0x3, with reg in the high nibble and + * iOperand in the low. + */ + if ((bOpcode == X86.OPCODE.ESC1 || bOpcode == X86.OPCODE.ESC3) && modReg >= 0x34) { + modReg = (reg << 4) | this.iOperand; + } - case X86.OPCODE.ESC0: - switch(modReg) { - case 0x00: - this.fnFADDsr(); // ST=ST+[short-real] - break; - case 0x30: - this.fnFADDi(r_m); // ST=ST+ST(i) - break; - case 0x31: - this.fnFMULi(r_m); // ST=ST*ST(i) - break; - case 0x32: - // this.fnFCOMi(r_m); - break; - case 0x33: - // this.fnFCOMPi(r_m); - break; - case 0x34: - // this.fnFSUBi(r_m); // ST=ST-ST(i) - break; - case 0x35: - // this.fnFSUBRi(r_m); // ST=ST-ST(i) - break; - case 0x36: - // this.fnFDIVi(r_m); // ST=ST/ST(i) - break; - case 0x37: - // this.fnFDIVRi(r_m); // ST=ST/ST(i) - break; - } - break; - - case X86.OPCODE.ESC3: - switch(modReg) { - case 0x34: - switch(r_m) { - case 0: - // this.fnFENI(); - break; - case 1: - // this.fnFDISI; - break; - case 2: - // this.fnFCLEX(); - break; - case 3: - this.fnFINIT(); - break; - } - break; - } - break; + var fnOp = X86FPU.aaOps[bOpcode][modReg]; + if (fnOp) { + fnOp.call(this); + } else { + this.opUnimplemented(); } }; -/** - * fnFINIT() - * - * @this {X86FPU} +/* + * Be sure to keep the following table in sync with Debugger.aaaOpFPUDescs */ -X86FPU.prototype.fnFINIT = function() -{ - this.resetFPU(); -}; - -/** - * fnFADDi() - * - * @this {X86FPU} - * @param {number} i - */ -X86FPU.prototype.fnFADDi = function(i) -{ - this.aFPURegs[this.iFPUReg] += this.aFPURegs[i]; -}; - -/** - * fnFMULi() - * - * @this {X86FPU} - * @param {number} i - */ -X86FPU.prototype.fnFMULi = function(i) -{ - this.aFPURegs[this.iFPUReg] *= this.aFPURegs[i]; -}; - -/** - * fnFADDsr() - * - * @this {X86FPU} - */ -X86FPU.prototype.fnFADDsr = function() -{ - this.setFPUTmpSRFromEA(); - this.aFPURegs[this.iFPUReg] += this.regFPUTmpSR[0]; +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.FSTPsti, + 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.FENI, 0x41: X86FPU.FDISI, 0x42: X86FPU.FCLEX, 0x43: X86FPU.FINIT + }, + 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.FCOMsti, 0x33: X86FPU.FCOMPsti, + 0x34: X86FPU.FSUBsti, 0x35: X86FPU.FSUBRsti, 0x36: X86FPU.FDIVsti, 0x37: X86FPU.FDIVRsti + }, + 0xDD: { + 0x00: X86FPU.FLDlr, 0x02: X86FPU.FSTlr, 0x03: X86FPU.FSTPlr, + 0x04: X86FPU.FRSTOR, 0x06: X86FPU.FSAVE, 0x07: X86FPU.FSTSW, + 0x30: X86FPU.FFREEsti, 0x31: X86FPU.FXCHsti, 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.FCOMPsti, 0x33: X86FPU.FCOMPPsti, + 0x34: X86FPU.FSUBPsti, 0x35: X86FPU.FSUBRPsti, 0x36: X86FPU.FDIVPsti, 0x37: X86FPU.FDIVRPsti + }, + 0xDF: { + 0x00: X86FPU.FILD16, 0x02: X86FPU.FIST16, 0x03: X86FPU.FISTP16, 0x04: X86FPU.FBLDpd, + 0x05: X86FPU.FILD64, 0x06: X86FPU.FBSTPpd, 0x07: X86FPU.FISTP64, 0x30: X86FPU.FFREEsti, + 0x31: X86FPU.FXCHsti, 0x32: X86FPU.FSTPsti, 0x33: X86FPU.FSTPsti + } }; /** diff --git a/modules/pcjs/lib/x86func.js b/modules/pcjs/lib/x86func.js index 64297a9a1..5ec885bfa 100644 --- a/modules/pcjs/lib/x86func.js +++ b/modules/pcjs/lib/x86func.js @@ -946,8 +946,7 @@ X86.fnDIVw = function(dst, src) X86.fnESC = function(dst, src) { if (this.fpu) { - this.fpu.fnFPU(this.bOpcode, this.bModRM, dst, src); - this.stopCPU(); + this.fpu.opFPU(this.bOpcode, this.bModRM, dst, src); } this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 2 : 8); return dst; @@ -3879,6 +3878,23 @@ X86.fnSRCxx = function() return this.regXX; }; +/** + * fnInterrupt(nIDT, nCycles) + * + * Helper to dispatch external interrupts. nCycles defaults to 11 for the 8086/8088 + * if no alternate value is specified. + * + * @this {X86CPU} + * @param {number} nIDT + * @param {number} [nCycles] (number of cycles in addition to the default of nOpCyclesInt) + */ +X86.fnInterrupt = function(nIDT, nCycles) +{ + this.nFault = nIDT; + if (nCycles === undefined) nCycles = 11; + X86.fnINT.call(this, nIDT, null, nCycles); +}; + /** * fnTrap(nIDT, nCycles) *