From 8c67a50f9251b92be97f6acd39cade738047725a Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Sat, 31 Oct 2015 07:31:32 -0700 Subject: [PATCH] Fixed 16-bit unsigned division overflow detection --- modules/pcjs/bin/test386.txt | 16 +++++------ modules/pcjs/lib/x86func.js | 52 +++++++++++++++++++----------------- modules/pcjs/lib/x86ops.js | 6 +++-- tests/pc/80386/test386.nasm | 2 ++ 4 files changed, 41 insertions(+), 35 deletions(-) diff --git a/modules/pcjs/bin/test386.txt b/modules/pcjs/bin/test386.txt index 64d593ae9..45bd3adcf 100644 --- a/modules/pcjs/bin/test386.txt +++ b/modules/pcjs/bin/test386.txt @@ -22902,10 +22902,10 @@ DIVAXW EAX=00000001 EDX=00004000 PS=0000 #DE EAX=00000001 EDX=00004000 PS=0000 DIVAXW EAX=00000001 EDX=00004001 PS=0000 #DE EAX=00000001 EDX=00004001 PS=0000 DIVAXW EAX=00000001 EDX=00007FFE PS=0000 #DE EAX=00000001 EDX=00007FFE PS=0000 DIVAXW EAX=00000001 EDX=00007FFF PS=0000 #DE EAX=00000001 EDX=00007FFF PS=0000 -DIVAXW EAX=00000001 EDX=00008000 PS=0000 EAX=00000001 EDX=00000000 PS=0000 -DIVAXW EAX=00000001 EDX=00008001 PS=0000 EAX=00000001 EDX=00000000 PS=0000 -DIVAXW EAX=00000001 EDX=0000FFFE PS=0000 EAX=00000001 EDX=00000000 PS=0000 -DIVAXW EAX=00000001 EDX=0000FFFF PS=0000 EAX=00000001 EDX=00000000 PS=0000 +DIVAXW EAX=00000001 EDX=00008000 PS=0000 #DE EAX=00000001 EDX=00008000 PS=0000 +DIVAXW EAX=00000001 EDX=00008001 PS=0000 #DE EAX=00000001 EDX=00008001 PS=0000 +DIVAXW EAX=00000001 EDX=0000FFFE PS=0000 #DE EAX=00000001 EDX=0000FFFE PS=0000 +DIVAXW EAX=00000001 EDX=0000FFFF PS=0000 #DE EAX=00000001 EDX=0000FFFF PS=0000 DIVAXW EAX=00000002 EDX=00000000 PS=0000 EAX=00000001 EDX=00000000 PS=0000 DIVAXW EAX=00000002 EDX=00000001 PS=0000 EAX=00008001 EDX=00000000 PS=0000 DIVAXW EAX=00000002 EDX=00000002 PS=0000 #DE EAX=00000002 EDX=00000002 PS=0000 @@ -23190,10 +23190,10 @@ DIVAXW EAX=00000001 EDX=00004000 PS=0000 #DE EAX=00000001 EDX=00004000 PS=0000 DIVAXW EAX=00000001 EDX=00004001 PS=0000 #DE EAX=00000001 EDX=00004001 PS=0000 DIVAXW EAX=00000001 EDX=00007FFE PS=0000 #DE EAX=00000001 EDX=00007FFE PS=0000 DIVAXW EAX=00000001 EDX=00007FFF PS=0000 #DE EAX=00000001 EDX=00007FFF PS=0000 -DIVAXW EAX=00000001 EDX=00008000 PS=0000 EAX=00000001 EDX=00000000 PS=0000 -DIVAXW EAX=00000001 EDX=00008001 PS=0000 EAX=00000001 EDX=00000000 PS=0000 -DIVAXW EAX=00000001 EDX=0000FFFE PS=0000 EAX=00000001 EDX=00000000 PS=0000 -DIVAXW EAX=00000001 EDX=0000FFFF PS=0000 EAX=00000001 EDX=00000000 PS=0000 +DIVAXW EAX=00000001 EDX=00008000 PS=0000 #DE EAX=00000001 EDX=00008000 PS=0000 +DIVAXW EAX=00000001 EDX=00008001 PS=0000 #DE EAX=00000001 EDX=00008001 PS=0000 +DIVAXW EAX=00000001 EDX=0000FFFE PS=0000 #DE EAX=00000001 EDX=0000FFFE PS=0000 +DIVAXW EAX=00000001 EDX=0000FFFF PS=0000 #DE EAX=00000001 EDX=0000FFFF PS=0000 DIVAXW EAX=00000002 EDX=00000000 PS=0000 EAX=00000001 EDX=00000000 PS=0000 DIVAXW EAX=00000002 EDX=00000001 PS=0000 EAX=00008001 EDX=00000000 PS=0000 DIVAXW EAX=00000002 EDX=00000002 PS=0000 #DE EAX=00000002 EDX=00000002 PS=0000 diff --git a/modules/pcjs/lib/x86func.js b/modules/pcjs/lib/x86func.js index 7123775bb..c1959c4bb 100644 --- a/modules/pcjs/lib/x86func.js +++ b/modules/pcjs/lib/x86func.js @@ -911,14 +911,13 @@ X86.fnDIVw = function(dst, src) * to force JavaScript to create a floating-point value that won't suffer from 32-bit-math side-effects. */ src = (this.regEDX & 0xffff) * 0x10000 + (this.regEAX & 0xffff); - var result = (src / dst)|0; + var result = (src / dst); if (result >= 0x10000) { X86.fnDIVOverflow.call(this); return dst; } this.regMDLo = (result & 0xffff); this.regMDHi = (src % dst) & 0xffff; - this.fMDSet = true; } else { if (!X86.fnDIV32.call(this, this.regEAX, this.regEDX, dst)) { @@ -927,9 +926,10 @@ X86.fnDIVw = function(dst, src) } this.regMDLo |= 0; this.regMDHi |= 0; - this.fMDSet = true; } + this.fMDSet = true; + this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesDivWR : this.cycleCounts.nOpCyclesDivWM); this.opFlags |= X86.OPFLAG.NOWRITE; return dst; @@ -1035,7 +1035,6 @@ X86.fnIDIVw = function(dst, src) this.regMDLo = (result & 0xffff); this.regMDHi = (src % div) & 0xffff; - this.fMDSet = true; } else { if (!X86.fnIDIV32.call(this, this.regEAX, this.regEDX, dst)) { @@ -1044,9 +1043,10 @@ X86.fnIDIVw = function(dst, src) } this.regMDLo |= 0; this.regMDHi |= 0; - this.fMDSet = true; } + this.fMDSet = true; + this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesIDivWR : this.cycleCounts.nOpCyclesIDivWM); this.opFlags |= X86.OPFLAG.NOWRITE; return dst; @@ -1171,7 +1171,6 @@ X86.fnIMULb = function(dst, src) var result = (((this.regEAX << 24) >> 24) * ((dst << 24) >> 24))|0; this.regMDLo = result & 0xffff; - this.fMDSet = true; if (result > 127 || result < -128) { this.setCF(); this.setOF(); @@ -1179,6 +1178,8 @@ X86.fnIMULb = function(dst, src) this.clearCF(); this.clearOF(); } + this.fMDSet = true; + this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesIMulBR : this.cycleCounts.nOpCyclesIMulBM); this.opFlags |= X86.OPFLAG.NOWRITE; return dst; @@ -1212,7 +1213,6 @@ X86.fnIMULw = function(dst, src) var result = (((src << 16) >> 16) * ((dst << 16) >> 16))|0; 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); @@ -1225,6 +1225,8 @@ X86.fnIMULw = function(dst, src) this.clearCF(); this.clearOF(); } + this.fMDSet = true; + this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesIMulWR : this.cycleCounts.nOpCyclesIMulWM); this.opFlags |= X86.OPFLAG.NOWRITE; return dst; @@ -1957,7 +1959,6 @@ X86.fnMOVxx = function(dst, src) X86.fnMULb = function(dst, src) { this.regMDLo = ((this.regEAX & 0xff) * dst) & 0xffff; - this.fMDSet = true; if (this.regMDLo & 0xff00) { this.setCF(); this.setOF(); @@ -1965,6 +1966,8 @@ X86.fnMULb = function(dst, src) this.clearCF(); this.clearOF(); } + this.fMDSet = true; + this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesMulBR : this.cycleCounts.nOpCyclesMulBM); this.opFlags |= X86.OPFLAG.NOWRITE; return dst; @@ -1984,24 +1987,22 @@ X86.fnMUL32 = function(dst, src) if (!(dst & ~0xffff) && !(src & ~0xffff)) { this.regMDLo = (dst * src)|0; this.regMDHi = 0; - this.fMDSet = true; - return; } + else { + var srcLo = src & 0xffff; + var srcHi = src >>> 16; + var dstLo = dst & 0xffff; + var dstHi = dst >>> 16; - 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)); - 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.regMDLo = (mul16 << 16) | (mul00 & 0xffff); - this.regMDHi = mul32|0; - this.fMDSet = true; + this.regMDLo = (mul16 << 16) | (mul00 & 0xffff); + this.regMDHi = mul32|0; + } }; /** @@ -2021,14 +2022,13 @@ X86.fnMULw = function(dst, src) var result = (src * dst)|0; 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) { if (this.regEAX == 0x0417A000 && dst == 0x00000081) { /* * Normally, the result should be 0x20FE7A000 (ie, regMDHi should be 0x2). - * I'm not sure what a typical failure looked like, so I'll just set regMDHi to 0. + * I'm not sure what a typical B1 stepping failure looked like, so I'll set regMDHi to 0. * * If you want a B1 stepping without this 32-bit multiplication flaw, select the B2 stepping. */ @@ -2044,6 +2044,8 @@ X86.fnMULw = function(dst, src) this.clearCF(); this.clearOF(); } + this.fMDSet = true; + this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesMulWR : this.cycleCounts.nOpCyclesMulWM); this.opFlags |= X86.OPFLAG.NOWRITE; return dst; diff --git a/modules/pcjs/lib/x86ops.js b/modules/pcjs/lib/x86ops.js index 2403cb4ce..576a16eaa 100644 --- a/modules/pcjs/lib/x86ops.js +++ b/modules/pcjs/lib/x86ops.js @@ -4169,7 +4169,9 @@ X86.opGRP3b = function() { this.fMDSet = false; this.aOpModGrpByte[this.getIPByte()].call(this, X86.aOpGrp3b, X86.fnSRCNone); - if (this.fMDSet) this.regEAX = (this.regEAX & ~this.maskData) | (this.regMDLo & this.maskData); + if (this.fMDSet) { + this.regEAX = (this.regEAX & ~this.maskData) | (this.regMDLo & this.maskData); + } }; /** @@ -4184,7 +4186,7 @@ X86.opGRP3b = function() * return value back into AX or DX, undoing fnMULw's update of DX:AX. And since fnMULw doesn't * 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 + * A simple, albeit kludgy, solution is for fnMULw to always save its result in a special * "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. diff --git a/tests/pc/80386/test386.nasm b/tests/pc/80386/test386.nasm index b54720f87..507ad046c 100644 --- a/tests/pc/80386/test386.nasm +++ b/tests/pc/80386/test386.nasm @@ -793,6 +793,8 @@ strPS: db "PS=",0 strDE: db "#DE ",0 ; when this is displayed, it indicates a Divide Error exception achSize db "BWD" +ALLOPS equ 1 + tableOps: defOp "ADD",add,al,dl,none,TYPE_ARITH defOp "ADD",add,ax,dx,none,TYPE_ARITH