Assorted Debugger fixes
This commit is contained in:
parent
a150bc51aa
commit
c2187e1e1b
28 changed files with 2360 additions and 2212 deletions
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue