Improvements to code and stack limit checks (still not perfect, but better)

This commit is contained in:
Jeff Parsons 2015-05-21 15:45:26 -07:00 committed by jeffpar
commit cfd02bb4ae
4 changed files with 138 additions and 74 deletions

View file

@ -1821,8 +1821,8 @@ X86CPU.prototype.getCS = function()
X86CPU.prototype.setCS = function(sel)
{
var regEIP = this.getIP();
this.regLIP = this.segCS.load(sel) + regEIP;
this.regLIPLimit = this.segCS.base + this.segCS.limit;
this.regLIP = (this.segCS.load(sel) + regEIP)|0;
this.regLIPLimit = (this.segCS.base + this.segCS.limit)|0;
if (I386) this.resetSizes();
if (!BUGS_8086) this.opFlags |= this.OPFLAG_NOINTR_8086;
if (PREFETCH) this.flushPrefetch(this.regLIP);
@ -1872,12 +1872,12 @@ X86CPU.prototype.getSS = function()
X86CPU.prototype.setSS = function(sel, fInterruptable)
{
var regESP = this.getSP();
this.regLSP = this.segSS.load(sel) + regESP;
this.regLSP = (this.segSS.load(sel) + regESP)|0;
if (this.segSS.fExpDown) {
this.regLSPLimit = this.segSS.base + this.segSS.addrMask;
this.regLSPLimitLow = this.segSS.base + this.segSS.limit;
this.regLSPLimit = (this.segSS.base + this.segSS.addrMask)|0;
this.regLSPLimitLow = (this.segSS.base + this.segSS.limit)|0;
} else {
this.regLSPLimit = this.segSS.base + this.segSS.limit;
this.regLSPLimit = (this.segSS.base + this.segSS.limit)|0;
this.regLSPLimitLow = this.segSS.base;
}
if (!BUGS_8086 && !fInterruptable) this.opFlags |= X86.OPFLAG.NOINTR;
@ -1966,7 +1966,7 @@ X86CPU.prototype.setGS = function(sel)
*/
X86CPU.prototype.getIP = function()
{
return this.regLIP - this.segCS.base;
return (this.regLIP - this.segCS.base)|0;
};
/**
@ -1983,7 +1983,7 @@ X86CPU.prototype.getIP = function()
*/
X86CPU.prototype.setIP = function(off)
{
this.regLIP = this.segCS.base + (off & (I386? this.dataMask : 0xffff));
this.regLIP = (this.segCS.base + (off & (I386? this.dataMask : 0xffff)))|0;
if (PREFETCH) this.flushPrefetch(this.regLIP);
};
@ -2018,8 +2018,8 @@ X86CPU.prototype.setCSIP = function(off, sel, fCall)
this.regEIP = off;
var base = this.segCS.load(sel);
if (base !== X86.ADDR_INVALID) {
this.regLIP = base + (this.regEIP & (I386? this.dataMask : 0xffff));
this.regLIPLimit = base + this.segCS.limit;
this.regLIP = (base + (this.regEIP & (I386? this.dataMask : 0xffff)))|0;
this.regLIPLimit = (base + this.segCS.limit)|0;
if (I386) this.resetSizes();
if (PREFETCH) this.flushPrefetch(this.regLIP);
return this.segCS.fStackSwitch;
@ -2039,23 +2039,76 @@ X86CPU.prototype.setCSBase = function(addr)
{
var regIP = this.getIP();
addr = this.segCS.setBase(addr);
this.regLIP = addr + regIP;
this.regLIPLimit = addr + this.segCS.limit;
this.regLIP = (addr + regIP)|0;
this.regLIPLimit = (addr + this.segCS.limit)|0;
};
/**
* advanceIP(inc)
*
* @this {X86CPU}
* @param {number} inc (may be +/-)
* @param {number} inc (positive)
*/
X86CPU.prototype.advanceIP = function(inc)
{
this.regLIP += inc;
if (this.regLIP <= this.regLIPLimit) {
if (PREFETCH) this.advancePrefetch(inc);
} else {
this.setIP(this.regLIP - this.segCS.base);
this.assert(inc > 0);
this.regLIP = (this.regLIP + inc)|0;
/*
* Properly comparing regLIP to regLIPLimit would normally require coercing both to unsigned
* (ie, floating-point) values. But instead, we do a subtraction, (regLIPLimit - regLIP), and
* if the result is negative, we need only be concerned if the signs of both numbers are the same
* (ie, the sign of their XOR'ed union is positive).
*
* TODO: I'm combining the old 8088 address-wrap check with the new segment-limit check,
* even though the correct time to do the latter is immediately BEFORE a fetch, not AFTER; eg,
* consider the following code:
*
* AX=0100 BX=0015 CX=0080 DX=F859 SP=0A62 BP=0A98 SI=0000 DI=0000
* SS=0038[1759E0,0B5F] DS=02E8[0107A0,017F] ES=0970[009700,6949] A20=ON
* CS=02E0[010080,06FB] LD=0028[000000,0000] GD=[11AEE0,4977] ID=[120082,03FF]
* TR=0010 MS=FFF3 PS=3202 V0 D0 I1 T0 S0 Z0 A0 P0 C0
* 02E0:06F9 C20400 RET 0004
*
* After fetching the 3rd byte of the "RET 0004" opcode at CS:06FB, the CPU wants to automatically
* advance IP to 06FC, which of course, exceeds the limit, but that doesn't matter unless we actually
* fetch a byte from 06FC, which won't happen. I'm working around this for now by applying a -1
* fudge factor to the fault check.
*/
var off = (this.regLIPLimit - this.regLIP)|0;
if (off < 0 && (this.regLIPLimit ^ this.regLIP) >= 0) {
if (this.model <= X86.MODEL_8088) {
this.setIP(this.regLIP - this.segCS.base);
} else if (off < -1) { // fudge factor
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
}
}
};
/**
* rewindIP(dec)
*
* @this {X86CPU}
* @param {number} dec (negative)
*/
X86CPU.prototype.rewindIP = function(dec)
{
this.assert(dec < 0);
this.regLIP = (this.regLIP + dec)|0;
/*
* Properly comparing regLIP to regLIPLimit would normally require coercing both to unsigned
* (ie, floating-point) values. But instead, we do a subtraction, (regLIPLimit - regLIP), and
* if the result is negative, we need only be concerned if the signs of both numbers are the same
* (ie, the sign of their XOR'ed union is positive).
*/
if (((this.regLIPLimit - this.regLIP)|0) >= 0) {
if (PREFETCH) this.advancePrefetch(dec);
}
else if ((this.regLIPLimit ^ this.regLIP) >= 0) {
if (this.model <= X86.MODEL_8088) {
this.setIP(this.regLIP - this.segCS.base);
} else {
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
}
}
};
@ -2071,7 +2124,7 @@ X86CPU.prototype.getSP = function()
this.assert(!((this.regLSP - this.segSS.base) & ~this.segSS.addrMask));
return (this.regESP & ~this.segSS.addrMask) | (this.regLSP - this.segSS.base);
}
return this.regLSP - this.segSS.base;
return (this.regLSP - this.segSS.base)|0;
};
/**
@ -2084,9 +2137,9 @@ X86CPU.prototype.setSP = function(off)
{
if (I386) {
this.regESP = off;
this.regLSP = this.segSS.base + (off & this.segSS.addrMask);
this.regLSP = (this.segSS.base + (off & this.segSS.addrMask))|0;
} else {
this.regLSP = this.segSS.base + off;
this.regLSP = (this.segSS.base + off)|0;
}
};
@ -3366,9 +3419,7 @@ X86CPU.prototype.getIPByte = function()
{
var b = (PREFETCH? this.getBytePrefetch(this.regLIP) : this.getByte(this.regLIP));
if (BACKTRACK) this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMemLo);
if (++this.regLIP > this.regLIPLimit) {
this.setIP(this.regLIP - this.segCS.base);
}
this.advanceIP(1);
return b;
};
@ -3385,10 +3436,7 @@ X86CPU.prototype.getIPShort = function()
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMemLo);
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMemHi);
}
this.regLIP += 2;
if (this.regLIP > this.regLIPLimit) {
this.setIP(this.regLIP - this.segCS.base);
}
this.advanceIP(2);
return w;
};
@ -3405,10 +3453,7 @@ X86CPU.prototype.getIPLong = function()
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMemLo);
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMemHi);
}
this.regLIP += 4;
if (this.regLIP > this.regLIPLimit) {
this.setIP(this.regLIP - this.segCS.base);
}
this.advanceIP(4);
return l;
};
@ -3428,10 +3473,7 @@ X86CPU.prototype.getIPAddr = function()
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMemLo);
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMemHi);
}
this.regLIP += this.addrSize;
if (this.regLIP > this.regLIPLimit) {
this.setIP(this.regLIP - this.segCS.base);
}
this.advanceIP(this.addrSize);
return w;
};
@ -3448,10 +3490,7 @@ X86CPU.prototype.getIPWord = function()
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMemLo);
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMemHi);
}
this.regLIP += this.dataSize;
if (this.regLIP > this.regLIPLimit) {
this.setIP(this.regLIP - this.segCS.base);
}
this.advanceIP(this.dataSize);
return w;
};
@ -3465,9 +3504,7 @@ X86CPU.prototype.getIPDisp = function()
{
var w = ((PREFETCH? this.getBytePrefetch(this.regLIP) : this.getByte(this.regLIP)) << 24) >> 24;
if (BACKTRACK) this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMemLo);
if (++this.regLIP > this.regLIPLimit) {
this.setIP(this.regLIP - this.segCS.base);
}
this.advanceIP(1);
return w;
};
@ -3482,9 +3519,7 @@ X86CPU.prototype.getSIBAddr = function(mod)
{
var b = PREFETCH? this.getBytePrefetch(this.regLIP) : this.getByte(this.regLIP);
if (BACKTRACK) this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMemLo);
if (++this.regLIP > this.regLIPLimit) {
this.setIP(this.regLIP - this.segCS.base);
}
this.advanceIP(1);
return X86ModSIB.aOpModSIB[b].call(this, mod);
};
@ -3497,10 +3532,24 @@ X86CPU.prototype.getSIBAddr = function(mod)
X86CPU.prototype.popWord = function()
{
var w = this.getWord(this.regLSP);
this.regLSP += (I386? this.dataSize : 2);
if (this.regLSP > this.regLSPLimit) {
// TODO: Generate exception in protected mode
this.setSP(this.regLSP - this.segSS.base);
this.regLSP = (this.regLSP + (I386? this.dataSize : 2))|0;
/*
* Properly comparing regLSP to regLSPLimit would normally require coercing both to unsigned
* (ie, floating-point) values. But instead, we do a subtraction, (regLSPLimit - regLSP), and
* if the result is negative, we need only be concerned if the signs of both numbers are the same
* (ie, the sign of their XOR'ed union is positive).
*
* TODO: I'm combining the old 8088 address-wrap check with the new segment-limit check,
* even though the correct time to do the latter is immediately BEFORE the fetch, not AFTER;
* I'm working around this for now by applying a -1 fudge factor to the fault check.
*/
var off = ((this.regLSPLimit - this.regLSP)|0);
if (off < 0 && (this.regLSPLimit ^ this.regLSP) >= 0) {
if (this.model <= X86.MODEL_8088) {
this.setSP(this.regLSP - this.segSS.base);
} else if (off < -1) { // fudge factor
X86.fnFault.call(this, X86.EXCEPTION.SS_FAULT, 0);
}
}
return w;
};
@ -3514,10 +3563,19 @@ X86CPU.prototype.popWord = function()
X86CPU.prototype.pushWord = function(w)
{
this.assert((w & this.dataMask) == w);
this.regLSP -= (I386? this.dataSize : 2);
if (this.regLSP < this.regLSPLimitLow) {
// TODO: Generate exception in protected mode (and bail)
this.setSP(this.regLSP - this.segSS.base);
this.regLSP = (this.regLSP - (I386? this.dataSize : 2))|0;
/*
* Properly comparing regLSP to regLSPLimitLow would normally require coercing both to unsigned
* (ie, floating-point) values. But instead, we do a subtraction, (regLSP - regLSPLimitLow), and
* if the result is negative, we need only be concerned if the signs of both numbers are the same
* (ie, the sign of their XOR'ed union is positive).
*/
if (((this.regLSP - this.regLSPLimitLow)|0) < 0 && (this.regLSPLimitLow ^ this.regLSP) >= 0) {
if (this.model <= X86.MODEL_8088) {
this.setSP(this.regLSP - this.segSS.base);
} else {
X86.fnFault.call(this, X86.EXCEPTION.SS_FAULT, 0);
}
}
this.setWord(this.regLSP, w);
};

View file

@ -1197,8 +1197,9 @@ X86.fnINCw = function INCw(dst, src)
* only knows how to load GDT and LDT descriptors, whereas interrupts must use setCS.loadIDT(), which
* deals exclusively with IDT descriptors.
*
* This means we must take care to replicate critical features of setCSIP(); eg, setting segCS.fCall
* before calling loadIDT(), updating LIP, and flushing the prefetch queue.
* This means we must take care to replicate critical features of setCSIP(); ie, setting segCS.fCall
* BEFORE calling loadIDT(), and updating regLIP and regLIPLimit, resetting default operand and address sizes,
* and flushing the prefetch queue AFTER calling loadIDT().
*
* @this {X86CPU}
* @param {number} nIDT
@ -1217,13 +1218,15 @@ X86.fnINT = function INT(nIDT, nError, nCycles)
var oldIP = this.getIP();
var addr = this.segCS.loadIDT(nIDT);
if (addr !== X86.ADDR_INVALID) {
this.regLIP = addr;
if (PREFETCH) this.flushPrefetch(this.regLIP);
this.pushWord(oldPS);
this.pushWord(oldCS);
this.pushWord(oldIP);
if (nError != null) this.pushWord(nError);
this.nFault = -1;
this.regLIP = addr;
this.regLIPLimit = (this.segCS.base + this.segCS.limit)|0;
if (I386) this.resetSizes();
if (PREFETCH) this.flushPrefetch(this.regLIP);
}
};

View file

@ -1483,7 +1483,7 @@ X86.opINSb = function INSb()
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
if (nReps) {
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support multiple overrides
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
@ -1540,7 +1540,7 @@ X86.opINSw = function INSw()
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
if (nReps) {
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support multiple overrides
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
@ -1585,7 +1585,7 @@ X86.opOUTSb = function OUTSb()
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
if (nReps) {
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support multiple overrides
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
@ -1640,7 +1640,7 @@ X86.opOUTSw = function OUTSw()
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
if (nReps) {
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support multiple overrides
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
@ -2558,7 +2558,7 @@ X86.opMOVSb = function MOVSb()
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
if (nReps) {
if (BUGS_8086) {
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
@ -2593,7 +2593,7 @@ X86.opMOVSw = function MOVSw()
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
if (nReps) {
if (BUGS_8086) {
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
@ -2638,7 +2638,7 @@ X86.opCMPSb = function CMPSb()
*/
if (nReps && this.getZF() == (this.opPrefixes & X86.OPFLAG.REPZ)) {
if (BUGS_8086) {
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
@ -2683,7 +2683,7 @@ X86.opCMPSw = function CMPSw()
*/
if (nReps && this.getZF() == (this.opPrefixes & X86.OPFLAG.REPZ)) {
if (BUGS_8086) {
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
@ -2741,7 +2741,7 @@ X86.opSTOSb = function STOSb()
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
if (nReps) {
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support multiple overrides
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
@ -2783,7 +2783,7 @@ X86.opSTOSw = function STOSw()
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
if (nReps) {
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support multiple overrides
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
@ -2817,7 +2817,7 @@ X86.opLODSb = function LODSb()
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
if (nReps) {
if (BUGS_8086) {
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
@ -2853,7 +2853,7 @@ X86.opLODSw = function LODSw()
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
if (nReps) {
if (BUGS_8086) {
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
@ -2894,7 +2894,7 @@ X86.opSCASb = function SCASb()
*/
if (nReps && this.getZF() == (this.opPrefixes & X86.OPFLAG.REPZ)) {
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support multiple overrides
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
@ -2935,7 +2935,7 @@ X86.opSCASw = function SCASw()
*/
if (nReps && this.getZF() == (this.opPrefixes & X86.OPFLAG.REPZ)) {
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support multiple overrides
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
@ -3806,7 +3806,7 @@ X86.opHLT = function HLT()
* on the theory that whoever's using the Debugger would like to see HLTs.
*/
if (DEBUGGER && this.dbg && this.messageEnabled(Messages.HALT)) {
this.advanceIP(-1); // this is purely for the Debugger's benefit, to show the HLT
this.rewindIP(-1); // this is purely for the Debugger's benefit, to show the HLT
this.stopCPU();
return;
}
@ -3815,7 +3815,7 @@ X86.opHLT = function HLT()
* in the water (we have no NMI generation mechanism at the moment).
*/
if (!this.getIF()) {
if (DEBUGGER && this.dbg) this.advanceIP(-1);
if (DEBUGGER && this.dbg) this.rewindIP(-1);
this.stopCPU();
}
};

View file

@ -853,6 +853,9 @@ X86Seg.prototype.switchTSS = function switchTSS(selNew, fNest)
* but the CS base must be set to 0x00FF0000 or 0xFFFF0000, respectively. To simplify life for setBase()
* callers, we allow them to specify 32-bit bases, which we then truncate to 24 bits as needed.
*
* WARNING: Since the CPU must maintain regLIP as the sum of the CS base and the current IP, all calls
* to segCS.setBase() need to go through setCSBase().
*
* @this {X86Seg}
* @param {number} addr
* @return {number} addr, truncated as needed