diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index 0f0257327..077f278ba 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -252,6 +252,20 @@ if (DEBUGGER) { Component.subclass(Debugger); + /* + * NOTE: Every Debugger property from here to the first prototype function definition (initBus()) is a + * considered a "class constant"; most of them use our "all-caps" convention (and all of them SHOULD, but + * that wouldn't help us catch any bugs). + * + * Technically, all of them should ALSO be preceded by a "@const" annotation, but that's a lot of work and it + * really clutters the code. I wish the Closure Compiler had a way to annotate every definition with a given + * section with a single annotation.... + * + * Bugs can slip through the cracks without those annotations; for example, I unthinkingly redefined TYPE_SI + * at one point, and if all the definitions had been preceded by an "@const", that mistake would have been + * caught at compile-time. + */ + /* * Information regarding interrupts of interest (used by messageInt() and others) */ @@ -398,7 +412,8 @@ if (DEBUGGER) { * 34-40: comparisons * 41-45: transcendental * 46-52: constants - * 53-76: coprocessor control + * 53-77: coprocessor control + * 78---: new for 80287 or higher * * Also, unlike the CPU instructions, there is no NONE ("INVALID") instruction; if an ESC instruction * can't be decoded as a valid FPU instruction, then it should remain an ESC instruction. @@ -413,7 +428,8 @@ if (DEBUGGER) { FLDPI: 48, FLDL2T: 49, FLDL2E: 50, FLDLG2: 51, FLDLN2: 52, FINIT: 53, FNINIT: 54, FDISI: 55, FNDISI: 56, FENI: 57, FNENI: 58, FLDCW: 59, FSTCW: 60, FNSTCW: 61, FSTSW: 62, FNSTSW: 63, FCLEX: 64, FNCLEX: 65, FSTENV: 66, FNSTENV:67, FLDENV: 68, FSAVE: 69, FNSAVE: 70, FRSTOR: 71, - FINCSTP:72, FDECSTP:73, FFREE: 74, FNOP: 75, FWAIT: 76 + FINCSTP:72, FDECSTP:73, FFREE: 74, FFREEP: 75, FNOP: 76, FWAIT: 77, FSETPM: 78, FSINCOS:79, + FSTSWAX:80 }; /* @@ -429,7 +445,8 @@ if (DEBUGGER) { "FLDPI", "FLDL2T", "FLDL2E", "FLDLG2", "FLDLN2", "FINIT", "FNINIT", "FDISI", "FNDISI", "FENI", "FNENI", "FLDCW", "FSTCW", "FNSTCW", "FSTSW", "FNSTSW", "FCLEX", "FNCLEX", "FSTENV", "FNSTENV","FLDENV", "FSAVE", "FNSAVE", "FRSTOR", - "FINCSTP","FDECSTP","FFREE", "FNOP", "FWAIT" + "FINCSTP","FDECSTP","FFREE", "FFREEP", "FNOP", "FWAIT", "FSETPM", "FSINCOS", + "FSTSWAX" ]; Debugger.FPU_TAGS = ["VALID", "ZERO ", "SPEC ", "EMPTY"]; @@ -548,14 +565,22 @@ if (DEBUGGER) { 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_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-bit) - Debugger.TYPE_PD = 0x000E; // FPU PD (packed-decimal, 18 digits; 80-bit) + /* + * The remaining TYPE_SIZE values are for the FPU. Note that there are not enough values + * within this nibble for every type to have a unique value, so to differentiate between two + * types of the same size (eg, SINT and SREAL), we can inspect the opcode string, because only + * FI* instructions use INT operands. Also, some FPU sizes are not in this list (eg, the + * so-called "word-integer"); since a word-integer is always 16 bits, we specify TYPE_SHORT, + * which the Debugger should display as "INT16" for FI* instructions. + */ + 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_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_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) @@ -614,9 +639,12 @@ if (DEBUGGER) { Debugger.TYPE_OUT = 0x2000; // operand is output Debugger.TYPE_BOTH = (Debugger.TYPE_IN | Debugger.TYPE_OUT); Debugger.TYPE_8086 = (Debugger.CPU_8086 << 14); + Debugger.TYPE_8087 = Debugger.TYPE_8086; Debugger.TYPE_80186 = (Debugger.CPU_80186 << 14); Debugger.TYPE_80286 = (Debugger.CPU_80286 << 14); + Debugger.TYPE_80287 = Debugger.TYPE_80286; Debugger.TYPE_80386 = (Debugger.CPU_80386 << 14); + Debugger.TYPE_80387 = Debugger.TYPE_80386; Debugger.TYPE_CPU_SHIFT = 14; /* @@ -1073,27 +1101,27 @@ if (DEBUGGER) { */ Debugger.aaaOpFPUDescs = { 0xD8: { - 0x00: [Debugger.FINS.FADD, Debugger.TYPE_MODRM | Debugger.TYPE_SR | Debugger.TYPE_IN], - 0x01: [Debugger.FINS.FMUL, Debugger.TYPE_MODRM | Debugger.TYPE_SR | Debugger.TYPE_IN], - 0x02: [Debugger.FINS.FCOM, Debugger.TYPE_MODRM | Debugger.TYPE_SR | Debugger.TYPE_IN], - 0x03: [Debugger.FINS.FCOMP, Debugger.TYPE_MODRM | Debugger.TYPE_SR | Debugger.TYPE_IN], - 0x04: [Debugger.FINS.FSUB, Debugger.TYPE_MODRM | Debugger.TYPE_SR | Debugger.TYPE_IN], - 0x05: [Debugger.FINS.FSUBR, Debugger.TYPE_MODRM | Debugger.TYPE_SR | Debugger.TYPE_IN], - 0x06: [Debugger.FINS.FDIV, Debugger.TYPE_MODRM | Debugger.TYPE_SR | Debugger.TYPE_IN], - 0x07: [Debugger.FINS.FDIVR, Debugger.TYPE_MODRM | Debugger.TYPE_SR | Debugger.TYPE_IN], - 0x30: [Debugger.FINS.FADD, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], - 0x31: [Debugger.FINS.FMUL, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], - 0x32: [Debugger.FINS.FCOM, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], - 0x33: [Debugger.FINS.FCOMP, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], - 0x34: [Debugger.FINS.FSUB, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], - 0x35: [Debugger.FINS.FSUBR, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], - 0x36: [Debugger.FINS.FDIV, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], - 0x37: [Debugger.FINS.FDIVR, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN] + 0x00: [Debugger.FINS.FADD, Debugger.TYPE_MODRM | Debugger.TYPE_SREAL | Debugger.TYPE_IN], + 0x01: [Debugger.FINS.FMUL, Debugger.TYPE_MODRM | Debugger.TYPE_SREAL | Debugger.TYPE_IN], + 0x02: [Debugger.FINS.FCOM, Debugger.TYPE_MODRM | Debugger.TYPE_SREAL | Debugger.TYPE_IN], + 0x03: [Debugger.FINS.FCOMP, Debugger.TYPE_MODRM | Debugger.TYPE_SREAL | Debugger.TYPE_IN], + 0x04: [Debugger.FINS.FSUB, Debugger.TYPE_MODRM | Debugger.TYPE_SREAL | Debugger.TYPE_IN], + 0x05: [Debugger.FINS.FSUBR, Debugger.TYPE_MODRM | Debugger.TYPE_SREAL | Debugger.TYPE_IN], + 0x06: [Debugger.FINS.FDIV, Debugger.TYPE_MODRM | Debugger.TYPE_SREAL | Debugger.TYPE_IN], + 0x07: [Debugger.FINS.FDIVR, Debugger.TYPE_MODRM | Debugger.TYPE_SREAL | Debugger.TYPE_IN], + 0x30: [Debugger.FINS.FADD, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], + 0x31: [Debugger.FINS.FMUL, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], + 0x32: [Debugger.FINS.FCOM, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], + 0x33: [Debugger.FINS.FCOMP, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], + 0x34: [Debugger.FINS.FSUB, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], + 0x35: [Debugger.FINS.FSUBR, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], + 0x36: [Debugger.FINS.FDIV, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], + 0x37: [Debugger.FINS.FDIVR, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN] }, 0xD9: { - 0x00: [Debugger.FINS.FLD, Debugger.TYPE_MODRM | Debugger.TYPE_SR | Debugger.TYPE_IN], - 0x02: [Debugger.FINS.FST, Debugger.TYPE_MODRM | Debugger.TYPE_SR | Debugger.TYPE_OUT], - 0x03: [Debugger.FINS.FSTP, Debugger.TYPE_MODRM | Debugger.TYPE_SR | Debugger.TYPE_OUT], + 0x00: [Debugger.FINS.FLD, Debugger.TYPE_MODRM | Debugger.TYPE_SREAL | Debugger.TYPE_IN], + 0x02: [Debugger.FINS.FST, Debugger.TYPE_MODRM | Debugger.TYPE_SREAL | Debugger.TYPE_OUT], + 0x03: [Debugger.FINS.FSTP, Debugger.TYPE_MODRM | Debugger.TYPE_SREAL | Debugger.TYPE_OUT], 0x04: [Debugger.FINS.FLDENV, Debugger.TYPE_MODRM | Debugger.TYPE_ENV | Debugger.TYPE_IN], 0x05: [Debugger.FINS.FLDCW, Debugger.TYPE_MODRM | Debugger.TYPE_SHORT | Debugger.TYPE_IN], 0x06: [Debugger.FINS.FSTENV, Debugger.TYPE_MODRM | Debugger.TYPE_ENV | Debugger.TYPE_OUT], @@ -1101,7 +1129,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], // Obsolete decoding 0x40: [Debugger.FINS.FCHS], 0x41: [Debugger.FINS.FABS], 0x44: [Debugger.FINS.FTST], @@ -1127,53 +1155,55 @@ if (DEBUGGER) { 0x75: [Debugger.FINS.FSCALE] }, 0xDA: { - 0x00: [Debugger.FINS.FIADD, Debugger.TYPE_MODRM | Debugger.TYPE_SI | Debugger.TYPE_IN], - 0x01: [Debugger.FINS.FIMUL, Debugger.TYPE_MODRM | Debugger.TYPE_SI | Debugger.TYPE_IN], - 0x02: [Debugger.FINS.FICOM, Debugger.TYPE_MODRM | Debugger.TYPE_SI | Debugger.TYPE_IN], - 0x03: [Debugger.FINS.FICOMP, Debugger.TYPE_MODRM | Debugger.TYPE_SI | Debugger.TYPE_IN], - 0x04: [Debugger.FINS.FISUB, Debugger.TYPE_MODRM | Debugger.TYPE_SI | Debugger.TYPE_IN], - 0x05: [Debugger.FINS.FISUBR, Debugger.TYPE_MODRM | Debugger.TYPE_SI | Debugger.TYPE_IN], - 0x06: [Debugger.FINS.FIDIV, Debugger.TYPE_MODRM | Debugger.TYPE_SI | Debugger.TYPE_IN], - 0x07: [Debugger.FINS.FIDIVR, Debugger.TYPE_MODRM | Debugger.TYPE_SI | Debugger.TYPE_IN] + 0x00: [Debugger.FINS.FIADD, Debugger.TYPE_MODRM | Debugger.TYPE_SINT | Debugger.TYPE_IN], + 0x01: [Debugger.FINS.FIMUL, Debugger.TYPE_MODRM | Debugger.TYPE_SINT | Debugger.TYPE_IN], + 0x02: [Debugger.FINS.FICOM, Debugger.TYPE_MODRM | Debugger.TYPE_SINT | Debugger.TYPE_IN], + 0x03: [Debugger.FINS.FICOMP, Debugger.TYPE_MODRM | Debugger.TYPE_SINT | Debugger.TYPE_IN], + 0x04: [Debugger.FINS.FISUB, Debugger.TYPE_MODRM | Debugger.TYPE_SINT | Debugger.TYPE_IN], + 0x05: [Debugger.FINS.FISUBR, Debugger.TYPE_MODRM | Debugger.TYPE_SINT | Debugger.TYPE_IN], + 0x06: [Debugger.FINS.FIDIV, Debugger.TYPE_MODRM | Debugger.TYPE_SINT | Debugger.TYPE_IN], + 0x07: [Debugger.FINS.FIDIVR, Debugger.TYPE_MODRM | Debugger.TYPE_SINT | Debugger.TYPE_IN] }, 0xDB: { - 0x00: [Debugger.FINS.FILD, Debugger.TYPE_MODRM | Debugger.TYPE_SI | Debugger.TYPE_IN], - 0x02: [Debugger.FINS.FIST, Debugger.TYPE_MODRM | Debugger.TYPE_SI | Debugger.TYPE_OUT], - 0x03: [Debugger.FINS.FISTP, Debugger.TYPE_MODRM | Debugger.TYPE_SI | Debugger.TYPE_OUT], - 0x05: [Debugger.FINS.FLD, Debugger.TYPE_MODRM | Debugger.TYPE_TR | Debugger.TYPE_IN], - 0x07: [Debugger.FINS.FSTP, Debugger.TYPE_MODRM | Debugger.TYPE_TR | Debugger.TYPE_OUT], + 0x00: [Debugger.FINS.FILD, Debugger.TYPE_MODRM | Debugger.TYPE_SINT | Debugger.TYPE_IN], + 0x02: [Debugger.FINS.FIST, Debugger.TYPE_MODRM | Debugger.TYPE_SINT | Debugger.TYPE_OUT], + 0x03: [Debugger.FINS.FISTP, Debugger.TYPE_MODRM | Debugger.TYPE_SINT | Debugger.TYPE_OUT], + 0x05: [Debugger.FINS.FLD, Debugger.TYPE_MODRM | Debugger.TYPE_TREAL | Debugger.TYPE_IN], + 0x07: [Debugger.FINS.FSTP, Debugger.TYPE_MODRM | Debugger.TYPE_TREAL | Debugger.TYPE_OUT], 0x40: [Debugger.FINS.FENI], 0x41: [Debugger.FINS.FDISI], 0x42: [Debugger.FINS.FCLEX], - 0x43: [Debugger.FINS.FINIT] + 0x43: [Debugger.FINS.FINIT], + 0x44: [Debugger.FINS.FSETPM, Debugger.TYPE_80287], + 0x73: [Debugger.FINS.FSINCOS, Debugger.TYPE_80387] }, 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_LREAL | Debugger.TYPE_IN], + 0x01: [Debugger.FINS.FMUL, Debugger.TYPE_MODRM | Debugger.TYPE_LREAL | Debugger.TYPE_IN], + 0x02: [Debugger.FINS.FCOM, Debugger.TYPE_MODRM | Debugger.TYPE_LREAL | Debugger.TYPE_IN], + 0x03: [Debugger.FINS.FCOMP, Debugger.TYPE_MODRM | Debugger.TYPE_LREAL | Debugger.TYPE_IN], + 0x04: [Debugger.FINS.FSUB, Debugger.TYPE_MODRM | Debugger.TYPE_LREAL | Debugger.TYPE_IN], + 0x05: [Debugger.FINS.FSUBR, Debugger.TYPE_MODRM | Debugger.TYPE_LREAL | Debugger.TYPE_IN], + 0x06: [Debugger.FINS.FDIV, Debugger.TYPE_MODRM | Debugger.TYPE_LREAL | Debugger.TYPE_IN], + 0x07: [Debugger.FINS.FDIVR, Debugger.TYPE_MODRM | Debugger.TYPE_LREAL | 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], - 0x33: [Debugger.FINS.FCOMP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], + 0x32: [Debugger.FINS.FCOM, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], // Obsolete decoding + 0x33: [Debugger.FINS.FCOMP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], // Obsolete decoding 0x34: [Debugger.FINS.FSUB, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_IN], 0x35: [Debugger.FINS.FSUBR, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_IN], 0x36: [Debugger.FINS.FDIV, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_IN], 0x37: [Debugger.FINS.FDIVR, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_IN] }, 0xDD: { - 0x00: [Debugger.FINS.FLD, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_IN], - 0x02: [Debugger.FINS.FST, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_OUT], - 0x03: [Debugger.FINS.FSTP, Debugger.TYPE_MODRM | Debugger.TYPE_LR | Debugger.TYPE_OUT], + 0x00: [Debugger.FINS.FLD, Debugger.TYPE_MODRM | Debugger.TYPE_LREAL | Debugger.TYPE_IN], + 0x02: [Debugger.FINS.FST, Debugger.TYPE_MODRM | Debugger.TYPE_LREAL | Debugger.TYPE_OUT], + 0x03: [Debugger.FINS.FSTP, Debugger.TYPE_MODRM | Debugger.TYPE_LREAL | Debugger.TYPE_OUT], 0x04: [Debugger.FINS.FRSTOR, Debugger.TYPE_MODRM | Debugger.TYPE_FPU | Debugger.TYPE_IN], 0x06: [Debugger.FINS.FSAVE, Debugger.TYPE_MODRM | Debugger.TYPE_FPU | Debugger.TYPE_OUT], 0x07: [Debugger.FINS.FSTSW, Debugger.TYPE_MODRM | Debugger.TYPE_SHORT | Debugger.TYPE_OUT], 0x30: [Debugger.FINS.FFREE, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], - 0x31: [Debugger.FINS.FXCH, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT], + 0x31: [Debugger.FINS.FXCH, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT], // Obsolete decoding 0x32: [Debugger.FINS.FST, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], 0x33: [Debugger.FINS.FSTP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN] }, @@ -1188,7 +1218,7 @@ if (DEBUGGER) { 0x07: [Debugger.FINS.FIDIVR, Debugger.TYPE_MODRM | Debugger.TYPE_SHORT | Debugger.TYPE_IN], 0x30: [Debugger.FINS.FADDP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_IN], 0x31: [Debugger.FINS.FMULP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_IN], - 0x32: [Debugger.FINS.FCOMP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], + 0x32: [Debugger.FINS.FCOMP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], // Obsolete decoding 0x33: [Debugger.FINS.FCOMPP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], 0x34: [Debugger.FINS.FSUBP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_IN], 0x35: [Debugger.FINS.FSUBRP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT, Debugger.TYPE_IMPREG | Debugger.TYPE_ST | Debugger.TYPE_IN], @@ -1199,14 +1229,15 @@ 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_PD | Debugger.TYPE_IN], - 0x05: [Debugger.FINS.FILD, Debugger.TYPE_MODRM | Debugger.TYPE_LI | Debugger.TYPE_IN], - 0x06: [Debugger.FINS.FBSTP, Debugger.TYPE_MODRM | Debugger.TYPE_PD | Debugger.TYPE_OUT], - 0x07: [Debugger.FINS.FISTP, Debugger.TYPE_MODRM | Debugger.TYPE_LI | Debugger.TYPE_OUT], - 0x30: [Debugger.FINS.FFREE, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], - 0x31: [Debugger.FINS.FXCH, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT], - 0x32: [Debugger.FINS.FSTP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], - 0x33: [Debugger.FINS.FSTP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN] + 0x04: [Debugger.FINS.FBLD, Debugger.TYPE_MODRM | Debugger.TYPE_DEC18 | 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], + 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 decoding + 0x31: [Debugger.FINS.FXCH, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_OUT], // Obsolete decoding + 0x32: [Debugger.FINS.FSTP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], // Obsolete decoding + 0x33: [Debugger.FINS.FSTP, Debugger.TYPE_IMPREG | Debugger.TYPE_STREG | Debugger.TYPE_IN], // Obsolete decoding + 0x34: [Debugger.FINS.FSTSWAX, Debugger.TYPE_80287] } }; @@ -4788,7 +4819,7 @@ if (DEBUGGER) { * like "LEA AX,BX", it will actually do something (on some if not all processors), so * there's probably some diagnostic value in allowing those cases to be disassembled. */ - sOperand = this.getModRMOperand(bModRM, type, cOperands, dbgAddr); + sOperand = this.getModRMOperand(sOpcode, bModRM, type, cOperands, dbgAddr); } else if (typeMode == Debugger.TYPE_MODREG) { /* @@ -5053,22 +5084,24 @@ if (DEBUGGER) { }; /** - * getModRMOperand(bModRM, type, cOperands, dbgAddr) + * getModRMOperand(sOpcode, bModRM, type, cOperands, dbgAddr) * * @this {Debugger} + * @param {string} sOpcode * @param {number} bModRM * @param {number} type * @param {number} cOperands (if 1, memory operands are prefixed with the size; otherwise, size can be inferred) * @param {DbgAddr} dbgAddr * @return {string} operand */ - Debugger.prototype.getModRMOperand = function(bModRM, type, cOperands, dbgAddr) + Debugger.prototype.getModRMOperand = function(sOpcode, bModRM, type, cOperands, dbgAddr) { var sOperand = ""; var bMod = bModRM >> 6; var bRM = bModRM & 0x7; if (bMod < 3) { var disp; + var fInteger = (sOpcode.indexOf("FI") == 0); if (!bMod && (!dbgAddr.fAddr32 && bRM == 6 || dbgAddr.fAddr32 && bRM == 5)) { bMod = 2; } else { @@ -5116,23 +5149,38 @@ if (DEBUGGER) { sPrefix = "BYTE"; break; case Debugger.TYPE_SHORT: + if (fInteger) { + sPrefix = "INT16"; + break; + } + /* falls through */ sPrefix = "WORD"; break; case Debugger.TYPE_LONG: sPrefix = "DWORD"; break; - case Debugger.TYPE_SI: - case Debugger.TYPE_SR: - sPrefix = "SREAL"; + case Debugger.TYPE_SINT: + if (fInteger) { + sPrefix = "INT32"; + break; + } + /* falls through */ + case Debugger.TYPE_SREAL: + sPrefix = "REAL32"; break; - case Debugger.TYPE_LI: - case Debugger.TYPE_LR: - sPrefix = "LREAL"; + case Debugger.TYPE_LINT: + if (fInteger) { + sPrefix = "INT64"; + break; + } + /* falls through */ + case Debugger.TYPE_LREAL: + sPrefix = "REAL64"; break; - case Debugger.TYPE_TR: - sPrefix = "TREAL"; + case Debugger.TYPE_TREAL: + sPrefix = "REAL80"; break; - case Debugger.TYPE_PD: + case Debugger.TYPE_DEC18: sPrefix = "DEC18"; break; } @@ -7778,7 +7826,7 @@ if (DEBUGGER) { * Also, to allow quoted strings *inside* breakpoint commands, we first replace all * DOUBLE double-quotes with single quotes. */ - sCmd = sCmd.replace(/""/g, "'"); + sCmd = sCmd.toLowerCase().replace(/""/g, "'"); var iPrev = 0; var chQuote = null; diff --git a/modules/pcjs/lib/x86.js b/modules/pcjs/lib/x86.js index 04579d48b..ce5b128c7 100644 --- a/modules/pcjs/lib/x86.js +++ b/modules/pcjs/lib/x86.js @@ -524,7 +524,7 @@ var X86 = { FPU: { MODEL_8087: 8087, MODEL_80287: 80287, - MODEL_80287XL: 80387, // internally, the 80287XL was an 80387SX, so I'm not sure we really want/need a unique identifier + MODEL_80287XL: 80387, // internally, the 80287XL was an 80387SX, so in general, we treat this as MODEL_80387 MODEL_80387: 80387, CONTROL: { // FPU Control Word IM: 0x0001, // bit 0: Invalid Operation Mask @@ -538,6 +538,10 @@ var X86 = { 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 + RC_NEAR: 0x0000, + RC_DOWN: 0x0400, + RC_UP: 0x0800, + RC_CHOP: 0x0C00, 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 @@ -557,10 +561,10 @@ var X86 = { C1: 0x0200, // bit 9: Condition Code 1 C2: 0x0400, // bit 10: Condition Code 2 ST: 0x3800, // bits 11-13: Stack Top + ST_SHIFT: 11, C3: 0x4000, // bit 14: Condition Code 3 CC: 0x4700, // all condition code bits - BUSY: 0x8000, // bit 15: Busy - ST_SHIFT: 11 + BUSY: 0x8000 // bit 15: Busy }, TAGS: { VALID: 0x0, diff --git a/modules/pcjs/lib/x86fpu.js b/modules/pcjs/lib/x86fpu.js index 9bf0d7572..ec5891991 100644 --- a/modules/pcjs/lib/x86fpu.js +++ b/modules/pcjs/lib/x86fpu.js @@ -109,7 +109,9 @@ function X86FPU(parmsFPU) this.intTmpSR = new Int32Array(this.regTmpSR.buffer); /* - * Used for "long-real" (LR) 64-bit floating-point operations. + * Used for "long-real" (LR) 64-bit floating-point operations. We also use intTmpLR as temporary storage + * for all "word-integer" (WI or INT16), "short-integer" (SI or INT32) and "long-integer" (LI or INT64) values, + * since it's just large enough to accommodate all three sizes of integers. */ this.regTmpLR = new Float64Array(1); this.intTmpLR = new Int32Array(this.regTmpLR.buffer); @@ -119,7 +121,7 @@ function X86FPU(parmsFPU) * where [0] contains TR bits 0-31, [1] contains TR bits 32-63, and [2] contains TR bits 64-79; the * upper 16 bits of [2] are not used and should remain zero. */ - this.regTmpTR = new Array(3); + this.intTmpTR = new Array(3); /* * Initialize other (non-floating-point) coprocessor registers that resetFPU() doesn't touch, @@ -142,12 +144,6 @@ function X86FPU(parmsFPU) this.intIndefinite = new Int32Array(this.regIndefinite.buffer); this.intIndefinite[0] = 0x00000000; this.intIndefinite[1] = 0xFFF8000; - 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) - /* * Initialize all other coprocessor registers (control word, tag word, status word, etc) by resetting them. */ @@ -156,6 +152,32 @@ function X86FPU(parmsFPU) Component.subclass(X86FPU); + +/** @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() * @@ -183,7 +205,7 @@ X86FPU.FABS = function() */ X86FPU.FADDlr = function() { - this.opUnimplemented(); + this.setST(0, this.doAdd(this.getST(0), this.getLRFromEA())); }; /** @@ -193,8 +215,7 @@ X86FPU.FADDlr = function() */ X86FPU.FADDsr = function() { - var result = this.addition(this.regStack[this.iST], this.getSRFromEA()); - if (result != null) this.regStack[this.iST] = result; + this.setST(0, this.doAdd(this.getST(0), this.getSRFromEA())); }; /** @@ -204,8 +225,7 @@ X86FPU.FADDsr = function() */ X86FPU.FADDst = function() { - var result = this.addition(this.regStack[this.iST], this.regStack[this.iStack]); - if (result != null) this.regStack[this.iST] = result; + this.setST(0, this.doAdd(this.getST(0), this.getST(this.iStack))); }; /** @@ -215,8 +235,7 @@ X86FPU.FADDst = function() */ X86FPU.FADDsti = function() { - var result = this.addition(this.regStack[this.iStack], this.regStack[this.iST]); - if (result != null) this.regStack[this.iStack] = result; + this.setST(this.iStack, this.doAdd(this.getST(this.iStack), this.getST(0))); }; /** @@ -314,6 +333,20 @@ X86FPU.FCOMsti = function() this.opUnimplemented(); }; +/** + * FCOM8087() + * + * NOTE: This is used with decoding(s) (0xDC,0xD0-0xD7) that were valid for the 8087 and 80287 + * but may no longer be valid as of the 80387. + * + * @this {X86FPU} + */ +X86FPU.FCOM8087 = function() +{ + this.opObsolete(); + X86FPU.FCOMsti.call(this); +}; + /** * FCOMPlr() * @@ -342,7 +375,7 @@ X86FPU.FCOMPsr = function() X86FPU.FCOMPst = function() { // if (this.opStop()) return; // for untested instructions: stops the CPU if it's running, but not if single-stepping - this.comparison(this.getST(0), this.getST(this.iStack)); + this.doCompare(this.getST(0), this.getST(this.iStack)); this.popValue(); }; @@ -356,6 +389,20 @@ X86FPU.FCOMPsti = function() this.opUnimplemented(); }; +/** + * FCOMP8087() + * + * NOTE: This is used with decodings (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. + * + * @this {X86FPU} + */ +X86FPU.FCOMP8087 = function() +{ + this.opObsolete(); + X86FPU.FCOMPsti.call(this); +}; + /** * FCOMPPsti() * @@ -393,8 +440,7 @@ X86FPU.FDISI = function() */ X86FPU.FDIVlr = function() { - var quotient = this.division(this.regStack[this.iST], this.getLRFromEA()); - if (quotient != null) this.regStack[this.iST] = quotient; + this.setST(0, this.doDivide(this.getST(0), this.getLRFromEA())); }; /** @@ -504,7 +550,23 @@ X86FPU.FENI = function() */ X86FPU.FFREEsti = function() { - this.opUnimplemented(); + this.setTag(this.iST, X86.FPU.TAGS.EMPTY); +}; + +/** + * FFREEP8087() + * + * NOTE: This is used with a decoding (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(); }; /** @@ -584,7 +646,7 @@ X86FPU.FIDIV16 = function() */ X86FPU.FIDIV32 = function() { - this.opUnimplemented(); + this.setST(0, this.doDivide(this.getST(0), this.getSIFromEA())); }; /** @@ -624,7 +686,8 @@ X86FPU.FILD16 = function() */ X86FPU.FILD32 = function() { - this.opUnimplemented(); + this.assert(this.cpu.regEA !== X86.ADDR_INVALID); + this.pushValue(this.cpu.getLong(this.cpu.regEA)); }; /** @@ -714,7 +777,10 @@ X86FPU.FISTP16 = function() */ X86FPU.FISTP32 = function() { - this.opUnimplemented(); + if (this.getSI(0)) { + this.setEAFromSI(); + this.popValue(); + } }; /** @@ -838,6 +904,7 @@ X86FPU.FLDtr = function() */ X86FPU.FLDCW = function() { + this.assert(this.cpu.regEA !== X86.ADDR_INVALID); this.setControl(this.cpu.getShort(this.cpu.regEA)); }; @@ -848,6 +915,7 @@ X86FPU.FLDCW = function() */ X86FPU.FLDENV = function() { + this.assert(this.cpu.regEA !== X86.ADDR_INVALID); this.loadEnv(this.cpu.regEA); }; @@ -868,7 +936,7 @@ X86FPU.FLD1 = function() */ X86FPU.FLDL2T = function() { - this.pushValue(this.regL2T); + this.pushValue(X86FPU.regL2T); }; /** @@ -878,7 +946,7 @@ X86FPU.FLDL2T = function() */ X86FPU.FLDL2E = function() { - this.pushValue(this.regL2E); + this.pushValue(X86FPU.regL2E); }; /** @@ -898,7 +966,7 @@ X86FPU.FLDPI = function() */ X86FPU.FLDLG2 = function() { - this.pushValue(this.regLG2); + this.pushValue(X86FPU.regLG2); }; /** @@ -908,7 +976,7 @@ X86FPU.FLDLG2 = function() */ X86FPU.FLDLN2 = function() { - this.pushValue(this.regLN2); + this.pushValue(X86FPU.regLN2); }; /** @@ -928,7 +996,7 @@ X86FPU.FLDZ = function() */ X86FPU.FMULlr = function() { - this.opUnimplemented(); + this.setST(0, this.doMultiply(this.getST(0), this.getLRFromEA())); }; /** @@ -938,8 +1006,7 @@ X86FPU.FMULlr = function() */ X86FPU.FMULsr = function() { - var result = this.multiplication(this.regStack[this.iST], this.getSRFromEA()); - if (result != null) this.regStack[this.iST] = result; + this.setST(0, this.doMultiply(this.getST(0), this.getSRFromEA())); }; /** @@ -949,8 +1016,7 @@ X86FPU.FMULsr = function() */ X86FPU.FMULst = function() { - var result = this.multiplication(this.regStack[this.iST], this.regStack[this.iStack]); - if (result != null) this.regStack[this.iST] = result; + this.setST(0, this.doMultiply(this.getST(0), this.getST(this.iStack))); }; /** @@ -960,8 +1026,7 @@ X86FPU.FMULst = function() */ X86FPU.FMULsti = function() { - var result = this.multiplication(this.regStack[this.iStack], this.regStack[this.iST]); - if (result != null) this.regStack[this.iStack] = result; + this.setST(this.iStack, this.doMultiply(this.getST(this.iStack), this.getST(0))); }; /** @@ -1022,7 +1087,7 @@ X86FPU.FRSTOR = function() { var cpu = this.cpu; var addr = this.loadEnv(cpu.regEA); - var a = this.regTmpTR; + var a = this.intTmpTR; for (var i = 0; i < this.regStack.length; i++) { a[0] = cpu.getLong(addr); a[1] = cpu.getLong(addr += 4); @@ -1072,11 +1137,11 @@ X86FPU.FSCALE = function() }; /** - * FSETPM() + * FSETPM287() * * @this {X86FPU} */ -X86FPU.FSETPM = function() +X86FPU.FSETPM287 = function() { if (this.isModel(X86.FPU.MODEL_80287)) { this.opUnimplemented(); @@ -1084,11 +1149,11 @@ X86FPU.FSETPM = function() }; /** - * FSINCOS() + * FSINCOS387() * * @this {X86FPU} */ -X86FPU.FSINCOS = function() +X86FPU.FSINCOS387 = function() { if (this.isAtLeastModel(X86.FPU.MODEL_80287XL)) { this.opUnimplemented(); @@ -1102,7 +1167,7 @@ X86FPU.FSINCOS = function() */ X86FPU.FSQRT = function() { - this.opUnimplemented(); + this.setST(0, this.doSquareRoot(this.getST(0))); }; /** @@ -1113,7 +1178,7 @@ X86FPU.FSQRT = function() X86FPU.FSTlr = function() { if (this.getLR(0)) { - this.setEAFromSR(); + this.setEAFromLR(); } }; @@ -1136,10 +1201,7 @@ X86FPU.FSTsr = function() */ X86FPU.FSTsti = function() { - var v = this.getST(0); - if (v != null) { - this.setST(this.iStack, v); - } + this.setST(this.iStack, this.getST(0)); }; /** @@ -1149,6 +1211,7 @@ X86FPU.FSTsti = function() */ 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) }; @@ -1186,13 +1249,25 @@ X86FPU.FSTPsr = function() */ X86FPU.FSTPsti = function() { - var v = this.getST(0); - if (v != null) { - this.setST(this.iStack, v); + if (this.setST(this.iStack, this.getST(0))) { this.popValue(); } }; +/** + * FSTP8087() + * + * NOTE: This is used with decodings (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() * @@ -1229,11 +1304,11 @@ X86FPU.FSTSW = function() }; /** - * FSTSWAX() + * FSTSWAX287() * * @this {X86FPU} */ -X86FPU.FSTSWAX = function() +X86FPU.FSTSWAX287 = function() { if (this.isAtLeastModel(X86.FPU.MODEL_80287)) { this.cpu.regEAX = (this.cpu.regEAX & ~0xffff) | this.getStatus(); @@ -1327,7 +1402,7 @@ X86FPU.FSUBRst = function() */ X86FPU.FSUBRsti = function() { - this.opUnimplemented(); + this.setST(this.iStack, this.doSubtract(this.getST(this.iStack), this.getST(0))); }; /** @@ -1337,7 +1412,9 @@ X86FPU.FSUBRsti = function() */ X86FPU.FSUBRPsti = function() { - this.opUnimplemented(); + if (this.setST(this.iStack, this.doSubtract(this.getST(this.iStack), this.getST(0)))) { + this.popValue(); + } }; /** @@ -1357,7 +1434,29 @@ X86FPU.FTST = function() */ X86FPU.FXAM = function() { - this.opUnimplemented(); + 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; + } + } }; /** @@ -1370,6 +1469,20 @@ X86FPU.FXCHsti = function() this.opUnimplemented(); }; +/** + * FXCH8087() + * + * NOTE: This is used with decodings (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() * @@ -1448,48 +1561,6 @@ X86FPU.prototype.powerUp = function(data, fRepower) if (!this.restore(data)) return false; } } - if (MAXDEBUG) { - /* - * Test loading all 7 constants. - */ - X86FPU.FLD1.call(this); - X86FPU.FLDZ.call(this); - X86FPU.FLDL2T.call(this); - X86FPU.FLDL2E.call(this); - X86FPU.FLDPI.call(this); - X86FPU.FLDLG2.call(this); - X86FPU.FLDLN2.call(this); - /* - * Test the ability of getTR() to convert any LR to a TR, and then verify that getST() can always obtain - * the original LR from that TR. TR->LR can be a lossy operation, but LR->TR->LR should never be. - * - * We'll start by taking the value PI, which should located at ST(2) after the above pushes, converting it - * to a TR, and then setting ST(7), which should still be empty after the above pushes, with the same TR value. - * - * If that simple test passes, then we'll proceed to more aggressive testing, using randomly-generated floats. - */ - var a = this.getTR(2, true); - this.setTR(7, a); - this.assert(this.getST(2) === this.getST(7)); - - var nTests = 10000; - while (nTests--) { - var lo = this.getRandomInt(0, 0xffffffff); - var hi = this.getRandomInt(0, 0xffffffff); - this.setST64(6, lo, hi); - a = this.getTR(6, true); - this.setTR(7, a); - if (this.getST(6) !== this.getST(7)) { - /* - * In JavaScript, NaN values are never equal to any other value, even another identical NaN (sigh) - */ - if (!isNaN(this.getST(6)) || !isNaN(this.getST(7))) { - this.println("test failure"); - break; - } - } - } - } return true; }; @@ -1572,8 +1643,8 @@ X86FPU.prototype.resetFPU = function() this.iST = 0; // the ST bits for regStatus are actually stored here if (DEBUG) { /* - * All the registers were tagged "unused" above, which is all that would normally happen, but debugging is - * a little easier if we also zero everything, too. + * All the registers were tagged "unused" above, which is all that would normally happen, + * but debugging is a little easier if we also zero everything, too. */ for (var iReg = 0; iReg < this.regStack.length; iReg++) { this.regStack[iReg] = 0.0; @@ -1667,6 +1738,19 @@ X86FPU.prototype.opNone = function() this.opStop(true); }; +/** + * opObsolete() + * + * Used for any coprocessor opcodes that are redundant and potentially obsolete. + * + * @this {X86FPU} + */ +X86FPU.prototype.opObsolete = function() +{ + if (DEBUG) this.println(this.idComponent + ".opObsolete(" + str.toHexByte(this.cpu.bOpcode) + "," + str.toHexByte(this.cpu.bModRM) + ")"); + this.opStop(true); +}; + /** * opUnimplemented() * @@ -1825,76 +1909,166 @@ X86FPU.prototype.checkResult = function(v) }; /** - * addition(operand1, operand2) + * doAdd(operand1, operand2) * * @this {X86FPU} - * @param {number} operand1 - * @param {number} operand2 + * @param {number|null} operand1 + * @param {number|null} operand2 * @return {number|null} */ -X86FPU.prototype.addition = function(operand1, operand2) +X86FPU.prototype.doAdd = function(operand1, operand2) { - var result = operand1 + operand2; - if (!this.checkResult(result)) result = null; + var result = null; + if (operand1 != null && operand2 != null) { + result = operand1 + operand2; + if (!this.checkResult(result)) result = null; + } return result; }; /** - * multiplication(operand1, operand2) + * doSubtract(operand1, operand2) * * @this {X86FPU} - * @param {number} operand1 - * @param {number} operand2 + * @param {number|null} operand1 + * @param {number|null} operand2 * @return {number|null} */ -X86FPU.prototype.multiplication = function(operand1, operand2) +X86FPU.prototype.doSubtract = function(operand1, operand2) { - var result = operand1 * operand2; - if (!this.checkResult(result)) result = null; + var result = null; + if (operand1 != null && operand2 != null) { + result = operand1 - operand2; + if (!this.checkResult(result)) result = null; + } return result; }; /** - * division(dividend, divisor) + * doMultiply(operand1, operand2) + * + * @this {X86FPU} + * @param {number|null} operand1 + * @param {number|null} operand2 + * @return {number|null} + */ +X86FPU.prototype.doMultiply = function(operand1, operand2) +{ + var result = null; + if (operand1 != null && operand2 != null) { + result = operand1 * operand2; + if (!this.checkResult(result)) result = null; + } + return result; +}; + +/** + * doDivide(dividend, divisor) * * TODO: IE exceptions: infinity / infinity, 0 / 0, 0 / pseudo-zero, or divisor is denormal or unnormal. * * @this {X86FPU} - * @param {number} dividend - * @param {number} divisor + * @param {number|null} dividend + * @param {number|null} divisor * @return {number|null} */ -X86FPU.prototype.division = function(dividend, divisor) +X86FPU.prototype.doDivide = function(dividend, divisor) { var quotient = null; - if (divisor || !this.setException(X86.FPU.STATUS.DE)) { - quotient = dividend / divisor; - if (!this.checkResult(quotient)) quotient = null; + if (dividend != null && divisor != null) { + if (divisor || !this.setException(X86.FPU.STATUS.DE)) { + quotient = dividend / divisor; + if (!this.checkResult(quotient)) quotient = null; + } } return quotient; }; /** - * comparison(operand1, operand2) + * doCompare(operand1, operand2) * * @this {X86FPU} * @param {number|null} operand1 * @param {number|null} operand2 */ -X86FPU.prototype.comparison = function(operand1, operand2) +X86FPU.prototype.doCompare = function(operand1, operand2) { - var cc = X86.FPU.STATUS.C0 | X86.FPU.STATUS.C2 | X86.FPU.STATUS.C3; - if (!isNaN(operand1) && !isNaN(operand2)) { - var result = operand1 - operand2; - if (result > 0) { - cc = 0; - } else if (result < 0) { - cc = X86.FPU.STATUS.C0; - } else { - cc = X86.FPU.STATUS.C3; + if (operand1 != null && operand2 != null) { + var cc = X86.FPU.STATUS.C0 | X86.FPU.STATUS.C2 | X86.FPU.STATUS.C3; + if (!isNaN(operand1) && !isNaN(operand2)) { + var result = operand1 - operand2; + if (result > 0) { + cc = 0; + } else if (result < 0) { + cc = X86.FPU.STATUS.C0; + } else { + cc = X86.FPU.STATUS.C3; + } } + this.regStatus = (this.regStatus & ~X86.FPU.STATUS.CC) | cc; } - this.regStatus = (this.regStatus & ~X86.FPU.STATUS.CC) | cc; +}; + +/** + * doSquareRoot(operand) + * + * @this {X86FPU} + * @param {number|null} operand + * @return {number|null} + */ +X86FPU.prototype.doSquareRoot = function(operand) +{ + var result = null; + /* + * Happily, -0 is ALSO >= 0. Also happily, Math.sqrt(-0) returns -0. + */ + if (operand >= 0 || !this.setException(X86.FPU.STATUS.IE)) { + result = Math.sqrt(operand); + if (!this.checkResult(result)) result = null; + } + return result; +}; + +/** + * roundInteger(operand, limit) + * + * @this {X86FPU} + * @param {number|null} operand + * @param {number} limit (ie, 0x8000, 0x80000000, or 0x8000000000000000) + * @return {boolean} true if intTmpLR was loaded, false if not + */ +X86FPU.prototype.roundInteger = function(operand, limit) +{ + var result; + var rc = (this.regControl & X86.FPU.CONTROL.RC); + + if (rc == X86.FPU.CONTROL.RC_NEAR) { + result = Math.round(operand); + if (result - operand === 0.5 && (result % 2)) result--; + } + else if (rc == X86.FPU.CONTROL.RC_DOWN || rc == X86.FPU.CONTROL.RC_CHOP && operand > 0) { + result = Math.floor(operand); + } + else { // X86.FPU.CONTROL.RC_UP or X86.FPU.CONTROL.RC_CHOP && operand <= 0 + result = Math.ceil(operand); + } + + if (result >= limit) { + if (this.setException(X86.FPU.STATUS.OE)) return false; + result = limit - 1; + } + else if (result < -limit) { + if (this.setException(X86.FPU.STATUS.UE)) return false; + result = -limit; + } + + this.intTmpLR[0] = result|0; + + if (limit > X86FPU.MAX_INT32) { + this.intTmpLR[1] = (result / 0x100000000)|0; + if (!this.intTmpLR[1] && result < 0) this.intTmpLR[1] = -1; + } + return true; }; /** @@ -1937,6 +2111,19 @@ X86FPU.prototype.getTags = function() return tags; }; +/** + * setTag(iReg, tag) + * + * @this {X86FPU} + * @param {number} iReg (register index) + * @param {number} tag value for register (EMPTY is the only supported value) + */ +X86FPU.prototype.setTag = function(iReg, tag) +{ + this.assert(!(iReg & ~0x7) && tag == X86.FPU.TAGS.EMPTY); + this.regUsed &= ~(1 << iReg); +}; + /** * setTags(n) * @@ -1958,6 +2145,21 @@ X86FPU.prototype.setTags = function(n) } }; +/** + * getSI(i) + * + * Gets a "short-integer" (SI aka INT32) + * + * @this {X86FPU} + * @param {number} i (eg, 0 for top-of-stack) + * @return {boolean} true if intTmpLR was loaded, false if not + */ +X86FPU.prototype.getSI = function(i) +{ + var v = this.getST(i); + return v != null && this.roundInteger(v, X86FPU.MAX_INT32); +}; + /** * getSR(i) * @@ -2017,18 +2219,36 @@ X86FPU.prototype.getST = function(i) return v; }; +/** + * getSTSign(i) + * + * Returns zero if sign bit clear, and non-zero (negative) if sign bit set. This is safer + * than comparing getST() to zero, because JavaScript comparisons involving NaNs are meaningless. + * + * For internal use only; ignores whether the register is empty, and performs no exception checks. + * + * @this {X86FPU} + * @param {number} i (eg, 0 for top-of-stack) + * @return {number} + */ +X86FPU.prototype.getSTSign = function(i) +{ + var iInt = ((this.iST + i) & 7) << 1; + return this.intStack[iInt + 1] & (0x80000000|0); +}; + /** * setST(i, v) * * @this {X86FPU} * @param {number} i (eg, 0 for top-of-stack) - * @param {number} v + * @param {number|null} v * @return {boolean} */ X86FPU.prototype.setST = function(i, v) { - var iReg = (this.iST + i) & 7; - if (this.checkOperand(v)) { + if (v != null && this.checkOperand(v)) { + var iReg = (this.iST + i) & 7; this.regStack[iReg] = v; this.regUsed |= (1 << iReg); return true; @@ -2036,26 +2256,6 @@ X86FPU.prototype.setST = function(i, v) return false; }; -/** - * setST64(i, lo, hi) - * - * Use this instead of setST() if you want to set the two 32-bit portions of ST(i) directly. - * - * @this {X86FPU} - * @param {number} i - * @param {number} lo - * @param {number} hi - */ -X86FPU.prototype.setST64 = function(i, lo, hi) -{ - var iReg = (this.iST + i) & 7; - var iInt = iReg << 1; - this.intStack[iInt] = lo; - this.intStack[iInt + 1] = hi; - this.checkOperand(this.regStack[iReg]); - this.regUsed |= (1 << iReg); -}; - /** * getTR(i, fNoException) * @@ -2086,7 +2286,24 @@ X86FPU.prototype.getTR = function(i, fNoException) */ X86FPU.prototype.setTR = function(i, a) { - if (a) this.setST(i, this.getLRFromTR(a)); + if (a) { + this.setST(i, this.getLRFromTR(a)); + } +}; + +/** + * getSIFromEA() + * + * Sets the internal intTmpLR register to the (32-bit) "short-integer" value located at regEA. + * + * @this {X86FPU} + * @return {number} v + */ +X86FPU.prototype.getSIFromEA = function() +{ + this.assert(this.cpu.regEA !== X86.ADDR_INVALID); + this.intTmpLR[0] = this.cpu.getLong(this.cpu.regEA); + return this.intTmpLR[0]; }; /** @@ -2123,18 +2340,58 @@ X86FPU.prototype.getLRFromEA = function() /** * getTRFromEA() * - * Sets the internal regTmpTR register to the (80-bit) "temp-real" value located at regEA. + * Sets the internal intTmpTR register to the (80-bit) "temp-real" value located at regEA. * * @this {X86FPU} - * @return {Array.} regTmpTR + * @return {Array.} intTmpTR */ X86FPU.prototype.getTRFromEA = function() { this.assert(this.cpu.regEA !== X86.ADDR_INVALID); - this.regTmpTR[0] = this.cpu.getLong(this.cpu.regEA); - this.regTmpTR[1] = this.cpu.getLong(this.cpu.regEA + 4); - this.regTmpTR[2] = this.cpu.getShort(this.cpu.regEA + 8); - return this.regTmpTR; + this.intTmpTR[0] = this.cpu.getLong(this.cpu.regEA); + this.intTmpTR[1] = this.cpu.getLong(this.cpu.regEA + 4); + this.intTmpTR[2] = this.cpu.getShort(this.cpu.regEA + 8); + return this.intTmpTR; +}; + +/** + * setEAFromWI() + * + * Stores the (16-bit) "word-integer" value in the internal intTmpLR register to the address in regEA. + * + * @this {X86FPU} + */ +X86FPU.prototype.setEAFromWI = function() +{ + this.assert(this.cpu.regEA !== X86.ADDR_INVALID); + this.cpu.setShort(this.cpu.regEA, this.intTmpLR[0]); +}; + +/** + * setEAFromSI() + * + * Stores the (32-bit) "short-integer" value in the internal intTmpLR register to the address in regEA. + * + * @this {X86FPU} + */ +X86FPU.prototype.setEAFromSI = function() +{ + this.assert(this.cpu.regEA !== X86.ADDR_INVALID); + this.cpu.setLong(this.cpu.regEA, this.intTmpLR[0]); +}; + +/** + * setEAFromLI() + * + * Stores the (64-bit) "long-integer" value in the internal intTmpLR register to the address in regEA. + * + * @this {X86FPU} + */ +X86FPU.prototype.setEAFromLI = function() +{ + this.assert(this.cpu.regEA !== X86.ADDR_INVALID); + this.cpu.setLong(this.cpu.regEA, this.intTmpLR[0]); + this.cpu.setLong(this.cpu.regEA + 4, this.intTmpLR[1]); }; /** @@ -2147,7 +2404,7 @@ X86FPU.prototype.getTRFromEA = function() X86FPU.prototype.setEAFromSR = function() { this.assert(this.cpu.regEA !== X86.ADDR_INVALID); - this.cpu.setLong(this.cpu.regEA, this.regTmpSR[0]); + this.cpu.setLong(this.cpu.regEA, this.intTmpSR[0]); }; /** @@ -2160,23 +2417,23 @@ X86FPU.prototype.setEAFromSR = function() X86FPU.prototype.setEAFromLR = function() { this.assert(this.cpu.regEA !== X86.ADDR_INVALID); - this.cpu.setLong(this.cpu.regEA, this.regTmpLR[0]); - this.cpu.setLong(this.cpu.regEA + 4, this.regTmpLR[1]); + this.cpu.setLong(this.cpu.regEA, this.intTmpLR[0]); + this.cpu.setLong(this.cpu.regEA + 4, this.intTmpLR[1]); }; /** * setEAFromTR() * - * Stores the (80-bit) "temp-real" value in the internal regTmpTR register to the address in regEA. + * Stores the (80-bit) "temp-real" value in the internal intTmpTR register to the address in regEA. * * @this {X86FPU} */ X86FPU.prototype.setEAFromTR = function() { this.assert(this.cpu.regEA !== X86.ADDR_INVALID); - this.cpu.setLong(this.cpu.regEA, this.regTmpTR[0]); - this.cpu.setLong(this.cpu.regEA + 4, this.regTmpTR[1]); - this.cpu.setShort(this.cpu.regEA + 8, this.regTmpTR[2]); + this.cpu.setLong(this.cpu.regEA, this.intTmpTR[0]); + this.cpu.setLong(this.cpu.regEA + 4, this.intTmpTR[1]); + this.cpu.setShort(this.cpu.regEA + 8, this.intTmpTR[2]); }; /** @@ -2188,7 +2445,7 @@ X86FPU.prototype.setEAFromTR = function() * does NOT store a leading 1 with the fraction, whereas the latter does. * * @this {X86FPU} - * @param {Array.} a (eg, regTmpTR) + * @param {Array.} a (eg, intTmpTR) * @return {number} v */ X86FPU.prototype.getLRFromTR = function(a) @@ -2236,7 +2493,7 @@ X86FPU.prototype.getLRFromTR = function(a) * @this {X86FPU} * @param {number} loLR * @param {number} hiLR - * @return {Array.} (regTmpTR) + * @return {Array.} (intTmpTR) */ X86FPU.prototype.getTRFromLR = function(loLR, hiLR) { @@ -2270,10 +2527,10 @@ X86FPU.prototype.getTRFromLR = function(loLR, hiLR) expTR += 0x3fff - 0x3ff; } - this.regTmpTR[0] = loTR; - this.regTmpTR[1] = hiTR; - this.regTmpTR[2] = signTR | expTR; - return this.regTmpTR; + this.intTmpTR[0] = loTR; + this.intTmpTR[1] = hiTR; + this.intTmpTR[2] = signTR | expTR; + return this.intTmpTR; }; /** @@ -2509,7 +2766,7 @@ X86FPU.aaOps = { 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, + 0x30: X86FPU.FLDsti, 0x31: X86FPU.FXCHsti, 0x32: X86FPU.FNOP, 0x33: X86FPU.FSTP8087, 0x40: X86FPU.FCHS, 0x41: X86FPU.FABS, 0x44: X86FPU.FTST, 0x45: X86FPU.FXAM, 0x50: X86FPU.FLD1, 0x51: X86FPU.FLDL2T, 0x52: X86FPU.FLDL2E, 0x53: X86FPU.FLDPI, @@ -2527,30 +2784,31 @@ X86FPU.aaOps = { 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, - 0x44: X86FPU.FSETPM, - 0X73: X86FPU.FSINCOS + 0x44: X86FPU.FSETPM287, + 0x73: X86FPU.FSINCOS387 }, 0xDC: { 0x00: X86FPU.FADDlr, 0x01: X86FPU.FMULlr, 0x02: X86FPU.FCOMlr, 0x03: X86FPU.FCOMPlr, 0x04: X86FPU.FSUBlr, 0x05: X86FPU.FSUBRlr, 0x06: X86FPU.FDIVlr, 0x07: X86FPU.FDIVRlr, - 0x30: X86FPU.FADDsti, 0x31: X86FPU.FMULsti, 0x32: X86FPU.FCOMsti, 0x33: X86FPU.FCOMPsti, + 0x30: X86FPU.FADDsti, 0x31: X86FPU.FMULsti, 0x32: X86FPU.FCOM8087, 0x33: X86FPU.FCOMP8087, 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 + 0x30: X86FPU.FFREEsti, 0x31: X86FPU.FXCH8087, 0x32: X86FPU.FSTsti, 0x33: X86FPU.FSTPsti }, 0xDE: { 0x00: X86FPU.FIADD16, 0x01: X86FPU.FIMUL16, 0x02: X86FPU.FICOM16, 0x03: X86FPU.FICOMP16, 0x04: X86FPU.FISUB16, 0x05: X86FPU.FISUBR16, 0x06: X86FPU.FIDIV16, 0x07: X86FPU.FIDIVR16, - 0x30: X86FPU.FADDPsti, 0x31: X86FPU.FMULPsti, 0x32: X86FPU.FCOMPsti, 0x33: X86FPU.FCOMPPsti, + 0x30: X86FPU.FADDPsti, 0x31: X86FPU.FMULPsti, 0x32: X86FPU.FCOMP8087, 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, 0x34: X86FPU.FSTSWAX + 0x00: X86FPU.FILD16, 0x02: X86FPU.FIST16, 0x03: X86FPU.FISTP16, + 0x04: X86FPU.FBLDpd, 0x05: X86FPU.FILD64, 0x06: X86FPU.FBSTPpd, 0x07: X86FPU.FISTP64, + 0x30: X86FPU.FFREEP8087,0x31: X86FPU.FXCH8087, 0x32: X86FPU.FSTP8087, 0x33: X86FPU.FSTP8087, + 0x34: X86FPU.FSTSWAX287 } }; @@ -2559,7 +2817,7 @@ X86FPU.aaOps = { */ X86FPU.afnPreserveExceptions = [ X86FPU.FCLEX, X86FPU.FINIT, X86FPU.FLDCW, X86FPU.FLDENV, X86FPU.FRSTOR, - X86FPU.FSAVE, X86FPU.FSTCW, X86FPU.FSTENV, X86FPU.FSTSW, X86FPU.FSTSWAX + X86FPU.FSAVE, X86FPU.FSTCW, X86FPU.FSTENV, X86FPU.FSTSW, X86FPU.FSTSWAX287 ]; /**