Made RETF restartable when the target segment is not present
This commit is contained in:
parent
2d7cbe2864
commit
5b64488286
5 changed files with 100 additions and 41 deletions
|
|
@ -627,7 +627,7 @@ if (DEBUGGER) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Debugger.TRACE_LIMIT = 100000;
|
Debugger.TRACE_LIMIT = 100000;
|
||||||
Debugger.HISTORY_LIMIT = DEBUG? 100000 : 10000;
|
Debugger.HISTORY_LIMIT = DEBUG? 100000 : 1000;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Opcode 0x0F has a distinguished history:
|
* Opcode 0x0F has a distinguished history:
|
||||||
|
|
|
||||||
|
|
@ -330,7 +330,7 @@ var X86 = {
|
||||||
EXT: 0x0001,
|
EXT: 0x0001,
|
||||||
IDT: 0x0002,
|
IDT: 0x0002,
|
||||||
LDT: 0x0004,
|
LDT: 0x0004,
|
||||||
MASK: 0xFFF8 // index of corresponding entry in GDT, LDT or IDT
|
SELMASK: 0xFFFC
|
||||||
},
|
},
|
||||||
RESULT: {
|
RESULT: {
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -1127,7 +1127,7 @@ X86CPU.prototype.getReg = function(i)
|
||||||
reg = this.regEBX;
|
reg = this.regEBX;
|
||||||
break;
|
break;
|
||||||
case 0x4:
|
case 0x4:
|
||||||
reg = this.regESP;
|
reg = this.getSP();
|
||||||
break;
|
break;
|
||||||
case 0x5:
|
case 0x5:
|
||||||
reg = this.regEBP;
|
reg = this.regEBP;
|
||||||
|
|
@ -1165,7 +1165,7 @@ X86CPU.prototype.setReg = function(i, reg)
|
||||||
this.regEBX = reg;
|
this.regEBX = reg;
|
||||||
break;
|
break;
|
||||||
case 0x4:
|
case 0x4:
|
||||||
this.regESP = reg;
|
this.setSP(reg);
|
||||||
break;
|
break;
|
||||||
case 0x5:
|
case 0x5:
|
||||||
this.regEBP = reg;
|
this.regEBP = reg;
|
||||||
|
|
@ -1295,6 +1295,13 @@ X86CPU.prototype.resetRegs = function()
|
||||||
*/
|
*/
|
||||||
this.nFault = -1;
|
this.nFault = -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These are used to snapshot regLIP and regLSP, to help make instructions restartable;
|
||||||
|
* currently opLIP is updated prior to every instruction, but opLSP is updated only for
|
||||||
|
* "problematic" instructions (eg, RETF) and should otherwise remain set to X86.ADDR_INVALID.
|
||||||
|
*/
|
||||||
|
this.opLIP = this.opLSP = X86.ADDR_INVALID;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Segment registers used to be defined as separate variables (eg, regCS and regCS0 stored the segment
|
* Segment registers used to be defined as separate variables (eg, regCS and regCS0 stored the segment
|
||||||
* number and base linear address, respectively), but segment registers are now defined as X86Seg objects.
|
* number and base linear address, respectively), but segment registers are now defined as X86Seg objects.
|
||||||
|
|
@ -1419,6 +1426,21 @@ X86CPU.prototype.resetRegs = function()
|
||||||
this.setProtMode();
|
this.setProtMode();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zeroSeg(seg)
|
||||||
|
*
|
||||||
|
* Helper to zero a segment register as privilege transitions require.
|
||||||
|
*
|
||||||
|
* @this {X86CPU}
|
||||||
|
* @param {X86Seg} seg
|
||||||
|
*/
|
||||||
|
X86CPU.prototype.zeroSeg = function(seg)
|
||||||
|
{
|
||||||
|
if ((seg.sel & X86.SEL.MASK) && seg.dpl < this.nCPL && (seg.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) != X86.DESC.ACC.TYPE.CODE_CONFORMING) {
|
||||||
|
seg.load(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* setAddrSize(size)
|
* setAddrSize(size)
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -2228,14 +2228,35 @@ X86.fnRCRd = function RCRd(dst, src)
|
||||||
/**
|
/**
|
||||||
* fnRETF(n)
|
* fnRETF(n)
|
||||||
*
|
*
|
||||||
* For protected-mode, this function must be prepared to pop any arguments off the current stack AND
|
* For protected-mode, this function must pop any arguments off the current stack AND whatever stack
|
||||||
* whatever stack we may have switched to (setCSIP() returns true only when a stack switch has occurred).
|
* we may have switched to; setCSIP() returns true if a stack switch occurred, false if not, and null
|
||||||
|
* if an error occurred.
|
||||||
|
*
|
||||||
|
* Take a look at our counterpart, fnCALLF():
|
||||||
|
*
|
||||||
|
* if (this.setCSIP(off, sel, true) != null) {
|
||||||
|
* this.pushWord(oldCS);
|
||||||
|
* this.pushWord(oldIP);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* That code makes opCALLF() restartable, because it doesn't modify the stack unless setCSIP() succeeds.
|
||||||
|
*
|
||||||
|
* Here, our task is a little more complicated, because 1) it's not convenient to defer our stack
|
||||||
|
* operations (it's much simpler to perform them BEFORE the setCSIP() call rather than AFTER); 2) we
|
||||||
|
* have to deal with an additional stack adjustment value (n); and 3) if setCSIP() triggers a fault
|
||||||
|
* (eg, NP_FAULT), fnFault() must be able to do the rewinding, which happens BEFORE setCSIP() returns.
|
||||||
|
*
|
||||||
|
* The current hack to make the stack "rewindable" involves copying regLSP to opLSP, similar to what we do
|
||||||
|
* for EIP (ie, by copying regLIP into opLIP prior to executing every opcode). However, I don't really want
|
||||||
|
* to snapshot more data inside the opcode loop, so my compromise is to set opLSP only within "problematic"
|
||||||
|
* instructions (like this one), and set it back to X86.ADDR_INVALID when we're done.
|
||||||
*
|
*
|
||||||
* @this {X86CPU}
|
* @this {X86CPU}
|
||||||
* @param {number} n
|
* @param {number} n
|
||||||
*/
|
*/
|
||||||
X86.fnRETF = function RETF(n)
|
X86.fnRETF = function RETF(n)
|
||||||
{
|
{
|
||||||
|
this.opLSP = this.regLSP;
|
||||||
var newIP = this.popWord();
|
var newIP = this.popWord();
|
||||||
var newCS = this.popWord();
|
var newCS = this.popWord();
|
||||||
|
|
||||||
|
|
@ -2246,12 +2267,12 @@ X86.fnRETF = function RETF(n)
|
||||||
if (this.setCSIP(newIP, newCS, false)) { // returns true if a stack switch occurred
|
if (this.setCSIP(newIP, newCS, false)) { // returns true if a stack switch occurred
|
||||||
/*
|
/*
|
||||||
* Fool me once, shame on... whatever. If setCSIP() indicates a stack switch occurred,
|
* Fool me once, shame on... whatever. If setCSIP() indicates a stack switch occurred,
|
||||||
* make sure we're in protected mode, because automatic stack switches can't occur in real mode,
|
* make sure we're in protected mode, because automatic stack switches can't occur in real mode.
|
||||||
* and adjusting SP again under those circumstances will likely cause great harm.
|
|
||||||
*/
|
*/
|
||||||
this.assert(!!(this.regCR0 & X86.CR0.MSW.PE));
|
this.assert(!!(this.regCR0 & X86.CR0.MSW.PE));
|
||||||
|
|
||||||
if (n) this.setSP(this.getSP() + n); // TODO: optimize
|
if (n) this.setSP(this.getSP() + n); // TODO: optimize
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* As per Intel documentation: "If any of [the DS or ES] registers refer to segments whose DPL is
|
* As per Intel documentation: "If any of [the DS or ES] registers refer to segments whose DPL is
|
||||||
* less than the new CPL (excluding conforming code segments), the segment register is loaded with
|
* less than the new CPL (excluding conforming code segments), the segment register is loaded with
|
||||||
|
|
@ -2261,15 +2282,14 @@ X86.fnRETF = function RETF(n)
|
||||||
* it safe and using CODE_CONFORMING instead of CODE_CONFORMING_READABLE. Also, for the record, I've not
|
* it safe and using CODE_CONFORMING instead of CODE_CONFORMING_READABLE. Also, for the record, I've not
|
||||||
* seen this situation occur yet (eg, in OS/2 1.0).
|
* seen this situation occur yet (eg, in OS/2 1.0).
|
||||||
*/
|
*/
|
||||||
if ((this.segDS.sel & X86.SEL.MASK) && this.segDS.dpl < this.nCPL && (this.segDS.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) != X86.DESC.ACC.TYPE.CODE_CONFORMING) {
|
this.zeroSeg(this.segDS);
|
||||||
this.assert(false); // I'm not asserting this is bad, I just want to see it in action
|
this.zeroSeg(this.segES);
|
||||||
this.segDS.load(0);
|
if (I386 && this.model >= X86.MODEL_80386) {
|
||||||
}
|
this.zeroSeg(this.segFS);
|
||||||
if ((this.segES.sel & X86.SEL.MASK) && this.segES.dpl < this.nCPL && (this.segES.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) != X86.DESC.ACC.TYPE.CODE_CONFORMING) {
|
this.zeroSeg(this.segGS);
|
||||||
this.assert(false); // I'm not asserting this is bad, I just want to see it in action
|
|
||||||
this.segES.load(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.opLSP = X86.ADDR_INVALID;
|
||||||
if (MAXDEBUG && n == 2 && this.cIntReturn) this.checkIntReturn(this.regLIP);
|
if (MAXDEBUG && n == 2 && this.cIntReturn) this.checkIntReturn(this.regLIP);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -3693,12 +3713,15 @@ X86.fnFault = function(nFault, nError, fHalt, nCycles)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* X86.OPFLAG.FAULT flag is used by selected opcodes to provide an early exit, restore register(s), or whatever is
|
* X86.OPFLAG.FAULT flag is used by selected opcodes to provide an early exit, restore register(s), or whatever is
|
||||||
* needed to help ensure instruction restartability; there is currently no mechanism for snapping and restoring all
|
* needed to help ensure instruction restartability; there is currently no general-purpose mechanism for snapping
|
||||||
* registers for any instruction that might fault, so it's every opcode for themselves....
|
* and restoring all registers for any instruction that might fault, so it's every opcode for themselves.
|
||||||
*
|
*
|
||||||
* X86.EXCEPTION.DEBUG exceptions set their own special flag, X86.OPFLAG.DEBUG, to prevent redundant DEBUG exceptions,
|
* X86.EXCEPTION.DEBUG exceptions set their own special flag, X86.OPFLAG.DEBUG, to prevent redundant DEBUG exceptions,
|
||||||
* so we don't need to set OPFLAG.FAULT in that case, because a DEBUG exception doesn't actually prevent an instruction
|
* so we don't need to set OPFLAG.FAULT in that case, because a DEBUG exception doesn't actually prevent an instruction
|
||||||
* from executing.
|
* from executing.
|
||||||
|
*
|
||||||
|
* TODO: Review the restartability of all our opcode handlers, starting with those that affect the segment registers
|
||||||
|
* and then moving on to the rest, and determine whether we really need a general-purpose solution instead.
|
||||||
*/
|
*/
|
||||||
if (nFault == X86.EXCEPTION.DEBUG) {
|
if (nFault == X86.EXCEPTION.DEBUG) {
|
||||||
this.opFlags |= X86.OPFLAG.DEBUG;
|
this.opFlags |= X86.OPFLAG.DEBUG;
|
||||||
|
|
@ -3719,6 +3742,10 @@ X86.fnFault = function(nFault, nError, fHalt, nCycles)
|
||||||
* Single-fault (error code is passed through, and the responsible instruction is restartable)
|
* Single-fault (error code is passed through, and the responsible instruction is restartable)
|
||||||
*/
|
*/
|
||||||
this.setIP(this.opLIP - this.segCS.base);
|
this.setIP(this.opLIP - this.segCS.base);
|
||||||
|
if (this.opLSP != X86.ADDR_INVALID) {
|
||||||
|
this.setSP((this.regESP & ~this.segSS.addrMask) | (this.opLSP - this.segSS.base));
|
||||||
|
this.opLSP = X86.ADDR_INVALID;
|
||||||
|
}
|
||||||
fDispatch = true;
|
fDispatch = true;
|
||||||
} else if (this.nFault != X86.EXCEPTION.DF_FAULT) {
|
} else if (this.nFault != X86.EXCEPTION.DF_FAULT) {
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -231,7 +231,7 @@ X86Seg.prototype.loadProt = function loadProt(sel)
|
||||||
return this.loadDesc8(addrDesc, sel);
|
return this.loadDesc8(addrDesc, sel);
|
||||||
}
|
}
|
||||||
if (this.id < X86Seg.ID.VER) {
|
if (this.id < X86Seg.ID.VER) {
|
||||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel);
|
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel & X86.ERRCODE.SELMASK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return X86.ADDR_INVALID;
|
return X86.ADDR_INVALID;
|
||||||
|
|
@ -282,9 +282,9 @@ X86Seg.prototype.loadIDTProt = function loadIDTProt(nIDT)
|
||||||
var addrDesc = (cpu.addrIDT + nIDT)|0;
|
var addrDesc = (cpu.addrIDT + nIDT)|0;
|
||||||
if (((cpu.addrIDTLimit - addrDesc)|0) >= 7) {
|
if (((cpu.addrIDTLimit - addrDesc)|0) >= 7) {
|
||||||
this.fCall = true;
|
this.fCall = true;
|
||||||
return this.loadDesc8(addrDesc, nIDT) + cpu.regEIP;
|
return this.loadDesc8(addrDesc, nIDT, true) + cpu.regEIP;
|
||||||
}
|
}
|
||||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, nIDT | X86.ERRCODE.IDT | X86.ERRCODE.EXT, true);
|
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, nIDT | X86.ERRCODE.IDT, true);
|
||||||
return X86.ADDR_INVALID;
|
return X86.ADDR_INVALID;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -499,7 +499,7 @@ X86Seg.prototype.loadAcc = function(sel, fGDT)
|
||||||
return cpu.getShort(addrDesc + X86.DESC.ACC.OFFSET);
|
return cpu.getShort(addrDesc + X86.DESC.ACC.OFFSET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel);
|
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel & X86.ERRCODE.SELMASK);
|
||||||
return X86.DESC.ACC.INVALID;
|
return X86.DESC.ACC.INVALID;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -540,7 +540,7 @@ X86Seg.prototype.loadDesc6 = function(addrDesc, sel)
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* loadDesc8(addrDesc, sel)
|
* loadDesc8(addrDesc, sel, fIDT)
|
||||||
*
|
*
|
||||||
* Used to load a protected-mode selector that refers to an 8-byte "descriptor table" (GDT, LDT, IDT) entry:
|
* Used to load a protected-mode selector that refers to an 8-byte "descriptor table" (GDT, LDT, IDT) entry:
|
||||||
*
|
*
|
||||||
|
|
@ -554,9 +554,10 @@ X86Seg.prototype.loadDesc6 = function(addrDesc, sel)
|
||||||
* @this {X86Seg}
|
* @this {X86Seg}
|
||||||
* @param {number} addrDesc is the descriptor address
|
* @param {number} addrDesc is the descriptor address
|
||||||
* @param {number} sel is the associated selector, or nIDT*8 if IDT descriptor
|
* @param {number} sel is the associated selector, or nIDT*8 if IDT descriptor
|
||||||
|
* @param {boolean} [fIDT] is true if sel refers to the IDT (only affects error handling)
|
||||||
* @return {number} base address of selected segment, or ADDR_INVALID if error
|
* @return {number} base address of selected segment, or ADDR_INVALID if error
|
||||||
*/
|
*/
|
||||||
X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fIDT)
|
||||||
{
|
{
|
||||||
var cpu = this.cpu;
|
var cpu = this.cpu;
|
||||||
var limit = cpu.getShort(addrDesc + X86.DESC.LIMIT.OFFSET);
|
var limit = cpu.getShort(addrDesc + X86.DESC.LIMIT.OFFSET);
|
||||||
|
|
@ -584,12 +585,12 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
||||||
if (this.id == X86Seg.ID.CODE) {
|
if (this.id == X86Seg.ID.CODE) {
|
||||||
this.fStackSwitch = false;
|
this.fStackSwitch = false;
|
||||||
var fCall = this.fCall;
|
var fCall = this.fCall;
|
||||||
var regPSClear, nFaultError, regSP;
|
var regPSClear, regSP;
|
||||||
var rpl = sel & X86.SEL.RPL;
|
var rpl = sel & X86.SEL.RPL;
|
||||||
var dpl = (acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT;
|
var dpl = (acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT;
|
||||||
|
|
||||||
if (selMasked && !(acc & X86.DESC.ACC.PRESENT)) {
|
if (selMasked && !(acc & X86.DESC.ACC.PRESENT)) {
|
||||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel);
|
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel & X86.ERRCODE.SELMASK);
|
||||||
base = addrDesc = X86.ADDR_INVALID;
|
base = addrDesc = X86.ADDR_INVALID;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -632,19 +633,16 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
||||||
else if (type == X86.DESC.ACC.TYPE.GATE_CALL || type == X86.DESC.ACC.TYPE.GATE386_CALL) {
|
else if (type == X86.DESC.ACC.TYPE.GATE_CALL || type == X86.DESC.ACC.TYPE.GATE386_CALL) {
|
||||||
fGate = true;
|
fGate = true;
|
||||||
regPSClear = 0;
|
regPSClear = 0;
|
||||||
nFaultError = sel;
|
|
||||||
if (rpl < this.cpl) rpl = this.cpl; // set RPL to max(RPL,CPL) for call gates
|
if (rpl < this.cpl) rpl = this.cpl; // set RPL to max(RPL,CPL) for call gates
|
||||||
}
|
}
|
||||||
else if (type == X86.DESC.ACC.TYPE.GATE286_INT || type == X86.DESC.ACC.TYPE.GATE386_INT) {
|
else if (type == X86.DESC.ACC.TYPE.GATE286_INT || type == X86.DESC.ACC.TYPE.GATE386_INT) {
|
||||||
fGate = true;
|
fGate = true;
|
||||||
regPSClear = (X86.PS.NT | X86.PS.TF | X86.PS.IF);
|
regPSClear = (X86.PS.NT | X86.PS.TF | X86.PS.IF);
|
||||||
nFaultError = sel | X86.ERRCODE.EXT;
|
|
||||||
cpu.assert(!(acc & 0x1f));
|
cpu.assert(!(acc & 0x1f));
|
||||||
}
|
}
|
||||||
else if (type == X86.DESC.ACC.TYPE.GATE286_TRAP || type == X86.DESC.ACC.TYPE.GATE386_TRAP) {
|
else if (type == X86.DESC.ACC.TYPE.GATE286_TRAP || type == X86.DESC.ACC.TYPE.GATE386_TRAP) {
|
||||||
fGate = true;
|
fGate = true;
|
||||||
regPSClear = (X86.PS.NT | X86.PS.TF);
|
regPSClear = (X86.PS.NT | X86.PS.TF);
|
||||||
nFaultError = sel | X86.ERRCODE.EXT;
|
|
||||||
cpu.assert(!(acc & 0x1f));
|
cpu.assert(!(acc & 0x1f));
|
||||||
}
|
}
|
||||||
else if (type == X86.DESC.ACC.TYPE.GATE_TASK) {
|
else if (type == X86.DESC.ACC.TYPE.GATE_TASK) {
|
||||||
|
|
@ -722,6 +720,10 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
||||||
cpu.setSS(cpu.getShort(addrTSS + offSS), true);
|
cpu.setSS(cpu.getShort(addrTSS + offSS), true);
|
||||||
cpu.setSP(cpu.getLong(addrTSS + offSP));
|
cpu.setSP(cpu.getLong(addrTSS + offSP));
|
||||||
if (regPS & X86.PS.VM) {
|
if (regPS & X86.PS.VM) {
|
||||||
|
/*
|
||||||
|
* segFS amd segGS exist only on 80386 machines
|
||||||
|
*/
|
||||||
|
cpu.assert(I386 && cpu.model >= X86.MODEL_80386);
|
||||||
cpu.pushWord(cpu.segGS.sel);
|
cpu.pushWord(cpu.segGS.sel);
|
||||||
cpu.setGS(0);
|
cpu.setGS(0);
|
||||||
cpu.pushWord(cpu.segFS.sel);
|
cpu.pushWord(cpu.segFS.sel);
|
||||||
|
|
@ -739,14 +741,10 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
||||||
}
|
}
|
||||||
return this.base;
|
return this.base;
|
||||||
}
|
}
|
||||||
cpu.assert(false);
|
|
||||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, nFaultError, true);
|
|
||||||
base = addrDesc = X86.ADDR_INVALID;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
else if (fGate !== false) {
|
if (fGate !== false) {
|
||||||
cpu.assert(false);
|
cpu.assert(false);
|
||||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
|
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, (sel & X86.ERRCODE.SELMASK) | (fIDT? X86.ERRCODE.IDT : 0), true);
|
||||||
base = addrDesc = X86.ADDR_INVALID;
|
base = addrDesc = X86.ADDR_INVALID;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -754,7 +752,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
||||||
else if (this.id == X86Seg.ID.DATA) {
|
else if (this.id == X86Seg.ID.DATA) {
|
||||||
if (selMasked) {
|
if (selMasked) {
|
||||||
if (!(acc & X86.DESC.ACC.PRESENT)) {
|
if (!(acc & X86.DESC.ACC.PRESENT)) {
|
||||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel);
|
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel & X86.ERRCODE.SELMASK);
|
||||||
base = addrDesc = X86.ADDR_INVALID;
|
base = addrDesc = X86.ADDR_INVALID;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -780,7 +778,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
||||||
*
|
*
|
||||||
* So, if the ACC field is zero, we won't set the last fnFault() parameter (fHalt) to true.
|
* So, if the ACC field is zero, we won't set the last fnFault() parameter (fHalt) to true.
|
||||||
*/
|
*/
|
||||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, !!acc);
|
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel & X86.ERRCODE.SELMASK, !!acc);
|
||||||
base = addrDesc = X86.ADDR_INVALID;
|
base = addrDesc = X86.ADDR_INVALID;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -788,12 +786,12 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
||||||
}
|
}
|
||||||
else if (this.id == X86Seg.ID.STACK) {
|
else if (this.id == X86Seg.ID.STACK) {
|
||||||
if (!(acc & X86.DESC.ACC.PRESENT)) {
|
if (!(acc & X86.DESC.ACC.PRESENT)) {
|
||||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.SS_FAULT, sel);
|
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.SS_FAULT, sel & X86.ERRCODE.SELMASK);
|
||||||
base = addrDesc = X86.ADDR_INVALID;
|
base = addrDesc = X86.ADDR_INVALID;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!selMasked || type < X86.DESC.ACC.TYPE.SEG || (type & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.WRITABLE)) != X86.DESC.ACC.TYPE.WRITABLE) {
|
if (!selMasked || type < X86.DESC.ACC.TYPE.SEG || (type & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.WRITABLE)) != X86.DESC.ACC.TYPE.WRITABLE) {
|
||||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
|
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel & X86.ERRCODE.SELMASK, true);
|
||||||
base = addrDesc = X86.ADDR_INVALID;
|
base = addrDesc = X86.ADDR_INVALID;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -801,7 +799,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
||||||
else if (this.id == X86Seg.ID.TSS) {
|
else if (this.id == X86Seg.ID.TSS) {
|
||||||
var typeTSS = type & ~X86.DESC.ACC.TSS_BUSY;
|
var typeTSS = type & ~X86.DESC.ACC.TSS_BUSY;
|
||||||
if (!selMasked || typeTSS != X86.DESC.ACC.TYPE.TSS286 && typeTSS != X86.DESC.ACC.TYPE.TSS386) {
|
if (!selMasked || typeTSS != X86.DESC.ACC.TYPE.TSS286 && typeTSS != X86.DESC.ACC.TYPE.TSS386) {
|
||||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
|
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel & X86.ERRCODE.SELMASK, true);
|
||||||
base = addrDesc = X86.ADDR_INVALID;
|
base = addrDesc = X86.ADDR_INVALID;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -885,7 +883,7 @@ X86Seg.prototype.switchTSS = function switchTSS(selNew, fNest)
|
||||||
* TODO: Verify that it is (always) correct to require that the BUSY bit be currently set.
|
* TODO: Verify that it is (always) correct to require that the BUSY bit be currently set.
|
||||||
*/
|
*/
|
||||||
if (!(cpu.segTSS.type & X86.DESC.ACC.TSS_BUSY)) {
|
if (!(cpu.segTSS.type & X86.DESC.ACC.TSS_BUSY)) {
|
||||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, selNew, true);
|
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, selNew & X86.ERRCODE.SELMASK, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
|
@ -905,7 +903,7 @@ X86Seg.prototype.switchTSS = function switchTSS(selNew, fNest)
|
||||||
|
|
||||||
if (fNest !== false) {
|
if (fNest !== false) {
|
||||||
if (cpu.segTSS.type & X86.DESC.ACC.TSS_BUSY) {
|
if (cpu.segTSS.type & X86.DESC.ACC.TSS_BUSY) {
|
||||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, selNew, true);
|
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, selNew & X86.ERRCODE.SELMASK, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
cpu.setShort(cpu.segTSS.addrDesc + X86.DESC.ACC.OFFSET, cpu.segTSS.acc |= X86.DESC.ACC.TSS_BUSY);
|
cpu.setShort(cpu.segTSS.addrDesc + X86.DESC.ACC.OFFSET, cpu.segTSS.acc |= X86.DESC.ACC.TSS_BUSY);
|
||||||
|
|
@ -979,8 +977,14 @@ X86Seg.prototype.switchTSS = function switchTSS(selNew, fNest)
|
||||||
cpu.setLong(addrOld + X86.TSS386.TASK_CS, cpu.segCS.sel);
|
cpu.setLong(addrOld + X86.TSS386.TASK_CS, cpu.segCS.sel);
|
||||||
cpu.setLong(addrOld + X86.TSS386.TASK_SS, cpu.segSS.sel);
|
cpu.setLong(addrOld + X86.TSS386.TASK_SS, cpu.segSS.sel);
|
||||||
cpu.setLong(addrOld + X86.TSS386.TASK_DS, cpu.segDS.sel);
|
cpu.setLong(addrOld + X86.TSS386.TASK_DS, cpu.segDS.sel);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* segFS amd segGS exist only on 80386 machines
|
||||||
|
*/
|
||||||
|
cpu.assert(I386 && cpu.model >= X86.MODEL_80386);
|
||||||
cpu.setLong(addrOld + X86.TSS386.TASK_FS, cpu.segFS.sel);
|
cpu.setLong(addrOld + X86.TSS386.TASK_FS, cpu.segFS.sel);
|
||||||
cpu.setLong(addrOld + X86.TSS386.TASK_GS, cpu.segGS.sel);
|
cpu.setLong(addrOld + X86.TSS386.TASK_GS, cpu.segGS.sel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reload all registers from the new TSS; it's important to reload the LDTR sooner
|
* Reload all registers from the new TSS; it's important to reload the LDTR sooner
|
||||||
* rather than later, so that as segment registers are reloaded, any LDT selectors will
|
* rather than later, so that as segment registers are reloaded, any LDT selectors will
|
||||||
|
|
@ -999,8 +1003,14 @@ X86Seg.prototype.switchTSS = function switchTSS(selNew, fNest)
|
||||||
cpu.regEDI = cpu.getLong(addrNew + X86.TSS386.TASK_EDI);
|
cpu.regEDI = cpu.getLong(addrNew + X86.TSS386.TASK_EDI);
|
||||||
cpu.segES.load(cpu.getShort(addrNew + X86.TSS386.TASK_ES));
|
cpu.segES.load(cpu.getShort(addrNew + X86.TSS386.TASK_ES));
|
||||||
cpu.segDS.load(cpu.getShort(addrNew + X86.TSS386.TASK_DS));
|
cpu.segDS.load(cpu.getShort(addrNew + X86.TSS386.TASK_DS));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* segFS amd segGS exist only on 80386 machines
|
||||||
|
*/
|
||||||
|
cpu.assert(I386 && cpu.model >= X86.MODEL_80386);
|
||||||
cpu.segFS.load(cpu.getShort(addrNew + X86.TSS386.TASK_FS));
|
cpu.segFS.load(cpu.getShort(addrNew + X86.TSS386.TASK_FS));
|
||||||
cpu.segGS.load(cpu.getShort(addrNew + X86.TSS386.TASK_GS));
|
cpu.segGS.load(cpu.getShort(addrNew + X86.TSS386.TASK_GS));
|
||||||
|
|
||||||
cpu.setCSIP(cpu.getLong(addrNew + X86.TSS386.TASK_EIP), cpu.getShort(addrNew + X86.TSS386.TASK_CS));
|
cpu.setCSIP(cpu.getLong(addrNew + X86.TSS386.TASK_EIP), cpu.getShort(addrNew + X86.TSS386.TASK_CS));
|
||||||
offSS = X86.TSS386.TASK_SS;
|
offSS = X86.TSS386.TASK_SS;
|
||||||
offSP = X86.TSS386.TASK_ESP;
|
offSP = X86.TSS386.TASK_ESP;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue