Added support for machine configs with a specific CPU stepping (also, fixed IMUL reg,r/m,imm disassembly)

This commit is contained in:
Jeff Parsons 2015-10-28 16:57:55 -07:00
commit 6dcb644a05
14 changed files with 1169 additions and 1096 deletions

View file

@ -480,8 +480,7 @@ if (DEBUGGER) {
Debugger.TYPE_OTHER = 0xF000; // "other" field
/*
* TYPE_SIZE values. Some of the values (eg, TYPE_WORDIB and TYPE_WORDIW)
* imply the presence of a third operand, for those weird cases....
* TYPE_SIZE values.
*/
Debugger.TYPE_NONE = 0x0000; // (all other TYPE fields ignored)
Debugger.TYPE_BYTE = 0x0001; // (b) byte, regardless of operand size
@ -493,8 +492,6 @@ if (DEBUGGER) {
Debugger.TYPE_FARP = 0x0007; // (p) 32-bit or 48-bit pointer for JMP/CALL
Debugger.TYPE_2WORD = 0x0008; // (a) two memory operands (BOUND only)
Debugger.TYPE_DESC = 0x0009; // (s) 6 byte pseudo-descriptor
Debugger.TYPE_WORDIB = 0x000A; // two source operands (eg, IMUL)
Debugger.TYPE_WORDIW = 0x000B; // two source operands (eg, IMUL)
Debugger.TYPE_PREFIX = 0x000F; // (treat similarly to TYPE_NONE)
/*
@ -763,9 +760,9 @@ if (DEBUGGER) {
/* 0x67 */ [Debugger.INS.AS, Debugger.TYPE_PREFIX | Debugger.TYPE_80386],
/* 0x68 */ [Debugger.INS.PUSH, Debugger.TYPE_IMM | Debugger.TYPE_VWORD | Debugger.TYPE_IN | Debugger.TYPE_80286],
/* 0x69 */ [Debugger.INS.IMUL, Debugger.TYPE_REG | Debugger.TYPE_WORD | Debugger.TYPE_BOTH | Debugger.TYPE_80286, Debugger.TYPE_MODRM | Debugger.TYPE_WORDIW | Debugger.TYPE_IN],
/* 0x69 */ [Debugger.INS.IMUL, Debugger.TYPE_REG | Debugger.TYPE_WORD | Debugger.TYPE_BOTH | Debugger.TYPE_80286, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_IN, Debugger.TYPE_IMM | Debugger.TYPE_VWORD | Debugger.TYPE_IN],
/* 0x6A */ [Debugger.INS.PUSH, Debugger.TYPE_IMM | Debugger.TYPE_SBYTE | Debugger.TYPE_IN | Debugger.TYPE_80286],
/* 0x6B */ [Debugger.INS.IMUL, Debugger.TYPE_REG | Debugger.TYPE_WORD | Debugger.TYPE_BOTH | Debugger.TYPE_80286, Debugger.TYPE_MODRM | Debugger.TYPE_WORDIB | Debugger.TYPE_IN],
/* 0x6B */ [Debugger.INS.IMUL, Debugger.TYPE_REG | Debugger.TYPE_WORD | Debugger.TYPE_OUT | Debugger.TYPE_80286, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_IN, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
/* 0x6C */ [Debugger.INS.INS, Debugger.TYPE_ESDI | Debugger.TYPE_BYTE | Debugger.TYPE_OUT | Debugger.TYPE_80286, Debugger.TYPE_DX | Debugger.TYPE_IN],
/* 0x6D */ [Debugger.INS.INS, Debugger.TYPE_ESDI | Debugger.TYPE_VWORD | Debugger.TYPE_OUT | Debugger.TYPE_80286, Debugger.TYPE_DX | Debugger.TYPE_IN],
/* 0x6E */ [Debugger.INS.OUTS, Debugger.TYPE_DX | Debugger.TYPE_IN | Debugger.TYPE_80286, Debugger.TYPE_DSSI | Debugger.TYPE_BYTE | Debugger.TYPE_IN],

View file

@ -34,7 +34,7 @@
var X86 = {
/*
* CPU model numbers
* CPU model numbers (supported)
*/
MODEL_8086: 8086,
MODEL_8088: 8088,
@ -43,6 +43,12 @@ var X86 = {
MODEL_80286: 80286,
MODEL_80386: 80386,
/*
* CPU stepping identifiers (supported)
*/
STEPPING_B1: 0xB1, // our version of the B1 stepping also includes the infamous 32-bit multiplication bug
STEPPING_B2: 0xB2, // this is an imaginary stepping that simply means "B1 without the 32-bit multiplication bug" (ie, a B1 with the "double sigma" stamp)
/*
* This constant is used to mark points in the code where the physical address being returned
* is invalid and should not be used. TODO: There are still functions that will use an invalid

View file

@ -76,6 +76,7 @@ if (!I386) {
* The X86CPU class uses the following (parmsCPU) properties:
*
* model: a number (eg, 8088) that should match one of the X86.MODEL values
* stepping: a string (eg, "B1") that should match one of the X86.STEPPING values
*
* This extends the CPU class and passes any remaining parmsCPU properties to the CPU class
* constructor, along with a default speed (cycles per second) based on the specified (or default)
@ -109,6 +110,9 @@ function X86CPU(parmsCPU)
{
this.model = parmsCPU['model'] || X86.MODEL_8088;
var stepping = parmsCPU['stepping'];
this.stepping = (stepping? str.parseInt(stepping, 16) : 0);
var nCyclesDefault = 0;
switch(this.model) {
case X86.MODEL_8088:
@ -2884,6 +2888,12 @@ X86CPU.prototype.probeAddr = function(addr, size, fLinear)
}
if (block) {
var off = addr & this.nBlockLimit;
/*
* TODO: We actually hit this assert in rare cases where the Debugger is disassembling
* an instruction straddling a page boundary that also references a short or long operand.
* The best solution is to change the Debugger's getShort(), getLong(), etc, functions to
* use getByte() internally, which in turn will never call probeAddr() with a size > 1.
*/
this.assert(off + (size || 1) <= this.nBlockSize);
switch(size) {
default:

View file

@ -2033,6 +2033,18 @@ X86.fnMULw = function(dst, src)
this.regMDHi = (result >> 16) & 0xffff;
} else {
X86.fnMUL32.call(this, dst, this.regEAX);
if (this.model == X86.MODEL_80386 && this.stepping == X86.STEPPING_B1) {
if (this.regEAX == 0x0417A000 && dst == 0x00000081) {
/*
* In this case, the result should be 0x20FE7A000 (ie, regMDHi should be 0x2), and I'm not
* sure what the typical failure would look like, so I'll just set regMDHi to 0.
*
* If you want a B1 stepping without this 32-bit multiplication flaw, select the B2 stepping.
*/
this.assert(this.regMDLo == 0x0FE7A000 && this.regMDHi == 0x00000002);
this.regMDHi = 0;
}
}
}
if (this.regMDHi) {

View file

@ -2940,8 +2940,27 @@ X86.opSTOSb = function STOSb()
if (this.opFlags & X86.OPFLAG.FAULT) return;
if (BACKTRACK) this.backTrack.btiMem0 = this.backTrack.btiAL;
this.regEDI = (this.regEDI & ~maskAddr) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -1 : 1)) & maskAddr);
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
/*
* Implement 80386 B1 Errata #7 (to the extent that Windows 95 checks for the errata). This
* isn't a rock-solid implementation of the errata (for example, the ADDRESS override on the next
* instruction, if it exists, may or may not be the first prefix byte), but it's close enough.
*
* Note that we carefully monkey with maskAddr only AFTER updating ECX, because this errata affects
* only EDI in the case of STOS. The other instructions mentioned below monkey with different
* registers, so read the errata carefully.
*
* TODO: Extend this errata to STOSW, as well as MOVSB, MOVSW, INSB, and INSW.
*/
if (this.model == X86.MODEL_80386 && this.stepping == X86.STEPPING_B1) {
if (!(this.opPrefixes & X86.OPFLAG.ADDRSIZE) != (this.getByte(this.regLIP) != X86.OPCODE.AS)) {
maskAddr ^= (0xffff0000|0);
}
}
this.regEDI = (this.regEDI & ~maskAddr) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -1 : 1)) & maskAddr);
this.nStepCycles -= nCycles;
if (nReps) {
this.resetIP(-2);

View file

@ -522,6 +522,12 @@
<xsl:otherwise>8088</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="stepping">
<xsl:choose>
<xsl:when test="@stepping"><xsl:value-of select="@stepping"/></xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="cycles">
<xsl:choose>
<xsl:when test="@cycles"><xsl:value-of select="@cycles"/></xsl:when>
@ -561,7 +567,7 @@
<xsl:call-template name="component">
<xsl:with-param name="machine" select="$machine"/>
<xsl:with-param name="class" select="'cpu'"/>
<xsl:with-param name="parms">,model:<xsl:value-of select="$model"/>,cycles:<xsl:value-of select="$cycles"/>,multiplier:<xsl:value-of select="$multiplier"/>,autoStart:<xsl:value-of select="$autoStart"/>,csStart:<xsl:value-of select="$csStart"/>,csInterval:<xsl:value-of select="$csInterval"/>,csStop:<xsl:value-of select="$csStop"/></xsl:with-param>
<xsl:with-param name="parms">,model:<xsl:value-of select="$model"/>,stepping:'<xsl:value-of select="$stepping"/>',cycles:<xsl:value-of select="$cycles"/>,multiplier:<xsl:value-of select="$multiplier"/>,autoStart:<xsl:value-of select="$autoStart"/>,csStart:<xsl:value-of select="$csStart"/>,csInterval:<xsl:value-of select="$csInterval"/>,csStop:<xsl:value-of select="$csStop"/></xsl:with-param>
</xsl:call-template>
</xsl:template>