Assorted Debugger fixes

This commit is contained in:
Jeff Parsons 2015-07-20 13:11:46 -07:00
commit c2187e1e1b
28 changed files with 2360 additions and 2212 deletions

View file

@ -453,10 +453,10 @@ if (DEBUGGER) {
Debugger.TYPE_IMPREG = 0x0060; // implicit register in TYPE_IREG
Debugger.TYPE_IMPSEG = 0x0070; // implicit segment register in TYPE_IREG
Debugger.TYPE_MODRM = 0x0080; // (E) standard ModRM decoding
Debugger.TYPE_MEM = 0x0090; // (M) ModRM refers to memory only
Debugger.TYPE_REG = 0x00A0; // (G) standard Reg decoding
Debugger.TYPE_SEGREG = 0x00B0; // (S) Reg selects segment register
Debugger.TYPE_MODREG = 0x00C0; // (R) Mod refers to register only
Debugger.TYPE_MODMEM = 0x0090; // (M) ModRM refers to memory only
Debugger.TYPE_MODREG = 0x00A0; // (R) ModRM refers to register only
Debugger.TYPE_REG = 0x00B0; // (G) standard Reg decoding
Debugger.TYPE_SEGREG = 0x00C0; // (S) Reg selects segment register
Debugger.TYPE_CTLREG = 0x00D0; // (C) Reg selects control register
Debugger.TYPE_DBGREG = 0x00E0; // (D) Reg selects debug register
Debugger.TYPE_TSTREG = 0x00F0; // (T) Reg selects test register
@ -779,7 +779,7 @@ if (DEBUGGER) {
/* 0x8A */ [Debugger.INS.MOV, Debugger.TYPE_REG | Debugger.TYPE_BYTE | Debugger.TYPE_OUT, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
/* 0x8B */ [Debugger.INS.MOV, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_IN],
/* 0x8C */ [Debugger.INS.MOV, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_SEGREG | Debugger.TYPE_WORD | Debugger.TYPE_IN],
/* 0x8D */ [Debugger.INS.LEA, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_MEM | Debugger.TYPE_VWORD],
/* 0x8D */ [Debugger.INS.LEA, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_MODMEM | Debugger.TYPE_VWORD],
/* 0x8E */ [Debugger.INS.MOV, Debugger.TYPE_SEGREG | Debugger.TYPE_WORD | Debugger.TYPE_OUT, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_IN],
/* 0x8F */ [Debugger.INS.POP, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_OUT],
@ -841,10 +841,10 @@ if (DEBUGGER) {
/* 0xC1 */ [Debugger.INS.GRP2W, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH | Debugger.TYPE_80186, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
/* 0xC2 */ [Debugger.INS.RET, Debugger.TYPE_IMM | Debugger.TYPE_WORD | Debugger.TYPE_IN],
/* 0xC3 */ [Debugger.INS.RET],
/* 0xC4 */ [Debugger.INS.LES, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_MEM | Debugger.TYPE_SEGP | Debugger.TYPE_IN],
/* 0xC5 */ [Debugger.INS.LDS, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_MEM | Debugger.TYPE_SEGP | Debugger.TYPE_IN],
/* 0xC6 */ [Debugger.INS.MOV, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_OUT, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
/* 0xC7 */ [Debugger.INS.MOV, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_IMM | Debugger.TYPE_VWORD | Debugger.TYPE_IN],
/* 0xC4 */ [Debugger.INS.LES, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_MODMEM | Debugger.TYPE_SEGP | Debugger.TYPE_IN],
/* 0xC5 */ [Debugger.INS.LDS, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_MODMEM | Debugger.TYPE_SEGP | Debugger.TYPE_IN],
/* 0xC6 */ [Debugger.INS.MOV, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_OUT, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
/* 0xC7 */ [Debugger.INS.MOV, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_IMM | Debugger.TYPE_VWORD | Debugger.TYPE_IN],
/* 0xC8 */ [Debugger.INS.ENTER, Debugger.TYPE_IMM | Debugger.TYPE_WORD | Debugger.TYPE_IN | Debugger.TYPE_80286, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
/* 0xC9 */ [Debugger.INS.LEAVE, Debugger.TYPE_NONE | Debugger.TYPE_80286],
@ -913,12 +913,12 @@ if (DEBUGGER) {
Debugger.aaOp0FDescs = {
0x00: [Debugger.INS.GRP6, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_BOTH],
0x01: [Debugger.INS.GRP7, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_BOTH],
0x02: [Debugger.INS.LAR, Debugger.TYPE_REG | Debugger.TYPE_WORD | Debugger.TYPE_OUT | Debugger.TYPE_80286, Debugger.TYPE_MEM | Debugger.TYPE_WORD | Debugger.TYPE_IN],
0x03: [Debugger.INS.LSL, Debugger.TYPE_REG | Debugger.TYPE_WORD | Debugger.TYPE_OUT | Debugger.TYPE_80286, Debugger.TYPE_MEM | Debugger.TYPE_WORD | Debugger.TYPE_IN],
0x02: [Debugger.INS.LAR, Debugger.TYPE_REG | Debugger.TYPE_WORD | Debugger.TYPE_OUT | Debugger.TYPE_80286, Debugger.TYPE_MODMEM | Debugger.TYPE_WORD | Debugger.TYPE_IN],
0x03: [Debugger.INS.LSL, Debugger.TYPE_REG | Debugger.TYPE_WORD | Debugger.TYPE_OUT | Debugger.TYPE_80286, Debugger.TYPE_MODMEM | Debugger.TYPE_WORD | Debugger.TYPE_IN],
0x05: [Debugger.INS.LOADALL,Debugger.TYPE_80286],
0x06: [Debugger.INS.CLTS, Debugger.TYPE_80286],
0x20: [Debugger.INS.MOV, Debugger.TYPE_REG | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_CTLREG | Debugger.TYPE_DWORD | Debugger.TYPE_IN],
0x22: [Debugger.INS.MOV, Debugger.TYPE_CTLREG | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_REG | Debugger.TYPE_DWORD | Debugger.TYPE_IN],
0x20: [Debugger.INS.MOV, Debugger.TYPE_MODREG | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_CTLREG | Debugger.TYPE_DWORD | Debugger.TYPE_IN],
0x22: [Debugger.INS.MOV, Debugger.TYPE_CTLREG | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_MODREG | Debugger.TYPE_DWORD | Debugger.TYPE_IN],
0x80: [Debugger.INS.JO, Debugger.TYPE_IMMREL | Debugger.TYPE_VWORD | Debugger.TYPE_IN | Debugger.TYPE_80386],
0x81: [Debugger.INS.JNO, Debugger.TYPE_IMMREL | Debugger.TYPE_VWORD | Debugger.TYPE_IN | Debugger.TYPE_80386],
0x82: [Debugger.INS.JC, Debugger.TYPE_IMMREL | Debugger.TYPE_VWORD | Debugger.TYPE_IN | Debugger.TYPE_80386],
@ -962,10 +962,10 @@ if (DEBUGGER) {
0xAC: [Debugger.INS.SHRD, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_IN, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
0xAD: [Debugger.INS.SHRD, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_IN, Debugger.TYPE_IMPREG | Debugger.TYPE_CL | Debugger.TYPE_IN],
0xAF: [Debugger.INS.IMUL, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH | Debugger.TYPE_80386, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_IN],
0xB2: [Debugger.INS.LSS, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_MEM | Debugger.TYPE_SEGP | Debugger.TYPE_IN],
0xB2: [Debugger.INS.LSS, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_MODMEM | Debugger.TYPE_SEGP | Debugger.TYPE_IN],
0xB3: [Debugger.INS.BTR, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_IN],
0xB4: [Debugger.INS.LFS, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_MEM | Debugger.TYPE_SEGP | Debugger.TYPE_IN],
0xB5: [Debugger.INS.LGS, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_MEM | Debugger.TYPE_SEGP | Debugger.TYPE_IN],
0xB4: [Debugger.INS.LFS, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_MODMEM | Debugger.TYPE_SEGP | Debugger.TYPE_IN],
0xB5: [Debugger.INS.LGS, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT, Debugger.TYPE_MODMEM | Debugger.TYPE_SEGP | Debugger.TYPE_IN],
0xB6: [Debugger.INS.MOVZX, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
0xB7: [Debugger.INS.MOVZX, Debugger.TYPE_REG | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_IN],
0xBA: [Debugger.INS.GRP8, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH | Debugger.TYPE_80386, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
@ -1703,7 +1703,7 @@ if (DEBUGGER) {
Debugger.prototype.hexOffset = function(off, sel, fAddr32)
{
if (sel != null) {
return str.toHex(sel, 4) + ":" + str.toHex(off, (off & (0xffff0000|0)) || fAddr32? 8 : 4);
return str.toHex(sel, 4) + ":" + str.toHex(off, (off & ~0xffff) || fAddr32? 8 : 4);
}
return str.toHex(off);
};
@ -1899,7 +1899,7 @@ if (DEBUGGER) {
if (fGate) {
sDump = "seg=" + str.toHexWord(seg.base & 0xffff) + " off=" + str.toHexWord(seg.limit);
} else {
sDump = "base=" + str.toHex(seg.base, this.cchAddr) + " limit=" + str.toHex(seg.limit, (seg.limit & ~0xffff)? 8 : 4);
sDump = "base=" + str.toHex(seg.base, this.cchAddr) + " limit=" + this.getLimitString(seg.limit);
}
/*
* When we dump the EXT word, we mask off the LIMIT1619 and BASE2431 bits, because those have already
@ -2981,23 +2981,23 @@ if (DEBUGGER) {
};
/**
* addBreakpoint(aBreak, dbgAddr, fTempBreak)
* addBreakpoint(aBreak, dbgAddr, fTemp)
*
* @this {Debugger}
* @param {Array} aBreak
* @param {{DbgAddr}} dbgAddr
* @param {boolean} [fTempBreak]
* @param {boolean} [fTemp]
* @return {boolean} true if breakpoint added, false if already exists
*/
Debugger.prototype.addBreakpoint = function(aBreak, dbgAddr, fTempBreak)
Debugger.prototype.addBreakpoint = function(aBreak, dbgAddr, fTemp)
{
if (!this.findBreakpoint(aBreak, dbgAddr)) {
dbgAddr.fTempBreak = fTempBreak;
dbgAddr.fTempBreak = fTemp;
aBreak.push(dbgAddr);
if (aBreak != this.aBreakExec) {
this.bus.addMemBreak(this.getAddr(dbgAddr), aBreak == this.aBreakWrite);
}
if (fTempBreak) {
if (fTemp) {
/*
* Force temporary breakpoints to be interpreted as linear breakpoints
* (hence the assertion that there IS a linear address stored in dbgAddr);
@ -3008,7 +3008,7 @@ if (DEBUGGER) {
} else {
this.println("breakpoint enabled: " + this.hexAddr(dbgAddr) + " (" + aBreak[0] + ")");
}
this.historyInit();
if (!fTemp) this.historyInit();
return true;
}
return false;
@ -3304,11 +3304,29 @@ if (DEBUGGER) {
if (bModRM < 0) {
bModRM = this.getByte(dbgAddr, 1);
}
if (typeMode >= Debugger.TYPE_REG) {
sOperand = this.getRegOperand((bModRM >> 3) & 0x7, type, dbgAddr);
if (typeMode < Debugger.TYPE_MODREG) {
/*
* This test also encompasses TYPE_MODMEM, which is basically the inverse of the case
* below (ie, only Mod values *other* than 11 are allowed); however, I believe that in
* some cases that's merely a convention, and that if you try to execute an instruction
* like "LEA AX,BX", it will actually do something (on some if not all processors), so
* there's probably some diagnostic value in allowing those cases to be disassembled.
*/
sOperand = this.getModRMOperand(bModRM, type, dbgAddr);
}
else if (typeMode == Debugger.TYPE_MODREG) {
/*
* TYPE_MODREG instructions assume that Mod is 11 (only certain early 80486 steppings
* actually *required* that Mod contain 11) and always treat RM as a register (which we
* could also simulate by setting Mod to 11 and letting getModRMOperand() do its thing).
*/
sOperand = this.getRegOperand(bModRM & 0x7, type, dbgAddr);
}
else {
sOperand = this.getModRMOperand(bModRM, type, dbgAddr);
/*
* All the remaining cases are Reg-centric; getRegOperand() will figure out which case.
*/
sOperand = this.getRegOperand((bModRM >> 3) & 0x7, type, dbgAddr);
}
}
else if (typeMode == Debugger.TYPE_ONE) {
@ -3647,6 +3665,18 @@ if (DEBUGGER) {
return sFlag + (b? '1' : '0') + ' ';
};
/**
* getLimitString(l)
*
* @this {Debugger}
* @param {number} l
* @return {string}
*/
Debugger.prototype.getLimitString = function(l)
{
return str.toHex(l, (l & ~0xffff)? 8 : 4);
};
/**
* getRegString(iReg)
*
@ -3672,7 +3702,7 @@ if (DEBUGGER) {
*/
Debugger.prototype.getSegString = function(seg, fProt)
{
return seg.sName + '=' + str.toHex(seg.sel, 4) + (fProt? '[' + str.toHex(seg.base, this.cchAddr) + ',' + str.toHex(seg.limit, (seg.limit & ~0xffff)? 8 : 4) + ']' : "");
return seg.sName + '=' + str.toHex(seg.sel, 4) + (fProt? '[' + str.toHex(seg.base, this.cchAddr) + ',' + this.getLimitString(seg.limit) + ']' : "");
};
/**
@ -4950,7 +4980,7 @@ if (DEBUGGER) {
}
var fValid = false;
var w = str.parseInt(sValue, 16);
if (!isNaN(w)) {
if (w !== undefined) {
fValid = true;
var sRegMatch = sReg.toUpperCase();
if (sRegMatch.charAt(0) == 'E' && this.cchReg <= 4) {
@ -5110,12 +5140,14 @@ if (DEBUGGER) {
break;
case "CR0":
this.cpu.regCR0 = w;
X86.fnLCR0.call(this.cpu, w);
break;
case "CR2":
this.cpu.regCR2 = w;
break;
case "CR3":
this.cpu.regCR3 = w;
X86.fnLCR3.call(this.cpu, w);
break;
/*
* TODO: Add support for DR0-DR7 and TR6-TR7.

View file

@ -438,8 +438,8 @@ X86CPU.CYCLES_80286 = {
};
/*
* TODO: Except for the cycle counts at the end of this table (ie, those marked "unique to the 80386"), all these
* values were simply copied from the 80286 table and still need to be modified and verified.
* TODO: All these values were simply copied from the 80286 table and still need to be modified and verified.
* Cycle counts for 80386-only instructions are hard-coded in their respective handlers, since those counts don't vary.
*/
X86CPU.CYCLES_80386 = {
nWordCyclePenalty: 0,
@ -554,25 +554,7 @@ X86CPU.CYCLES_80386 = {
nOpCyclesTestMI: 6,
nOpCyclesXchgRR: 3,
nOpCyclesXchgRM: 5,
nOpCyclesXLAT: 5,
/*
* Cycle counts unique to the 80386
*/
nOpCyclesBitScan: 11,
nOpCyclesBitSetR: 6,
nOpCyclesBitSetM: 8,
nOpCyclesBitSetMExtra: 5, // extra cycle cost for non-immediate BTC/BTR/BTS opcodes
nOpCyclesBitTestR: 3,
nOpCyclesBitTestM: 6,
nOpCyclesBitTestMExtra: 6, // extra cycle cost for non-immediate BT opcode
nOpCyclesIMulR: 9,
nOpCyclesIMulM: 12,
nOpCyclesMovXR: 3,
nOpCyclesMovXM: 6,
nOpCyclesSetR: 4,
nOpCyclesSetM: 5,
nOpCyclesShiftDR: 3,
nOpCyclesShiftDM: 7
nOpCyclesXLAT: 5
};
/**
@ -723,6 +705,7 @@ X86CPU.prototype.enablePageBlocks = function()
}
}
this.aBlocksPaged = [];
this.stopCPU();
};
/**
@ -1351,6 +1334,25 @@ X86CPU.prototype.resetRegs = function()
this.setProtMode();
};
/**
* setAddrSize(size)
*
* This is used by opcodes that require a particular ADDRESS size, which we enforce by
* internally simulating an ADDRESS size override, if needed.
*
* @this {X86CPU}
* @param {number} size (2 for 2-byte/16-bit operands, or 4 for 4-byte/32-bit operands)
*/
X86CPU.prototype.setAddrSize = function(size)
{
if (this.addrSize != size) {
this.opPrefixes |= X86.OPFLAG.ADDRSIZE;
this.addrSize = size;
this.addrMask = (size == 2? 0xffff : (0xffffffff|0));
this.updateAddrSize();
}
};
/**
* updateAddrSize()
*

View file

@ -226,7 +226,7 @@ X86.fnBSF = function BSF(dst, src)
n++; // TODO: Determine if n should be incremented before the bailout for an accurate cycle count
}
}
this.nStepCycles -= this.cycleCounts.nOpCyclesBitScan + n * 3;
this.nStepCycles -= 11 + n * 3;
return dst;
};
@ -263,7 +263,7 @@ X86.fnBSR = function BSR(dst, src)
}
}
this.nStepCycles -= this.cycleCounts.nOpCyclesBitScan + n * 3;
this.nStepCycles -= 11 + n * 3;
return dst;
};
@ -278,7 +278,7 @@ X86.fnBSR = function BSR(dst, src)
X86.fnBT = function BT(dst, src)
{
if (dst & (1 << (src & 0x1f))) this.setCF(); else this.clearCF();
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesBitTestR : this.cycleCounts.nOpCyclesBitTestM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 3 : 6);
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
};
@ -295,7 +295,7 @@ X86.fnBTC = function BTC(dst, src)
{
var bit = 1 << (src & 0x1f);
if (dst & bit) this.setCF(); else this.clearCF();
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesBitSetR : this.cycleCounts.nOpCyclesBitSetM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 6 : 8);
return dst ^ bit;
};
@ -311,7 +311,7 @@ X86.fnBTR = function BTR(dst, src)
{
var bit = 1 << (src & 0x1f);
if (dst & bit) this.setCF(); else this.clearCF();
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesBitSetR : this.cycleCounts.nOpCyclesBitSetM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 6 : 8);
return dst & ~bit;
};
@ -327,7 +327,7 @@ X86.fnBTS = function BTS(dst, src)
{
var bit = 1 << (src & 0x1f);
if (dst & bit) this.setCF(); else this.clearCF();
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesBitSetR : this.cycleCounts.nOpCyclesBitSetM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 6 : 8);
return dst | bit;
};
@ -1118,7 +1118,7 @@ X86.fnIMULrw = function IMULrw(dst, src)
this.clearCF(); this.clearOF();
}
result &= 0xffff;
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesIMulR : this.cycleCounts.nOpCyclesIMulM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 9 : 12);
return result;
};
@ -1139,7 +1139,7 @@ X86.fnIMULrd = function IMULrd(dst, src)
this.clearCF(); this.clearOF();
}
result |= 0;
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesIMulR : this.cycleCounts.nOpCyclesIMulM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 9 : 12);
return result;
};
@ -2438,7 +2438,7 @@ X86.fnSETcc = function SETcc(fnSet)
{
this.opFlags |= X86.OPFLAG.NOREAD;
this.aOpModMemByte[this.getIPByte()].call(this, fnSet);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesSetR : this.cycleCounts.nOpCyclesSetM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 4 : 5);
};
/**

View file

@ -200,29 +200,23 @@ 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 (regXX), which our helper function
* (fnMOVxx) will use to replace the decoder's src operand.
* NOTE: Since this instruction uses only 32-bit general-purpose registers, our ModRM decoders
* are going to be more hindrance than help, so we fully decode and execute the instruction ourselves.
*
* From PCMag_Prog_TechRef, p.476: "The 80386 executes the MOV to/from control registers (CRn)
* regardless of the setting of the MOD field. The MOD field should be set to 0b11, but an early
* 80386 documentation error indicated that the MOD field value was a don't care. Early versions
* of the 80486 detect a MOD != 0b11 as an illegal opcode. This was changed in later versions to
* ignore the value of MOD. Assemblers that generate MOD != 0b11 for these instructions will fail
* on some 80486s."
* From PCMag_Prog_TechRef, p.476: "The 80386 executes the MOV to/from control registers (CRn) regardless
* of the setting of the MOD field. The MOD field should be set to 0b11, but an early 80386 documentation
* error indicated that the MOD field value was a don't care. Early versions of the 80486 detect
* a MOD != 0b11 as an illegal opcode. This was changed in later versions to ignore the value of MOD.
* Assemblers that generate MOD != 0b11 for these instructions will fail on some 80486s."
*
* And in fact, the Compaq DeskPro 386 ROM BIOS executes this instruction with MOD set to 0b00, so we have
* to ignore it.
*
* @this {X86CPU}
*/
X86.opMOVrc = function MOVrc()
{
/*
* We address the MOD field problem (see above) by coercing it to 0b11 (0xc0), regardless.
*
* TODO: One issue not clearly addressed is if, when an assembler/compiler generated a bogus MOD value,
* it also generated the additional displacement bytes, if any, that would typically accompany such a MOD
* value. I assume not.
*/
var bModRM = this.getIPByte() | 0xc0;
var bModRM = this.getIPByte();
if (this.segCS.cpl) {
/*
@ -233,35 +227,54 @@ X86.opMOVrc = function MOVrc()
return;
}
var reg = (bModRM & 0x38) >> 3;
switch(reg) {
var reg;
switch((bModRM & 0x38) >> 3) {
case 0x0:
this.regXX = this.regCR0;
break;
case 0x1:
this.regXX = this.regCR1;
reg = this.regCR0;
break;
case 0x2:
this.regXX = this.regCR2;
reg = this.regCR2;
break;
case 0x3:
this.regXX = this.regCR3;
reg = this.regCR3;
break;
default:
X86.opUndefined.call(this);
return;
}
switch(bModRM & 0x7) {
case 0x0:
this.regEAX = reg;
break;
case 0x1:
this.regECX = reg;
break;
case 0x2:
this.regEDX = reg;
break;
case 0x3:
this.regEBX = reg;
break;
case 0x4:
this.regESP = reg;
break;
case 0x5:
this.regEBP = reg;
break;
case 0x6:
this.regESI = reg;
break;
case 0x7:
this.regEDI = reg;
break;
}
this.nStepCycles -= 6;
/*
* Like other MOV operations, the destination does not need to be read, just written;
* however, it's moot, because we've already restricted this opcode to registers only.
*
* this.opFlags |= X86.OPFLAG.NOREAD;
*
* Another issue, however, is that this instruction always assumes a 32-bit OPERAND size,
* so we must call setDataSize(4) first.
* TODO: Implement BACKTRACK for this instruction....
*/
this.setDataSize(4);
this.aOpModRegWord[bModRM].call(this, X86.fnMOVxx);
};
/**
@ -269,30 +282,23 @@ X86.opMOVrc = function MOVrc()
*
* op=0x0F,0x22 (MOV creg,reg)
*
* NOTE: Since the ModRM decoders deal only with general-purpose registers, we have to make a note
* of which general-purpose register will be overwritten, so that we can restore it after moving the
* modified value to the correct control register.
* NOTE: Since this instruction uses only 32-bit general-purpose registers, our ModRM decoders
* are going to be more hindrance than help, so we fully decode and execute the instruction ourselves.
*
* From PCMag_Prog_TechRef, p.476: "The 80386 executes the MOV to/from control registers (CRn)
* regardless of the setting of the MOD field. The MOD field should be set to 0b11, but an early
* 80386 documentation error indicated that the MOD field value was a don't care. Early versions
* of the 80486 detect a MOD != 0b11 as an illegal opcode. This was changed in later versions to
* ignore the value of MOD. Assemblers that generate MOD != 0b11 for these instructions will fail
* on some 80486s."
* From PCMag_Prog_TechRef, p.476: "The 80386 executes the MOV to/from control registers (CRn) regardless
* of the setting of the MOD field. The MOD field should be set to 0b11, but an early 80386 documentation
* error indicated that the MOD field value was a don't care. Early versions of the 80486 detect
* a MOD != 0b11 as an illegal opcode. This was changed in later versions to ignore the value of MOD.
* Assemblers that generate MOD != 0b11 for these instructions will fail on some 80486s."
*
* And in fact, the Compaq DeskPro 386 ROM BIOS executes this instruction with MOD set to 0b00, so we have
* to ignore it.
*
* @this {X86CPU}
*/
X86.opMOVcr = function MOVcr()
{
var temp;
/*
* We address the MOD field problem (see above) by coercing it to 0b11 (0xc0), regardless.
*
* TODO: One issue not clearly addressed is if, when an assembler/compiler generated a bogus MOD value,
* it also generated the additional displacement bytes, if any, that would typically accompany such a MOD
* value. I assume not.
*/
var bModRM = this.getIPByte() | 0xc0;
var bModRM = this.getIPByte();
if (this.segCS.cpl) {
/*
@ -303,51 +309,55 @@ X86.opMOVcr = function MOVcr()
return;
}
var reg = (bModRM & 0x38) >> 3;
switch(reg) {
var reg;
switch(bModRM & 0x7) {
case 0x0:
temp = this.regEAX;
reg = this.regEAX;
break;
case 0x1:
temp = this.regECX; // TODO: Is setting CR1 actually allowed on an 80386?
reg = this.regECX;
break;
case 0x2:
temp = this.regEDX;
reg = this.regEDX;
break;
case 0x3:
temp = this.regEBX;
reg = this.regEBX;
break;
case 0x4:
reg = this.regESP;
break;
case 0x5:
reg = this.regEBP;
break;
case 0x6:
reg = this.regESI;
break;
case 0x7:
reg = this.regEDI;
break;
}
switch((bModRM & 0x38) >> 3) {
case 0x0:
X86.fnLCR0.call(this, reg);
this.nStepCycles -= 10;
break;
case 0x2:
this.regCR2 = reg;
this.nStepCycles -= 4;
break;
case 0x3:
X86.fnLCR3.call(this, reg);
this.nStepCycles -= 5;
break;
default:
X86.opInvalid.call(this);
X86.opUndefined.call(this);
return;
}
/*
* This instruction always assumes a 32-bit OPERAND size, so we must call setDataSize(4) first.
* TODO: Implement BACKTRACK for this instruction....
*/
this.setDataSize(4);
this.aOpModRegWord[bModRM].call(this, X86.fnMOV);
switch(reg) {
case 0x0:
reg = this.regEAX;
this.regEAX = temp;
X86.fnLCR0.call(this, reg);
break;
case 0x1:
this.regCR1 = this.regECX;
this.regECX = temp;
break;
case 0x2:
this.regCR2 = this.regEDX;
this.regEDX = temp;
break;
case 0x3:
reg = this.regEBX;
this.regEBX = temp;
X86.fnLCR3.call(this, reg);
break;
}
};
/*
@ -883,7 +893,7 @@ X86.opPOPFS = function POPFS()
X86.opBT = function BT()
{
this.aOpModMemWord[this.getIPByte()].call(this, X86.fnBT);
if (this.regEA !== X86.ADDR_INVALID) this.nStepCycles -= this.cycleCounts.nOpCyclesBitTestMExtra;
if (this.regEA !== X86.ADDR_INVALID) this.nStepCycles -= 6;
};
/**
@ -896,7 +906,7 @@ X86.opBT = function BT()
X86.opSHLDn = function SHLDn()
{
this.aOpModMemWord[this.getIPByte()].call(this, this.dataSize == 2? X86.fnSHLDwi : X86.fnSHLDdi);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesShiftDR : this.cycleCounts.nOpCyclesShiftDM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 3 : 7);
};
/**
@ -909,7 +919,7 @@ X86.opSHLDn = function SHLDn()
X86.opSHLDcl = function SHLDcl()
{
this.aOpModMemWord[this.getIPByte()].call(this, this.dataSize == 2? X86.fnSHLDwCL : X86.fnSHLDdCL);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesShiftDR : this.cycleCounts.nOpCyclesShiftDM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 3 : 7);
};
/**
@ -948,7 +958,7 @@ X86.opPOPGS = function POPGS()
X86.opBTS = function BTS()
{
this.aOpModMemWord[this.getIPByte()].call(this, X86.fnBTS);
if (this.regEA !== X86.ADDR_INVALID) this.nStepCycles -= this.cycleCounts.nOpCyclesBitSetMExtra;
if (this.regEA !== X86.ADDR_INVALID) this.nStepCycles -= 5;
};
/**
@ -961,7 +971,7 @@ X86.opBTS = function BTS()
X86.opSHRDn = function SHRDn()
{
this.aOpModMemWord[this.getIPByte()].call(this, this.dataSize == 2? X86.fnSHRDwi : X86.fnSHRDdi);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesShiftDR : this.cycleCounts.nOpCyclesShiftDM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 3 : 7);
};
/**
@ -974,7 +984,7 @@ X86.opSHRDn = function SHRDn()
X86.opSHRDcl = function SHRDcl()
{
this.aOpModMemWord[this.getIPByte()].call(this, this.dataSize == 2? X86.fnSHRDwCL : X86.fnSHRDdCL);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesShiftDR : this.cycleCounts.nOpCyclesShiftDM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 3 : 7);
};
/**
@ -1013,7 +1023,7 @@ X86.opLSS = function LSS()
X86.opBTR = function BTR()
{
this.aOpModMemWord[this.getIPByte()].call(this, X86.fnBTR);
if (this.regEA !== X86.ADDR_INVALID) this.nStepCycles -= this.cycleCounts.nOpCyclesBitSetMExtra;
if (this.regEA !== X86.ADDR_INVALID) this.nStepCycles -= 5;
};
/**
@ -1113,7 +1123,7 @@ X86.opMOVZXb = function MOVZXb()
this.regEBX = temp;
break;
}
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesMovXR : this.cycleCounts.nOpCyclesMovXM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 3 : 6);
};
/**
@ -1154,7 +1164,7 @@ X86.opMOVZXw = function MOVZXw()
this.regEDI = (this.regEDI & 0xffff);
break;
}
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesMovXR : this.cycleCounts.nOpCyclesMovXM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 3 : 6);
};
/**
@ -1177,7 +1187,7 @@ X86.opGRP8 = function GRP8()
X86.opBTC = function BTC()
{
this.aOpModMemWord[this.getIPByte()].call(this, X86.fnBTC);
if (this.regEA !== X86.ADDR_INVALID) this.nStepCycles -= this.cycleCounts.nOpCyclesBitSetMExtra;
if (this.regEA !== X86.ADDR_INVALID) this.nStepCycles -= 5;
};
/**
@ -1273,7 +1283,7 @@ X86.opMOVSXb = function MOVSXb()
this.regEBX = temp;
break;
}
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesMovXR : this.cycleCounts.nOpCyclesMovXM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 3 : 6);
};
/**
@ -1314,7 +1324,7 @@ X86.opMOVSXw = function MOVSXw()
this.regEDI = ((this.regEDI << 16) >> 16);
break;
}
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.cycleCounts.nOpCyclesMovXR : this.cycleCounts.nOpCyclesMovXM);
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 3 : 6);
};
X86.aOps0F = new Array(256);
@ -1333,14 +1343,26 @@ X86.aOps0F[0x06] = X86.opCLTS;
X86.aOps0F[0x0B] = X86.opInvalid;
/*
* NOTE: Any other opcode slots NOT explicitly initialized above with either a dedicated function OR opInvalid()
* will be set to opUndefined() when initProcessor() finalizes the opcode tables. If the processor is an 80386,
* initProcessor() will also incorporate all the handlers listed below in aOps0F386.
* The following 0x0F opcodes are of no consequence to us, since they were all introduced post-80386;
* 0x0F,0xA6 and 0x0F,0xA7 were introduced on some 80486 processors (and then deprecated), while 0x0F,0xB0
* and 0x0F,0xB1 were introduced on 80586 (aka Pentium) processors.
*
* A call to opUndefined() implies something serious has occurred that merits our attention (eg, perhaps someone
* is using an undocumented opcode that we haven't implemented yet), whereas a call to opInvalid() may or may not.
* CMPXCHG r/m8,reg8 ; 0F B0 /r [PENT]
* CMPXCHG r/m16,reg16 ; o16 0F B1 /r [PENT]
* CMPXCHG r/m32,reg32 ; o32 0F B1 /r [PENT]
* CMPXCHG486 r/m8,reg8 ; 0F A6 /r [486,UNDOC]
* CMPXCHG486 r/m16,reg16 ; o16 0F A7 /r [486,UNDOC]
* CMPXCHG486 r/m32,reg32 ; o32 0F A7 /r [486,UNDOC]
*
* For example, when Windows initializes in protected-mode, it sets a DPMI exception handler for UD_FAULT and
* So why are we even mentioning them here? Only because some software (eg, Windows 3.00) attempts to execute
* 0x0F,0xA6, so we need to explicitly mark it as invalid. TODO: Purely out of curiosity, I would like to
* eventually learn *why* Windows 3.00 does this; is it hoping to use the CMPXCHG486 opcode, or is it performing
* a CPU/stepping check to detect/work-around some errata, or....?
*/
X86.aOps0F[0xA6] = X86.opInvalid;
/*
* When Windows 95 Setup initializes in protected-mode, it sets a DPMI exception handler for UD_FAULT and
* then attempts to generate that exception with undefined opcode 0x0F,0xFF. Apparently, whoever wrote that code
* (davidw?) didn't get the Intel memo regarding the preferred invalid opcode (0x0F,0x0B, aka UD2), or perhaps Intel
* hadn't written that memo yet -- although if that's the case, then Intel should have followed Microsoft's lead and
@ -1350,6 +1372,15 @@ X86.aOps0F[0x0B] = X86.opInvalid;
*/
X86.aOps0F[0xFF] = X86.opInvalid;
/*
* NOTE: Any other opcode slots NOT explicitly initialized above with either a dedicated function OR opInvalid()
* will be set to opUndefined() when initProcessor() finalizes the opcode tables. If the processor is an 80386,
* initProcessor() will also incorporate all the handlers listed below in aOps0F386.
*
* A call to opUndefined() implies something serious has occurred that merits our attention (eg, perhaps someone
* is using an undocumented opcode that we haven't implemented yet), whereas a call to opInvalid() may or may not.
*/
if (I386) {
X86.aOps0F386 = [];
X86.aOps0F386[0x20] = X86.opMOVrc;