From 38c29288827d9ed1fb71d3d8570cc5a6a45c549f Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Wed, 26 Nov 2014 16:34:54 -0800 Subject: [PATCH] Fixed INT and RETF when stack switches occur --- .editorconfig | 6 +++++- modules/pcjs/lib/x86cpu.js | 4 ++-- modules/pcjs/lib/x86grps.js | 30 +++++++++++++++--------------- modules/pcjs/lib/x86help.js | 14 ++++++++++---- modules/pcjs/lib/x86op0f.js | 12 ++++++------ modules/pcjs/lib/x86opxx.js | 34 +++++++++++++++++----------------- modules/pcjs/lib/x86seg.js | 30 ++++++++++++++++++++++++++---- 7 files changed, 81 insertions(+), 49 deletions(-) diff --git a/.editorconfig b/.editorconfig index 54208b256..b29ff3a0b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,10 +7,14 @@ root = true end_of_line = lf insert_final_newline = true +[{*.ASM,*.INC}] +indent_style = tab +indent_size = 8 +trim_trailing_whitespace = false + [*.asm] indent_style = tab indent_size = 8 -trim_trailing_whitespace = true [modules/**.js] indent_style = space diff --git a/modules/pcjs/lib/x86cpu.js b/modules/pcjs/lib/x86cpu.js index 4ff35b33f..148479124 100644 --- a/modules/pcjs/lib/x86cpu.js +++ b/modules/pcjs/lib/x86cpu.js @@ -722,8 +722,8 @@ X86CPU.prototype.initProcessor = function() this.aOps[X86.OPCODE.INSW] = X86OpXX.opINSw; this.aOps[X86.OPCODE.OUTSB] = X86OpXX.opOUTSb; this.aOps[X86.OPCODE.OUTSW] = X86OpXX.opOUTSw; - this.aOps[0xC0] = X86OpXX.opGRP2ab; - this.aOps[0xC1] = X86OpXX.opGRP2aw; + this.aOps[0xC0] = X86OpXX.opGrp2ab; + this.aOps[0xC1] = X86OpXX.opGrp2aw; this.aOps[X86.OPCODE.ENTER] = X86OpXX.opENTER; this.aOps[X86.OPCODE.LEAVE] = X86OpXX.opLEAVE; this.aOps[0xF1] = X86OpXX.opINT1; diff --git a/modules/pcjs/lib/x86grps.js b/modules/pcjs/lib/x86grps.js index f48e333f7..67dd8bfd7 100644 --- a/modules/pcjs/lib/x86grps.js +++ b/modules/pcjs/lib/x86grps.js @@ -1254,7 +1254,7 @@ var X86Grps = { }; /* - * A word (or two) on instruction groups (eg, GRP1, GRP2), which are groups of instructions that + * A word (or two) on instruction groups (eg, Grp1, Grp2), which are groups of instructions that * use a mod/reg/rm byte, where the reg field of that byte selects a function rather than a register. * * I start with the groupings used by Intel's "Pentium Processor User's Manual (Volume 3: Architecture @@ -1263,21 +1263,21 @@ var X86Grps = { * * Opcodes Intel PCjs PC Mag TechRef * ------- ----- ---- -------------- - * 0x80-0x83 Grp1 GRP1b, GRP1w, GRP1b, and GRP1sw Group A - * 0xC0-0xC1 Grp2a GRP2ab and GRP2aw Group B - * 0xD0-0xD3 Grp2 GRP2b and GRP2w Group B - * 0xF6-0xF7 Grp3 GRP3b and GRP3w Group C - * 0xFE Grp4 GRP4b Group D - * 0xFF Grp5 GRP4w Group E - * 0x0F,0x00 Grp6 GRP6 (SLDT, STR, LLDT, LTR, VERR, VERW) Group F - * 0x0F,0x01 Grp7 GRP7 (SGDT, SIDT, LGDT, LIDT, SMSW, LMSW, INVLPG) Group G - * 0x0F,0xBA Grp8 GRP8 (BT, BTS, BTR, BTC) Group H - * 0x0F,0xC7 Grp9 GRP9 (CMPXCH) (N/A, 80386 and up?) + * 0x80-0x83 Grp1 Grp1b, Grp1w, Grp1b, and Grp1sw Group A + * 0xC0-0xC1 Grp2a Grp2ab and Grp2aw Group B + * 0xD0-0xD3 Grp2 Grp2b and Grp2w Group B + * 0xF6-0xF7 Grp3 Grp3b and Grp3w Group C + * 0xFE Grp4 Grp4b Group D + * 0xFF Grp5 Grp4w Group E + * 0x0F,0x00 Grp6 Grp6 (SLDT, STR, LLDT, LTR, VERR, VERW) Group F + * 0x0F,0x01 Grp7 Grp7 (SGDT, SIDT, LGDT, LIDT, SMSW, LMSW, INVLPG) Group G + * 0x0F,0xBA Grp8 Grp8 (BT, BTS, BTR, BTC) Group H + * 0x0F,0xC7 Grp9 Grp9 (CMPXCH) (N/A, 80386 and up) * - * My only serious deviation is Grp5, which I refer to as GRP4w, because it contains word forms of - * the INC and DEC instructions found in GRP4b. Granted, GRP4w also contains versions of the CALL, - * JMP and PUSH instructions, which are not in GRP4b, but there's nothing in GRP4b that conflicts with - * GRP4w, so I think my nomenclature makes more sense. To compensate, I don't use GRP5, so that the + * My only serious deviation is Grp5, which I refer to as Grp4w, because it contains word forms of + * the INC and DEC instructions found in Grp4b. Granted, Grp4w also contains versions of the CALL, + * JMP and PUSH instructions, which are not in Grp4b, but there's nothing in Grp4b that conflicts with + * Grp4w, so I think my nomenclature makes more sense. To compensate, I don't use Grp5, so that the * remaining group numbers remain in sync with Intel's. */ X86Grps.aOpGrp1b = [ diff --git a/modules/pcjs/lib/x86help.js b/modules/pcjs/lib/x86help.js index 0f38a32f5..ab801ddcc 100644 --- a/modules/pcjs/lib/x86help.js +++ b/modules/pcjs/lib/x86help.js @@ -574,16 +574,22 @@ var X86Help = { * Helper to push processor state, CS:IP, and optional error code onto the stack, and then jump * to whatever CS:IP was fetched into descIDT by opHelpLoadIDT(). * + * For protected-mode, this function must attempt to load the new code segment first, because if the new segment + * requires a change in privilege level, the return address must be pushed on the NEW stack, not the current stack. + * * @this {X86CPU} * @param {number|null|undefined} nError */ opHelpPushPS: function(nError) { - this.pushWord(this.getPS()); + var regPS = this.getPS(); + var regCS = this.segCS.sel; + var regIP = this.regIP; this.regPS &= this.descIDT.maskPS; - this.pushWord(this.segCS.sel); - this.pushWord(this.regIP); + this.setCSIP(this.descIDT.off, this.descIDT.sel, true); + this.pushWord(regPS); + this.pushWord(regCS); + this.pushWord(regIP); if (nError != null) this.pushWord(nError); - this.setCSIP(this.descIDT.off, this.descIDT.sel); this.nFault = -1; }, /** diff --git a/modules/pcjs/lib/x86op0f.js b/modules/pcjs/lib/x86op0f.js index e48d0e48e..01c6d1a08 100644 --- a/modules/pcjs/lib/x86op0f.js +++ b/modules/pcjs/lib/x86op0f.js @@ -46,7 +46,7 @@ var X86Op0F = { * * op=0x0F,0x00 (grp6 rm) */ - opGRP6: function() { + opGrp6: function() { var bModRM = this.getIPByte(); if ((bModRM & 0x38) < 0x10) { // possible reg values: 0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38 if (EAFUNCS) this.modEAWord = this.modEAWordDisabled; else this.opFlags |= X86.OPFLAG.NOREAD; @@ -59,7 +59,7 @@ var X86Op0F = { * * op=0x0F,0x01 (grp7 rm) */ - opGRP7: function() { + opGrp7: function() { var bModRM = this.getIPByte(); if (!(bModRM & 0x10)) { if (EAFUNCS) this.modEAWord = this.modEAWordDisabled; else this.opFlags |= X86.OPFLAG.NOREAD; @@ -438,7 +438,7 @@ var X86Op0F = { }; X86Op0F.aOps0F = [ - X86Op0F.opGRP6, X86Op0F.opGRP7, X86Op0F.opLAR, X86Op0F.opLSL, // 0x00-0x03 + X86Op0F.opGrp6, X86Op0F.opGrp7, X86Op0F.opLAR, X86Op0F.opLSL, // 0x00-0x03 X86OpXX.opUndefined, X86Op0F.opLOADALL, X86Op0F.opCLTS, X86OpXX.opUndefined, // 0x04-0x07 /* * On all processors (except the 8086/8088, of course), 0x0F,0x0B is also referred to as "UD2": an @@ -509,8 +509,8 @@ X86Op0F.aOps0F = [ ]; /* - * These instruction groups are not as orthogonal as the original 8086/8088 groups (GRP1 through GRP4): some of - * the instructions in GRP6 and GRP7 only read their dst operand (eg, LLDT), which means the ModRM helper function + * These instruction groups are not as orthogonal as the original 8086/8088 groups (Grp1 through Grp4): some of + * the instructions in Grp6 and Grp7 only read their dst operand (eg, LLDT), which means the ModRM helper function * must insure that setEAWord() is disabled, while others only write their dst operand (eg, SLDT), which means that * getEAWord() should be disabled *prior* to calling the ModRM helper function. This latter case requires that * we decode the reg field of the ModRM byte before dispatching. @@ -526,7 +526,7 @@ X86Op0F.aOpGrp6Real = [ ]; /* - * Unlike GRP6, GRP7 does not require separate real-mode and protected-mode dispatch tables, because all GRP7 + * Unlike Grp6, Grp7 does not require separate real-mode and protected-mode dispatch tables, because all Grp7 * instructions are valid in both modes. */ X86Op0F.aOpGrp7 = [ diff --git a/modules/pcjs/lib/x86opxx.js b/modules/pcjs/lib/x86opxx.js index 2cbe4b738..74b29aebf 100644 --- a/modules/pcjs/lib/x86opxx.js +++ b/modules/pcjs/lib/x86opxx.js @@ -1590,7 +1590,7 @@ var X86OpXX = { * * op=0x80/0x82 (grp1b rm,imm8) */ - opGRP1b: function() { + opGrp1b: function() { X86Mods.aOpModsGrpByte[this.getIPByte()].call(this, X86Grps.aOpGrp1b, this.getIPByte); if (EAFUNCS) this.setEAByte = this.setEAByteEnabled; this.nStepCycles -= (this.regEAWrite < 0? 1 : this.CYCLES.nOpCyclesArithMID); @@ -1600,7 +1600,7 @@ var X86OpXX = { * * op=0x81 (grp1w rm,imm16) */ - opGRP1w: function() { + opGrp1w: function() { X86Mods.aOpModsGrpWord[this.getIPByte()].call(this, X86Grps.aOpGrp1w, this.getIPWord); if (EAFUNCS) this.setEAWord = this.setEAWordEnabled; this.nStepCycles -= (this.regEAWrite < 0? 1 : this.CYCLES.nOpCyclesArithMID); @@ -1610,7 +1610,7 @@ var X86OpXX = { * * op=0x83 (grp1sw rm,disp) */ - opGRP1sw: function() { + opGrp1sw: function() { X86Mods.aOpModsGrpWord[this.getIPByte()].call(this, X86Grps.aOpGrp1w, this.getIPDisp); if (EAFUNCS) this.setEAWord = this.setEAWordEnabled; this.nStepCycles -= (this.regEAWrite < 0? 1 : this.CYCLES.nOpCyclesArithMID); @@ -2639,7 +2639,7 @@ var X86OpXX = { * * op=0xC0 (grp2ab rm) (80186/80188 and up) */ - opGRP2ab: function() { + opGrp2ab: function() { X86Mods.aOpModsGrpByte[this.getIPByte()].call(this, X86Grps.aOpGrp2ab, X86Grps.opGrp2CountImm); }, /** @@ -2647,7 +2647,7 @@ var X86OpXX = { * * op=0xC1 (grp2aw rm) (80186/80188 and up) */ - opGRP2aw: function() { + opGrp2aw: function() { X86Mods.aOpModsGrpWord[this.getIPByte()].call(this, X86Grps.aOpGrp2aw, X86Grps.opGrp2CountImm); }, /** @@ -2849,7 +2849,7 @@ var X86OpXX = { * * op=0xD0 (grp2b rm,1) */ - opGRP2b1: function() { + opGrp2b1: function() { X86Mods.aOpModsGrpByte[this.getIPByte()].call(this, X86Grps.aOpGrp2b, X86Grps.opGrp2Count1); }, /** @@ -2857,7 +2857,7 @@ var X86OpXX = { * * op=0xD1 (grp2w rm,1) */ - opGRP2w1: function() { + opGrp2w1: function() { X86Mods.aOpModsGrpWord[this.getIPByte()].call(this, X86Grps.aOpGrp2w, X86Grps.opGrp2Count1); }, /** @@ -2865,7 +2865,7 @@ var X86OpXX = { * * op=0xD2 (grp2b rm,CL) */ - opGRP2bCL: function() { + opGrp2bCL: function() { X86Mods.aOpModsGrpByte[this.getIPByte()].call(this, X86Grps.aOpGrp2b, X86Grps.opGrp2CountCL); }, /** @@ -2873,7 +2873,7 @@ var X86OpXX = { * * op=0xD3 (grp2w rm,CL) */ - opGRP2wCL: function() { + opGrp2wCL: function() { X86Mods.aOpModsGrpWord[this.getIPByte()].call(this, X86Grps.aOpGrp2w, X86Grps.opGrp2CountCL); }, /** @@ -3241,7 +3241,7 @@ var X86OpXX = { * * Similar issues with IMUL (and DIV and IDIV) are resolved using the same special variable(s). */ - opGRP3b: function() { + opGrp3b: function() { this.regMD16 = -1; X86Mods.aOpModsGrpByte[this.getIPByte()].call(this, X86Grps.aOpGrp3b, X86Grps.opGrpNoSrc); if (this.regMD16 >= 0) this.regAX = this.regMD16; @@ -3265,7 +3265,7 @@ var X86OpXX = { * (eg, regMD16/regMD32), which we will then put back into regAX/regDX if it's been updated. This also relieves * us from having to decode any part of the ModRM byte, so maybe it's not such a bad work-around after all. */ - opGRP3w: function() { + opGrp3w: function() { this.regMD16 = -1; X86Mods.aOpModsGrpWord[this.getIPByte()].call(this, X86Grps.aOpGrp3w, X86Grps.opGrpNoSrc); if (this.regMD16 >= 0) { @@ -3334,7 +3334,7 @@ var X86OpXX = { * * op=0xFE (grp4b rm) */ - opGRP4b: function() { + opGrp4b: function() { X86Mods.aOpModsGrpByte[this.getIPByte()].call(this, X86Grps.aOpGrp4b, X86Grps.opGrpNoSrc); }, /** @@ -3342,7 +3342,7 @@ var X86OpXX = { * * op=0xFF (grp4w rm) */ - opGRP4w: function() { + opGrp4w: function() { X86Mods.aOpModsGrpWord[this.getIPByte()].call(this, X86Grps.aOpGrp4w, X86Grps.opGrpNoSrc); if (EAFUNCS) this.setEAWord = this.setEAWordEnabled; }, @@ -3416,7 +3416,7 @@ X86OpXX.aOps = [ * to opcode 0x82 as a "reserved" instruction, but also cryptically refers to it as "MOVB AL,imm". This is * assumed to be an error in the manual, because as far as I know, 0x82 has always mirrored 0x80. */ - X86OpXX.opGRP1b, X86OpXX.opGRP1w, X86OpXX.opGRP1b, X86OpXX.opGRP1sw, // 0x80-0x83 + X86OpXX.opGrp1b, X86OpXX.opGrp1w, X86OpXX.opGrp1b, X86OpXX.opGrp1sw, // 0x80-0x83 X86OpXX.opTESTrb, X86OpXX.opTESTrw, X86OpXX.opXCHGrb, X86OpXX.opXCHGrw, // 0x84-0x87 X86OpXX.opMOVmb, X86OpXX.opMOVmw, X86OpXX.opMOVrb, X86OpXX.opMOVrw, // 0x88-0x8B X86OpXX.opMOVSegSrc, X86OpXX.opLEA, X86OpXX.opMOVSegDst, X86OpXX.opPOPmw, // 0x8C-0x8F @@ -3439,7 +3439,7 @@ X86OpXX.aOps = [ X86OpXX.opLES, X86OpXX.opLDS, X86OpXX.opMOVb, X86OpXX.opMOVw, // 0xC4-0xC7 X86OpXX.opRETFn, X86OpXX.opRETF, X86OpXX.opRETFn, X86OpXX.opRETF, // 0xC8-0xCB X86OpXX.opINT3, X86OpXX.opINTn, X86OpXX.opINTO, X86OpXX.opIRET, // 0xCC-0xCF - X86OpXX.opGRP2b1, X86OpXX.opGRP2w1, X86OpXX.opGRP2bCL, X86OpXX.opGRP2wCL, // 0xD0-0xD3 + X86OpXX.opGrp2b1, X86OpXX.opGrp2w1, X86OpXX.opGrp2bCL, X86OpXX.opGrp2wCL, // 0xD0-0xD3 /* * Even as of the Pentium, opcode 0xD6 is still marked as "reserved", but it's always been SETALC/SALC. */ @@ -3455,9 +3455,9 @@ X86OpXX.aOps = [ * a prefix on those processors, so we treat it as such. As of the Pentium, it is still marked as "reserved". */ X86OpXX.opLOCK, X86OpXX.opLOCK, X86OpXX.opREPNZ, X86OpXX.opREPZ, // 0xF0-0xF3 - X86OpXX.opHLT, X86OpXX.opCMC, X86OpXX.opGRP3b, X86OpXX.opGRP3w, // 0xF4-0xF7 + X86OpXX.opHLT, X86OpXX.opCMC, X86OpXX.opGrp3b, X86OpXX.opGrp3w, // 0xF4-0xF7 X86OpXX.opCLC, X86OpXX.opSTC, X86OpXX.opCLI, X86OpXX.opSTI, // 0xF8-0xFB - X86OpXX.opCLD, X86OpXX.opSTD, X86OpXX.opGRP4b, X86OpXX.opGRP4w // 0xFC-0xFF + X86OpXX.opCLD, X86OpXX.opSTD, X86OpXX.opGrp4b, X86OpXX.opGrp4w // 0xFC-0xFF ]; if (typeof module !== 'undefined') module.exports = X86OpXX; diff --git a/modules/pcjs/lib/x86seg.js b/modules/pcjs/lib/x86seg.js index 5bc42cad8..f82a43b25 100644 --- a/modules/pcjs/lib/x86seg.js +++ b/modules/pcjs/lib/x86seg.js @@ -59,7 +59,31 @@ function X86Seg(cpu, id, sName, fProt) this.addrDesc = null; this.cpl = 0; this.dpl = 0; + /* + * The following properties are used for CODE segments only (ie, segCS); if the process of loading + * CS also requires a stack switch, then fStackSwitch will be set to true; additionally, if the stack + * switch was the result of a CALL (ie, fCall is true) and one or more (up to 32) parameters are on + * the old stack, they will be copied to awScratch, and then once the stack is switched, the parameters + * will be pushed from awScratch onto the new stack. + * + * The typical ways of loading a new segment into CS are JMPF, CALLF (or INT), and RETF (or IRET); + * prior to calling segCS.load(), each of those operations must first set segCS.fCall to one of null, + * true, or false, respectively. + * + * It's critical that fCall be properly set prior to calling segCS.load(); fCall == null means NO + * privilege level transition may occur, fCall == true allows a stack switch and a privilege transition + * to a numerically lower privilege, and fCall == false allows a stack switch (restore) and a privilege + * transition to a numerically greater privilege. + * + * As long as setCSIP() is used for all CS changes, the foregoing is automatically taken care of. + * + * TODO: Consider making fCall a parameter to load(), instead of a property that must be set prior to + * calling load(); the downside (and why I didn't do that in the first place) is that such a parameter + * to load() would be meaningless for segments other than segCS. + */ this.awScratch = (this.id == X86Seg.ID.CODE? new Array(32) : []); + this.fCall = null; + this.fStackSwitch = false; this.updateAccess(fProt); } @@ -470,7 +494,7 @@ X86Seg.prototype.setBase = function(addr) * save() * * Early versions of PCjs saved only segment selectors, since that's all that mattered in real-mode; - * newer versions need to save/restore the entire segment object. + * newer versions need to save/restore all the "defining" properties of the X86Seg object. * * @this {X86Seg} * @return {Array} @@ -484,7 +508,7 @@ X86Seg.prototype.save = function() * restore(a) * * Early versions of PCjs saved only segment selectors, since that's all that mattered in real-mode; - * newer versions need to save/restore the entire segment object. + * newer versions need to save/restore all the "defining" properties of the X86Seg object. * * @this {X86Seg} * @param {Array|number} a @@ -555,8 +579,6 @@ X86Seg.prototype.updateAccess = function(fProt) this.cpl = this.dpl = 0; this.addrDesc = null; } - this.fCall = null; // true if CALLF in progress, false if RETF in progress, null/undefined otherwise (X86Seg.ID.CODE only) - this.fStackSwitch = false; // true if a stack switch occurred on the last loadDesc8(), false otherwise (X86Seg.ID.CODE only) return fProt; };