Corrected frame size for 32-bit INT gates, fixed BT/BTC/BTR/BTS instructions for bit indexes > 31, and disabled the Debugger's IF clear warning when IOPL < CPL

This commit is contained in:
Jeff Parsons 2015-09-03 18:47:40 -07:00
commit 3f52d18182
5 changed files with 191 additions and 21 deletions

View file

@ -3365,7 +3365,7 @@ if (DEBUGGER) {
/*
* Halt if running with interrupts disabled and IOPL < CPL, because that's likely an error
*/
if (!(this.cpu.regPS & X86.PS.IF) && this.cpu.nIOPL < this.cpu.nCPL) {
if (MAXDEBUG && !(this.cpu.regPS & X86.PS.IF) && this.cpu.nIOPL < this.cpu.nCPL) {
this.printMessage("interrupts disabled at IOPL " + this.cpu.nIOPL + " and CPL " + this.cpu.nCPL, true);
return true;
}
@ -6931,6 +6931,8 @@ if (DEBUGGER) {
var ch = sCmd.charAt(0);
if (ch == '"' || ch == "'") return true;
this.sMessagePrev = null;
/*
* I've relaxed the !isBusy() requirement, to maximize our ability to issue Debugger commands externally.
*/

View file

@ -3922,6 +3922,7 @@ X86CPU.prototype.popWord = function()
*/
X86CPU.prototype.pushData = function(d, size)
{
this.assert(size == 2 || size == 4);
this.regLSP = (this.regLSP - size)|0;
/*
* Properly comparing regLSP to regLSPLimitLow would normally require coercing both to unsigned

View file

@ -353,6 +353,151 @@ X86.fnBTS = function BTS(dst, src)
return dst | bit;
};
/**
* fnBTMem(dst, src)
*
* In this form of BT, src is a register operand, which is NOT truncated to mod 32 if dst is a memory operand;
* however, if dst is also a register operand, then we defer to the simpler function, fnBT().
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
X86.fnBTMem = function BTMem(dst, src)
{
if (this.regEA === X86.ADDR_INVALID) {
return X86.fnBT.call(this, dst, src);
}
var offByte = src >>> 3;
if (offByte >= this.sizeData) {
/*
* offByte is src divided by 8, but now we need src divided by 16 or 32, according to the OPERAND size,
* which means shifting it right by either 4 or 5 bits. That gives us a short or long INDEX, which we then
* multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we add to regEA.
*/
var i = src >>> (this.sizeData == 2? 4 : 5);
dst = this.getWord(this.regEA += i * this.sizeData);
}
/*
* Now we convert src from a bit index into a bit mask.
*/
src = 1 << (src & (this.sizeData == 2? 0xf : 0x1f));
if (dst & src) this.setCF(); else this.clearCF();
this.nStepCycles -= 6;
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
};
/**
* fnBTCMem(dst, src)
*
* In this form of BTC, src is a register operand, which is NOT truncated to mod 32 if dst is a memory operand;
* however, if dst is also a register operand, then we defer to the simpler function, fnBTC().
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
X86.fnBTCMem = function BTCMem(dst, src)
{
if (this.regEA === X86.ADDR_INVALID) {
return X86.fnBTC.call(this, dst, src);
}
var offByte = src >>> 3;
if (offByte >= this.sizeData) {
/*
* offByte is src divided by 8, but now we need src divided by 16 or 32, according to the OPERAND size,
* which means shifting it right by either 4 or 5 bits. That gives us a short or long INDEX, which we then
* multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we add to regEA.
*/
var i = src >>> (this.sizeData == 2? 4 : 5);
dst = this.getWord(this.regEA += i * this.sizeData);
}
/*
* Now we convert src from a bit index into a bit mask.
*/
src = 1 << (src & (this.sizeData == 2? 0xf : 0x1f));
if (dst & src) this.setCF(); else this.clearCF();
this.nStepCycles -= 8;
return dst ^ src;
};
/**
* fnBTRMem(dst, src)
*
* In this form of BTR, src is a register operand, which is NOT truncated to mod 32 if dst is a memory operand;
* however, if dst is also a register operand, then we defer to the simpler function, fnBTR().
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
X86.fnBTRMem = function BTRMem(dst, src)
{
if (this.regEA === X86.ADDR_INVALID) {
return X86.fnBTR.call(this, dst, src);
}
var offByte = src >>> 3;
if (offByte >= this.sizeData) {
/*
* offByte is src divided by 8, but now we need src divided by 16 or 32, according to the OPERAND size,
* which means shifting it right by either 4 or 5 bits. That gives us a short or long INDEX, which we then
* multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we add to regEA.
*/
var i = src >>> (this.sizeData == 2? 4 : 5);
dst = this.getWord(this.regEA += i * this.sizeData);
}
/*
* Now we convert src from a bit index into a bit mask.
*/
src = 1 << (src & (this.sizeData == 2? 0xf : 0x1f));
if (dst & src) this.setCF(); else this.clearCF();
this.nStepCycles -= 8;
return dst & ~src;
};
/**
* fnBTSMem(dst, src)
*
* In this form of BTS, src is a register operand, which is NOT truncated to mod 32 if dst is a memory operand;
* however, if dst is also a register operand, then we defer to the simpler function, fnBTS().
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
X86.fnBTSMem = function BTSMem(dst, src)
{
if (this.regEA === X86.ADDR_INVALID) {
return X86.fnBTS.call(this, dst, src);
}
var offByte = src >>> 3;
if (offByte >= this.sizeData) {
/*
* offByte is src divided by 8, but now we need src divided by 16 or 32, according to the OPERAND size,
* which means shifting it right by either 4 or 5 bits. That gives us a short or long INDEX, which we then
* multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we add to regEA.
*/
var i = src >>> (this.sizeData == 2? 4 : 5);
dst = this.getWord(this.regEA += i * this.sizeData);
}
/*
* Now we convert src from a bit index into a bit mask.
*/
src = 1 << (src & (this.sizeData == 2? 0xf : 0x1f));
if (dst & src) this.setCF(); else this.clearCF();
this.nStepCycles -= 8;
return dst | src;
};
/**
* fnCALLw(dst, src)
*
@ -1246,10 +1391,11 @@ X86.fnINT = function INT(nIDT, nError, nCycles)
var oldIP = this.getIP();
var addr = this.segCS.loadIDT(nIDT);
if (addr !== X86.ADDR_INVALID) {
this.pushWord(oldPS);
this.pushWord(oldCS);
this.pushWord(oldIP);
if (nError != null) this.pushWord(nError);
var size = this.segCS.sizeFrame;
this.pushData(oldPS, size);
this.pushData(oldCS, size);
this.pushData(oldIP, size);
if (nError != null) this.pushData(nError, size);
this.nFault = -1;
/*
* TODO: Should this code be factored into a setLIP() function? The other primary client would be setCSIP().

View file

@ -1128,7 +1128,7 @@ X86.opPOPFS = function POPFS()
*/
X86.opBT = function BT()
{
this.aOpModMemWord[this.getIPByte()].call(this, X86.fnBT);
this.aOpModMemWord[this.getIPByte()].call(this, X86.fnBTMem);
if (this.regEA !== X86.ADDR_INVALID) this.nStepCycles -= 6;
};
@ -1195,7 +1195,7 @@ X86.opPOPGS = function POPGS()
*/
X86.opBTS = function BTS()
{
this.aOpModMemWord[this.getIPByte()].call(this, X86.fnBTS);
this.aOpModMemWord[this.getIPByte()].call(this, X86.fnBTSMem);
if (this.regEA !== X86.ADDR_INVALID) this.nStepCycles -= 5;
};
@ -1260,7 +1260,7 @@ X86.opLSS = function LSS()
*/
X86.opBTR = function BTR()
{
this.aOpModMemWord[this.getIPByte()].call(this, X86.fnBTR);
this.aOpModMemWord[this.getIPByte()].call(this, X86.fnBTRMem);
if (this.regEA !== X86.ADDR_INVALID) this.nStepCycles -= 5;
};
@ -1424,7 +1424,7 @@ X86.opGRP8 = function GRP8()
*/
X86.opBTC = function BTC()
{
this.aOpModMemWord[this.getIPByte()].call(this, X86.fnBTC);
this.aOpModMemWord[this.getIPByte()].call(this, X86.fnBTCMem);
if (this.regEA !== X86.ADDR_INVALID) this.nStepCycles -= 5;
};
@ -1602,8 +1602,8 @@ X86.aOps0F[0xA6] = X86.opInvalid;
/*
* When Windows 95 Setup initializes in protected-mode, it sets a DPMI exception handler for UD_FAULT and
* then attempts to generate that exception with undefined opcode 0x0F,0xFF. Apparently, whoever wrote that code
* (davidw?) didn't get the Intel memo regarding the preferred invalid opcode (0x0F,0x0B, aka UD2), or perhaps Intel
* hadn't written that memo yet -- although if that's the case, then Intel should have followed Microsoft's lead and
* didn't get the Intel memo regarding the preferred invalid opcode (0x0F,0x0B, aka UD2), or perhaps Intel hadn't
* written that memo yet -- although if that's the case, then Intel should have followed Microsoft's lead and
* selected 0x0F,0xFF instead of 0x0F,0x0B.
*
* In any case, this means we need to explicitly set the handler for that opcode to opInvalid(), too.

View file

@ -126,6 +126,7 @@ function X86Seg(cpu, id, sName, fProt)
*/
this.fCall = null;
this.fStackSwitch = false;
this.sizeFrame = 2; // must be set by all loadIDT() calls so that callers know the proper frame size
this.awParms = (this.id == X86Seg.ID.CODE? new Array(32) : []);
this.updateMode(true, fProt);
}
@ -273,6 +274,7 @@ X86Seg.prototype.loadIDTReal = function loadIDTReal(nIDT)
*/
var addrIDT = cpu.addrIDT + (nIDT << 2);
var off = cpu.getShort(addrIDT);
this.sizeFrame = 2;
cpu.regPS &= ~(X86.PS.TF | X86.PS.IF);
return (this.load(cpu.getShort(addrIDT + 2)) + off)|0;
};
@ -632,11 +634,13 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
case X86Seg.ID.CODE:
this.fStackSwitch = false;
this.sizeFrame = this.sizeData;
var fCall = this.fCall;
var rpl = sel & X86.SEL.RPL;
var dpl = (acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT;
var fGate, selCode, cplOld, addrTSS, offSP, lenSP, regSPPrev, regSSPrev, regPSClear, regSP;
var sizeGate, selCode, cplOld, addrTSS, offSP, lenSP, regSPPrev, regSSPrev, regPSClear, regSP;
/*
* TODO: As discussed below for X86Seg.ID.DATA, it's likely that testing the PRESENT bit should
@ -672,7 +676,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
cpu.setSP(regSP);
this.fStackSwitch = true;
}
fGate = false;
sizeGate = 0;
}
else if (type == X86.DESC.ACC.TYPE.TSS286 || type == X86.DESC.ACC.TYPE.TSS386) {
if (!this.switchTSS(sel, fCall)) {
@ -680,18 +684,33 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
}
return this.base;
}
else if (type == X86.DESC.ACC.TYPE.GATE_CALL || type == X86.DESC.ACC.TYPE.GATE386_CALL) {
fGate = true;
else if (type == X86.DESC.ACC.TYPE.GATE_CALL) {
sizeGate = 2;
regPSClear = 0;
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) {
fGate = true;
else if (type == X86.DESC.ACC.TYPE.GATE386_CALL) {
sizeGate = 4;
regPSClear = 0;
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) {
sizeGate = 2;
regPSClear = (X86.PS.VM | X86.PS.NT | X86.PS.TF | X86.PS.IF);
cpu.assert(!(acc & 0x1f));
}
else if (type == X86.DESC.ACC.TYPE.GATE286_TRAP || type == X86.DESC.ACC.TYPE.GATE386_TRAP) {
fGate = true;
else if (type == X86.DESC.ACC.TYPE.GATE386_INT) {
sizeGate = 4;
regPSClear = (X86.PS.VM | X86.PS.NT | X86.PS.TF | X86.PS.IF);
cpu.assert(!(acc & 0x1f));
}
else if (type == X86.DESC.ACC.TYPE.GATE286_TRAP) {
sizeGate = 2;
regPSClear = (X86.PS.VM | X86.PS.NT | X86.PS.TF);
cpu.assert(!(acc & 0x1f));
}
else if (type == X86.DESC.ACC.TYPE.GATE386_TRAP) {
sizeGate = 4;
regPSClear = (X86.PS.VM | X86.PS.NT | X86.PS.TF);
cpu.assert(!(acc & 0x1f));
}
@ -702,7 +721,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
return this.base;
}
if (fGate) {
if (sizeGate) {
/*
* Note that since GATE_INT/GATE_TRAP descriptors should appear in the IDT only, that means sel
* will actually be nIDT * 8, which means the rpl will always be zero; additionally, the nWords
@ -768,6 +787,8 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
return X86.ADDR_INVALID;
}
this.sizeFrame = sizeGate;
cpu.regEIP = limit;
cpu.assert(this.cpl == cplNew);
@ -816,7 +837,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
}
}
if (fGate !== false) {
if (sizeGate !== 0) {
var nError = sel & X86.ERRCODE.SELMASK;
if (addrDesc >= cpu.addrIDT && addrDesc < cpu.addrIDTLimit) nError |= X86.ERRCODE.IDT;
/*