Phase 1 of consolidating descriptor processing in x86seg.js (TSS descriptors are next)
This commit is contained in:
parent
38c2928882
commit
6194aac5af
4 changed files with 132 additions and 45 deletions
|
|
@ -2216,6 +2216,11 @@ if (DEBUGGER) {
|
|||
*/
|
||||
if (DEBUG) {
|
||||
this.assert(!(this.cpu.regAX & ~0xffff) && !(this.cpu.regBX & ~0xffff) && !(this.cpu.regCX & ~0xffff) && !(this.cpu.regDX & ~0xffff), "register out of bounds");
|
||||
/*
|
||||
if (!fSkipBP && this.cInstructions == 11303367) {
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
if (!fSkipBP && MAXDEBUG) {
|
||||
if (!this.cpu.regIP) {
|
||||
this.println("suspicious IP");
|
||||
|
|
@ -4728,6 +4733,9 @@ if (DEBUGGER) {
|
|||
if (!sCmd) {
|
||||
sCmd = this.aPrevCmds[this.iPrevCmd+1];
|
||||
} else {
|
||||
if (this.iPrevCmd < 0 && this.aPrevCmds.length) {
|
||||
this.iPrevCmd = 0;
|
||||
}
|
||||
if (this.iPrevCmd < 0 || sCmd != this.aPrevCmds[this.iPrevCmd]) {
|
||||
this.aPrevCmds.splice(0, 0, sCmd);
|
||||
this.iPrevCmd = 0;
|
||||
|
|
|
|||
|
|
@ -1308,7 +1308,7 @@ X86CPU.prototype.setIP = function(off)
|
|||
* @param {number} off
|
||||
* @param {number} sel
|
||||
* @param {boolean} [fCall] is true if CALLF in progress, false if RETF in progress, null/undefined otherwise
|
||||
* @return {boolean} true if a stack switch occurred; the only opcode that needs to care about this is opRETFn()
|
||||
* @return {boolean|null} true if a stack switch occurred; the only opcode that really needs to care is opRETFn()
|
||||
*/
|
||||
X86CPU.prototype.setCSIP = function(off, sel, fCall)
|
||||
{
|
||||
|
|
@ -1316,13 +1316,14 @@ X86CPU.prototype.setCSIP = function(off, sel, fCall)
|
|||
this.segCS.fCall = fCall;
|
||||
/*
|
||||
* We break this operation into the following discrete steps (eg, set IP, load CS, and then update EIP)
|
||||
* so that segCS.load(sel) has the option of modifying IP when sel refers to a call gate.
|
||||
* so that segCS.load(sel) has the option of modifying IP when sel refers to a gate (call, interrupt, trap, etc).
|
||||
*/
|
||||
this.regIP = off;
|
||||
var base = this.segCS.load(sel);
|
||||
if (base != null) {
|
||||
this.regEIP = base + this.regIP;
|
||||
if (base == null) {
|
||||
return null;
|
||||
}
|
||||
this.regEIP = base + this.regIP;
|
||||
if (PREFETCH) this.flushPrefetch(this.regEIP);
|
||||
return this.segCS.fStackSwitch;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -448,9 +448,10 @@ var X86Help = {
|
|||
opHelpCallF: function(off, sel) {
|
||||
var regCS = this.segCS.sel;
|
||||
var regIP = this.regIP;
|
||||
this.setCSIP(off, sel, true);
|
||||
this.pushWord(regCS);
|
||||
this.pushWord(regIP);
|
||||
if (this.setCSIP(off, sel, true) != null) {
|
||||
this.pushWord(regCS);
|
||||
this.pushWord(regIP);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* opHelpDIVOverflow()
|
||||
|
|
@ -474,10 +475,22 @@ var X86Help = {
|
|||
*/
|
||||
opHelpINT: function(nIDT, nError, nCycles) {
|
||||
/*
|
||||
* TODO: We assess the cycle cost up front, because otherwise, if opHelpLoadIDT() fails and we end up in
|
||||
* opHelpFault(), no cost may be assessed. Ultimately, opHelpFault() needs to determine an appropriate cost.
|
||||
* TODO: We assess the cycle cost up front, because otherwise, if loadIDT() fails, no cost may be assessed.
|
||||
*/
|
||||
this.nStepCycles -= this.CYCLES.nOpCyclesInt + nCycles;
|
||||
var regPS = this.getPS();
|
||||
var regCS = this.segCS.sel;
|
||||
var regIP = this.regIP;
|
||||
var base = this.segCS.loadIDT(nIDT);
|
||||
if (base != null) {
|
||||
this.regEIP = base + this.regIP;
|
||||
this.pushWord(regPS);
|
||||
this.pushWord(regCS);
|
||||
this.pushWord(regIP);
|
||||
if (nError != null) this.pushWord(nError);
|
||||
this.nFault = -1;
|
||||
}
|
||||
/*
|
||||
if (X86Help.opHelpLoadIDT.call(this, nIDT)) {
|
||||
if (this.descIDT.maskPS) {
|
||||
X86Help.opHelpPushPS.call(this, nError);
|
||||
|
|
@ -487,6 +500,7 @@ var X86Help = {
|
|||
return;
|
||||
}
|
||||
X86Help.opHelpFault.call(this, X86.EXCEPTION.GP_FAULT, (nIDT << 3) | X86.ERRCODE.IDT | X86.ERRCODE.EXT, true);
|
||||
*/
|
||||
},
|
||||
/**
|
||||
* opHelpIRET()
|
||||
|
|
@ -506,9 +520,13 @@ var X86Help = {
|
|||
return;
|
||||
}
|
||||
}
|
||||
this.setCSIP(this.popWord(), this.popWord(), false);
|
||||
this.setPS(this.popWord());
|
||||
if (this.cIntReturn) this.checkIntReturn(this.regEIP);
|
||||
var regIP = this.popWord();
|
||||
var regCS = this.popWord();
|
||||
var regPS = this.popWord();
|
||||
if (this.setCSIP(regIP, regCS, false) != null) {
|
||||
this.setPS(regPS);
|
||||
if (this.cIntReturn) this.checkIntReturn(this.regEIP);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* opHelpLoadIDT(nIDT)
|
||||
|
|
@ -568,30 +586,6 @@ var X86Help = {
|
|||
this.descIDT.maskPS = ~(X86.PS.TF | X86.PS.IF);
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
* opHelpPushPS(nError)
|
||||
*
|
||||
* 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) {
|
||||
var regPS = this.getPS();
|
||||
var regCS = this.segCS.sel;
|
||||
var regIP = this.regIP;
|
||||
this.regPS &= this.descIDT.maskPS;
|
||||
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.nFault = -1;
|
||||
},
|
||||
/**
|
||||
* opHelpSwitchTSS(selNew, fNest)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -115,8 +115,6 @@ X86Seg.ID = {
|
|||
X86Seg.loadReal = function loadReal(sel, fSuppress)
|
||||
{
|
||||
this.sel = sel;
|
||||
this.limit = 0xffff;
|
||||
this.cpl = this.dpl = 0;
|
||||
return this.base = sel << 4;
|
||||
};
|
||||
|
||||
|
|
@ -126,7 +124,7 @@ X86Seg.loadReal = function loadReal(sel, fSuppress)
|
|||
* This replaces the segment's default load() function whenever the segment is notified via updateAccess() by the
|
||||
* CPU's setProtMode() that the processor is now in protected-mode.
|
||||
*
|
||||
* Segments in protected-mode are referenced by selectors, which are indexes into descriptor tables (GDT, LDT, IDT)
|
||||
* Segments in protected-mode are referenced by selectors, which are indexes into descriptor tables (GDT or LDT)
|
||||
* whose descriptors are 4-word (8-byte) entries:
|
||||
*
|
||||
* word 0: segment limit (0-15)
|
||||
|
|
@ -136,6 +134,8 @@ X86Seg.loadReal = function loadReal(sel, fSuppress)
|
|||
*
|
||||
* See X86.DESC for offset and bit definitions.
|
||||
*
|
||||
* IDT descriptor entries are handled separately by loadIDT().
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} sel
|
||||
* @param {boolean} [fSuppress] is true to suppress any errors, cycle assessment, etc
|
||||
|
|
@ -168,6 +168,52 @@ X86Seg.loadProt = function loadProt(sel, fSuppress)
|
|||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* loadRealIDT(nIDT)
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} nIDT
|
||||
* @return {number|null} base address of selected segment, or null if error
|
||||
*/
|
||||
X86Seg.loadRealIDT = function loadRealIDT(nIDT)
|
||||
{
|
||||
if (DEBUG) {
|
||||
this.cpu.assert(nIDT >= 0 && nIDT < 256 && !this.cpu.addrIDT && this.cpu.addrIDTLimit == 0x03FF);
|
||||
}
|
||||
/*
|
||||
* Intel documentation for INT/INTO under "REAL ADDRESS MODE EXCEPTIONS" says:
|
||||
*
|
||||
* "[T]he 80286 will shut down if the SP = 1, 3, or 5 before executing the INT or INTO instruction--due to lack of stack space"
|
||||
*
|
||||
* TODO: Verify that 80286 real-mode actually enforces the above. See http://localhost:8088/pubs/pc/reference/intel/80286/progref/#page-260
|
||||
*/
|
||||
var offIDT = this.cpu.addrIDT + (nIDT << 2);
|
||||
this.cpu.regIP = this.cpu.getWord(offIDT);
|
||||
this.sel = this.cpu.getWord(offIDT + 2);
|
||||
this.cpu.regPS &= ~(X86.PS.TF | X86.PS.IF);
|
||||
return this.base = this.sel << 4;
|
||||
};
|
||||
|
||||
/**
|
||||
* loadProtIDT(nIDT)
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} nIDT
|
||||
* @return {number|null} base address of selected segment, or null if error
|
||||
*/
|
||||
X86Seg.loadProtIDT = function loadProtIDT(nIDT)
|
||||
{
|
||||
if (DEBUG) this.cpu.assert(nIDT >= 0 && nIDT < 256);
|
||||
|
||||
nIDT <<= 3;
|
||||
var addrDesc = this.cpu.addrIDT + nIDT;
|
||||
if (addrDesc + 7 <= this.cpu.addrIDTLimit) {
|
||||
return this.loadDesc8(nIDT, addrDesc);
|
||||
}
|
||||
X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, nIDT | X86.ERRCODE.IDT | X86.ERRCODE.EXT, true);
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* checkReadReal(off, cb, fSuppress)
|
||||
*
|
||||
|
|
@ -367,6 +413,9 @@ X86Seg.prototype.loadDesc8 = function(sel, addrDesc)
|
|||
var selMasked = sel & X86.SEL.MASK;
|
||||
|
||||
while (true) {
|
||||
|
||||
var cplPrev, addrTSS, offSP, offSS, regSPPrev, regSSPrev;
|
||||
|
||||
if (this.id == X86Seg.ID.CODE) {
|
||||
this.fStackSwitch = false;
|
||||
var fCall = this.fCall;
|
||||
|
|
@ -379,7 +428,7 @@ X86Seg.prototype.loadDesc8 = function(sel, addrDesc)
|
|||
*/
|
||||
if (rpl < this.cpl) rpl = this.cpl;
|
||||
if (rpl <= dpl) {
|
||||
var cplPrev = this.cpl;
|
||||
cplPrev = this.cpl;
|
||||
if (this.load(base & 0xffff, true) != null) {
|
||||
this.cpu.regIP = limit;
|
||||
if (this.cpl < cplPrev) {
|
||||
|
|
@ -393,11 +442,11 @@ X86Seg.prototype.loadDesc8 = function(sel, addrDesc)
|
|||
this.awScratch[i++] = this.cpu.getSOWord(this.cpu.segSS, regSP);
|
||||
regSP += 2;
|
||||
}
|
||||
var addrTSS = this.cpu.segTSS.base;
|
||||
var offSP = (this.cpl << 2) + X86.TSS.CPL0_SP;
|
||||
var offSS = offSP + 2;
|
||||
var regSPPrev = this.cpu.regSP;
|
||||
var regSSPrev = this.cpu.segSS.sel;
|
||||
addrTSS = this.cpu.segTSS.base;
|
||||
offSP = (this.cpl << 2) + X86.TSS.CPL0_SP;
|
||||
offSS = offSP + 2;
|
||||
regSPPrev = this.cpu.regSP;
|
||||
regSSPrev = this.cpu.segSS.sel;
|
||||
this.cpu.regSP = this.cpu.getWord(addrTSS + offSP);
|
||||
this.cpu.segSS.load(this.cpu.getWord(addrTSS + offSS));
|
||||
this.cpu.pushWord(regSSPrev);
|
||||
|
|
@ -409,6 +458,38 @@ X86Seg.prototype.loadDesc8 = function(sel, addrDesc)
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (type == X86.DESC.ACC.TYPE.GATE_INT || type == X86.DESC.ACC.TYPE.GATE_TRAP) {
|
||||
if (rpl < this.cpl) rpl = this.cpl;
|
||||
if (rpl <= dpl) {
|
||||
cplPrev = this.cpl;
|
||||
if (this.load(base & 0xffff, true) != null) {
|
||||
this.cpu.regIP = limit;
|
||||
if (this.cpl < cplPrev) {
|
||||
if (fCall !== true) {
|
||||
base = null;
|
||||
break;
|
||||
}
|
||||
regSP = this.cpu.regSP;
|
||||
addrTSS = this.cpu.segTSS.base;
|
||||
offSP = (this.cpl << 2) + X86.TSS.CPL0_SP;
|
||||
offSS = offSP + 2;
|
||||
regSPPrev = this.cpu.regSP;
|
||||
regSSPrev = this.cpu.segSS.sel;
|
||||
this.cpu.regSP = this.cpu.getWord(addrTSS + offSP);
|
||||
this.cpu.segSS.load(this.cpu.getWord(addrTSS + offSS));
|
||||
this.cpu.pushWord(regSSPrev);
|
||||
this.cpu.pushWord(regSPPrev);
|
||||
this.fStackSwitch = true;
|
||||
}
|
||||
if (type == X86.DESC.ACC.TYPE.GATE_INT) {
|
||||
this.cpu.regPS &= ~(X86.PS.NT | X86.PS.TF | X86.PS.IF);
|
||||
} else {
|
||||
this.cpu.regPS &= ~(X86.PS.NT | X86.PS.TF);
|
||||
}
|
||||
return this.base;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type >= X86.DESC.ACC.TYPE.CODE_EXECONLY /* || dpl > this.cpu.segCS.cpl */) {
|
||||
rpl = sel & X86.SEL.RPL;
|
||||
if (rpl > this.cpl) {
|
||||
|
|
@ -547,6 +628,7 @@ X86Seg.prototype.updateAccess = function(fProt)
|
|||
}
|
||||
if (fProt) {
|
||||
this.load = X86Seg.loadProt;
|
||||
this.loadIDT = X86Seg.loadProtIDT;
|
||||
this.checkRead = X86Seg.checkReadProt;
|
||||
this.checkWrite = X86Seg.checkWriteProt;
|
||||
if (this.acc & X86.DESC.ACC.TYPE.SEG) {
|
||||
|
|
@ -574,8 +656,10 @@ X86Seg.prototype.updateAccess = function(fProt)
|
|||
this.dpl = (this.acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT;
|
||||
} else {
|
||||
this.load = X86Seg.loadReal;
|
||||
this.loadIDT = X86Seg.loadRealIDT;
|
||||
this.checkRead = X86Seg.checkReadReal;
|
||||
this.checkWrite = X86Seg.checkWriteReal;
|
||||
this.limit = 0xffff;
|
||||
this.cpl = this.dpl = 0;
|
||||
this.addrDesc = null;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue