Limited Debug register support
This commit is contained in:
parent
884f49d6b5
commit
9fa2941394
7 changed files with 168 additions and 85 deletions
|
|
@ -1662,6 +1662,10 @@ if (DEBUGGER) {
|
|||
/**
|
||||
* newAddr(off, sel, addr, fProt, fData32, fAddr32)
|
||||
*
|
||||
* NOTES: If 'off' is undefined, it may be because parseExpression() was unable to parse the given value,
|
||||
* in which case we need to make sure that 'off' passes through as undefined, so that callers can test for
|
||||
* "dbgAddr.off == null" to detect invalid addresses.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {number|null|undefined} [off] (default is zero)
|
||||
* @param {number|null|undefined} [sel] (default is undefined)
|
||||
|
|
@ -2406,9 +2410,11 @@ if (DEBUGGER) {
|
|||
while ((i = s.indexOf('$', i)) >= 0) {
|
||||
sAddr = s.substr(i+1, 9);
|
||||
dbgAddr = this.parseAddr(sAddr);
|
||||
sReplace = sAddr + ' "' + this.getSZ(dbgAddr) + '"';
|
||||
s = s.replace('$' + sAddr, sReplace);
|
||||
i += sReplace.length;
|
||||
if (dbgAddr) {
|
||||
sReplace = sAddr + ' "' + this.getSZ(dbgAddr) + '"';
|
||||
s = s.replace('$' + sAddr, sReplace);
|
||||
i += sReplace.length;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Replace every ^XXXX:XXXX, where XXXX:XXXX is a segmented address, with the FCB filename stored at that address.
|
||||
|
|
@ -2417,10 +2423,12 @@ if (DEBUGGER) {
|
|||
while ((i = s.indexOf('^', i)) >= 0) {
|
||||
sAddr = s.substr(i+1, 9);
|
||||
dbgAddr = this.parseAddr(sAddr);
|
||||
this.incAddr(dbgAddr);
|
||||
sReplace = sAddr + ' "' + this.getSZ(dbgAddr, 11) + '"';
|
||||
s = s.replace('^' + sAddr, sReplace);
|
||||
i += sReplace.length;
|
||||
if (dbgAddr) {
|
||||
this.incAddr(dbgAddr);
|
||||
sReplace = sAddr + ' "' + this.getSZ(dbgAddr, 11) + '"';
|
||||
s = s.replace('^' + sAddr, sReplace);
|
||||
i += sReplace.length;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
|
@ -4049,7 +4057,7 @@ if (DEBUGGER) {
|
|||
* @param {string|undefined} sAddr
|
||||
* @param {number|undefined} [type] is the address segment type, in case sAddr doesn't specify a segment
|
||||
* @param {boolean} [fNoChecks] (eg, true when setting breakpoints that may not be valid now, but will be later)
|
||||
* @return {{DbgAddr}}
|
||||
* @return {{DbgAddr}|null|undefined}
|
||||
*/
|
||||
Debugger.prototype.parseAddr = function(sAddr, type, fNoChecks)
|
||||
{
|
||||
|
|
@ -4076,6 +4084,7 @@ if (DEBUGGER) {
|
|||
addr = null;
|
||||
} else {
|
||||
addr = this.parseExpression(sAddr);
|
||||
if (addr == null) off = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -4085,8 +4094,10 @@ if (DEBUGGER) {
|
|||
}
|
||||
}
|
||||
|
||||
dbgAddr = this.newAddr(off, sel, addr);
|
||||
if (!fNoChecks) this.checkLimit(dbgAddr);
|
||||
if (off != null) {
|
||||
dbgAddr = this.newAddr(off, sel, addr);
|
||||
if (!fNoChecks) this.checkLimit(dbgAddr);
|
||||
}
|
||||
return dbgAddr;
|
||||
};
|
||||
|
||||
|
|
@ -4548,7 +4559,7 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.doAssemble = function(asArgs)
|
||||
{
|
||||
var dbgAddr = this.parseAddr(asArgs[1], Debugger.ADDR_CODE);
|
||||
if (dbgAddr.off == null) return;
|
||||
if (!dbgAddr) return;
|
||||
|
||||
this.dbgAddrAssemble = dbgAddr;
|
||||
if (asArgs[2] === undefined) {
|
||||
|
|
@ -4626,7 +4637,7 @@ if (DEBUGGER) {
|
|||
var dbgAddr = {};
|
||||
if (sAddr != "*") {
|
||||
dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_CODE, true);
|
||||
if (dbgAddr.off == null) return;
|
||||
if (!dbgAddr) return;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -4767,7 +4778,7 @@ if (DEBUGGER) {
|
|||
}
|
||||
}
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_DATA);
|
||||
if (dbgAddr.off == null || dbgAddr.sel == null && dbgAddr.addr == null) return;
|
||||
if (!dbgAddr) return;
|
||||
|
||||
var sDump = "";
|
||||
if (BACKTRACK && sCmd == "di") {
|
||||
|
|
@ -4821,7 +4832,8 @@ if (DEBUGGER) {
|
|||
return;
|
||||
}
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_DATA);
|
||||
if (dbgAddr.off == null) return;
|
||||
if (!dbgAddr) return;
|
||||
|
||||
for (var i = 2; i < asArgs.length; i++) {
|
||||
var b = str.parseInt(asArgs[i], 16);
|
||||
if (b === undefined) {
|
||||
|
|
@ -5004,8 +5016,7 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.doList = function(sSymbol)
|
||||
{
|
||||
var dbgAddr = this.parseAddr(sSymbol, Debugger.ADDR_CODE);
|
||||
|
||||
if (dbgAddr.off == null && dbgAddr.addr == null) return;
|
||||
if (!dbgAddr) return;
|
||||
|
||||
var addr = this.getAddr(dbgAddr);
|
||||
sSymbol = sSymbol? (sSymbol + ": ") : "";
|
||||
|
|
@ -5064,7 +5075,9 @@ if (DEBUGGER) {
|
|||
|
||||
var fJSON = (asArgs[1] == "json");
|
||||
var iDrive, iSector = 0, nSectors = 0;
|
||||
|
||||
var dbgAddr = (fJSON? {} : this.parseAddr(asArgs[1], Debugger.ADDR_DATA));
|
||||
if (!dbgAddr) return;
|
||||
|
||||
iDrive = this.parseValue(asArgs[2], "drive #");
|
||||
if (iDrive === undefined) return;
|
||||
|
|
@ -5086,7 +5099,7 @@ if (DEBUGGER) {
|
|||
* drive and its disk are inseparable; it's certainly possible that its disk object may be empty at
|
||||
* this point, but that will only affect whether the read succeeds or not.
|
||||
*/
|
||||
var dc = this.fdc;
|
||||
var dc = this .fdc;
|
||||
if (iDrive >= 2 && this.hdc) {
|
||||
iDrive -= 2;
|
||||
dc = this.hdc;
|
||||
|
|
@ -5547,7 +5560,7 @@ if (DEBUGGER) {
|
|||
{
|
||||
if (sAddr !== undefined) {
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_CODE);
|
||||
if (dbgAddr.off == null) return;
|
||||
if (!dbgAddr) return;
|
||||
this.setTempBreakpoint(dbgAddr);
|
||||
}
|
||||
if (!this.runCPU(true)) {
|
||||
|
|
@ -5841,7 +5854,7 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.doUnassemble = function(sAddr, sAddrEnd, n)
|
||||
{
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_CODE);
|
||||
if (dbgAddr.off == null) return;
|
||||
if (!dbgAddr) return;
|
||||
|
||||
if (n === undefined) n = 1;
|
||||
var dbgAddrEnd = this.newAddr(this.maskReg, dbgAddr.sel, this.bus.nBusLimit);
|
||||
|
|
@ -5850,7 +5863,7 @@ if (DEBUGGER) {
|
|||
if (sAddrEnd !== undefined) {
|
||||
|
||||
dbgAddrEnd = this.parseAddr(sAddrEnd, Debugger.ADDR_CODE);
|
||||
if (dbgAddrEnd.off == null || dbgAddrEnd.off < dbgAddr.off) return;
|
||||
if (!dbgAddrEnd || dbgAddrEnd.off < dbgAddr.off) return;
|
||||
|
||||
cb = dbgAddrEnd.off - dbgAddr.off;
|
||||
if (!DEBUG && cb > 0x100) {
|
||||
|
|
|
|||
|
|
@ -2142,7 +2142,7 @@ Disk.prototype.toJSON = function()
|
|||
/*
|
||||
* Last but not least, insert line breaks after every object definition, to ease the pain on text editors.
|
||||
*/
|
||||
s = s.replace(/},/gm, "},\n");
|
||||
s = s.replace(/([\]}]),/gm, "$1,\n");
|
||||
return s;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ function Memory(addr, used, size, type, controller, cpu)
|
|||
this.type = type || Memory.TYPE.NONE;
|
||||
this.fReadOnly = (type == Memory.TYPE.ROM);
|
||||
this.controller = null;
|
||||
this.cpu = cpu;
|
||||
this.cpu = cpu; // If a CPU reference is provided, then this must be an UNPAGED Memory block allocation
|
||||
this.fDirty = this.fDirtyEver = false;
|
||||
this.setPhysBlock();
|
||||
|
||||
|
|
@ -490,6 +490,8 @@ Memory.prototype = {
|
|||
/**
|
||||
* getPageBlock(addr, fWrite)
|
||||
*
|
||||
* Called for UNPAGED Memory blocks only.
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} addr
|
||||
* @param {boolean} fWrite (true if called for a write, false if for a read)
|
||||
|
|
@ -526,7 +528,7 @@ Memory.prototype = {
|
|||
*
|
||||
* NOTE: Some Memory blocks already require access to the CPU (eg, UNPAGED blocks that need to call cpu.mapPageBlock()),
|
||||
* while others require access only if the CPU has set a read or write breakpoint in one of its Debug registers; the latter
|
||||
* case is handled here by virtue of the cpu parameter.
|
||||
* case is handled here by virtue of the CPU parameter.
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
|
|
@ -552,9 +554,15 @@ Memory.prototype = {
|
|||
/**
|
||||
* removeBreakpoint(off, fWrite)
|
||||
*
|
||||
* NOTE: If this Memory block is not an UNPAGED block that might need to call cpu.mapPageBlock()), and it no longer
|
||||
* has any read or write breakpoints associated with it, then it no longer needs a CPU reference. However, the latter
|
||||
* is a moot point, because the "checked" memory access functions should be swapped out when this function is done.
|
||||
* NOTE: If this Memory block is not an UNPAGED block that might need to call cpu.mapPageBlock()), and it no
|
||||
* longer has any read or write breakpoints associated with it, then it no longer needs a CPU reference. The
|
||||
* existence of a CPU reference only impacts the performance of the "checked" memory access functions, so it's
|
||||
* not critical to eliminate it, but we do it anyway, in case the "checked" functions become re-enabled later
|
||||
* (eg, by the Debugger -- yeah, the Debugger, which is why eliminating it isn't critical).
|
||||
*
|
||||
* TODO: Another option would be to count CPU references separately from Debugger references, so that when the
|
||||
* former goes to zero, we can unconditionally remove the CPU reference; UNPAGED blocks would automatically
|
||||
* increment that reference count, so their CPU reference would never go away.
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
|
|
@ -575,6 +583,7 @@ Memory.prototype = {
|
|||
}
|
||||
Component.assert(this.cWriteBreakpoints >= 0);
|
||||
}
|
||||
if (!this.cReadBreakpoints && !this.cWriteBreakpoints && this.type != Memory.TYPE.UNPAGED) this.cpu = null;
|
||||
},
|
||||
/**
|
||||
* readNone(off)
|
||||
|
|
|
|||
|
|
@ -409,18 +409,19 @@ var X86 = {
|
|||
* Bit values for opFlags, which are all reset to zero prior to each instruction
|
||||
*/
|
||||
OPFLAG: {
|
||||
NOREAD: 0x0001,
|
||||
NOWRITE: 0x0002,
|
||||
NOINTR: 0x0004, // indicates a segreg has been set, or a prefix, or an STI (delay INTR acknowledgement)
|
||||
FAULT: 0x0008, // indicates a fault occurred during the current instruction
|
||||
NOREAD: 0x0001, // disable memory reads for the remainder of the current instruction
|
||||
NOWRITE: 0x0002, // disable memory writes for the remainder of the current instruction
|
||||
NOINTR: 0x0004, // a segreg has been set, or a prefix, or an STI (delay INTR acknowledgement)
|
||||
SEG: 0x0010, // segment override
|
||||
LOCK: 0x0020, // lock prefix
|
||||
REPZ: 0x0040, // repeat while Z (NOTE: this value MUST match PS.ZF; see opCMPSb/opCMPSw/opSCASb/opSCASw)
|
||||
REPNZ: 0x0080, // repeat while NZ
|
||||
REPEAT: 0x0100, // indicates that an instruction is being repeated (ie, some iteration AFTER the first)
|
||||
REPEAT: 0x0100, // an instruction is being repeated (ie, some iteration AFTER the first)
|
||||
PUSHSP: 0x0200, // the SP register is potentially being referenced by a PUSH SP opcode, adjustment may be required
|
||||
DATASIZE: 0x1000, // data size override
|
||||
ADDRSIZE: 0x2000 // address size override
|
||||
DATASIZE: 0x0400, // data size override
|
||||
ADDRSIZE: 0x0800, // address size override
|
||||
FAULT: 0x1000, // a fault occurred during the current instruction
|
||||
DEBUG: 0x2000 // a DEBUG exception occurred during the current instruction
|
||||
},
|
||||
/*
|
||||
* Bit values for intFlags
|
||||
|
|
|
|||
|
|
@ -1683,7 +1683,7 @@ X86CPU.prototype.checkIntReturn = function(addr)
|
|||
*
|
||||
* These functions provide Debug register functionality by leveraging the same Memory block-based breakpoint
|
||||
* support originally created for our built-in Debugger. Only minimal changes were required to the Memory
|
||||
* component, by adding additional checkMemoryException() call-outs to the "checked" Memory access functions.
|
||||
* component, by adding additional checkMemoryException() call-outs from the "checked" Memory access functions.
|
||||
*
|
||||
* Note that those call-outs occur only AFTER our own Debugger (if present) has checked the address and has
|
||||
* passed on it, because we want our own Debugger's breakpoints to take precedence over any breakpoints that
|
||||
|
|
@ -1712,6 +1712,58 @@ X86CPU.prototype.removeMemCheck = function(addr, fWrite)
|
|||
this.aMemBlocks[iBlock].removeBreakpoint(addr & this.nBlockLimit, fWrite);
|
||||
};
|
||||
|
||||
/**
|
||||
* checkDebugRegisters(fEnable)
|
||||
*
|
||||
* opMOVdr() simplifies its life by doing work ONLY if the contents of a Debug register is actually changing.
|
||||
*
|
||||
* Whenever a single register is about to change, it calls this function with fEnable set to false to REMOVE any
|
||||
* active checks, then updates the Debug register, then calls us again with fEnable set to true to (re)ADD active
|
||||
* checks.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {boolean} fEnable
|
||||
*/
|
||||
X86CPU.prototype.checkDebugRegisters = function(fEnable)
|
||||
{
|
||||
/*
|
||||
* We use a constant mask for the enable bits (X86.DR7.L0 | X86.DR7.G0) and shift our copy of regDR7
|
||||
* right 2 bits after each Debug register check.
|
||||
*
|
||||
* Similarly, we make a copy of regDR7 in bitsDR7 and shift the latter right 4 bits at a time, so that
|
||||
* the RW and LEN bits for the next Debug register are always in positions 1-0 and 3-2, respectively.
|
||||
*/
|
||||
var regDR7 = this.regDR[7];
|
||||
var bitsDR7 = regDR7 >> 16;
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
if (regDR7 & (X86.DR7.L0 | X86.DR7.G0)) {
|
||||
/*
|
||||
* We look only to the low bit of the RW field to determine if we should be watching for a write.
|
||||
* FYI, if the low bit is clear but the high bit is set, that's "undefined"; we treat it as a read.
|
||||
*/
|
||||
var fWrite = !!(bitsDR7 & 0x1);
|
||||
/*
|
||||
* The address in regDR[i] should already be masked with ~0x1 for 2-byte accesses (LEN == 0x1) or
|
||||
* with ~0x3 for 4-byte accesses (LEN == 0x3), but if the client forgets, the hardware supposedly
|
||||
* enforces it, so that's what we do here, too.
|
||||
*
|
||||
* FYI, if LEN is set to the "undefined" value of (0x2), we still apply a mask to the address, albeit
|
||||
* a nonsensical mask of ~0x2 or 0xfffffffd. That's how we define that particular "undefined" LEN.
|
||||
*/
|
||||
var addr = this.regDR[i];
|
||||
var len = ((bitsDR7 >> 2) & 0x3);
|
||||
addr &= ~len; // NOTE: if LEN == 0x0, we don't need to mask, but ~0x0 is equivalent to no mask
|
||||
if (fEnable) {
|
||||
this.addMemCheck(addr, fWrite);
|
||||
} else {
|
||||
this.removeMemCheck(addr, fWrite);
|
||||
}
|
||||
}
|
||||
regDR7 >>= 2; bitsDR7 >>= 4;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* checkMemoryException(addr, nb, fWrite)
|
||||
*
|
||||
|
|
@ -1719,8 +1771,8 @@ X86CPU.prototype.removeMemCheck = function(addr, fWrite)
|
|||
* giving us the opportunity look for a matching "read" or "write" breakpoint enabled in one of the DRn registers.
|
||||
*
|
||||
* TODO: This currently does not discriminate between data reads and execution reads. When we switch to a true
|
||||
* "prefetch" model, that would also be a good time to include a signal to this function that any "read" accesses
|
||||
* are actually "exec" accesses.
|
||||
* "prefetch" model, that would also be a good time to include a signal to this function which "read" accesses are
|
||||
* are actually "exec" accesses.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} addr
|
||||
|
|
@ -1729,24 +1781,45 @@ X86CPU.prototype.removeMemCheck = function(addr, fWrite)
|
|||
*/
|
||||
X86CPU.prototype.checkMemoryException = function(addr, nb, fWrite)
|
||||
{
|
||||
if (this.regDR[7] & X86.DR7.ENABLE) {
|
||||
/*
|
||||
* NOTE: We're preventing redundant X86.EXCEPTION.DEBUG exceptions for a single instruction by checking
|
||||
* X86.OPFLAG.DEBUG. I decided not to rely on the generic X86.OPFLAG.FAULT, because if an instruction
|
||||
* first triggers a DIFFERENT exception which then triggers a DEBUG exception (eg, because a Debug register
|
||||
* was set on the IDT entry of the first exception), that we'd actually like to see the DEBUG exception,
|
||||
* as opposed to, say, a double fault. TODO: Determine if that SHOULD generate a double-fault.
|
||||
*/
|
||||
if (!(this.opFlags & X86.OPFLAG.FAULT) && (this.regDR[7] & X86.DR7.ENABLE)) {
|
||||
nb--;
|
||||
/*
|
||||
* We use a constant mask for the enable bits (X86.DR7.L0 | X86.DR7.G0) and shift our copy of regDR7
|
||||
* right 2 bits after each Debug register check.
|
||||
*
|
||||
* Similarly, we make a copy of regDR7 in bitsDR7 and shift the latter right 4 bits at a time, so that
|
||||
* the RW and LEN bits for the next Debug register are always in positions 1-0 and 3-2, respectively.
|
||||
*/
|
||||
var regDR7 = this.regDR[7];
|
||||
var bitsEnabled = X86.DR7.L0 | X86.DR7.G0;
|
||||
var bitsRWMask = 0x00030000;
|
||||
var bitsRWRequired = (fWrite? 0x00010000 : (fWrite == false? 0x00030000 : 0));
|
||||
var bitsLENMask = 0x000C0000;
|
||||
var bitsLENRequired = (nb == 1? 0x00000000 : (nb == 2? 0x00040000 : 0x000C000));
|
||||
var bitsDR7 = regDR7 >> 16;
|
||||
|
||||
var bitsRWMask = X86.DR7.RW0 >> 16;
|
||||
var bitsRWRequired = (fWrite? 0x1 : (fWrite == false? 0x3 : 0x0));
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
if ((regDR7 & bitsEnabled) && (regDR7 & bitsRWMask) == bitsRWRequired) {
|
||||
if ((regDR7 & bitsLENMask) == bitsLENRequired && addr == this.regDR[i]) {
|
||||
if ((regDR7 & (X86.DR7.L0 | X86.DR7.G0)) && (bitsDR7 & bitsRWMask) == bitsRWRequired) {
|
||||
/*
|
||||
* NOTE: We reduced nb from 1-4 to 0-3 above, so we don't need to add 1 to len either.
|
||||
*/
|
||||
var len = (bitsDR7 >> 2);
|
||||
/*
|
||||
* Time to determine if addr through addr + nb overlaps regDR[i] through regDR[i] + len.
|
||||
*/
|
||||
if (addr + nb >= this.regDR[i] && addr <= this.regDR[i] + len) {
|
||||
this.regDR[6] |= (1 << i);
|
||||
this.opFlags |= X86.OPFLAG.DEBUG;
|
||||
X86.fnFault.call(this, X86.EXCEPTION.DEBUG);
|
||||
return;
|
||||
}
|
||||
}
|
||||
bitsEnabled <<= 2;
|
||||
bitsRWMask <<= 2; bitsRWRequired <<= 2;
|
||||
bitsLENMask <<= 2; bitsLENRequired <<= 2;
|
||||
regDR7 >>= 2; bitsDR7 >>= 4;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3693,6 +3693,13 @@ X86.fnSrcNone = function SrcNone()
|
|||
*/
|
||||
X86.fnFault = function(nFault, nError, fHalt, nCycles)
|
||||
{
|
||||
/*
|
||||
* This flag is used by selected opcodes to provide an early exit if X86.OPFLAG.FAULT is set, in some cases
|
||||
* making it possible for an instruction to be restarted (eg, opINSw()), and in other cases preventing a redundant
|
||||
* fault from being generated. However, to prevent multiple X86.EXCEPTION.DEBUG exceptions on a single instruction,
|
||||
* checkMemoryException() currently relies on its own OPFLAG.DEBUG, on the theory that we should be allowed to see
|
||||
* DEBUG exceptions triggered by other exceptions.
|
||||
*/
|
||||
this.opFlags |= X86.OPFLAG.FAULT;
|
||||
|
||||
if (!this.aFlags.fComplete) {
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ X86.opMOVrc = function MOVrc()
|
|||
*/
|
||||
if (this.nCPL) {
|
||||
/*
|
||||
* You're not allowed to read control registers if the current privilege level is not zero
|
||||
* You're not allowed to read control registers if the current privilege level is not zero.
|
||||
*/
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
|
|
@ -366,7 +366,7 @@ X86.opMOVrc = function MOVrc()
|
|||
this.nStepCycles -= 6;
|
||||
|
||||
/*
|
||||
* TODO: Implement BACKTRACK for this instruction (although Control registers are not likely to be a conduit for much interesting data).
|
||||
* TODO: Implement BACKTRACK for this instruction (although Control registers are not likely to be a conduit for interesting data).
|
||||
*/
|
||||
};
|
||||
|
||||
|
|
@ -387,7 +387,7 @@ X86.opMOVrd = function MOVrd()
|
|||
*/
|
||||
if (this.nCPL) {
|
||||
/*
|
||||
* You're not allowed to read control registers if the current privilege level is not zero
|
||||
* You're not allowed to read control registers if the current privilege level is not zero.
|
||||
*/
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
|
|
@ -406,7 +406,7 @@ X86.opMOVrd = function MOVrd()
|
|||
this.nStepCycles -= 22;
|
||||
|
||||
/*
|
||||
* TODO: Implement BACKTRACK for this instruction (although Debug registers are not likely to be a conduit for much interesting data).
|
||||
* TODO: Implement BACKTRACK for this instruction (although Debug registers are not likely to be a conduit for interesting data).
|
||||
*/
|
||||
};
|
||||
|
||||
|
|
@ -436,7 +436,7 @@ X86.opMOVcr = function MOVcr()
|
|||
*/
|
||||
if (this.nCPL) {
|
||||
/*
|
||||
* You're not allowed to write control registers if the current privilege level is not zero
|
||||
* You're not allowed to write control registers if the current privilege level is not zero.
|
||||
*/
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
|
|
@ -464,7 +464,7 @@ X86.opMOVcr = function MOVcr()
|
|||
}
|
||||
|
||||
/*
|
||||
* TODO: Implement BACKTRACK for this instruction (although Control registers are not likely to be a conduit for much interesting data).
|
||||
* TODO: Implement BACKTRACK for this instruction (although Control registers are not likely to be a conduit for interesting data).
|
||||
*/
|
||||
};
|
||||
|
||||
|
|
@ -485,7 +485,7 @@ X86.opMOVdr = function MOVdr()
|
|||
*/
|
||||
if (this.nCPL) {
|
||||
/*
|
||||
* You're not allowed to write control registers if the current privilege level is not zero
|
||||
* You're not allowed to write control registers if the current privilege level is not zero.
|
||||
*/
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
|
|
@ -498,38 +498,18 @@ X86.opMOVdr = function MOVdr()
|
|||
X86.opUndefined.call(this);
|
||||
return;
|
||||
}
|
||||
|
||||
var regDR = this.getReg(bModRM & 0x7);
|
||||
|
||||
if (iDst == 7) {
|
||||
var regDR7 = this.regDR[7];
|
||||
if ((regDR ^ regDR7) & X86.DR7.ENABLE) {
|
||||
/*
|
||||
* We need to check changes to the Debug Control Register (DR7); specifically, if any of
|
||||
* DR0-DR3 are transitioning from disabled to enabled, or vice versa, then we need to call
|
||||
* addMemCheck(), or removeMemCheck(), as appropriate.
|
||||
*/
|
||||
var bitsEnabled = X86.DR7.L0 | X86.DR7.G0;
|
||||
var bitsRWMask = 0x00030000;
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var fEnabled = (regDR & bitsEnabled);
|
||||
if (!fEnabled != !(regDR7 & bitsEnabled)) {
|
||||
var bitsRW = (regDR & bitsRWMask) >> (i << 1);
|
||||
var fWrite = !!(bitsRW & 0x00010000);
|
||||
if (fEnabled) {
|
||||
this.addMemCheck(this.regDR[i], fWrite);
|
||||
} else {
|
||||
this.removeMemCheck(this.regDR[i], fWrite);
|
||||
}
|
||||
}
|
||||
bitsEnabled <<= 2;
|
||||
bitsRWMask <<= 2;
|
||||
}
|
||||
}
|
||||
if (regDR != this.regDR[iDst]) {
|
||||
this.checkDebugRegisters(false);
|
||||
this.regDR[iDst] = regDR;
|
||||
this.checkDebugRegisters(true);
|
||||
}
|
||||
this.regDR[iDst] = regDR;
|
||||
|
||||
this.nStepCycles -= (iDst < 4? 22 : 14);
|
||||
/*
|
||||
* TODO: Implement BACKTRACK for this instruction (although Debug registers are not likely to be a conduit for much interesting data).
|
||||
* TODO: Implement BACKTRACK for this instruction (although Debug registers are not likely to be a conduit for interesting data).
|
||||
*/
|
||||
};
|
||||
|
||||
|
|
@ -550,7 +530,7 @@ X86.opMOVrt = function MOVrt()
|
|||
*/
|
||||
if (this.nCPL) {
|
||||
/*
|
||||
* You're not allowed to read control registers if the current privilege level is not zero
|
||||
* You're not allowed to read control registers if the current privilege level is not zero.
|
||||
*/
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
|
|
@ -567,7 +547,7 @@ X86.opMOVrt = function MOVrt()
|
|||
this.nStepCycles -= 12;
|
||||
|
||||
/*
|
||||
* TODO: Implement BACKTRACK for this instruction (although Test registers are not likely to be a conduit for much interesting data).
|
||||
* TODO: Implement BACKTRACK for this instruction (although Test registers are not likely to be a conduit for interesting data).
|
||||
*/
|
||||
};
|
||||
|
||||
|
|
@ -588,7 +568,7 @@ X86.opMOVtr = function MOVtr()
|
|||
*/
|
||||
if (this.nCPL) {
|
||||
/*
|
||||
* You're not allowed to write control registers if the current privilege level is not zero
|
||||
* You're not allowed to write control registers if the current privilege level is not zero.
|
||||
*/
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
|
|
@ -610,7 +590,7 @@ X86.opMOVtr = function MOVtr()
|
|||
this.nStepCycles -= 12;
|
||||
|
||||
/*
|
||||
* TODO: Implement BACKTRACK for this instruction (although Test registers are not likely to be a conduit for much interesting data).
|
||||
* TODO: Implement BACKTRACK for this instruction (although Test registers are not likely to be a conduit for interesting data).
|
||||
*/
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue