Fixed problems with SHRD, SHLD (need to use unsigned shifts) and BT* instructions that use signed bit offsets

This commit is contained in:
Jeff Parsons 2015-09-05 16:03:49 -07:00
commit 40709af7f5
2 changed files with 57 additions and 22 deletions

View file

@ -2262,6 +2262,9 @@ if (DEBUGGER) {
this.dumpBlocks(aBlocks, asArgs[0]); this.dumpBlocks(aBlocks, asArgs[0]);
}; };
/*
* Table of system (non-segment) descriptors, including indicators of which ones are gates.
*/
Debugger.SYSDESCS = { Debugger.SYSDESCS = {
0x0100: ["tss286", false], 0x0100: ["tss286", false],
0x0200: ["ldt", false], 0x0200: ["ldt", false],

View file

@ -292,6 +292,9 @@ X86.fnBSR = function BSR(dst, src)
/** /**
* fnBT(dst, src) * fnBT(dst, src)
* *
* In this form of BT, src is an immediate operand (OR dst is register operand); immediate operands
* are supposed to be masked with either 0xf or 0x1f for 16-bit or 32-bit operands, respectively.
*
* @this {X86CPU} * @this {X86CPU}
* @param {number} dst * @param {number} dst
* @param {number} src * @param {number} src
@ -299,7 +302,8 @@ X86.fnBSR = function BSR(dst, src)
*/ */
X86.fnBT = function BT(dst, src) X86.fnBT = function BT(dst, src)
{ {
if (dst & (1 << (src & 0x1f))) this.setCF(); else this.clearCF(); var bit = 1 << (src & (this.sizeData == 2? 0xf : 0x1f));
if (dst & bit) this.setCF(); else this.clearCF();
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 3 : 6); this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 3 : 6);
this.opFlags |= X86.OPFLAG.NOWRITE; this.opFlags |= X86.OPFLAG.NOWRITE;
return dst; return dst;
@ -308,6 +312,9 @@ X86.fnBT = function BT(dst, src)
/** /**
* fnBTC(dst, src) * fnBTC(dst, src)
* *
* In this form of BTC, src is an immediate operand (OR dst is register operand); immediate operands
* are supposed to be masked with either 0xf or 0x1f for 16-bit or 32-bit operands, respectively.
*
* @this {X86CPU} * @this {X86CPU}
* @param {number} dst * @param {number} dst
* @param {number} src * @param {number} src
@ -315,7 +322,7 @@ X86.fnBT = function BT(dst, src)
*/ */
X86.fnBTC = function BTC(dst, src) X86.fnBTC = function BTC(dst, src)
{ {
var bit = 1 << (src & 0x1f); var bit = 1 << (src & (this.sizeData == 2? 0xf : 0x1f));
if (dst & bit) this.setCF(); else this.clearCF(); if (dst & bit) this.setCF(); else this.clearCF();
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 6 : 8); this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 6 : 8);
return dst ^ bit; return dst ^ bit;
@ -324,6 +331,9 @@ X86.fnBTC = function BTC(dst, src)
/** /**
* fnBTR(dst, src) * fnBTR(dst, src)
* *
* In this form of BTR, src is an immediate operand (OR dst is register operand); immediate operands
* are supposed to be masked with either 0xf or 0x1f for 16-bit or 32-bit operands, respectively.
*
* @this {X86CPU} * @this {X86CPU}
* @param {number} dst * @param {number} dst
* @param {number} src * @param {number} src
@ -331,7 +341,7 @@ X86.fnBTC = function BTC(dst, src)
*/ */
X86.fnBTR = function BTR(dst, src) X86.fnBTR = function BTR(dst, src)
{ {
var bit = 1 << (src & 0x1f); var bit = 1 << (src & (this.sizeData == 2? 0xf : 0x1f));
if (dst & bit) this.setCF(); else this.clearCF(); if (dst & bit) this.setCF(); else this.clearCF();
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 6 : 8); this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 6 : 8);
return dst & ~bit; return dst & ~bit;
@ -340,6 +350,9 @@ X86.fnBTR = function BTR(dst, src)
/** /**
* fnBTS(dst, src) * fnBTS(dst, src)
* *
* In this form of BTS, src is an immediate operand (OR dst is register operand); immediate operands
* are supposed to be masked with either 0xf or 0x1f for 16-bit or 32-bit operands, respectively.
*
* @this {X86CPU} * @this {X86CPU}
* @param {number} dst * @param {number} dst
* @param {number} src * @param {number} src
@ -347,7 +360,7 @@ X86.fnBTR = function BTR(dst, src)
*/ */
X86.fnBTS = function BTS(dst, src) X86.fnBTS = function BTS(dst, src)
{ {
var bit = 1 << (src & 0x1f); var bit = 1 << (src & (this.sizeData == 2? 0xf : 0x1f));
if (dst & bit) this.setCF(); else this.clearCF(); if (dst & bit) this.setCF(); else this.clearCF();
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 6 : 8); this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 6 : 8);
return dst | bit; return dst | bit;
@ -356,7 +369,7 @@ X86.fnBTS = function BTS(dst, src)
/** /**
* fnBTMem(dst, src) * 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; * In this form of BT, src is a register operand, which is NOT truncated if dst is a memory operand;
* however, if dst is also a register operand, then we defer to the simpler function, fnBT(). * however, if dst is also a register operand, then we defer to the simpler function, fnBT().
* *
* @this {X86CPU} * @this {X86CPU}
@ -373,15 +386,19 @@ X86.fnBTMem = function BTMem(dst, src)
/* /*
* TODO: Consider a worker function that performs the following block of code for: BT, BTC, BTR, and BTS. * TODO: Consider a worker function that performs the following block of code for: BT, BTC, BTR, and BTS.
* It's somewhat inconvenient, because it needs to provide two results: an updated src AND an updated dst. * It's somewhat inconvenient, because it needs to provide two results: an updated src AND an updated dst.
*
* src is usually positive BUT can also be negative (as the IA32 spec says: "The offset operand then selects
* a bit position within the range −231 to 231 − 1 for a register offset and 0 to 31 for an immediate offset.")
*/ */
if ((src >>> 3) >= this.sizeData) { var max = this.sizeData << 3;
if (src >= max || src < -max) {
/* /*
* We just divided src by 8, but now we need to divide src by 16 or 32, according to the OPERAND size, * We just divided src by 8, but now we need to divide src 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 * 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 must add to * multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we must add to
* the original EA offset. * the original EA offset.
*/ */
var i = src >>> (this.sizeData == 2? 4 : 5); var i = src >> (this.sizeData == 2? 4 : 5);
dst = this.getEAWord(this.segEA, this.offEA + i * this.sizeData); dst = this.getEAWord(this.segEA, this.offEA + i * this.sizeData);
} }
/* /*
@ -401,7 +418,7 @@ X86.fnBTMem = function BTMem(dst, src)
/** /**
* fnBTCMem(dst, src) * 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; * In this form of BTC, src is a register operand, which is NOT truncated if dst is a memory operand;
* however, if dst is also a register operand, then we defer to the simpler function, fnBTC(). * however, if dst is also a register operand, then we defer to the simpler function, fnBTC().
* *
* @this {X86CPU} * @this {X86CPU}
@ -415,14 +432,19 @@ X86.fnBTCMem = function BTCMem(dst, src)
return X86.fnBTC.call(this, dst, src); return X86.fnBTC.call(this, dst, src);
} }
if ((src >>> 3) >= this.sizeData) { /*
* src is usually positive BUT can also be negative (as the IA32 spec says: "The offset operand then selects
* a bit position within the range −231 to 231 − 1 for a register offset and 0 to 31 for an immediate offset.")
*/
var max = this.sizeData << 3;
if (src >= max || src < -max) {
/* /*
* We just divided src by 8, but now we need to divide src by 16 or 32, according to the OPERAND size, * We just divided src by 8, but now we need to divide src 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 * 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 must add to * multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we must add to
* the original EA offset. * the original EA offset.
*/ */
var i = src >>> (this.sizeData == 2? 4 : 5); var i = src >> (this.sizeData == 2? 4 : 5);
dst = this.getEAWord(this.segEA, this.offEA + i * this.sizeData); dst = this.getEAWord(this.segEA, this.offEA + i * this.sizeData);
} }
/* /*
@ -438,7 +460,7 @@ X86.fnBTCMem = function BTCMem(dst, src)
/** /**
* fnBTRMem(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; * In this form of BTR, src is a register operand, which is NOT truncated if dst is a memory operand;
* however, if dst is also a register operand, then we defer to the simpler function, fnBTR(). * however, if dst is also a register operand, then we defer to the simpler function, fnBTR().
* *
* @this {X86CPU} * @this {X86CPU}
@ -452,14 +474,19 @@ X86.fnBTRMem = function BTRMem(dst, src)
return X86.fnBTR.call(this, dst, src); return X86.fnBTR.call(this, dst, src);
} }
if ((src >>> 3) >= this.sizeData) { /*
* src is usually positive BUT can also be negative (as the IA32 spec says: "The offset operand then selects
* a bit position within the range −231 to 231 − 1 for a register offset and 0 to 31 for an immediate offset.")
*/
var max = this.sizeData << 3;
if (src >= max || src < -max) {
/* /*
* We just divided src by 8, but now we need to divide src by 16 or 32, according to the OPERAND size, * We just divided src by 8, but now we need to divide src 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 * 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 must add to * multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we must add to
* the original EA offset. * the original EA offset.
*/ */
var i = src >>> (this.sizeData == 2? 4 : 5); var i = src >> (this.sizeData == 2? 4 : 5);
dst = this.getEAWord(this.segEA, this.offEA + i * this.sizeData); dst = this.getEAWord(this.segEA, this.offEA + i * this.sizeData);
} }
/* /*
@ -475,7 +502,7 @@ X86.fnBTRMem = function BTRMem(dst, src)
/** /**
* fnBTSMem(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; * In this form of BTS, src is a register operand, which is NOT truncated if dst is a memory operand;
* however, if dst is also a register operand, then we defer to the simpler function, fnBTS(). * however, if dst is also a register operand, then we defer to the simpler function, fnBTS().
* *
* @this {X86CPU} * @this {X86CPU}
@ -489,14 +516,19 @@ X86.fnBTSMem = function BTSMem(dst, src)
return X86.fnBTS.call(this, dst, src); return X86.fnBTS.call(this, dst, src);
} }
if ((src >>> 3) >= this.sizeData) { /*
* src is usually positive BUT can also be negative (as the IA32 spec says: "The offset operand then selects
* a bit position within the range −231 to 231 − 1 for a register offset and 0 to 31 for an immediate offset.")
*/
var max = this.sizeData << 3;
if (src >= max || src < -max) {
/* /*
* We just divided src by 8, but now we need to divide src by 16 or 32, according to the OPERAND size, * We just divided src by 8, but now we need to divide src 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 * 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 must add to * multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we must add to
* the original EA offset. * the original EA offset.
*/ */
var i = src >>> (this.sizeData == 2? 4 : 5); var i = src >> (this.sizeData == 2? 4 : 5);
dst = this.getEAWord(this.segEA, this.offEA + i * this.sizeData); dst = this.getEAWord(this.segEA, this.offEA + i * this.sizeData);
} }
/* /*
@ -3093,7 +3125,7 @@ X86.fnSHLDw = function SHLDw(dst, src, count)
count -= 16; count -= 16;
} }
var carry = dst << (count - 1); var carry = dst << (count - 1);
dst = ((carry << 1) | (src >> (16 - count))) & 0xffff; dst = ((carry << 1) | (src >>> (16 - count))) & 0xffff;
this.setLogicResult(dst, X86.RESULT.WORD, carry & X86.RESULT.WORD); this.setLogicResult(dst, X86.RESULT.WORD, carry & X86.RESULT.WORD);
} }
return dst; return dst;
@ -3112,7 +3144,7 @@ X86.fnSHLDd = function SHLDd(dst, src, count)
{ {
if (count) { if (count) {
var carry = dst << (count - 1); var carry = dst << (count - 1);
dst = (carry << 1) | (src >> (32 - count)); dst = (carry << 1) | (src >>> (32 - count));
this.setLogicResult(dst, X86.RESULT.DWORD, carry & X86.RESULT.DWORD); this.setLogicResult(dst, X86.RESULT.DWORD, carry & X86.RESULT.DWORD);
} }
return dst; return dst;
@ -3243,8 +3275,8 @@ X86.fnSHRDw = function SHRDw(dst, src, count)
dst = src; dst = src;
count -= 16; count -= 16;
} }
var carry = dst >> (count - 1); var carry = dst >>> (count - 1);
dst = ((carry >> 1) | (src << (16 - count))) & 0xffff; dst = ((carry >>> 1) | (src << (16 - count))) & 0xffff;
this.setLogicResult(dst, X86.RESULT.WORD, carry & 0x1); this.setLogicResult(dst, X86.RESULT.WORD, carry & 0x1);
} }
return dst; return dst;
@ -3262,8 +3294,8 @@ X86.fnSHRDw = function SHRDw(dst, src, count)
X86.fnSHRDd = function SHRDd(dst, src, count) X86.fnSHRDd = function SHRDd(dst, src, count)
{ {
if (count) { if (count) {
var carry = dst >> (count - 1); var carry = dst >>> (count - 1);
dst = (carry >> 1) | (src << (32 - count)); dst = (carry >>> 1) | (src << (32 - count));
this.setLogicResult(dst, X86.RESULT.DWORD, carry & 0x1); this.setLogicResult(dst, X86.RESULT.DWORD, carry & 0x1);
} }
return dst; return dst;