32-bit signed division fixes

This commit is contained in:
Jeff Parsons 2015-10-30 18:09:04 -07:00
commit a22bf5dc85
3 changed files with 72 additions and 69 deletions

View file

@ -28681,7 +28681,7 @@ IDIVEAXD EAX=80000001 EDX=0000FFFF PS=0000 EAX=FFFE0001 EDX=00020000 PS=0000
IDIVEAXD EAX=80000001 EDX=00000000 PS=0000 EAX=FFFFFFFF EDX=00000002 PS=0000
IDIVEAXD EAX=80000001 EDX=00000001 PS=0000 EAX=FFFFFFFD EDX=00000004 PS=0000
IDIVEAXD EAX=80000001 EDX=00000002 PS=0000 EAX=FFFFFFFB EDX=00000006 PS=0000
IDIVEAXD EAX=80000001 EDX=3FFFFFFF PS=0000 #DE EAX=80000001 EDX=3FFFFFFF PS=0000
IDIVEAXD EAX=80000001 EDX=3FFFFFFF PS=0000 EAX=80000000 EDX=00000001 PS=0000
IDIVEAXD EAX=80000001 EDX=40000000 PS=0000 #DE EAX=80000001 EDX=40000000 PS=0000
IDIVEAXD EAX=80000001 EDX=40000001 PS=0000 #DE EAX=80000001 EDX=40000001 PS=0000
IDIVEAXD EAX=80000001 EDX=7FFFFFFE PS=0000 #DE EAX=80000001 EDX=7FFFFFFE PS=0000

View file

@ -139,7 +139,7 @@ function CPU(parmsCPU, nCyclesDefault)
this.aVideo = [];
var cpu = this;
this.onRunTimeout = function() { cpu.runCPU(); };
this.onRunTimeout = function onRunTimeout() { cpu.runCPU(); };
this.setReady();
}

View file

@ -683,28 +683,6 @@ X86.fnDECw = function(dst, src)
return w & this.maskData;
};
/**
* fnIBTS(dst, src)
*
* As best I can determine, this function copies the specified bits from src (starting at bit 0 for CL
* bits) to dst (starting at bit offset in AX). For register operands, that's simple enough.
*
* TODO: If dst refers to a memory location, then the bit index may refer to higher memory locations, just
* like the BT/BTC/BTR/BTS instructions. For an instruction that no one was really able to use, except
* as a CPU stepping discriminator, that doesn't seem worth the effort.
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
X86.fnIBTS = function(dst, src)
{
var shift = (this.regEAX & this.maskData);
var mask = ((1 << (this.regECX & 0x1f)) - 1);
return (dst & ~(mask << shift)) | ((src & mask) << shift);
};
/**
* fnSet64(lo, hi)
*
@ -789,21 +767,20 @@ X86.fnShr64 = function(dst)
*
* This sets regMDLo to dstHi:dstLo / src, and regMDHi to dstHi:dstLo % src; all inputs are treated as unsigned.
*
* If fMDSet is not set, however, then there was a divide exception (ie, the divisor was either zero or too small).
*
* Refer to: http://lxr.linux.no/linux+v2.6.22/lib/div64.c
*
* @this {X86CPU}
* @param {number} dstLo (low 32-bit portion of dividend)
* @param {number} dstHi (high 32-bit portion of dividend)
* @param {number} src (32-bit divisor)
* @return {boolean} true if successful, false if overflow (ie, the divisor was either zero or too small)
*/
X86.fnDIV32 = function(dstLo, dstHi, src)
{
this.fMDSet = false;
src >>>= 0;
if (!src || src <= (dstHi >>> 0)) return;
if (!src || src <= (dstHi >>> 0)) {
return false;
}
var result = 0, bit = 1;
@ -827,7 +804,7 @@ X86.fnDIV32 = function(dstLo, dstHi, src)
this.regMDLo = result; // result is the quotient, which callers expect in the low MD register
this.regMDHi = rem[0]; // rem[0] is the remainder, which callers expect in the high MD register
this.fMDSet = true;
return true;
};
/**
@ -835,32 +812,42 @@ X86.fnDIV32 = function(dstLo, dstHi, src)
*
* This sets regMDLo to dstHi:dstLo / src, and regMDHi to dstHi:dstLo % src; all inputs are treated as signed.
*
* If fMDSet is not set, however, then there was a divide exception (ie, the divisor was either zero or too small).
*
* Refer to: http://lxr.linux.no/linux+v2.6.22/lib/div64.c
*
* @this {X86CPU}
* @param {number} dstLo (low 32-bit portion of dividend)
* @param {number} dstHi (high 32-bit portion of dividend)
* @param {number} src (32-bit divisor)
* @return {boolean} true if successful, false if overflow (ie, the divisor was either zero or too small)
*/
X86.fnIDIV32 = function(dstLo, dstHi, src)
{
var fNegLo = false, fNegHi = false;
var bNegLo = 0, bNegHi = 0;
/*
* dividend divisor quotient remainder
* (dst) (src) (lo) (hi)
* -------- ------- -------- ---------
* + + -> + +
* + - -> - +
* - + -> - -
* - - -> + -
*/
if (src < 0) {
src = -src|0;
fNegLo = !fNegLo;
bNegLo = 1 - bNegLo;
}
if (dstHi < 0) {
dstLo = -dstLo|0;
dstHi = (~dstHi + (dstLo? 0 : 1))|0;
fNegHi = true;
fNegLo = !fNegLo;
bNegHi = 1;
bNegLo = 1 - bNegLo;
}
X86.fnDIV32.call(this, dstLo, dstHi, src);
if (this.regMDLo > 0x7fffffff) this.fMDSet = false;
if (fNegLo) this.regMDLo = -this.regMDLo;
if (fNegHi) this.regMDHi = -this.regMDHi;
if (!X86.fnDIV32.call(this, dstLo, dstHi, src) || this.regMDLo > 0x7fffffff+bNegLo || this.regMDHi > 0x7fffffff+bNegHi) {
return false;
}
if (bNegLo) this.regMDLo = -this.regMDLo;
if (bNegHi) this.regMDHi = -this.regMDHi;
return true;
};
/**
@ -890,8 +877,8 @@ X86.fnDIVb = function(dst, src)
return dst;
}
this.fMDSet = true;
this.regMDLo = (result & 0xff) | (((src % dst) & 0xff) << 8);
this.fMDSet = true;
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesDivBR : this.cycleCounts.nOpCyclesDivBM);
this.opFlags |= X86.OPFLAG.NOWRITE;
@ -929,18 +916,18 @@ X86.fnDIVw = function(dst, src)
X86.fnDIVOverflow.call(this);
return dst;
}
this.fMDSet = true;
this.regMDLo = (result & 0xffff);
this.regMDHi = (src % dst) & 0xffff;
this.fMDSet = true;
}
else {
X86.fnDIV32.call(this, this.regEAX, this.regEDX, dst);
if (!this.fMDSet) {
if (!X86.fnDIV32.call(this, this.regEAX, this.regEDX, dst)) {
X86.fnDIVOverflow.call(this);
return dst;
}
this.regMDLo |= 0;
this.regMDHi |= 0;
this.fMDSet = true;
}
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesDivWR : this.cycleCounts.nOpCyclesDivWM);
@ -999,8 +986,8 @@ X86.fnIDIVb = function(dst, src)
return dst;
}
this.fMDSet = true;
this.regMDLo = (result & 0xff) | (((src % div) & 0xff) << 8);
this.fMDSet = true;
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesIDivBR : this.cycleCounts.nOpCyclesIDivBM);
this.opFlags |= X86.OPFLAG.NOWRITE;
@ -1046,18 +1033,18 @@ X86.fnIDIVw = function(dst, src)
return dst;
}
this.fMDSet = true;
this.regMDLo = (result & 0xffff);
this.regMDHi = (src % div) & 0xffff;
this.fMDSet = true;
}
else {
X86.fnIDIV32.call(this, this.regEAX, this.regEDX, dst);
if (!this.fMDSet) {
if (!X86.fnIDIV32.call(this, this.regEAX, this.regEDX, dst)) {
X86.fnDIVOverflow.call(this);
return dst;
}
this.regMDLo |= 0;
this.regMDHi |= 0;
this.fMDSet = true;
}
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesIDivWR : this.cycleCounts.nOpCyclesIDivWM);
@ -1084,23 +1071,17 @@ X86.fnIDIVw = function(dst, src)
*/
X86.fnIMUL8 = function(dst, src)
{
dst = this.getIPDisp();
var result = (((src << 16) >> 16) * dst)|0;
if (result > 32767 || result < -32768) {
this.setCF(); this.setOF();
} else {
this.clearCF(); this.clearOF();
}
result &= 0xffff;
/*
* NOTE: getIPDisp() already sign-extends the dst parameter, so fnIMULrw() needlessly sign-extends it again;
* a small price to pay for a common function.
*/
var result = X86.fnIMULrw.call(this, this.getIPDisp(), src);
/*
* NOTE: These are the cycle counts for the 80286; the 80186/80188 have slightly different values (ranges):
* 22-25 and 29-32 instead of 21 and 24, respectively. However, accurate cycle counts for the 80186/80188 is
* not super-critical. TODO: Fix this someday.
* NOTE: The above function already accounted for the 80386 cycle count, so we are simply accounting for the
* increased time on an 80286; the 80186/80188 have even larger values, but we'll worry about that another day.
*/
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 21 : 24);
if (this.model < X86.MODEL_80386) this.nStepCycles -= 12;
return result;
};
@ -1187,10 +1168,10 @@ X86.fnIMUL32 = function(dst, src)
*/
X86.fnIMULb = function(dst, src)
{
var result = ((((src = this.regEAX) << 24) >> 24) * ((dst << 24) >> 24))|0;
var result = (((this.regEAX << 24) >> 24) * ((dst << 24) >> 24))|0;
this.fMDSet = true;
this.regMDLo = result & 0xffff;
this.fMDSet = true;
if (result > 127 || result < -128) {
this.setCF(); this.setOF();
@ -1229,9 +1210,9 @@ X86.fnIMULw = function(dst, src)
if (this.sizeData == 2) {
src = this.regEAX & 0xffff;
var result = (((src << 16) >> 16) * ((dst << 16) >> 16))|0;
this.fMDSet = true;
this.regMDLo = result & 0xffff;
this.regMDHi = (result >> 16) & 0xffff;
this.fMDSet = true;
fOverflow = (result > 32767 || result < -32768);
} else {
X86.fnIMUL32.call(this, dst, this.regEAX);
@ -1975,8 +1956,8 @@ X86.fnMOVxx = function(dst, src)
*/
X86.fnMULb = function(dst, src)
{
this.regMDLo = ((this.regEAX & 0xff) * dst) & 0xffff;
this.fMDSet = true;
this.regMDLo = ((src = this.regEAX & 0xff) * dst) & 0xffff;
if (this.regMDLo & 0xff00) {
this.setCF(); this.setOF();
@ -2001,9 +1982,9 @@ X86.fnMULb = function(dst, src)
X86.fnMUL32 = function(dst, src)
{
if (!(dst & ~0xffff) && !(src & ~0xffff)) {
this.fMDSet = true;
this.regMDLo = (dst * src)|0;
this.regMDHi = 0;
this.fMDSet = true;
return;
}
@ -2018,9 +1999,9 @@ X86.fnMUL32 = function(dst, src)
mul16 = ((mul16 & 0xffff) + (srcLo * dstHi));
mul32 += ((mul16 >>> 16) + (srcHi * dstHi));
this.fMDSet = true;
this.regMDLo = (mul16 << 16) | (mul00 & 0xffff);
this.regMDHi = mul32|0;
this.fMDSet = true;
};
/**
@ -2038,9 +2019,9 @@ X86.fnMULw = function(dst, src)
if (this.sizeData == 2) {
src = this.regEAX & 0xffff;
var result = (src * dst)|0;
this.fMDSet = true;
this.regMDLo = result & 0xffff;
this.regMDHi = (result >> 16) & 0xffff;
this.fMDSet = true;
} else {
X86.fnMUL32.call(this, dst, this.regEAX);
if (this.stepping == X86.STEPPING_80386_B1) {
@ -3548,6 +3529,28 @@ X86.fnVERW = function(dst, src)
return dst;
};
/**
* fnIBTS(dst, src)
*
* As best I can determine, this function copies the specified bits from src (starting at bit 0 for CL
* bits) to dst (starting at bit offset in AX). For register operands, that's simple enough.
*
* TODO: If dst refers to a memory location, then the bit index may refer to higher memory locations, just
* like the BT/BTC/BTR/BTS instructions. For an instruction that no one was really able to use, except
* as a CPU stepping discriminator, that doesn't seem worth the effort.
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
X86.fnIBTS = function(dst, src)
{
var shift = (this.regEAX & this.maskData);
var mask = ((1 << (this.regECX & 0x1f)) - 1);
return (dst & ~(mask << shift)) | ((src & mask) << shift);
};
/**
* fnXBTS(dst, src)
*