Baby steps

This commit is contained in:
Jeff Parsons 2015-02-02 14:36:30 -08:00 committed by jeffpar
commit 2cd13b2058
11 changed files with 2808 additions and 2743 deletions

View file

@ -106,6 +106,16 @@ var BACKTRACK = !COMPILED;
*/
var SAMPLER = false;
/**
* @define {boolean}
*
* Enables support for known 8086 bugs. It's turned off by default, because 1) it adds overhead, and 2) it's
* hard to imagine any software actually being dependent on any of the bugs covered by this (eg, the failure to
* properly restart string instructions with multiple prefixes, or the failure to inhibit hardware interrupts
* following SS segment loads).
*/
var BUGS_8086 = false;
/**
* @define {boolean}
*

View file

@ -356,7 +356,7 @@ X86.PS.SET = (X86.PS.BIT1 | X86.PS.IOPL.MASK | X86.PS.NT | X86.PS.BIT15);
* These "result registers" are created/reset by an initial call to setPS(0); they include:
*
* this.resultSize (must be set to one of: SIZE_BYTE or SIZE_WORD)
* this.resultValue
* this.resultZeroCarry
* this.resultParitySign
* this.resultAuxOverflow
*

View file

@ -813,7 +813,7 @@ X86CPU.prototype.resetRegs = function()
this.regEBX = 0;
this.regECX = 0;
this.regEDX = 0;
this.regESP = 0; // this isn't needed in a 16-bit environment, but we'll need it later
this.regESP = 0; // this isn't needed in a 16-bit environment, but is required for I386
this.regEBP = 0;
this.regESI = 0;
this.regEDI = 0;
@ -1356,9 +1356,9 @@ X86CPU.prototype.getCS = function()
X86CPU.prototype.setCS = function(sel)
{
var regEIP = this.getIP();
this.regLIP = this.segCS.load(sel & 0xffff) + regEIP;
this.regLIP = this.segCS.load(sel) + regEIP;
this.regLIPLimit = this.segCS.base + this.segCS.limit;
this.opFlags |= this.OPFLAG_NOINTR8086;
if (!BUGS_8086) this.opFlags |= this.OPFLAG_NOINTR8086;
if (PREFETCH) this.flushPrefetch(this.regLIP);
};
@ -1381,8 +1381,8 @@ X86CPU.prototype.getDS = function()
*/
X86CPU.prototype.setDS = function(sel)
{
this.segDS.load(sel & 0xffff);
this.opFlags |= this.OPFLAG_NOINTR8086;
this.segDS.load(sel);
if (!BUGS_8086) this.opFlags |= this.OPFLAG_NOINTR8086;
};
/**
@ -1401,14 +1401,20 @@ X86CPU.prototype.getSS = function()
*
* @this {X86CPU}
* @param {number} sel
* @param {boolean} [fInterruptable]
*/
X86CPU.prototype.setSS = function(sel)
X86CPU.prototype.setSS = function(sel, fInterruptable)
{
var regESP = this.getSP();
this.regLSP = this.segSS.load(sel & 0xffff) + regESP;
this.regLSPLimit = this.segSS.base + this.segSS.limit;
this.regLSPLimitLow = this.segSS.base; // TODO: Set this to the actual low limit
this.opFlags |= X86.OPFLAG.NOINTR;
this.regLSP = this.segSS.load(sel) + regESP;
if (this.segSS.fExpDown) {
this.regLSPLimit = this.segSS.base + this.segSS.addrMask;
this.regLSPLimitLow = this.segSS.base + this.segSS.limit;
} else {
this.regLSPLimit = this.segSS.base + this.segSS.limit;
this.regLSPLimitLow = this.segSS.base;
}
if (!BUGS_8086 && !fInterruptable) this.opFlags |= X86.OPFLAG.NOINTR;
};
/**
@ -1430,8 +1436,8 @@ X86CPU.prototype.getES = function()
*/
X86CPU.prototype.setES = function(sel)
{
this.segES.load(sel & 0xffff);
this.opFlags |= this.OPFLAG_NOINTR8086;
this.segES.load(sel);
if (!BUGS_8086) this.opFlags |= this.OPFLAG_NOINTR8086;
};
/**
@ -1546,6 +1552,10 @@ X86CPU.prototype.advanceIP = function(inc)
*/
X86CPU.prototype.getSP = function()
{
if (I386) {
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;
};
@ -1557,7 +1567,12 @@ X86CPU.prototype.getSP = function()
*/
X86CPU.prototype.setSP = function(off)
{
this.regLSP = this.segSS.base + (off & (I386? this.segSS.addrMask : 0xffff));
if (I386) {
this.regESP = off;
this.regLSP = this.segSS.base + (off & this.segSS.addrMask);
} else {
this.regLSP = this.segSS.base + off;
}
};
/**
@ -1568,7 +1583,7 @@ X86CPU.prototype.setSP = function(off)
*/
X86CPU.prototype.getCF = function()
{
return (this.resultValue & this.resultSize)? X86.PS.CF : 0;
return (this.resultZeroCarry & this.resultSize)? X86.PS.CF : 0;
};
/**
@ -1621,7 +1636,7 @@ X86CPU.prototype.getAF = function()
*/
X86CPU.prototype.getZF = function()
{
return (this.resultValue & (this.resultSize - 1))? 0 : X86.PS.ZF;
return (this.resultZeroCarry & (this.resultSize - 1))? 0 : X86.PS.ZF;
};
/**
@ -1686,7 +1701,7 @@ X86CPU.prototype.getDF = function()
*/
X86CPU.prototype.clearCF = function()
{
this.resultValue &= ~this.resultSize;
this.resultZeroCarry &= ~this.resultSize;
};
/**
@ -1716,7 +1731,7 @@ X86CPU.prototype.clearAF = function()
*/
X86CPU.prototype.clearZF = function()
{
this.resultValue |= (this.resultSize - 1);
this.resultZeroCarry |= (this.resultSize - 1);
};
/**
@ -1770,7 +1785,7 @@ X86CPU.prototype.clearOF = function()
*/
X86CPU.prototype.setCF = function()
{
this.resultValue |= this.resultSize;
this.resultZeroCarry |= this.resultSize;
};
/**
@ -1800,7 +1815,7 @@ X86CPU.prototype.setAF = function()
*/
X86CPU.prototype.setZF = function()
{
this.resultValue &= ~(this.resultSize - 1);
this.resultZeroCarry &= ~(this.resultSize - 1);
};
/**
@ -1868,7 +1883,7 @@ X86CPU.prototype.getPS = function()
X86CPU.prototype.setPS = function(regPS, cpl)
{
this.resultSize = X86.RESULT.SIZE_BYTE; // NOTE: We could have chosen SIZE_WORD, too; it's irrelevant
this.resultValue = this.resultParitySign = this.resultAuxOverflow = 0;
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = 0;
if (regPS & X86.PS.CF) this.setCF();
if (!(regPS & X86.PS.PF)) this.resultParitySign |= 0x1;
@ -2608,7 +2623,10 @@ X86CPU.prototype.popWord = function()
{
var w = (I386? this.opMem.getWord(this.regLSP) : this.getShort(this.regLSP));
this.regLSP += (I386? this.dataSize : 2);
if (this.regLSP > this.regLSPLimit) this.setSP(this.regLSP - this.segSS.base);
if (this.regLSP > this.regLSPLimit) {
// TODO: Generate exception in protected mode
this.setSP(this.regLSP - this.segSS.base);
}
return w;
};
@ -2622,7 +2640,10 @@ X86CPU.prototype.pushWord = function(w)
{
this.assert((w & this.dataMask) == w);
this.regLSP -= (I386? this.dataSize : 2);
if (this.regLSP < this.regLSPLimitLow) this.setSP(this.regLSP - this.segSS.base);
if (this.regLSP < this.regLSPLimitLow) {
// TODO: Generate exception in protected mode (and bail)
this.setSP(this.regLSP - this.segSS.base);
}
if (!I386) this.setShort(this.regLSP, w); else this.opMem.setWord(this.regLSP, w);
};
@ -2904,6 +2925,19 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
if (opPrefixes) {
this.opPrefixes |= opPrefixes;
} else {
/*
* opLIP is used, among other things, to help string instructions rewind to the first prefix
* byte whenever the instruction needs to be repeated. Repeating string instructions in this
* manner (essentially restarting them) is a bit heavy-handed, but ultimately it's more compatible,
* because it allows hardware interrupts (as well as Trap processing and Debugger single-stepping)
* to occur at any point during the string operation, without any additional effort.
*
* NOTE: The way we restart string instructions actually fixes an 8086/8088 flaw, because string
* instructions with multiple prefixes (eg, a REP and a segment override) would not be restarted
* properly following a hardware interrupt. The recommended workarounds were to either turn off
* interrupts or make sure the REP prefix was first and follow the string instruction with a LOOPNZ
* back to the REP. To emulate this flawed behavior, turn on BUGS_8086.
*/
this.opLIP = this.regLIP;
this.regEA = this.regEAWrite = X86.ADDR_INVALID;
this.segData = this.segDS;

View file

@ -48,7 +48,7 @@ var X86Grps = {
this.resultAuxOverflow = dst ^ src;
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesArithRM) : this.CYCLES.nOpCyclesArithMR);
return (this.resultValue = this.resultParitySign = dst + src) & 0xff;
return (this.resultZeroCarry = this.resultParitySign = dst + src) & 0xff;
},
/**
* @this {X86CPU}
@ -59,7 +59,7 @@ var X86Grps = {
opGrpORb: function(dst, src) {
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesArithRM) : this.CYCLES.nOpCyclesArithMR);
return (this.resultValue = this.resultParitySign = this.resultAuxOverflow = dst | src) & 0xff;
return (this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = dst | src) & 0xff;
},
/**
* NOTE: Notice that some of the simpler math functions could get away with updating resultSize before
@ -72,10 +72,10 @@ var X86Grps = {
*/
opGrpADCb: function(dst, src) {
this.resultAuxOverflow = dst ^ src;
this.resultValue = this.resultParitySign = dst + src + ((this.resultValue & this.resultSize)? 1 : 0);
this.resultZeroCarry = this.resultParitySign = dst + src + ((this.resultZeroCarry & this.resultSize)? 1 : 0);
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesArithRM) : this.CYCLES.nOpCyclesArithMR);
return this.resultValue & 0xff;
return this.resultZeroCarry & 0xff;
},
/**
* NOTE: Notice that some of the simpler math functions could get away with updating resultSize before
@ -88,10 +88,10 @@ var X86Grps = {
*/
opGrpSBBb: function(dst, src) {
this.resultAuxOverflow = dst ^ src;
this.resultValue = this.resultParitySign = dst - src - ((this.resultValue & this.resultSize)? 1 : 0);
this.resultZeroCarry = this.resultParitySign = dst - src - ((this.resultZeroCarry & this.resultSize)? 1 : 0);
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesArithRM) : this.CYCLES.nOpCyclesArithMR);
return this.resultValue & 0xff;
return this.resultZeroCarry & 0xff;
},
/**
* @this {X86CPU}
@ -102,7 +102,7 @@ var X86Grps = {
opGrpANDb: function(dst, src) {
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesArithRM) : this.CYCLES.nOpCyclesArithMR);
return (this.resultValue = this.resultParitySign = this.resultAuxOverflow = dst & src) & 0xff;
return (this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = dst & src) & 0xff;
},
/**
* @this {X86CPU}
@ -114,7 +114,7 @@ var X86Grps = {
this.resultAuxOverflow = dst ^ src;
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesArithRM) : this.CYCLES.nOpCyclesArithMR);
return (this.resultValue = this.resultParitySign = dst - src) & 0xff;
return (this.resultZeroCarry = this.resultParitySign = dst - src) & 0xff;
},
/**
* @this {X86CPU}
@ -125,7 +125,7 @@ var X86Grps = {
opGrpXORb: function(dst, src) {
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesArithRM) : this.CYCLES.nOpCyclesArithMR);
return (this.resultValue = this.resultParitySign = this.resultAuxOverflow = dst ^ src) & 0xff;
return (this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = dst ^ src) & 0xff;
},
/**
* @this {X86CPU}
@ -136,7 +136,7 @@ var X86Grps = {
opGrpCMPb: function(dst, src) {
this.resultAuxOverflow = dst ^ src;
this.resultSize = X86.RESULT.SIZE_BYTE;
this.resultValue = this.resultParitySign = dst - src;
this.resultZeroCarry = this.resultParitySign = dst - src;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesCompareRM) : this.CYCLES.nOpCyclesArithRM);
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
@ -162,14 +162,14 @@ var X86Grps = {
* So, we can use (dst ^ ((dst ^ src) & (src ^ res))) >>> 15 to shift the calculated carry bit (bit 31)
* into the conventional SIZE_WORD position (bit 16); eg:
*
* resultValue = ((resultValue >>> 16) | (resultValue & 0xffff)) | (((dst ^ ((dst ^ src) & (src ^ resultValue))) >>> 15) & SIZE_WORD);
* resultZeroCarry = ((resultZeroCarry >>> 16) | (resultZeroCarry & 0xffff)) | (((dst ^ ((dst ^ src) & (src ^ resultZeroCarry))) >>> 15) & SIZE_WORD);
*
* Essentially, were cramming all 32 result bits into the low 16 bits (which will effectively represent the
* zero flag), and then setting bit 16 to the effective carry flag. This transforms the zero and carry conditions
* for a DWORD computation into the corresponding conditions for a WORD computation. This will slow down 32-bit
* addition, but it allows 8-bit and 16-bit addition to remain fast. Languages that support 64-bit values in
* conjunction with bit-wise operators can omit that one-line transformation, and we can set SIZE_DWORD to a 33-bit
* value, but sadly, we cannot do that in JavaScript.
* conjunction with bit-wise operators can omit that one-line transformation, allowing us to set SIZE_DWORD to a
* 33-bit value, but sadly, we cannot do that in JavaScript.
*
* Alternatively, we could store src and dst into their own result variables (eg, resultSrc and resultDst) and
* compute carry lazily, but that would affect MUCH more existing code (eg, all code that currently inspects carry
@ -185,7 +185,7 @@ var X86Grps = {
this.resultAuxOverflow = dst ^ src;
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesArithRM) : this.CYCLES.nOpCyclesArithMR);
return (this.resultValue = this.resultParitySign = dst + src) & 0xffff;
return (this.resultZeroCarry = this.resultParitySign = dst + src) & 0xffff;
},
/**
* @this {X86CPU}
@ -196,7 +196,7 @@ var X86Grps = {
opGrpORw: function(dst, src) {
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesArithRM) : this.CYCLES.nOpCyclesArithMR);
return (this.resultValue = this.resultParitySign = this.resultAuxOverflow = dst | src) & 0xffff;
return (this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = dst | src) & 0xffff;
},
/**
* NOTE: Notice that some of the simpler math functions could get away with updating resultSize before
@ -209,10 +209,10 @@ var X86Grps = {
*/
opGrpADCw: function(dst, src) {
this.resultAuxOverflow = dst ^ src;
this.resultValue = this.resultParitySign = dst + src + ((this.resultValue & this.resultSize)? 1 : 0);
this.resultZeroCarry = this.resultParitySign = dst + src + ((this.resultZeroCarry & this.resultSize)? 1 : 0);
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesArithRM) : this.CYCLES.nOpCyclesArithMR);
return this.resultValue & 0xffff;
return this.resultZeroCarry & 0xffff;
},
/**
* NOTE: Notice that some of the simpler math functions could get away with updating resultSize before
@ -225,10 +225,10 @@ var X86Grps = {
*/
opGrpSBBw: function(dst, src) {
this.resultAuxOverflow = dst ^ src;
this.resultValue = this.resultParitySign = dst - src - ((this.resultValue & this.resultSize)? 1 : 0);
this.resultZeroCarry = this.resultParitySign = dst - src - ((this.resultZeroCarry & this.resultSize)? 1 : 0);
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesArithRM) : this.CYCLES.nOpCyclesArithMR);
return this.resultValue & 0xffff;
return this.resultZeroCarry & 0xffff;
},
/**
* @this {X86CPU}
@ -239,7 +239,7 @@ var X86Grps = {
opGrpANDw: function(dst, src) {
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesArithRM) : this.CYCLES.nOpCyclesArithMR);
return (this.resultValue = this.resultParitySign = this.resultAuxOverflow = dst & src) & 0xffff;
return (this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = dst & src) & 0xffff;
},
/**
* @this {X86CPU}
@ -251,7 +251,7 @@ var X86Grps = {
this.resultAuxOverflow = dst ^ src;
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesArithRM) : this.CYCLES.nOpCyclesArithMR);
return (this.resultValue = this.resultParitySign = dst - src) & 0xffff;
return (this.resultZeroCarry = this.resultParitySign = dst - src) & 0xffff;
},
/**
* @this {X86CPU}
@ -262,7 +262,7 @@ var X86Grps = {
opGrpXORw: function(dst, src) {
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesArithRM) : this.CYCLES.nOpCyclesArithMR);
return (this.resultValue = this.resultParitySign = this.resultAuxOverflow = dst ^ src) & 0xffff;
return (this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = dst ^ src) & 0xffff;
},
/**
* @this {X86CPU}
@ -273,7 +273,7 @@ var X86Grps = {
opGrpCMPw: function(dst, src) {
this.resultAuxOverflow = dst ^ src;
this.resultSize = X86.RESULT.SIZE_WORD;
this.resultValue = this.resultParitySign = dst - src;
this.resultZeroCarry = this.resultParitySign = dst - src;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesArithRR : this.CYCLES.nOpCyclesCompareRM) : this.CYCLES.nOpCyclesArithRM);
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
@ -319,7 +319,7 @@ var X86Grps = {
* @param {number} size
*/
opGrpRotateFlags: function(result, size) {
this.resultValue = (this.resultValue & (this.resultSize - 1)) | ((result & size)? this.resultSize : 0);
this.resultZeroCarry = (this.resultZeroCarry & (this.resultSize - 1)) | ((result & size)? this.resultSize : 0);
if ((result ^ (result >> 1)) & (size >> 1)) this.setOF(); else this.clearOF();
},
/**
@ -417,9 +417,9 @@ var X86Grps = {
var temp;
var shift = (src & this.nShiftCountMask) % 0x9;
if (!shift) {
temp = dst | (((this.resultValue & this.resultSize)? 1 : 0) << 8);
temp = dst | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 8);
} else {
temp = (dst << shift) | (((this.resultValue & this.resultSize)? 1 : 0) << (shift - 1)) | (dst >> (9 - shift));
temp = (dst << shift) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << (shift - 1)) | (dst >> (9 - shift));
result = temp & 0xff;
}
X86Grps.opGrpRotateFlags.call(this, temp, X86.RESULT.SIZE_BYTE);
@ -440,9 +440,9 @@ var X86Grps = {
var temp;
var shift = (src & this.nShiftCountMask) % 0x11;
if (!shift) {
temp = dst | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
temp = dst | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
temp = (dst << shift) | (((this.resultValue & this.resultSize)? 1 : 0) << (shift - 1)) | (dst >> (17 - shift));
temp = (dst << shift) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << (shift - 1)) | (dst >> (17 - shift));
result = temp & 0xffff;
}
X86Grps.opGrpRotateFlags.call(this, temp, X86.RESULT.SIZE_WORD);
@ -461,7 +461,7 @@ var X86Grps = {
var flagsIn = (DEBUG? this.getPS() : 0);
if (src) {
var shift = (src & this.nShiftCountMask) % 0x9;
result = (dst >> shift) | (((this.resultValue & this.resultSize)? 1 : 0) << (8 - shift)) | (dst << (9 - shift));
result = (dst >> shift) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << (8 - shift)) | (dst << (9 - shift));
X86Grps.opGrpRotateFlags.call(this, result, X86.RESULT.SIZE_BYTE);
result &= 0xff;
}
@ -479,7 +479,7 @@ var X86Grps = {
var flagsIn = (DEBUG? this.getPS() : 0);
if (src) {
var shift = (src & this.nShiftCountMask) % 0x11;
result = (dst >> shift) | (((this.resultValue & this.resultSize)? 1 : 0) << (16 - shift)) | (dst << (17 - shift));
result = (dst >> shift) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << (16 - shift)) | (dst << (17 - shift));
X86Grps.opGrpRotateFlags.call(this, result, X86.RESULT.SIZE_WORD);
result &= 0xffff;
}
@ -508,9 +508,9 @@ var X86Grps = {
var flagsIn = (DEBUG? this.getPS() : 0);
if (src) {
if (src > 8) // this comparison obviates the need to mask with this.nShiftCountMask
result = this.resultValue = this.resultParitySign = 0;
result = this.resultZeroCarry = this.resultParitySign = 0;
else
result = (this.resultValue = this.resultParitySign = (dst << src)) & 0xff;
result = (this.resultZeroCarry = this.resultParitySign = (dst << src)) & 0xff;
this.resultAuxOverflow = 0;
this.resultSize = X86.RESULT.SIZE_BYTE;
}
@ -533,9 +533,9 @@ var X86Grps = {
var flagsIn = (DEBUG? this.getPS() : 0);
if (src) {
if (src > 16) // this comparison obviates the need to mask with this.nShiftCountMask
result = this.resultValue = this.resultParitySign = 0;
result = this.resultZeroCarry = this.resultParitySign = 0;
else
result = (this.resultValue = this.resultParitySign = (dst << src)) & 0xffff;
result = (this.resultZeroCarry = this.resultParitySign = (dst << src)) & 0xffff;
this.resultAuxOverflow = 0;
this.resultSize = X86.RESULT.SIZE_WORD;
}
@ -556,14 +556,14 @@ var X86Grps = {
opGrpSHRb: function(dst, src) {
if (src) { // the following comparison obviates the need to mask with this.nShiftCountMask
var temp = (src > 8? 0 : (dst >> (src - 1)));
this.resultValue = this.resultParitySign = temp >> 1;
this.resultZeroCarry = this.resultParitySign = temp >> 1;
if (temp & 0x01)
this.resultValue |= X86.RESULT.SIZE_BYTE;
this.resultZeroCarry |= X86.RESULT.SIZE_BYTE;
else
this.resultValue &= ~X86.RESULT.SIZE_BYTE;
this.resultAuxOverflow = dst ^ this.resultValue;
this.resultZeroCarry &= ~X86.RESULT.SIZE_BYTE;
this.resultAuxOverflow = dst ^ this.resultZeroCarry;
this.resultSize = X86.RESULT.SIZE_BYTE;
dst = this.resultValue;
dst = this.resultZeroCarry;
}
return dst & 0xff;
},
@ -581,14 +581,14 @@ var X86Grps = {
opGrpSHRw: function(dst, src) {
if (src) { // the following comparison obviates the need to mask with this.nShiftCountMask
var temp = (src > 16? 0 : (dst >> (src - 1)));
this.resultValue = this.resultParitySign = temp >> 1;
this.resultZeroCarry = this.resultParitySign = temp >> 1;
if (temp & 0x01)
this.resultValue |= X86.RESULT.SIZE_WORD;
this.resultZeroCarry |= X86.RESULT.SIZE_WORD;
else
this.resultValue &= ~X86.RESULT.SIZE_WORD;
this.resultAuxOverflow = dst ^ this.resultValue;
this.resultZeroCarry &= ~X86.RESULT.SIZE_WORD;
this.resultAuxOverflow = dst ^ this.resultZeroCarry;
this.resultSize = X86.RESULT.SIZE_WORD;
dst = this.resultValue;
dst = this.resultZeroCarry;
}
return dst & 0xffff;
},
@ -607,14 +607,14 @@ var X86Grps = {
if (src) {
if (src > 8) src = 9; // this comparison obviates the need to mask with this.nShiftCountMask
var temp = ((dst << 24) >> 24) >> (src - 1);
this.resultValue = this.resultParitySign = temp >> 1;
this.resultZeroCarry = this.resultParitySign = temp >> 1;
if (temp & 0x01)
this.resultValue |= X86.RESULT.SIZE_BYTE;
this.resultZeroCarry |= X86.RESULT.SIZE_BYTE;
else
this.resultValue &= ~X86.RESULT.SIZE_BYTE;
this.resultAuxOverflow = dst ^ this.resultValue;
this.resultZeroCarry &= ~X86.RESULT.SIZE_BYTE;
this.resultAuxOverflow = dst ^ this.resultZeroCarry;
this.resultSize = X86.RESULT.SIZE_BYTE;
dst = this.resultValue;
dst = this.resultZeroCarry;
}
return dst & 0xff;
},
@ -633,14 +633,14 @@ var X86Grps = {
if (src) {
if (src > 16) src = 17; // this comparison obviates the need to mask with this.nShiftCountMask
var temp = ((dst << 16) >> 16) >> (src - 1);
this.resultValue = this.resultParitySign = temp >> 1;
this.resultZeroCarry = this.resultParitySign = temp >> 1;
if (temp & 0x01)
this.resultValue |= X86.RESULT.SIZE_WORD;
this.resultZeroCarry |= X86.RESULT.SIZE_WORD;
else
this.resultValue &= ~X86.RESULT.SIZE_WORD;
this.resultAuxOverflow = dst ^ this.resultValue;
this.resultZeroCarry &= ~X86.RESULT.SIZE_WORD;
this.resultAuxOverflow = dst ^ this.resultZeroCarry;
this.resultSize = X86.RESULT.SIZE_WORD;
dst = this.resultValue;
dst = this.resultZeroCarry;
}
return dst & 0xffff;
},
@ -652,7 +652,7 @@ var X86Grps = {
*/
opGrpTEST8: function(dst, src) {
src = this.getIPByte();
this.resultValue = this.resultParitySign = this.resultAuxOverflow = dst & src;
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = dst & src;
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= (this.regEA < 0? this.CYCLES.nOpCyclesTestRI : this.CYCLES.nOpCyclesTestMI);
this.opFlags |= X86.OPFLAG.NOWRITE;
@ -666,7 +666,7 @@ var X86Grps = {
*/
opGrpTEST16: function(dst, src) {
src = this.getIPWord();
this.resultValue = this.resultParitySign = this.resultAuxOverflow = dst & src;
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = dst & src;
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= (this.regEA < 0? this.CYCLES.nOpCyclesTestRI : this.CYCLES.nOpCyclesTestMI);
this.opFlags |= X86.OPFLAG.NOWRITE;
@ -703,7 +703,7 @@ var X86Grps = {
this.resultAuxOverflow = dst ^ src;
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= (this.regEA < 0? this.CYCLES.nOpCyclesNegR : this.CYCLES.nOpCyclesNegM);
return (this.resultValue = this.resultParitySign = src - dst) & 0xff;
return (this.resultZeroCarry = this.resultParitySign = src - dst) & 0xff;
},
/**
* @this {X86CPU}
@ -716,7 +716,7 @@ var X86Grps = {
this.resultAuxOverflow = dst ^ src;
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= (this.regEA < 0? this.CYCLES.nOpCyclesNegR : this.CYCLES.nOpCyclesNegM);
return (this.resultValue = this.resultParitySign = src - dst) & 0xffff;
return (this.resultZeroCarry = this.resultParitySign = src - dst) & 0xffff;
},
/**
* @this {X86CPU}
@ -725,8 +725,8 @@ var X86Grps = {
* @return {number} (we return dst unchanged, since it's actually AX that's modified)
*/
opGrpMULb: function(dst, src) {
this.regEAX = this.regMD16 = (this.resultValue = (src = this.regEAX & 0xff) * dst) & 0xffff;
this.resultAuxOverflow = this.resultParitySign = this.resultValue;
this.regEAX = this.regMD16 = (this.resultZeroCarry = (src = this.regEAX & 0xff) * dst) & 0xffff;
this.resultAuxOverflow = this.resultParitySign = this.resultZeroCarry;
this.resultSize = X86.RESULT.SIZE_BYTE;
/*
* TODO: Look into a more efficient way of setting/synchronizing CF and OF; this code works,
@ -770,7 +770,7 @@ var X86Grps = {
opGrpIMULb: function(dst, src) {
var result = (((src = this.regEAX) << 24) >> 24) * ((dst << 24) >> 24);
this.regEAX = this.regMD16 = result & 0xffff;
this.resultValue = this.resultAuxOverflow = this.resultParitySign = result;
this.resultZeroCarry = this.resultAuxOverflow = this.resultParitySign = result;
this.resultSize = X86.RESULT.SIZE_BYTE;
/*
* TODO: Look into a more efficient way of setting/synchronizing CF and OF; this code works,
@ -818,7 +818,7 @@ var X86Grps = {
/*
* TODO: Verify that all of the arithmetic flags are "undefined" after DIV, and that this code unnecessary
*/
this.resultParitySign = this.resultAuxOverflow = (this.resultValue = uQuotient | X86.RESULT.SIZE_BYTE);
this.resultParitySign = this.resultAuxOverflow = (this.resultZeroCarry = uQuotient | X86.RESULT.SIZE_BYTE);
this.resultSize = X86.RESULT.SIZE_BYTE;
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
@ -865,7 +865,7 @@ var X86Grps = {
/*
* TODO: Verify that all of the arithmetic flags are "undefined" after IDIV, and that this code unnecessary
*/
this.resultParitySign = this.resultAuxOverflow = (this.resultValue = lQuotient | X86.RESULT.SIZE_BYTE);
this.resultParitySign = this.resultAuxOverflow = (this.resultZeroCarry = lQuotient | X86.RESULT.SIZE_BYTE);
this.resultSize = X86.RESULT.SIZE_BYTE;
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
@ -885,9 +885,9 @@ var X86Grps = {
* @return {number} (we return dst unchanged, since it's actually DX:AX that's modified)
*/
opGrpMULw: function(dst, src) {
this.regMD16 = this.regEAX = (this.resultValue = (src = this.regEAX) * dst) & 0xffff;
this.regMD32 = this.regEDX = (this.resultValue >> 16) & 0xffff;
this.resultAuxOverflow = this.resultParitySign = this.resultValue;
this.regMD16 = this.regEAX = (this.resultZeroCarry = (src = this.regEAX) * dst) & 0xffff;
this.regMD32 = this.regEDX = (this.resultZeroCarry >> 16) & 0xffff;
this.resultAuxOverflow = this.resultParitySign = this.resultZeroCarry;
this.resultSize = X86.RESULT.SIZE_WORD;
/*
* TODO: Look into a more efficient way of setting/synchronizing CF and OF; this code works,
@ -932,7 +932,7 @@ var X86Grps = {
var result = (((src = this.regEAX) << 16) >> 16) * ((dst << 16) >> 16);
this.regEAX = this.regMD16 = result & 0xffff;
this.regEDX = this.regMD32 = (result >> 16) & 0xffff;
this.resultValue = this.resultAuxOverflow = this.resultParitySign = result;
this.resultZeroCarry = this.resultAuxOverflow = this.resultParitySign = result;
this.resultSize = X86.RESULT.SIZE_WORD;
/*
* TODO: Look into a more efficient way of setting/synchronizing CF and OF; this code works,
@ -986,7 +986,7 @@ var X86Grps = {
/*
* TODO: Verify that all of the arithmetic flags are "undefined" after DIV, and that this code unnecessary
*/
this.resultParitySign = this.resultAuxOverflow = (this.resultValue = uQuotient | X86.RESULT.SIZE_WORD);
this.resultParitySign = this.resultAuxOverflow = (this.resultZeroCarry = uQuotient | X86.RESULT.SIZE_WORD);
this.resultSize = X86.RESULT.SIZE_WORD;
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
@ -1036,7 +1036,7 @@ var X86Grps = {
/*
* TODO: Verify that all of the arithmetic flags are "undefined" after IDIV, and that this code unnecessary
*/
this.resultParitySign = this.resultAuxOverflow = (this.resultValue = lQuotient | X86.RESULT.SIZE_WORD);
this.resultParitySign = this.resultAuxOverflow = (this.resultZeroCarry = lQuotient | X86.RESULT.SIZE_WORD);
this.resultSize = X86.RESULT.SIZE_WORD;
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
@ -1058,7 +1058,7 @@ var X86Grps = {
opGrpINCb: function(dst, src) {
this.resultAuxOverflow = dst;
dst = (this.resultParitySign = dst + 1) & 0xff;
this.resultValue = dst | (((this.resultValue & this.resultSize)? 1 : 0) << 8);
this.resultZeroCarry = dst | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 8);
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= (this.regEA < 0? this.CYCLES.nOpCyclesIncR : this.CYCLES.nOpCyclesIncM);
return dst;
@ -1072,7 +1072,7 @@ var X86Grps = {
opGrpDECb: function(dst, src) {
this.resultAuxOverflow = dst;
dst = (this.resultParitySign = dst - 1) & 0xff;
this.resultValue = dst | (((this.resultValue & this.resultSize)? 1 : 0) << 8);
this.resultZeroCarry = dst | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 8);
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= (this.regEA < 0? this.CYCLES.nOpCyclesIncR : this.CYCLES.nOpCyclesIncM);
return dst;
@ -1086,7 +1086,7 @@ var X86Grps = {
opGrpINCw: function(dst, src) {
this.resultAuxOverflow = dst;
dst = (this.resultParitySign = dst + 1) & 0xffff;
this.resultValue = dst | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultZeroCarry = dst | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= (this.regEA < 0? this.CYCLES.nOpCyclesIncR : this.CYCLES.nOpCyclesIncM);
return dst;
@ -1100,7 +1100,7 @@ var X86Grps = {
opGrpDECw: function(dst, src) {
this.resultAuxOverflow = dst;
dst = (this.resultParitySign = dst - 1) & 0xffff;
this.resultValue = dst | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultZeroCarry = dst | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= (this.regEA < 0? this.CYCLES.nOpCyclesIncR : this.CYCLES.nOpCyclesIncM);
return dst;

View file

@ -64,7 +64,7 @@ var X86Help = {
* @return {number}
*/
opHelpTESTb: function(dst, src) {
this.resultValue = this.resultParitySign = this.resultAuxOverflow = dst & src;
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = dst & src;
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesTestRR : this.CYCLES.nOpCyclesTestRM) : this.CYCLES.nOpCyclesTestRM);
this.opFlags |= X86.OPFLAG.NOWRITE;
@ -77,7 +77,7 @@ var X86Help = {
* @return {number}
*/
opHelpTESTw: function(dst, src) {
this.resultValue = this.resultParitySign = this.resultAuxOverflow = dst & src;
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = dst & src;
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesTestRR : this.CYCLES.nOpCyclesTestRM) : this.CYCLES.nOpCyclesTestRM);
this.opFlags |= X86.OPFLAG.NOWRITE;
@ -100,7 +100,7 @@ var X86Help = {
*/
opHelpIMUL8: function(dst, src) {
var result = ((src << 16) >> 16) * ((this.getIPByte() << 24) >> 24);
this.resultValue = this.resultAuxOverflow = this.resultParitySign = result;
this.resultZeroCarry = this.resultAuxOverflow = this.resultParitySign = result;
this.resultSize = X86.RESULT.SIZE_BYTE;
/*
* TODO: Look into a more efficient way of setting/synchronizing CF and OF; this code works,
@ -138,7 +138,7 @@ var X86Help = {
*/
opHelpIMUL16: function(dst, src) {
var result = ((src << 16) >> 16) * ((this.getIPWord() << 16) >> 16);
this.resultValue = this.resultAuxOverflow = this.resultParitySign = result;
this.resultZeroCarry = this.resultAuxOverflow = this.resultParitySign = result;
this.resultSize = X86.RESULT.SIZE_WORD;
/*
* TODO: Look into a more efficient way of setting/synchronizing CF and OF; this code works,

View file

@ -88,9 +88,8 @@ var X86OpXX = {
opADDALb: function() {
this.regEAX = (this.regEAX & ~0xff) | X86Grps.opGrpADDb.call(this, this.regEAX & 0xff, this.getIPByte());
/*
* BACKTRACK note: I'm going to say this just once, even though it applies to MANY instructions. The
* result is a blending of btiAL and btiMemLo, so technically, a new bti should be allocated to reflect
* that fact; however, I'm leaving perfect BACKTRACKing for another day.
* NOTE: Whenever the result is "blended" value (eg, of btiAL and btiMemLo), a new bti should be
* allocated to reflect that fact; however, I'm leaving "perfect" BACKTRACK support for another day.
*/
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiMemLo;
/*
@ -465,7 +464,7 @@ var X86OpXX = {
opDAA: function() {
var AL = this.regEAX & 0xff;
var fAuxCarry = this.getAF();
var fCarry = (this.resultValue & this.resultSize);
var fCarry = (this.resultZeroCarry & this.resultSize);
if ((AL & 0xf) > 9 || fAuxCarry) {
AL += 0x6;
fAuxCarry = true;
@ -474,9 +473,9 @@ var X86OpXX = {
AL += 0x60;
fCarry = true;
}
this.regEAX = (this.regEAX & ~0xff) | (this.resultValue = this.resultParitySign = (AL & 0xff));
this.regEAX = (this.regEAX & ~0xff) | (this.resultZeroCarry = this.resultParitySign = (AL & 0xff));
this.resultSize = X86.RESULT.SIZE_WORD;
if (fCarry) this.resultValue |= this.resultSize;
if (fCarry) this.resultZeroCarry |= this.resultSize;
if (fAuxCarry) this.setAF(); else this.clearAF();
this.nStepCycles -= this.CYCLES.nOpCyclesAAA; // AAA and DAA have the same cycle times
},
@ -564,7 +563,7 @@ var X86OpXX = {
opDAS: function() {
var AL = this.regEAX & 0xff;
var fAuxCarry = this.getAF();
var fCarry = (this.resultValue & this.resultSize);
var fCarry = (this.resultZeroCarry & this.resultSize);
if ((AL & 0xf) > 9 || fAuxCarry) {
AL -= 0x6;
fAuxCarry = true;
@ -573,9 +572,9 @@ var X86OpXX = {
AL -= 0x60;
fCarry = true;
}
this.regEAX = (this.regEAX & ~0xff) | (this.resultValue = this.resultParitySign = (AL & 0xff));
this.regEAX = (this.regEAX & ~0xff) | (this.resultZeroCarry = this.resultParitySign = (AL & 0xff));
this.resultSize = X86.RESULT.SIZE_WORD;
if (fCarry) this.resultValue |= this.resultSize;
if (fCarry) this.resultZeroCarry |= this.resultSize;
if (fAuxCarry) this.setAF(); else this.clearAF();
this.nStepCycles -= this.CYCLES.nOpCyclesAAA; // AAA and DAS have the same cycle times
},
@ -672,9 +671,9 @@ var X86OpXX = {
} else {
fCarry = fAuxCarry = false;
}
this.regEAX = (this.regEAX & ~0xffff) | (AH << 8) | (this.resultValue = AL);
this.regEAX = (this.regEAX & ~0xffff) | (AH << 8) | (this.resultZeroCarry = AL);
this.resultSize = X86.RESULT.SIZE_WORD;
if (fCarry) this.resultValue |= this.resultSize;
if (fCarry) this.resultZeroCarry |= this.resultSize;
if (fAuxCarry) this.setAF(); else this.clearAF();
this.nStepCycles -= this.CYCLES.nOpCyclesAAA;
},
@ -774,11 +773,11 @@ var X86OpXX = {
} else {
fCarry = fAuxCarry = false;
}
this.regEAX = (this.regEAX & ~0xffff) | (AH << 8) | (this.resultValue = AL);
this.regEAX = (this.regEAX & ~0xffff) | (AH << 8) | (this.resultZeroCarry = AL);
this.resultSize = X86.RESULT.SIZE_WORD;
if (fCarry) this.resultValue |= this.resultSize;
if (fCarry) this.resultZeroCarry |= this.resultSize;
if (fAuxCarry) this.setAF(); else this.clearAF();
this.nStepCycles -= this.CYCLES.nOpCyclesAAA; // AAA and AAS have the same cycle times
this.nStepCycles -= this.CYCLES.nOpCyclesAAA; // AAA and AAS have the same cycle times
},
/**
* op=0x40 (INC AX)
@ -786,9 +785,14 @@ var X86OpXX = {
* @this {X86CPU}
*/
opINCAX: function() {
this.resultAuxOverflow = this.regEAX;
this.regEAX = (this.regEAX & ~this.dataMask) | (this.resultParitySign = this.regEAX + 1) & this.dataMask;
this.resultValue = this.regEAX | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultParitySign = (this.resultAuxOverflow = this.regEAX) + 1;
if (I386) {
this.regEAX = (this.regEAX & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEAX) & this.dataMask) >>> 16) | (this.regEAX & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEAX = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEAX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
},
@ -798,9 +802,14 @@ var X86OpXX = {
* @this {X86CPU}
*/
opINCCX: function() {
this.resultAuxOverflow = this.regECX;
this.regECX = (this.regECX & ~this.dataMask) | (this.resultParitySign = this.regECX + 1) & this.dataMask;
this.resultValue = this.regECX | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultParitySign = (this.resultAuxOverflow = this.regECX) + 1;
if (I386) {
this.regECX = (this.regECX & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regECX) & this.dataMask) >>> 16) | (this.regECX & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regECX = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regECX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
},
@ -810,9 +819,14 @@ var X86OpXX = {
* @this {X86CPU}
*/
opINCDX: function() {
this.resultAuxOverflow = this.regEDX;
this.regEDX = (this.regEDX & ~this.dataMask) | (this.resultParitySign = this.regEDX + 1) & this.dataMask;
this.resultValue = this.regEDX | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultParitySign = (this.resultAuxOverflow = this.regEDX) + 1;
if (I386) {
this.regEDX = (this.regEDX & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEDX) & this.dataMask) >>> 16) | (this.regEDX & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEDX = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEDX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
},
@ -822,9 +836,14 @@ var X86OpXX = {
* @this {X86CPU}
*/
opINCBX: function() {
this.resultAuxOverflow = this.regEBX;
this.regEBX = (this.regEBX & ~this.dataMask) | (this.resultParitySign = this.regEBX + 1) & this.dataMask;
this.resultValue = this.regEBX | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultParitySign = (this.resultAuxOverflow = this.regEBX) + 1;
if (I386) {
this.regEBX = (this.regEBX & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEBX) & this.dataMask) >>> 16) | (this.regEBX & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEBX = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEBX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
},
@ -834,9 +853,10 @@ var X86OpXX = {
* @this {X86CPU}
*/
opINCSP: function() {
this.resultAuxOverflow = this.getSP();
this.setSP((this.resultAuxOverflow & ~this.dataMask) | (this.resultParitySign = this.resultAuxOverflow + 1) & this.dataMask);
this.resultValue = this.getSP() | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
var regESP;
this.resultParitySign = (this.resultAuxOverflow = this.getSP()) + 1;
this.setSP(regESP = (this.resultAuxOverflow & ~this.dataMask) | (this.resultParitySign & this.dataMask));
this.resultZeroCarry = (((regESP) & this.dataMask) >>> 16) | (regESP & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
},
@ -846,9 +866,14 @@ var X86OpXX = {
* @this {X86CPU}
*/
opINCBP: function() {
this.resultAuxOverflow = this.regEBP;
this.regEBP = (this.regEBP & ~this.dataMask) | (this.resultParitySign = this.regEBP + 1) & this.dataMask;
this.resultValue = this.regEBP | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultParitySign = (this.resultAuxOverflow = this.regEBP) + 1;
if (I386) {
this.regEBP = (this.regEBP & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEBP) & this.dataMask) >>> 16) | (this.regEBP & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEBP = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEBP | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
},
@ -858,9 +883,14 @@ var X86OpXX = {
* @this {X86CPU}
*/
opINCSI: function() {
this.resultAuxOverflow = this.regESI;
this.regESI = (this.regESI & ~this.dataMask) | (this.resultParitySign = this.regESI + 1) & this.dataMask;
this.resultValue = this.regESI | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultParitySign = (this.resultAuxOverflow = this.regESI) + 1;
if (I386) {
this.regESI = (this.regESI & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regESI) & this.dataMask) >>> 16) | (this.regESI & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regESI = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regESI | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
},
@ -870,9 +900,14 @@ var X86OpXX = {
* @this {X86CPU}
*/
opINCDI: function() {
this.resultAuxOverflow = this.regEDI;
this.regEDI = (this.regEDI & ~this.dataMask) | (this.resultParitySign = this.regEDI + 1) & this.dataMask;
this.resultValue = this.regEDI | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultParitySign = (this.resultAuxOverflow = this.regEDI) + 1;
if (I386) {
this.regEDI = (this.regEDI & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEDI) & this.dataMask) >>> 16) | (this.regEDI & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEDI = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEDI | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
},
@ -882,9 +917,14 @@ var X86OpXX = {
* @this {X86CPU}
*/
opDECAX: function() {
this.resultAuxOverflow = this.regEAX;
this.regEAX = (this.regEAX & ~this.dataMask) | (this.resultParitySign = this.regEAX - 1) & this.dataMask;
this.resultValue = this.regEAX | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultParitySign = (this.resultAuxOverflow = this.regEAX) - 1;
if (I386) {
this.regEAX = (this.regEAX & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEAX) & this.dataMask) >>> 16) | (this.regEAX & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEAX = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEAX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
},
@ -894,9 +934,9 @@ var X86OpXX = {
* @this {X86CPU}
*/
opDECCX: function() {
this.resultAuxOverflow = this.regECX;
this.regECX = (this.regECX & ~this.dataMask) | (this.resultParitySign = this.regECX - 1) & this.dataMask;
this.resultValue = this.regECX | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultParitySign = (this.resultAuxOverflow = this.regECX) - 1;
this.regECX = (I386? (this.regECX & ~this.dataMask) | (this.resultParitySign & this.dataMask) : this.resultParitySign & 0xffff);
this.resultZeroCarry = this.regECX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
},
@ -906,9 +946,9 @@ var X86OpXX = {
* @this {X86CPU}
*/
opDECDX: function() {
this.resultAuxOverflow = this.regEDX;
this.regEDX = (this.regEDX & ~this.dataMask) | (this.resultParitySign = this.regEDX - 1) & this.dataMask;
this.resultValue = this.regEDX | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultParitySign = (this.resultAuxOverflow = this.regEDX) - 1;
this.regEDX = (I386? (this.regEDX & ~this.dataMask) | (this.resultParitySign & this.dataMask) : this.resultParitySign & 0xffff);
this.resultZeroCarry = this.regEDX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
},
@ -918,9 +958,14 @@ var X86OpXX = {
* @this {X86CPU}
*/
opDECBX: function() {
this.resultAuxOverflow = this.regEBX;
this.regEBX = (this.regEBX & ~this.dataMask) | (this.resultParitySign = this.regEBX - 1) & this.dataMask;
this.resultValue = this.regEBX | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultParitySign = (this.resultAuxOverflow = this.regEBX) - 1;
if (I386) {
this.regEBX = (this.regEBX & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEBX) & this.dataMask) >>> 16) | (this.regEBX & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEBX = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEBX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
},
@ -930,9 +975,10 @@ var X86OpXX = {
* @this {X86CPU}
*/
opDECSP: function() {
this.resultAuxOverflow = this.getSP();
this.setSP((this.resultAuxOverflow & ~this.dataMask) | (this.resultParitySign = this.resultAuxOverflow - 1) & this.dataMask);
this.resultValue = this.getSP() | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
var regESP;
this.resultParitySign = (this.resultAuxOverflow = this.getSP()) - 1;
this.setSP(regESP = (this.resultAuxOverflow & ~this.dataMask) | (this.resultParitySign & this.dataMask));
this.resultZeroCarry = (((regESP) & this.dataMask) >>> 16) | (regESP & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
},
@ -942,9 +988,14 @@ var X86OpXX = {
* @this {X86CPU}
*/
opDECBP: function() {
this.resultAuxOverflow = this.regEBP;
this.regEBP = (this.regEBP & ~this.dataMask) | (this.resultParitySign = this.regEBP - 1) & this.dataMask;
this.resultValue = this.regEBP | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultParitySign = (this.resultAuxOverflow = this.regEBP) - 1;
if (I386) {
this.regEBP = (this.regEBP & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEBP) & this.dataMask) >>> 16) | (this.regEBP & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEBP = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEBP | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
},
@ -954,21 +1005,31 @@ var X86OpXX = {
* @this {X86CPU}
*/
opDECSI: function() {
this.resultAuxOverflow = this.regESI;
this.regESI = (this.regESI & ~this.dataMask) | (this.resultParitySign = this.regESI - 1) & this.dataMask;
this.resultValue = this.regESI | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultParitySign = (this.resultAuxOverflow = this.regESI) - 1;
if (I386) {
this.regESI = (this.regESI & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regESI) & this.dataMask) >>> 16) | (this.regESI & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regESI = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regESI | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
},
/**
/**`
* op=0x4F (DEC DI)
*
* @this {X86CPU}
*/
opDECDI: function() {
this.resultAuxOverflow = this.regEDI;
this.regEDI = (this.regEDI & ~this.dataMask) | (this.resultParitySign = this.regEDI - 1) & this.dataMask;
this.resultValue = this.regEDI | (((this.resultValue & this.resultSize)? 1 : 0) << 16);
this.resultParitySign = (this.resultAuxOverflow = this.regEDI) - 1;
if (I386) {
this.regEDI = (this.regEDI & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEDI) & this.dataMask) >>> 16) | (this.regEDI & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEDI = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEDI | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
},
@ -1337,8 +1398,6 @@ var X86OpXX = {
* op=0x6C (INSB) (80186/80188 and up)
*
* NOTE: Segment overrides are ignored for this instruction, so we must use segES instead of segData.
* In fact, this is a good thing, because otherwise we would need a separate internal register to track
* the effect of segment overrides on ES (eg, segExtra), because segData tracks overrides for DS only.
*
* @this {X86CPU}
*/
@ -1370,13 +1429,12 @@ var X86OpXX = {
this.nStepCycles -= nCycles;
this.regECX -= nDelta;
if (nReps) {
/*
* We have to back up to the prefix byte(s), not just to the string instruction,
* because if a h/w interrupt is acknowledged before the next repetition begins,
* the interrupt handler will return us to an invalid state.
*/
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -1385,8 +1443,6 @@ var X86OpXX = {
* op=0x6D (INSW) (80186/80188 and up)
*
* NOTE: Segment overrides are ignored for this instruction, so we must use segDS instead of segData.
* In fact, this is a good thing, because otherwise we would need a separate internal register to track
* the effect of segment overrides on ES (eg, segExtra), because segData tracks overrides for DS only.
*
* @this {X86CPU}
*/
@ -1420,13 +1476,12 @@ var X86OpXX = {
this.nStepCycles -= nCycles;
this.regECX -= nDelta;
if (nReps) {
/*
* We have to back up to the prefix byte(s), not just to the string instruction,
* because if a h/w interrupt is acknowledged before the next repetition begins,
* the interrupt handler will return us to an invalid state.
*/
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -1465,13 +1520,12 @@ var X86OpXX = {
if (BACKTRACK) this.backTrack.btiIO = this.backTrack.btiMemLo;
this.bus.checkPortOutputNotify(this.regEDX, b, this.regLIP - nDelta - 1);
if (nReps) {
/*
* We have to back up to the prefix byte(s), not just to the string instruction,
* because if a h/w interrupt is acknowledged before the next repetition begins,
* the interrupt handler will return us to an invalid state.
*/
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -1513,13 +1567,12 @@ var X86OpXX = {
if (BACKTRACK) this.backTrack.btiIO = this.backTrack.btiMemHi;
this.bus.checkPortOutputNotify(this.regEDX, w >> 8, addrFrom);
if (nReps) {
/*
* We have to back up to the prefix byte(s), not just to the string instruction,
* because if a h/w interrupt is acknowledged before the next repetition begins,
* the interrupt handler will return us to an invalid state.
*/
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2030,8 +2083,8 @@ var X86OpXX = {
*/
opXCHGCX: function() {
var temp = this.regEAX;
this.regEAX = (this.regEAX & ~this.dataMask) | (this.regECX & this.dataMask);
this.regECX = (this.regECX & ~this.dataMask) | (temp & this.dataMask);
this.regEAX = (I386? (this.regEAX & ~this.dataMask) | (this.regECX & this.dataMask) : this.regECX);
this.regECX = (I386? (this.regECX & ~this.dataMask) | (temp & this.dataMask) : temp);
if (BACKTRACK) {
temp = this.backTrack.btiAL; this.backTrack.btiAL = this.backTrack.btiCL; this.backTrack.btiCL = temp;
temp = this.backTrack.btiAH; this.backTrack.btiAH = this.backTrack.btiCH; this.backTrack.btiCH = temp;
@ -2045,8 +2098,8 @@ var X86OpXX = {
*/
opXCHGDX: function() {
var temp = this.regEAX;
this.regEAX = (this.regEAX & ~this.dataMask) | (this.regEDX & this.dataMask);
this.regEDX = (this.regEDX & ~this.dataMask) | (temp & this.dataMask);
this.regEAX = (I386? (this.regEAX & ~this.dataMask) | (this.regEDX & this.dataMask) : this.regEDX);
this.regEDX = (I386? (this.regEDX & ~this.dataMask) | (temp & this.dataMask) : temp);
if (BACKTRACK) {
temp = this.backTrack.btiAL; this.backTrack.btiAL = this.backTrack.btiDL; this.backTrack.btiDL = temp;
temp = this.backTrack.btiAH; this.backTrack.btiAH = this.backTrack.btiDH; this.backTrack.btiDH = temp;
@ -2060,8 +2113,8 @@ var X86OpXX = {
*/
opXCHGBX: function() {
var temp = this.regEAX;
this.regEAX = (this.regEAX & ~this.dataMask) | (this.regEBX & this.dataMask);
this.regEBX = (this.regEBX & ~this.dataMask) | (temp & this.dataMask);
this.regEAX = (I386? (this.regEAX & ~this.dataMask) | (this.regEBX & this.dataMask) : this.regEBX);
this.regEBX = (I386? (this.regEBX & ~this.dataMask) | (temp & this.dataMask) : temp);
if (BACKTRACK) {
temp = this.backTrack.btiAL; this.backTrack.btiAL = this.backTrack.btiBL; this.backTrack.btiBL = temp;
temp = this.backTrack.btiAH; this.backTrack.btiAH = this.backTrack.btiBH; this.backTrack.btiBH = temp;
@ -2076,8 +2129,8 @@ var X86OpXX = {
opXCHGSP: function() {
var temp = this.regEAX;
var regESP = this.getSP();
this.regEAX = (this.regEAX & ~this.dataMask) | (regESP & this.dataMask);
this.setSP((regESP & ~this.dataMask) | (temp & this.dataMask));
this.regEAX = (I386? (this.regEAX & ~this.dataMask) | (regESP & this.dataMask) : regESP);
this.setSP((I386? (regESP & ~this.dataMask) | (temp & this.dataMask) : temp));
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiAH = 0;
this.nStepCycles -= 3; // this form of XCHG takes 3 cycles on all CPUs
},
@ -2088,8 +2141,8 @@ var X86OpXX = {
*/
opXCHGBP: function() {
var temp = this.regEAX;
this.regEAX = (this.regEAX & ~this.dataMask) | (this.regEBP & this.dataMask);
this.regEBP = (this.regEBP & ~this.dataMask) | (temp & this.dataMask);
this.regEAX = (I386? (this.regEAX & ~this.dataMask) | (this.regEBP & this.dataMask) : this.regEBP);
this.regEBP = (I386? (this.regEBP & ~this.dataMask) | (temp & this.dataMask) : temp);
if (BACKTRACK) {
temp = this.backTrack.btiAL; this.backTrack.btiAL = this.backTrack.btiBPLo; this.backTrack.btiBPLo = temp;
temp = this.backTrack.btiAH; this.backTrack.btiAH = this.backTrack.btiBPHi; this.backTrack.btiBPHi = temp;
@ -2103,8 +2156,8 @@ var X86OpXX = {
*/
opXCHGSI: function() {
var temp = this.regEAX;
this.regEAX = (this.regEAX & ~this.dataMask) | (this.regESI & this.dataMask);
this.regESI = (this.regESI & ~this.dataMask) | (temp & this.dataMask);
this.regEAX = (I386? (this.regEAX & ~this.dataMask) | (this.regESI & this.dataMask) : this.regESI);
this.regESI = (I386? (this.regESI & ~this.dataMask) | (temp & this.dataMask) : temp);
if (BACKTRACK) {
temp = this.backTrack.btiAL; this.backTrack.btiAL = this.backTrack.btiSILo; this.backTrack.btiSILo = temp;
temp = this.backTrack.btiAH; this.backTrack.btiAH = this.backTrack.btiSIHi; this.backTrack.btiSIHi = temp;
@ -2118,8 +2171,8 @@ var X86OpXX = {
*/
opXCHGDI: function() {
var temp = this.regEAX;
this.regEAX = (this.regEAX & ~this.dataMask) | (this.regEDI & this.dataMask);
this.regEDI = (this.regEDI & ~this.dataMask) | (temp & this.dataMask);
this.regEAX = (I386? (this.regEAX & ~this.dataMask) | (this.regEDI & this.dataMask) : this.regEDI);
this.regEDI = (I386? (this.regEDI & ~this.dataMask) | (temp & this.dataMask) : temp);
if (BACKTRACK) {
temp = this.backTrack.btiAL; this.backTrack.btiAL = this.backTrack.btiDILo; this.backTrack.btiDILo = temp;
temp = this.backTrack.btiAH; this.backTrack.btiAH = this.backTrack.btiDIHi; this.backTrack.btiDIHi = temp;
@ -2317,17 +2370,12 @@ var X86OpXX = {
this.nStepCycles -= nCycles;
this.regECX -= nDelta;
if (nReps) {
/*
* We have to back up to the prefix byte(s), not just to the string instruction,
* because if a h/w interrupt is acknowledged before the next repetition begins,
* the interrupt handler will return us to an invalid state.
*
* TODO: Decide what to do about string instructions with multiple (ie, redundant)
* SEG prefixes, and whether we should strictly emulate the 8086's failure to restart
* string instructions with multiple prefixes.
*/
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
if (BUGS_8086) {
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2355,17 +2403,12 @@ var X86OpXX = {
this.nStepCycles -= nCycles;
this.regECX -= nDelta;
if (nReps) {
/*
* We have to back up to the prefix byte(s), not just to the string instruction,
* because if a h/w interrupt is acknowledged before the next repetition begins,
* the interrupt handler will return us to an invalid state.
*
* TODO: Decide what to do about string instructions with multiple (ie, redundant)
* SEG prefixes, and whether we should strictly emulate the 8086's failure to restart
* string instructions with multiple prefixes.
*/
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
if (BUGS_8086) {
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2403,17 +2446,12 @@ var X86OpXX = {
* two values are equal, we must continue.
*/
if (nReps && this.getZF() == (this.opPrefixes & X86.OPFLAG.REPZ)) {
/*
* We have to back up to the prefix byte(s), not just to the string instruction,
* because if a h/w interrupt is acknowledged before the next repetition begins,
* the interrupt handler will return us to an invalid state.
*
* TODO: Decide what to do about string instructions with multiple (ie, redundant)
* SEG prefixes, and whether we should strictly emulate the 8086's failure to restart
* string instructions with multiple prefixes.
*/
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
if (BUGS_8086) {
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2451,17 +2489,12 @@ var X86OpXX = {
* two values are equal, we must continue.
*/
if (nReps && this.getZF() == (this.opPrefixes & X86.OPFLAG.REPZ)) {
/*
* We have to back up to the prefix byte(s), not just to the string instruction,
* because if a h/w interrupt is acknowledged before the next repetition begins,
* the interrupt handler will return us to an invalid state.
*
* TODO: Decide what to do about string instructions with multiple (ie, redundant)
* SEG prefixes, and whether we should strictly emulate the 8086's failure to restart
* string instructions with multiple prefixes.
*/
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
if (BUGS_8086) {
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2472,7 +2505,7 @@ var X86OpXX = {
* @this {X86CPU}
*/
opTESTALb: function() {
this.resultValue = this.resultParitySign = this.resultAuxOverflow = this.regEAX & this.getIPByte();
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = this.regEAX & this.getIPByte();
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= this.CYCLES.nOpCyclesAAA;
},
@ -2482,7 +2515,7 @@ var X86OpXX = {
* @this {X86CPU}
*/
opTESTAXw: function() {
this.resultValue = this.resultParitySign = this.resultAuxOverflow = this.regEAX & this.getIPWord();
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = this.regEAX & this.getIPWord();
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= this.CYCLES.nOpCyclesAAA;
},
@ -2490,8 +2523,6 @@ var X86OpXX = {
* op=0xAA (STOSB)
*
* NOTES: Segment overrides are ignored for this instruction, so we must use segES instead of segData.
* In fact, this is a good thing, because otherwise we would need a separate internal register to track
* the effect of segment overrides on ES (eg, segExtra), because segData tracks overrides for DS only.
*
* @this {X86CPU}
*/
@ -2515,13 +2546,12 @@ var X86OpXX = {
this.nStepCycles -= nCycles;
this.regECX -= nDelta;
if (nReps) {
/*
* We have to back up to the prefix byte(s), not just to the string instruction,
* because if a h/w interrupt is acknowledged before the next repetition begins,
* the interrupt handler will return us to an invalid state.
*/
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2530,8 +2560,6 @@ var X86OpXX = {
* op=0xAB (STOSW)
*
* NOTES: Segment overrides are ignored for this instruction, so we must use segES instead of segData.
* In fact, this is a good thing, because otherwise we would need a separate internal register to track
* the effect of segment overrides on ES (eg, segExtra), because segData tracks overrides for DS only.
*
* @this {X86CPU}
*/
@ -2558,13 +2586,12 @@ var X86OpXX = {
this.nStepCycles -= nCycles;
this.regECX -= nDelta;
if (nReps) {
/*
* We have to back up to the prefix byte(s), not just to the string instruction,
* because if a h/w interrupt is acknowledged before the next repetition begins,
* the interrupt handler will return us to an invalid state.
*/
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2591,17 +2618,12 @@ var X86OpXX = {
this.nStepCycles -= nCycles;
this.regECX -= nDelta;
if (nReps) {
/*
* We have to back up to the prefix byte(s), not just to the string instruction,
* because if a h/w interrupt is acknowledged before the next repetition begins,
* the interrupt handler will return us to an invalid state.
*
* TODO: Decide what to do about string instructions with multiple (ie, redundant)
* SEG prefixes, and whether we should strictly emulate the 8086's failure to restart
* string instructions with multiple prefixes.
*/
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
if (BUGS_8086) {
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2630,17 +2652,12 @@ var X86OpXX = {
this.nStepCycles -= nCycles;
this.regECX -= nDelta;
if (nReps) {
/*
* We have to back up to the prefix byte(s), not just to the string instruction,
* because if a h/w interrupt is acknowledged before the next repetition begins,
* the interrupt handler will return us to an invalid state.
*
* TODO: Decide what to do about string instructions with multiple (ie, redundant)
* SEG prefixes, and whether we should strictly emulate the 8086's failure to restart
* string instructions with multiple prefixes.
*/
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
if (BUGS_8086) {
this.advanceIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2674,13 +2691,12 @@ var X86OpXX = {
* two values are equal, we must continue.
*/
if (nReps && this.getZF() == (this.opPrefixes & X86.OPFLAG.REPZ)) {
/*
* We have to back up to the prefix byte(s), not just to the string instruction,
* because if a h/w interrupt is acknowledged before the next repetition begins,
* the interrupt handler will return us to an invalid state.
*/
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2714,13 +2730,12 @@ var X86OpXX = {
* two values are equal, we must continue.
*/
if (nReps && this.getZF() == (this.opPrefixes & X86.OPFLAG.REPZ)) {
/*
* We have to back up to the prefix byte(s), not just to the string instruction,
* because if a h/w interrupt is acknowledged before the next repetition begins,
* the interrupt handler will return us to an invalid state.
*/
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
if (BUGS_8086) {
this.advanceIP(-2); // this instruction does not support segment overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -3151,7 +3166,7 @@ var X86OpXX = {
var bRemainder = AL % bDivisor;
this.regEAX = (bQuotient << 8) | bRemainder;
this.resultSize = X86.RESULT.SIZE_BYTE;
this.resultValue = this.resultParitySign = AL;
this.resultZeroCarry = this.resultParitySign = AL;
this.nStepCycles -= this.CYCLES.nOpCyclesAAM;
},
/**
@ -3172,7 +3187,7 @@ var X86OpXX = {
*/
opAAD: function() {
var bMultiplier = this.getIPByte();
this.resultValue = this.resultParitySign = this.regEAX = (((this.regEAX >> 8) * bMultiplier) + this.regEAX) & 0xff;
this.resultZeroCarry = this.resultParitySign = this.regEAX = (((this.regEAX >> 8) * bMultiplier) + this.regEAX) & 0xff;
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= this.CYCLES.nOpCyclesAAD;
},
@ -3218,7 +3233,7 @@ var X86OpXX = {
*/
opLOOPNZ: function() {
var disp = this.getIPDisp();
if ((this.regECX = (this.regECX - 1) & this.addrMask) && (this.resultValue & (this.resultSize - 1))) {
if ((this.regECX = (this.regECX - 1) & this.addrMask) && (this.resultZeroCarry & (this.resultSize - 1))) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.CYCLES.nOpCyclesLoopNZ;
return;
@ -3232,7 +3247,7 @@ var X86OpXX = {
*/
opLOOPZ: function() {
var disp = this.getIPDisp();
if ((this.regECX = (this.regECX - 1) & this.addrMask) && !(this.resultValue & (this.resultSize - 1))) {
if ((this.regECX = (this.regECX - 1) & this.addrMask) && !(this.resultZeroCarry & (this.resultSize - 1))) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.CYCLES.nOpCyclesLoopZ;
return;
@ -3544,7 +3559,7 @@ var X86OpXX = {
* @this {X86CPU}
*/
opCLC: function() {
this.resultValue &= ~this.resultSize;
this.resultZeroCarry &= ~this.resultSize;
this.nStepCycles -= 2; // CLC takes 2 cycles on all CPUs
},
/**
@ -3553,7 +3568,7 @@ var X86OpXX = {
* @this {X86CPU}
*/
opSTC: function() {
this.resultValue |= this.resultSize;
this.resultZeroCarry |= this.resultSize;
this.nStepCycles -= 2; // STC takes 2 cycles on all CPUs
},
/**

View file

@ -1,5 +1,5 @@
/**
* @fileoverview Implements PCjs X86 Segment objects
* @fileoverview Implements PCjs X86 Segment Registers
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
* @version 1.0
* Created 2014-Sep-10
@ -45,7 +45,7 @@ if (typeof module !== 'undefined') {
* @constructor
* @param {X86CPU} cpu
* @param {number} id
* @param {string} [sName] segment name
* @param {string} [sName] segment register name
* @param {boolean} [fProt] true if segment register used exclusively in protected-mode (eg, segLDT)
*/
function X86Seg(cpu, id, sName, fProt)
@ -81,12 +81,17 @@ function X86Seg(cpu, id, sName, fProt)
* As long as setCSIP() or opHelpINT() are used for all CS changes, fCall is set automatically.
*
* TODO: Consider making fCall a parameter to load(), instead of a property that must be set prior to
* calling load(); the downside (and why I didn't do that in the first place) is that such a parameter
* is meaningless for segments other than segCS.
* calling load(); the downside is that such a parameter is meaningless for segments other than segCS.
*/
this.awParms = (this.id == X86Seg.ID.CODE? new Array(32) : []);
this.fCall = null;
this.fStackSwitch = false;
/*
* The following properties are used for STACK segments only (ie, segSS); we want to make it easier
* for setSS() to set stack lower and upper limits, which requires knowing whether or not the segment is
* marked as EXPDOWN.
*/
this.fExpDown = false;
this.updateMode(fProt);
}
@ -117,6 +122,7 @@ X86Seg.ID = {
*/
X86Seg.loadReal = function loadReal(sel, fSuppress)
{
this.cpu.assert(!(sel & ~0xffff));
this.sel = sel;
this.dataSize = this.addrSize = 2;
this.dataMask = this.addrMask = 0xffff;
@ -152,6 +158,8 @@ X86Seg.loadProt = function loadProt(sel, fSuppress)
var addrDTLimit;
var cpu = this.cpu;
this.cpu.assert(!(sel & ~0xffff));
if (!(sel & X86.SEL.LDT)) {
addrDT = cpu.addrGDT;
addrDTLimit = cpu.addrGDTLimit;
@ -166,8 +174,8 @@ X86Seg.loadProt = function loadProt(sel, fSuppress)
* grief here. We avoid that grief by 1) relying on the Debugger setting fSuppress to true, and 2) skipping
* segment lookup if the descriptor table being referenced is zero.
*
* TODO: This could probably be simplified to a test of addrDT; please note, however, that there's nothing
* in the design of the CPU that prevents the GDT or LDT being located at physical address zero.
* TODO: This could probably be simplified to a test of addrDT; however, there's nothing in the design
* of the CPU that prevents the GDT or LDT being located at physical address zero.
*/
if (!fSuppress || addrDT) {
var addrDesc = addrDT + (sel & X86.SEL.MASK);
@ -193,7 +201,7 @@ X86Seg.loadProt = function loadProt(sel, fSuppress)
*
* @this {X86Seg}
* @param {number} nIDT
* @return {number} address from selected veector, or ADDR_INVALID if error (TODO: No error conditions exist yet)
* @return {number} address from selected vector, or ADDR_INVALID if error (TODO: No error conditions exist yet)
*/
X86Seg.loadIDTReal = function loadIDTReal(nIDT)
{
@ -455,7 +463,7 @@ X86Seg.switchTSS = function switchTSS(selNew, fNest)
offSP = (this.cpl << 2) + X86.TSS.CPL0_SP;
offSS = offSP + 2;
}
cpu.setSS(cpu.getShort(addrNew + offSS)); // TODO: Do we care that cpu.setSS() -- unlike segSS.load() -- will also set X86.OPFLAG.NOINTR?
cpu.setSS(cpu.getShort(addrNew + offSS), true);
cpu.setSP(cpu.getShort(addrNew + offSP));
cpu.segLDT.load(cpu.getShort(addrNew + X86.TSS.TASK_LDT));
if (fNest) cpu.setShort(addrNew + X86.TSS.PREV_TSS, selOld);
@ -592,7 +600,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
break;
}
regSP = cpu.popWord();
cpu.setSS(cpu.popWord()); // TODO: Do we care that cpu.setSS() -- unlike segSS.load() -- will also set X86.OPFLAG.NOINTR?
cpu.setSS(cpu.popWord(), true);
cpu.setSP(regSP);
this.fStackSwitch = true;
}
@ -659,7 +667,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
offSS = offSP + 2;
regSSPrev = cpu.getSS();
regSPPrev = cpu.getSP();
cpu.setSS(cpu.getShort(addrTSS + offSS)); // TODO: Do we care that cpu.setSS() -- unlike segSS.load() -- will also set X86.OPFLAG.NOINTR?
cpu.setSS(cpu.getShort(addrTSS + offSS), true);
cpu.setSP(cpu.getShort(addrTSS + offSP));
cpu.pushWord(regSSPrev);
cpu.pushWord(regSPPrev);
@ -819,6 +827,7 @@ X86Seg.prototype.updateMode = function(fProt)
if (fProt === undefined) {
fProt = !!(this.cpu.regMSW & X86.MSW.PE);
}
this.fExpDown = false;
if (fProt) {
this.load = X86Seg.loadProt;
this.loadIDT = X86Seg.loadIDTProt;
@ -843,6 +852,7 @@ X86Seg.prototype.updateMode = function(fProt)
if ((this.acc & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.EXPDOWN)) == X86.DESC.ACC.TYPE.EXPDOWN) {
if (this.checkRead == X86Seg.checkReadProt) this.checkRead = X86Seg.checkReadProtDown;
if (this.checkWrite == X86Seg.checkWriteProt) this.checkWrite = X86Seg.checkWriteProtDown;
this.fExpDown = true;
}
}
this.cpl = this.sel & X86.SEL.RPL;