Fixed INT and RETF when stack switches occur
This commit is contained in:
parent
9523de773d
commit
38c2928882
7 changed files with 81 additions and 49 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 = [
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
},
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 = [
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue