32-bit multiplication

This commit is contained in:
Jeff Parsons 2015-04-22 17:24:40 -07:00 committed by jeffpar
commit d68f1bd985
6 changed files with 157 additions and 63 deletions

View file

@ -1969,7 +1969,7 @@ if (DEBUGGER) {
};
/**
* traceLog(prop, dst, src, flagsIn, flagsOut, result)
* traceLog(prop, dst, src, flagsIn, flagsOut, resultLo, resultHi)
*
* @this {Debugger}
* @param {string} prop
@ -1977,15 +1977,16 @@ if (DEBUGGER) {
* @param {number} src
* @param {number|null} flagsIn
* @param {number|null} flagsOut
* @param {number} result
* @param {number} resultLo
* @param {number} [resultHi]
*/
Debugger.prototype.traceLog = function(prop, dst, src, flagsIn, flagsOut, result)
Debugger.prototype.traceLog = function(prop, dst, src, flagsIn, flagsOut, resultLo, resultHi)
{
if (DEBUG) {
if (this.traceEnabled !== undefined && this.traceEnabled[prop]) {
var trace = Debugger.TRACE[prop];
var len = (trace.size >> 2);
var s = this.hexOffset(this.cpu.opLIP - this.cpu.segCS.base, this.cpu.getCS()) + " " + Debugger.INS_NAMES[trace.ins] + "(" + str.toHex(dst, len) + "," + str.toHex(src, len) + "," + (flagsIn === null? "-" : str.toHexWord(flagsIn)) + ") " + str.toHex(result, len) + "," + (flagsOut === null? "-" : str.toHexWord(flagsOut));
var s = this.hexOffset(this.cpu.opLIP - this.cpu.segCS.base, this.cpu.getCS()) + " " + Debugger.INS_NAMES[trace.ins] + "(" + str.toHex(dst, len) + "," + str.toHex(src, len) + "," + (flagsIn === null? "-" : str.toHexWord(flagsIn)) + ") " + str.toHex(resultLo, len) + "," + (flagsOut === null? "-" : str.toHexWord(flagsOut));
if (!this.aTraceBuffer.length) this.aTraceBuffer = new Array(Debugger.TRACE_LIMIT);
this.aTraceBuffer[this.iTraceBuffer++] = s;
if (this.iTraceBuffer >= this.aTraceBuffer.length) {

View file

@ -1000,7 +1000,9 @@ X86CPU.prototype.resetRegs = function()
* instructions perform calculations that must be propagated to specific registers (eg, AX and/or DX), which
* the ModRM decoder functions don't know about. We initialize them here mainly for documentation purposes.
*/
this.regMD16 = this.regMD32 = -1;
this.fMDSet = false; // regMDHi and/or regMDLo are invalid unless fMDSet is true
this.regMDLo = this.regMDHi = 0;
this.regXX = 0; // internal register for segment register and control register moves
/*
* Another internal "register" we occasionally need is an interim copy of bModRM, set inside selected opcode
@ -2438,7 +2440,7 @@ X86CPU.prototype.setPS = function(regPS, cpl)
};
/**
* traceLog(prop, dst, src, flagsIn, flagsOut, result)
* traceLog(prop, dst, src, flagsIn, flagsOut, resultLo, resultHi)
*
* @this {X86CPU}
* @param {string} prop
@ -2446,12 +2448,13 @@ X86CPU.prototype.setPS = function(regPS, cpl)
* @param {number} src
* @param {number|null} flagsIn
* @param {number|null} flagsOut
* @param {number} result
* @param {number} resultLo
* @param {number} [resultHi]
*/
X86CPU.prototype.traceLog = function(prop, dst, src, flagsIn, flagsOut, result)
X86CPU.prototype.traceLog = function(prop, dst, src, flagsIn, flagsOut, resultLo, resultHi)
{
if (DEBUG && this.dbg) {
this.dbg.traceLog(prop, dst, src, flagsIn, flagsOut, result);
this.dbg.traceLog(prop, dst, src, flagsIn, flagsOut, resultLo, resultHi);
}
};

View file

@ -490,14 +490,15 @@ X86.fnDIVb = function DIVb(dst, src)
X86.fnDIVOverflow.call(this);
return dst;
}
this.regMD16 = this.regEAX = (uQuotient & 0xff) | (((this.regEAX % dst) & 0xff) << 8);
this.fMDSet = true;
this.regMDLo = this.regEAX = (uQuotient & 0xff) | (((this.regEAX % dst) & 0xff) << 8);
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
if (DEBUG && DEBUGGER) this.traceLog('DIVB', src, dst, null, this.getPS(), this.regMD16);
if (DEBUG && DEBUGGER) this.traceLog('DIVB', src, dst, null, this.getPS(), this.regMDLo);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesDivBR : this.cycleCounts.nOpCyclesDivBM);
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
@ -533,15 +534,16 @@ X86.fnDIVw = function DIVw(dst, src)
X86.fnDIVOverflow.call(this);
return dst;
}
this.regMD16 = this.regEAX = (uQuotient & 0xffff);
this.regMD32 = this.regEDX = (src % dst) & 0xffff;
this.fMDSet = true;
this.regMDLo = this.regEAX = (uQuotient & 0xffff);
this.regMDHi = this.regEDX = (src % dst) & 0xffff;
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
if (DEBUG && DEBUGGER) this.traceLog('DIVW', src, dst, null, this.getPS(), this.regMD16 | (this.regMD32 << 16));
if (DEBUG && DEBUGGER) this.traceLog('DIVW', src, dst, null, this.getPS(), this.regMDLo | (this.regMDHi << 16));
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesDivWR : this.cycleCounts.nOpCyclesDivWM);
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
@ -593,14 +595,15 @@ X86.fnIDIVb = function IDIVb(dst, src)
X86.fnDIVOverflow.call(this);
return dst;
}
this.regMD16 = this.regEAX = (lQuotient & 0xff) | (((((this.regEAX << 16) >> 16) % ((dst << 24) >> 24)) & 0xff) << 8);
this.fMDSet = true;
this.regMDLo = this.regEAX = (lQuotient & 0xff) | (((((this.regEAX << 16) >> 16) % ((dst << 24) >> 24)) & 0xff) << 8);
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
if (DEBUG && DEBUGGER) this.traceLog('IDIVB', src, dst, null, this.getPS(), this.regMD16);
if (DEBUG && DEBUGGER) this.traceLog('IDIVB', src, dst, null, this.getPS(), this.regMDLo);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesIDivBR : this.cycleCounts.nOpCyclesIDivBM);
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
@ -641,15 +644,16 @@ X86.fnIDIVw = function IDIVw(dst, src)
X86.fnDIVOverflow.call(this);
return dst;
}
this.regMD16 = this.regEAX = (lQuotient & 0xffff);
this.regMD32 = this.regEDX = (src % lDivisor) & 0xffff;
this.fMDSet = true;
this.regMDLo = this.regEAX = (lQuotient & 0xffff);
this.regMDHi = this.regEDX = (src % lDivisor) & 0xffff;
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
if (DEBUG && DEBUGGER) this.traceLog('IDIVW', src, dst, null, this.getPS(), this.regMD16 | (this.regMD32 << 16));
if (DEBUG && DEBUGGER) this.traceLog('IDIVW', src, dst, null, this.getPS(), this.regMDLo | (this.regMDHi << 16));
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesIDivWR : this.cycleCounts.nOpCyclesIDivWM);
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
@ -751,7 +755,8 @@ X86.fnIMULn = function IMULn(dst, src)
X86.fnIMULb = function IMULb(dst, src)
{
var result = (((src = this.regEAX) << 24) >> 24) * ((dst << 24) >> 24);
this.regEAX = this.regMD16 = result & 0xffff;
this.fMDSet = true;
this.regEAX = this.regMDLo = result & 0xffff;
if (result > 127 || result < -128) {
this.setCF(); this.setOF();
} else {
@ -763,7 +768,7 @@ X86.fnIMULb = function IMULb(dst, src)
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
if (DEBUG && DEBUGGER) this.traceLog('IMULB', src, dst, null, this.getPS(), this.regMD16);
if (DEBUG && DEBUGGER) this.traceLog('IMULB', src, dst, null, this.getPS(), this.regMDLo);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesIMulBR : this.cycleCounts.nOpCyclesIMulBM);
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
@ -793,8 +798,9 @@ X86.fnIMULb = function IMULb(dst, src)
X86.fnIMULw = function IMULw(dst, src)
{
var result = (((src = this.regEAX) << 16) >> 16) * ((dst << 16) >> 16);
this.regEAX = this.regMD16 = result & 0xffff;
this.regEDX = this.regMD32 = (result >> 16) & 0xffff;
this.fMDSet = true;
this.regEAX = this.regMDLo = result & 0xffff;
this.regEDX = this.regMDHi = (result >> 16) & 0xffff;
if (result > 32767 || result < -32768) {
this.setCF(); this.setOF();
} else {
@ -806,7 +812,7 @@ X86.fnIMULw = function IMULw(dst, src)
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
if (DEBUG && DEBUGGER) this.traceLog('IMULW', src, dst, null, this.getPS(), this.regMD16 | (this.regMD32 << 16));
if (DEBUG && DEBUGGER) this.traceLog('IMULW', src, dst, null, this.getPS(), this.regMDLo | (this.regMDHi << 16));
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesIMulWR : this.cycleCounts.nOpCyclesIMulWM);
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
@ -1362,16 +1368,16 @@ X86.fnMOVn = function MOVn(dst, src)
};
/**
* fnMOVMD16(dst, src)
* fnMOVxx(dst, src)
*
* @this {X86CPU}
* @param {number} dst (current value, ignored)
* @param {number} src (new value)
* @return {number} dst (src is overridden, replaced with regMD16, as specified by opMOVwsr())
* @return {number} dst (src is overridden, replaced with regXX, as specified by opMOVwsr())
*/
X86.fnMOVMD16 = function MOVMD16(dst, src)
X86.fnMOVxx = function MOVxx(dst, src)
{
return X86.fnMOV.call(this, dst, this.regMD16);
return X86.fnMOV.call(this, dst, this.regXX);
};
/**
@ -1384,24 +1390,96 @@ X86.fnMOVMD16 = function MOVMD16(dst, src)
*/
X86.fnMULb = function MULb(dst, src)
{
this.regEAX = this.regMD16 = ((src = this.regEAX & 0xff) * dst) & 0xffff;
if (this.regEAX & 0xff00) {
this.fMDSet = true;
this.regMDLo = ((src = this.regEAX & 0xff) * dst) & 0xffff;
if (this.regMDLo & 0xff00) {
this.setCF(); this.setOF();
} else {
this.clearCF(); this.clearOF();
}
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
if (DEBUG && DEBUGGER) this.traceLog('MULB', src, dst, null, this.getPS(), this.regMD16);
if (DEBUG && DEBUGGER) this.traceLog('MULB', src, dst, null, this.getPS(), this.regMDLo);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesMulBR : this.cycleCounts.nOpCyclesMulBM);
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
};
/**
* fnMUL32(dst, src)
*
* This sets regMDHi:regMDLo to the 64-bit result of dst * src, both of which are treated as unsigned.
*
* TODO: Some potential optimizations include:
*
* 1) Early outs if either parameter is zero, since the result will obviously be zero
* 2) Using "normal" JavaScript multiplication if both parameters are < 32767
*
* Refer to: http://stackoverflow.com/questions/13597364/32-bit-signed-multiplication-with-a-64-bit-result-in-javascript
*
* @this {X86CPU}
* @param {number} dst (any 32-bit number, treated as unsigned)
* @param {number} src (any 32-bit number, treated as unsigned)
*/
X86.fnMUL32 = function MUL32(dst, src)
{
var srcLo = src & 0xffff;
var srcHi = src >>> 16;
var dstLo = dst & 0xffff;
var dstHi = dst >>> 16;
var mul00 = srcLo * dstLo;
var mul16 = ((mul00 >>> 16) + (srcHi * dstLo));
var mul32 = mul16 >>> 16;
mul16 = ((mul16 & 0xffff) + (srcLo * dstHi));
mul32 += ((mul16 >>> 16) + (srcHi * dstHi));
this.fMDSet = true;
this.regMDLo = (mul16 << 16) | (mul00 & 0xffff);
this.regMDHi = mul32|0;
};
/**
* fnIMUL32(dst, src)
*
* This sets regMDHi:regMDLo to the 64-bit result of dst * src, either of which may be signed or unsigned.
*
* TODO: Some potential optimizations include:
*
* 1) Early outs if either parameter is zero, since the result will obviously be zero
* 2) Using "normal" JavaScript multiplication if both parameters are >= -32768 && <= 32767
*
* Refer to: http://stackoverflow.com/questions/13597364/32-bit-signed-multiplication-with-a-64-bit-result-in-javascript
*
* @this {X86CPU}
* @param {number} dst (any 32-bit number, treated as signed)
* @param {number} src (any 32-bit number, treated as signed)
*/
X86.fnIMUL32 = function IMUL32(dst, src)
{
var fNeg = false;
if (src < 0) {
src = -src|0;
fNeg = !fNeg;
}
if (dst < 0) {
dst = -dst|0;
fNeg = !fNeg;
}
X86.fnMUL32.call(this, dst, src);
if (fNeg) {
this.regMDLo = (~this.regMDLo + 1)|0;
this.regMDHi = (~this.regMDHi + (this.regMDLo? 0 : 1))|0;
}
};
/**
* fnMULw(dst, src)
*
@ -1412,21 +1490,35 @@ X86.fnMULb = function MULb(dst, src)
*/
X86.fnMULw = function MULw(dst, src)
{
var result = (src = this.regEAX) * dst;
this.regMD16 = this.regEAX = result & 0xffff;
this.regMD32 = this.regEDX = (result >> 16) & 0xffff;
if (this.regEDX) {
if (this.dataSize == 2) {
src = this.regEAX & 0xffff;
var result = src * dst;
this.fMDSet = true;
this.regMDLo = result & 0xffff;
this.regMDHi = (result >> 16) & 0xffff;
} else {
X86.fnMUL32.call(this, dst, this.regEAX);
}
if (this.regMDHi) {
this.setCF(); this.setOF();
} else {
this.clearCF(); this.clearOF();
}
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
if (DEBUG && DEBUGGER) this.traceLog('MULW', src, dst, null, this.getPS(), this.regMD16 | (this.regMD32 << 16));
if (DEBUG && DEBUGGER) {
if (this.dataSize == 2) {
this.traceLog('MULW', src, dst, null, this.getPS(), this.regMDLo | (this.regMDHi << 16));
} else {
this.traceLog('MULD', src, dst, null, this.getPS(), this.regMDLo, this.regMDHi);
}
}
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesMulWR : this.cycleCounts.nOpCyclesMulWM);
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;

View file

@ -201,8 +201,8 @@ X86.opCLTS = function CLTS()
* op=0x0F,0x20 (MOV reg,creg)
*
* NOTE: Since the ModRM decoders deal only with general-purpose registers, we must move
* the appropriate control register into a special variable (regMD16), which our helper function
* (fnMOVMD16) will use to replace the decoder's src operand.
* the appropriate control register into a special variable (regXX), which our helper function
* (fnMOVxx) will use to replace the decoder's src operand.
*
* @this {X86CPU}
*/
@ -221,16 +221,16 @@ X86.opMOVrc = function MOVrc()
var reg = (bModRM & 0x38) >> 3;
switch(reg) {
case 0x0:
this.regMD16 = this.regCR0;
this.regXX = this.regCR0;
break;
case 0x1:
this.regMD16 = this.regCR1;
this.regXX = this.regCR1;
break;
case 0x2:
this.regMD16 = this.regCR2;
this.regXX = this.regCR2;
break;
case 0x3:
this.regMD16 = this.regCR3;
this.regXX = this.regCR3;
break;
default:
X86.opUndefined.call(this);
@ -242,7 +242,7 @@ X86.opMOVrc = function MOVrc()
*
* this.opFlags |= X86.OPFLAG.NOREAD;
*/
this.aOpModRegWord[bModRM].call(this, X86.fnMOVMD16);
this.aOpModRegWord[bModRM].call(this, X86.fnMOVxx);
};
/**

View file

@ -2057,8 +2057,8 @@ X86.opMOVrw = function MOVrw()
* op=0x8C (MOV word,sreg)
*
* NOTE: Since the ModRM decoders deal only with general-purpose registers, we must move
* the appropriate segment register into a special variable (regMD16), which our helper function
* (fnMOVMD16) will use to replace the decoder's src operand.
* the appropriate segment register into a special variable (regXX), which our helper function
* (fnMOVxx) will use to replace the decoder's src operand.
*
* @this {X86CPU}
*/
@ -2068,27 +2068,27 @@ X86.opMOVwsr = function MOVwsr()
var reg = (bModRM & 0x38) >> 3;
switch (reg) {
case 0x0:
this.regMD16 = this.segES.sel;
this.regXX = this.segES.sel;
break;
case 0x1:
this.regMD16 = this.segCS.sel;
this.regXX = this.segCS.sel;
break;
case 0x2:
this.regMD16 = this.segSS.sel;
this.regXX = this.segSS.sel;
break;
case 0x3:
this.regMD16 = this.segDS.sel;
this.regXX = this.segDS.sel;
break;
case 0x4:
if (I386 && this.model >= X86.MODEL_80386) {
this.regMD16 = this.segFS.sel;
this.regXX = this.segFS.sel;
break;
}
X86.opInvalid.call(this);
break;
case 0x5:
if (I386 && this.model >= X86.MODEL_80386) {
this.regMD16 = this.segGS.sel;
this.regXX = this.segGS.sel;
break;
}
/* falls through */
@ -2100,7 +2100,7 @@ X86.opMOVwsr = function MOVwsr()
* Like other MOV operations, the destination does not need to be read, just written.
*/
this.opFlags |= X86.OPFLAG.NOREAD;
this.aOpModMemWord[bModRM].call(this, X86.fnMOVMD16);
this.aOpModMemWord[bModRM].call(this, X86.fnMOVxx);
};
/**
@ -2207,8 +2207,6 @@ X86.opMOVsrw = function MOVsrw()
this.setDS(this.regEDI);
this.regEDI = temp;
break;
default:
break; // there IS no other case, but JavaScript inspections don't know that
}
};
@ -3841,7 +3839,7 @@ X86.opCMC = function CMC()
* know what the target is (only the target's value), it cannot easily work around the problem.
*
* A simple, albeit kludgy, solution is for fnMULb to always save its result in a special
* "register" (eg, regMD16), which we will then put back into regEAX if it's been updated.
* "register" (eg, regMDLo), which we will then put back into regEAX if it's been updated.
* This also relieves us from having to decode any part of the ModRM byte, so maybe it's not
* such a bad work-around after all.
*
@ -3851,9 +3849,9 @@ X86.opCMC = function CMC()
*/
X86.opGRP3b = function GRP3b()
{
this.regMD16 = -1;
this.fMDSet = false;
this.aOpModGrpByte[this.getIPByte()].call(this, X86.aOpGrp3b, X86.fnSrcNone);
if (this.regMD16 >= 0) this.regEAX = this.regMD16;
if (this.fMDSet) this.regEAX = (this.regEAX & ~this.dataMask) | (this.regMDLo & this.dataMask);
};
/**
@ -3869,7 +3867,7 @@ X86.opGRP3b = function GRP3b()
* know what the target is (only the target's value), it cannot easily work around the problem.
*
* A simple, albeit kludgey, solution is for fnMULw to always save its result in a special
* "register" (eg, regMD16/regMD32), which we will then put back into regEAX/regEDX if it's been
* "register" (eg, regMDLo/regMDHi), which we will then put back into regEAX/regEDX if it's been
* updated. This also relieves us from having to decode any part of the ModRM byte, so maybe
* it's not such a bad work-around after all.
*
@ -3877,11 +3875,11 @@ X86.opGRP3b = function GRP3b()
*/
X86.opGRP3w = function GRP3w()
{
this.regMD16 = -1;
this.fMDSet = false;
this.aOpModGrpWord[this.getIPByte()].call(this, X86.aOpGrp3w, X86.fnSrcNone);
if (this.regMD16 >= 0) {
this.regEAX = this.regMD16;
this.regEDX = this.regMD32;
if (this.fMDSet) {
this.regEAX = (this.regEAX & ~this.dataMask) | (this.regMDLo & this.dataMask);
this.regEDX = (this.regEDX & ~this.dataMask) | (this.regMDHi & this.dataMask);
}
};

View file

@ -7,7 +7,7 @@
start: mov eax,0x44332211
mov edx,0x88776655
shrd eax,edx,4
mul edx
call dword 0xf000:start