v1.19.5: Finally bit the bullet and throw JavaScript exceptions for improved CPU exception handling
This commit is contained in:
parent
d33e0da167
commit
e207cabc0a
9 changed files with 991 additions and 915 deletions
|
|
@ -1010,7 +1010,13 @@ CPU.prototype.runCPU = function(fOnClick)
|
|||
* nCyclesPerBurst is how many cycles we WANT to run on each iteration of stepCPU(), but it may run
|
||||
* significantly less (or slightly more, since we can't execute partial instructions).
|
||||
*/
|
||||
this.stepCPU(nCyclesPerBurst);
|
||||
try {
|
||||
this.stepCPU(nCyclesPerBurst);
|
||||
}
|
||||
catch(exception) {
|
||||
if (typeof exception != "number") throw exception;
|
||||
if (MAXDEBUG) this.println("CPU exception " + str.toHexByte(exception));
|
||||
}
|
||||
|
||||
/*
|
||||
* nBurstCycles, less any remaining nStepCycles, is how many cycles stepCPU() ACTUALLY ran (nCycles).
|
||||
|
|
|
|||
|
|
@ -3370,9 +3370,12 @@ if (DEBUGGER) {
|
|||
this.cOpcodes++;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
this.nCycles = 0;
|
||||
this.cpu.setError(e.stack || e.message);
|
||||
catch(exception) {
|
||||
if (typeof exception != "number") {
|
||||
var e = exception;
|
||||
this.nCycles = 0;
|
||||
this.cpu.setError(e.stack || e.message);
|
||||
}
|
||||
}
|
||||
} while (this.cpu.opFlags & X86.OPFLAG_PREFIXES);
|
||||
|
||||
|
|
|
|||
|
|
@ -2362,18 +2362,6 @@ X86CPU.prototype.advanceIP = function(inc)
|
|||
{
|
||||
// DEBUG: this.assert(inc > 0);
|
||||
|
||||
/*
|
||||
* TODO: This is a crude work-around to deal with certain instructions that CMP, MOV, etc,
|
||||
* immediate operands to/from memory, because if the memory access triggered a fault, we can't
|
||||
* permit the rest of the instruction decoding to modify IP, because that'll just screw up
|
||||
* the fault dispatch.
|
||||
*
|
||||
* Ultimately, I'm probably going to have to bite the bullet and throw real JavaScript exceptions
|
||||
* to halt instructions in their tracks, but I'm going to stick with this more limited strategy
|
||||
* for now.
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
this.regLIP = (this.regLIP + inc)|0;
|
||||
/*
|
||||
* Properly comparing regLIP to regLIPLimit would normally require coercing both to unsigned
|
||||
|
|
|
|||
|
|
@ -3964,27 +3964,25 @@ X86.fnFault = function(nFault, nError, fHalt, nCycles)
|
|||
this.opLIP = this.regLIP;
|
||||
|
||||
/*
|
||||
* 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 general-purpose mechanism for snapping
|
||||
* and restoring all registers for any instruction that might fault, so it's every opcode for themselves.
|
||||
* 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 general
|
||||
* mechanism for snapping and restoring all registers for any instruction that might fault.
|
||||
*
|
||||
* 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
|
||||
* from executing (and therefore doesn't need to be restarted).
|
||||
*
|
||||
* 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.
|
||||
* 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 from executing (and therefore doesn't need to be restarted).
|
||||
*/
|
||||
if (nFault == X86.EXCEPTION.DEBUG) {
|
||||
this.opFlags |= X86.OPFLAG.DEBUG;
|
||||
} else if (nFault >= 0) {
|
||||
} else {
|
||||
this.assert(nFault >= 0);
|
||||
this.opFlags |= X86.OPFLAG.FAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since this fault is likely being issued in the context of an instruction that hasn't finished
|
||||
* executing, and since we currently don't do anything to interrupt that execution (eg, throw a
|
||||
* JavaScript exception), we should shut off all further reads/writes for the current instruction.
|
||||
* executing, if we don't do anything to interrupt that execution (eg, throw a JavaScript exception),
|
||||
* then we would need to shut off all further reads/writes for the current instruction.
|
||||
*
|
||||
* That's easy for any EA-based memory accesses: simply set both the NOREAD and NOWRITE flags.
|
||||
* However, there are also direct, non-EA-based memory accesses to consider. A perfect example is
|
||||
|
|
@ -3992,9 +3990,12 @@ X86.fnFault = function(nFault, nError, fHalt, nCycles)
|
|||
* cause another fault, which we will misinterpret as a double-fault -- unless the handler for
|
||||
* such an opcode checks this.opFlags for X86.OPFLAG.FAULT after each step of the operation.
|
||||
*
|
||||
* TODO: Throw a special JavaScript exception that cpu.js must intercept and quietly redirect.
|
||||
* this.opFlags |= (X86.OPFLAG.NOREAD | X86.OPFLAG.NOWRITE);
|
||||
*
|
||||
* Fortunately, we now throw an exception that terminates the current instruction, so the above hack
|
||||
* should no longer be necessary.
|
||||
*/
|
||||
this.opFlags |= (X86.OPFLAG.NOREAD | X86.OPFLAG.NOWRITE);
|
||||
throw nFault;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1538,7 +1538,12 @@ X86.opINSb = function INSb()
|
|||
if (!this.checkIOPM(port, 1, true)) return;
|
||||
var b = this.bus.checkPortInputNotify(port, 1, this.regLIP - nDelta - 1);
|
||||
this.setSOByte(this.segES, this.regEDI & maskAddr, b);
|
||||
|
||||
/*
|
||||
* TODO: Remove this once we've done enough testing of fnFault() throwing exceptions
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
if (BACKTRACK) this.backTrack.btiMem0 = this.backTrack.btiIO;
|
||||
this.regEDI = (this.regEDI & ~maskAddr) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -1 : 1)) & maskAddr);
|
||||
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
|
||||
|
|
@ -1592,7 +1597,12 @@ X86.opINSw = function INSw()
|
|||
this.backTrack.btiMem1 = this.backTrack.btiIO;
|
||||
}
|
||||
this.setSOWord(this.segES, this.regEDI & maskAddr, w);
|
||||
|
||||
/*
|
||||
* TODO: Remove this once we've done enough testing of fnFault() throwing exceptions
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
this.regEDI = (this.regEDI & ~maskAddr) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -this.sizeData : this.sizeData)) & maskAddr);
|
||||
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
|
||||
this.nStepCycles -= nCycles;
|
||||
|
|
@ -1639,7 +1649,12 @@ X86.opOUTSb = function OUTSb()
|
|||
var port = this.regEDX & 0xffff;
|
||||
if (!this.checkIOPM(port, 1, false)) return;
|
||||
var b = this.getSOByte(this.segDS, this.regESI & maskAddr);
|
||||
|
||||
/*
|
||||
* TODO: Remove this once we've done enough testing of fnFault() throwing exceptions
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
if (BACKTRACK) this.backTrack.btiIO = this.backTrack.btiMem0;
|
||||
this.bus.checkPortOutputNotify(port, 1, b, this.regLIP - nDelta - 1);
|
||||
this.regESI = (this.regESI & ~maskAddr) | ((this.regESI + ((this.regPS & X86.PS.DF)? -1 : 1)) & maskAddr);
|
||||
|
|
@ -1686,7 +1701,12 @@ X86.opOUTSw = function OUTSw()
|
|||
}
|
||||
if (nReps--) {
|
||||
var w = this.getSOWord(this.segDS, this.regESI & maskAddr);
|
||||
|
||||
/*
|
||||
* TODO: Remove this once we've done enough testing of fnFault() throwing exceptions
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
var port = this.regEDX & 0xffff;
|
||||
if (!this.checkIOPM(port, this.sizeData, false)) return;
|
||||
if (BACKTRACK) {
|
||||
|
|
@ -2185,7 +2205,7 @@ X86.opLEA = function LEA()
|
|||
*/
|
||||
X86.opMOVsrw = function MOVsrw()
|
||||
{
|
||||
var temp;
|
||||
var temp, sel;
|
||||
var bModRM = this.getIPByte();
|
||||
var reg = (bModRM & 0x38) >> 3;
|
||||
switch(reg) {
|
||||
|
|
@ -2227,44 +2247,52 @@ X86.opMOVsrw = function MOVsrw()
|
|||
this.aOpModRegWord[bModRM].call(this, X86.fnMOV);
|
||||
switch (reg) {
|
||||
case 0x0:
|
||||
this.setES(this.regEAX);
|
||||
sel = this.regEAX;
|
||||
this.regEAX = temp;
|
||||
this.setES(sel);
|
||||
break;
|
||||
case 0x1:
|
||||
this.setCS(this.regECX);
|
||||
sel = this.regECX;
|
||||
this.regECX = temp;
|
||||
this.setCS(sel);
|
||||
break;
|
||||
case 0x2:
|
||||
this.setSS(this.regEDX);
|
||||
sel = this.regEDX;
|
||||
this.regEDX = temp;
|
||||
this.setSS(sel);
|
||||
break;
|
||||
case 0x3:
|
||||
this.setDS(this.regEBX);
|
||||
sel = this.regEBX;
|
||||
this.regEBX = temp;
|
||||
this.setDS(sel);
|
||||
break;
|
||||
case 0x4:
|
||||
if (I386 && this.model >= X86.MODEL_80386) {
|
||||
this.setFS(this.getSP());
|
||||
} else {
|
||||
this.setES(this.getSP());
|
||||
}
|
||||
sel = this.getSP();
|
||||
this.setSP(temp);
|
||||
if (I386 && this.model >= X86.MODEL_80386) {
|
||||
this.setFS(sel);
|
||||
} else {
|
||||
this.setES(sel);
|
||||
}
|
||||
break;
|
||||
case 0x5:
|
||||
if (I386 && this.model >= X86.MODEL_80386) {
|
||||
this.setGS(this.regEBP);
|
||||
} else {
|
||||
this.setCS(this.regEBP);
|
||||
}
|
||||
sel = this.regEBP;
|
||||
this.regEBP = temp;
|
||||
if (I386 && this.model >= X86.MODEL_80386) {
|
||||
this.setGS(sel);
|
||||
} else {
|
||||
this.setCS(sel);
|
||||
}
|
||||
break;
|
||||
case 0x6:
|
||||
this.setSS(this.regESI);
|
||||
sel = this.regESI;
|
||||
this.regESI = temp;
|
||||
this.setSS(sel);
|
||||
break;
|
||||
case 0x7:
|
||||
this.setDS(this.regEDI);
|
||||
sel = this.regEDI;
|
||||
this.regEDI = temp;
|
||||
this.setDS(sel);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
|
@ -2666,7 +2694,12 @@ X86.opMOVSb = function MOVSb()
|
|||
}
|
||||
if (nReps--) {
|
||||
this.setSOByte(this.segES, this.regEDI & maskAddr, this.getSOByte(this.segData, this.regESI & maskAddr));
|
||||
|
||||
/*
|
||||
* TODO: Remove this once we've done enough testing of fnFault() throwing exceptions
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
var nInc = ((this.regPS & X86.PS.DF)? -1 : 1);
|
||||
this.regESI = (this.regESI & ~maskAddr) | ((this.regESI + nInc) & maskAddr);
|
||||
this.regEDI = (this.regEDI & ~maskAddr) | ((this.regEDI + nInc) & maskAddr);
|
||||
|
|
@ -2704,7 +2737,12 @@ X86.opMOVSw = function MOVSw()
|
|||
}
|
||||
if (nReps--) {
|
||||
this.setSOWord(this.segES, this.regEDI & maskAddr, this.getSOWord(this.segData, this.regESI & maskAddr));
|
||||
|
||||
/*
|
||||
* TODO: Remove this once we've done enough testing of fnFault() throwing exceptions
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
var nInc = ((this.regPS & X86.PS.DF)? -this.sizeData : this.sizeData);
|
||||
this.regESI = (this.regESI & ~maskAddr) | ((this.regESI + nInc) & maskAddr);
|
||||
this.regEDI = (this.regEDI & ~maskAddr) | ((this.regEDI + nInc) & maskAddr);
|
||||
|
|
@ -2743,7 +2781,12 @@ X86.opCMPSb = function CMPSb()
|
|||
if (nReps--) {
|
||||
var bDst = this.getEAByte(this.segData, this.regESI & maskAddr);
|
||||
var bSrc = this.modEAByte(this.segES, this.regEDI & maskAddr);
|
||||
|
||||
/*
|
||||
* TODO: Remove this once we've done enough testing of fnFault() throwing exceptions
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
X86.fnCMPb.call(this, bDst, bSrc);
|
||||
var nInc = ((this.regPS & X86.PS.DF)? -1 : 1);
|
||||
this.regESI = (this.regESI & ~maskAddr) | ((this.regESI + nInc) & maskAddr);
|
||||
|
|
@ -2791,7 +2834,12 @@ X86.opCMPSw = function CMPSw()
|
|||
if (nReps--) {
|
||||
var wDst = this.getEAWord(this.segData, this.regESI & maskAddr);
|
||||
var wSrc = this.modEAWord(this.segES, this.regEDI & maskAddr);
|
||||
|
||||
/*
|
||||
* TODO: Remove this once we've done enough testing of fnFault() throwing exceptions
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
X86.fnCMPw.call(this, wDst, wSrc);
|
||||
var nInc = ((this.regPS & X86.PS.DF)? -this.sizeData : this.sizeData);
|
||||
this.regESI = (this.regESI & ~maskAddr) | ((this.regESI + nInc) & maskAddr);
|
||||
|
|
@ -2862,7 +2910,12 @@ X86.opSTOSb = function STOSb()
|
|||
}
|
||||
if (nReps--) {
|
||||
this.setSOByte(this.segES, this.regEDI & maskAddr, this.regEAX);
|
||||
|
||||
/*
|
||||
* TODO: Remove this once we've done enough testing of fnFault() throwing exceptions
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
if (BACKTRACK) this.backTrack.btiMem0 = this.backTrack.btiAL;
|
||||
this.regEDI = (this.regEDI & ~maskAddr) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -1 : 1)) & maskAddr);
|
||||
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
|
||||
|
|
@ -2901,7 +2954,12 @@ X86.opSTOSw = function STOSw()
|
|||
}
|
||||
if (nReps--) {
|
||||
this.setSOWord(this.segES, this.regEDI & maskAddr, this.regEAX);
|
||||
|
||||
/*
|
||||
* TODO: Remove this once we've done enough testing of fnFault() throwing exceptions
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
if (BACKTRACK) {
|
||||
this.backTrack.btiMem0 = this.backTrack.btiAL; this.backTrack.btiMem1 = this.backTrack.btiAH;
|
||||
}
|
||||
|
|
@ -2940,7 +2998,12 @@ X86.opLODSb = function LODSb()
|
|||
}
|
||||
if (nReps--) {
|
||||
var b = this.getSOByte(this.segData, this.regESI & maskAddr);
|
||||
|
||||
/*
|
||||
* TODO: Remove this once we've done enough testing of fnFault() throwing exceptions
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
this.regEAX = (this.regEAX & ~0xff) | b;
|
||||
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiMem0;
|
||||
this.regESI = (this.regESI & ~maskAddr) | ((this.regESI + ((this.regPS & X86.PS.DF)? -1 : 1)) & maskAddr);
|
||||
|
|
@ -2978,7 +3041,12 @@ X86.opLODSw = function LODSw()
|
|||
}
|
||||
if (nReps--) {
|
||||
var w = this.getSOWord(this.segData, this.regESI & maskAddr);
|
||||
|
||||
/*
|
||||
* TODO: Remove this once we've done enough testing of fnFault() throwing exceptions
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
this.regEAX = (this.regEAX & ~this.maskData) | w;
|
||||
if (BACKTRACK) {
|
||||
this.backTrack.btiAL = this.backTrack.btiMem0; this.backTrack.btiAH = this.backTrack.btiMem1;
|
||||
|
|
@ -3018,7 +3086,12 @@ X86.opSCASb = function SCASb()
|
|||
}
|
||||
if (nReps--) {
|
||||
X86.fnCMPb.call(this, this.regEAX & 0xff, this.modEAByte(this.segES, this.regEDI & maskAddr));
|
||||
|
||||
/*
|
||||
* TODO: Remove this once we've done enough testing of fnFault() throwing exceptions
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
this.regEDI = (this.regEDI & ~maskAddr) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -1 : 1)) & maskAddr);
|
||||
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
|
||||
/*
|
||||
|
|
@ -3062,7 +3135,12 @@ X86.opSCASw = function SCASw()
|
|||
}
|
||||
if (nReps--) {
|
||||
X86.fnCMPw.call(this, this.regEAX & this.maskData, this.modEAWord(this.segES, this.regEDI & maskAddr));
|
||||
|
||||
/*
|
||||
* TODO: Remove this once we've done enough testing of fnFault() throwing exceptions
|
||||
*/
|
||||
if (this.opFlags & X86.OPFLAG.FAULT) return;
|
||||
|
||||
this.regEDI = (this.regEDI & ~maskAddr) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -this.sizeData : this.sizeData)) & maskAddr);
|
||||
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in a new issue