Working prefetch support (speeds up 80386 operation when paging)

This commit is contained in:
Jeff Parsons 2015-10-27 11:25:02 -07:00
commit 526da018d0
5 changed files with 256 additions and 458 deletions

View file

@ -200,6 +200,12 @@ function Debugger(parmsDbg)
this.aBreakExec = this.aBreakRead = this.aBreakWrite = [];
this.clearBreakpoints();
/*
* The new "bn" command allows you to specify a number of instructions to execute and then stop;
* "bn 0" disables any outstanding count.
*/
this.nBreakIns = 0;
/*
* Execution history is allocated by historyInit() whenever checksEnabled() conditions change.
* Execution history is updated whenever the CPU calls checkInstruction(), which will happen
@ -3885,7 +3891,7 @@ if (DEBUGGER) {
*/
Debugger.prototype.checksEnabled = function(fRelease)
{
return ((DEBUG && !fRelease)? true : (this.aBreakExec.length > 1 || this.messageEnabled(Messages.INT) /* || this.aBreakRead.length > 1 || this.aBreakWrite.length > 1 */));
return ((DEBUG && !fRelease)? true : (this.aBreakExec.length > 1 || !!this.nBreakIns || this.messageEnabled(Messages.INT) /* || this.aBreakRead.length > 1 || this.aBreakWrite.length > 1 */));
};
/**
@ -3902,6 +3908,9 @@ if (DEBUGGER) {
Debugger.prototype.checkInstruction = function(addr, nState)
{
if (nState > 0) {
if (this.nBreakIns && !--this.nBreakIns) {
return true;
}
if (this.checkBreakpoint(addr, 1, this.aBreakExec)) {
return true;
}
@ -5776,6 +5785,8 @@ if (DEBUGGER) {
* These two new commands operate as toggles so that if "*" is used to trap all input (or output),
* you can also use these commands to NOT trap specific ports.
*
* bn [n] break after [n] instructions
*
* TODO: Update the "bl" command to include any/all I/O breakpoints, and the "bc" command to
* clear them. Because "bi" and "bo" commands are piggy-backing on Bus functions, those breakpoints
* are currently outside the realm of what the "bl" and "bc" commands are aware of.
@ -5796,6 +5807,7 @@ if (DEBUGGER) {
this.println("\tbw [a]\tset write breakpoint at addr [a]");
this.println("\tbc [a]\tclear breakpoint at addr [a]");
this.println("\tbl\tlist all breakpoints");
this.println("\tbn [n]\tbreak after [n] instruction(s)");
return;
}
var sParm = sCmd.charAt(1);
@ -5807,6 +5819,11 @@ if (DEBUGGER) {
if (!cBreaks) this.println("no breakpoints");
return;
}
if (sParm == 'n') {
this.nBreakIns = this.parseValue(sAddr);
this.println("break after " + this.nBreakIns + " instruction(s)");
return;
}
if (sAddr === undefined) {
this.println("missing breakpoint address");
return;

View file

@ -56,10 +56,8 @@ var DEBUGGER = true; // this @define is overridden by the Closure Com
* @define {boolean}
*
* PREFETCH enables the use of a prefetch queue.
*
* See the Bus component for details.
*/
var PREFETCH = false;
var PREFETCH = true;
/**
* @define {boolean}

View file

@ -88,8 +88,8 @@ if (!I386) {
* This is a logical simulation, not a physical simulation, and performance is critical, second only
* to the accuracy of the simulation when running real-world x86 software. Consequently, it takes a
* few liberties with the operation of the simulated hardware, especially with regard to timings,
* little-used features, etc. We do make an effort to maintain accurate instruction cycle counts,
* but there are many other obstacles (eg, prefetch queue, wait states) to achieving perfect timings.
* little-used features, etc. We do make an effort to maintain approximate instruction cycle counts,
* but there are many other obstacles (eg, prefetch queue, wait states) to achieving accurate timings.
*
* For example, our 8237 DMA controller performs all DMA transfers immediately, since internally
* they are all memory-to-memory, and attempting to interleave DMA cycles with instruction execution
@ -181,6 +181,11 @@ function X86CPU(parmsCPU)
this.nBusMask = this.nMemMask = 0;
this.nBlockShift = this.nBlockSize = this.nBlockLimit = this.nBlockTotal = this.nBlockMask = 0;
if (PREFETCH) {
this.cbPrefetch = 0;
this.adwPrefetch = null;
}
/*
* This initial resetRegs() call is important to create all the registers (eg, the X86Seg registers),
* so that if/when we call restore(), it will have something to fill in.
@ -423,183 +428,53 @@ X86CPU.CYCLES_80286 = {
};
/*
* TODO: All these values were simply copied from the 80286 table and still need to be modified and verified.
* TODO: All 80386 cycle counts are based on 80286 counts until I have time to hand-generate an 80386-specific table;
* Cycle counts for 80386-only instructions are hard-coded in their respective handlers, since those counts don't vary.
*/
X86CPU.CYCLES_80386 = {
nWordCyclePenalty: 0,
nEACyclesBase: 0,
nEACyclesDisp: 0,
nEACyclesBaseIndex: 0,
nEACyclesBaseIndexExtra: 0,
nEACyclesBaseDisp: 0,
nEACyclesBaseIndexDisp: 1,
nEACyclesBaseIndexDispExtra:1,
nOpCyclesAAA: 3,
nOpCyclesAAD: 14,
nOpCyclesAAM: 16,
nOpCyclesArithRR: 2,
nOpCyclesArithRM: 7,
nOpCyclesArithMR: 7,
nOpCyclesArithMID: 0,
nOpCyclesCall: 7, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesCallF: 13, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesCallWR: 7, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesCallWM: 11, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesCallDM: 16, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesCLI: 3,
nOpCyclesCompareRM: 6,
nOpCyclesCWD: 2,
nOpCyclesBound: 13,
nOpCyclesInP: 5,
nOpCyclesInDX: 5,
nOpCyclesIncR: 2,
nOpCyclesIncM: 7,
nOpCyclesInt: 23, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesInt3D: 0,
nOpCyclesIntOD: 1,
nOpCyclesIntOFall: 3,
nOpCyclesIRet: 17, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesJmp: 7, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesJmpF: 11, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesJmpC: 7, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesJmpCFall: 3,
nOpCyclesJmpWR: 7, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesJmpWM: 11, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesJmpDM: 15, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesLAHF: 2,
nOpCyclesLEA: 3,
nOpCyclesLS: 7,
nOpCyclesLoop: 8, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesLoopZ: 8, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesLoopNZ: 8, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesLoopFall: 4,
nOpCyclesLoopZFall: 4,
nOpCyclesMovRR: 2, // this is actually the same as the 8086...
nOpCyclesMovRM: 3,
nOpCyclesMovMR: 5,
nOpCyclesMovRI: 2,
nOpCyclesMovMI: 3,
nOpCyclesMovAM: 5, // this is actually slower than the MOD/RM form of MOV AX,mem (see nOpCyclesMovRM)
nOpCyclesMovMA: 3,
nOpCyclesDivBR: 14,
nOpCyclesDivWR: 22,
nOpCyclesDivBM: 17,
nOpCyclesDivWM: 25,
nOpCyclesIDivBR: 17,
nOpCyclesIDivWR: 25,
nOpCyclesIDivBM: 20,
nOpCyclesIDivWM: 28,
nOpCyclesMulBR: 13,
nOpCyclesMulWR: 21,
nOpCyclesMulBM: 16,
nOpCyclesMulWM: 24,
nOpCyclesIMulBR: 13,
nOpCyclesIMulWR: 21,
nOpCyclesIMulBM: 16,
nOpCyclesIMulWM: 24,
nOpCyclesNegR: 2,
nOpCyclesNegM: 7,
nOpCyclesOutP: 5,
nOpCyclesOutDX: 5,
nOpCyclesPopAll: 19,
nOpCyclesPopReg: 5,
nOpCyclesPopMem: 5,
nOpCyclesPushAll: 17,
nOpCyclesPushReg: 3,
nOpCyclesPushMem: 5,
nOpCyclesPushSeg: 3,
nOpCyclesPrefix: 0,
nOpCyclesCmpS: 8,
nOpCyclesCmpSr0: 5,
nOpCyclesCmpSrn: 9,
nOpCyclesLodS: 5,
nOpCyclesLodSr0: 5,
nOpCyclesLodSrn: 4,
nOpCyclesMovS: 5,
nOpCyclesMovSr0: 5,
nOpCyclesMovSrn: 4,
nOpCyclesScaS: 7,
nOpCyclesScaSr0: 5,
nOpCyclesScaSrn: 8,
nOpCyclesStoS: 3,
nOpCyclesStoSr0: 4,
nOpCyclesStoSrn: 3,
nOpCyclesRet: 11, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesRetn: 11, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesRetF: 15, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesRetFn: 15, // on the 80286, this ALSO includes the number of bytes in the target instruction
nOpCyclesShift1M: 7,
nOpCyclesShiftCR: 5,
nOpCyclesShiftCM: 8,
nOpCyclesShiftCS: 0,
nOpCyclesTestRR: 2,
nOpCyclesTestRM: 6,
nOpCyclesTestRI: 3,
nOpCyclesTestMI: 6,
nOpCyclesXchgRR: 3,
nOpCyclesXchgRM: 5,
nOpCyclesXLAT: 5
};
X86CPU.CYCLES_80386 = X86CPU.CYCLES_80286;
/**
* Memory Simulation Notes
*
* Memory accesses are currently hard-coded to simulate 8088 characteristics.
* For example, every 16-bit memory access is assumed to require an additional 4 cycles
* for the upper byte; on an 8086, that would be true only when the memory address was odd.
*
* Similarly, the effective prefetch queue size is 4 bytes (same as an 8088), although
* that can easily be changed to 6 bytes if/when we decide to fully implement 8086 support
* (see X86CPU.PREFETCH.QUEUE). It's just not clear whether that support will be a goal.
*/
X86CPU.PREFETCH = {
QUEUE: 4,
ARRAY: 8, // smallest power-of-two > PREFETCH.QUEUE
MASK: 0x7 // (X86CPU.PREFETCH.ARRAY - 1)
};
if (PREFETCH) {
/*
* NOTE: X86CPU.PREFETCH.LENGTH must be set to a power of two, so that LENGTH - 1 will form a mask
* (IP_MASK) we can use to create a sliding prefetch window of LENGTH bytes. We also zero the low
* 2 bits of IP_MASK so that the sliding window always starts on a 32-bit (long) boundary. Finally,
* instead breaking breaking all the longs we prefetch into bytes, we simply store the longs as-is
* into every 4th element of the queue (the queue is sparse array).
*/
X86CPU.PREFETCH = {
LENGTH: 16 // 16 generates a 16-byte prefetch queue consisting of 4 32-bit entries
};
X86CPU.PREFETCH.IP_MASK = ((X86CPU.PREFETCH.LENGTH - 1) & ~0x3);
}
/**
* initMemory(aMemBlocks, nBlockShift)
*
* Notification from Bus.initMemory(), giving us direct access to the entire memory space
* (aMemBlocks).
* (aMemBlocks). Since the CPU must perform additional layers of address decoding depending
* on the mode (real-mode, protected-mode, paging), it's best if the CPU can avoid going
* through the Bus component for every memory access.
*
* We also initialize an instruction byte prefetch queue, aPrefetch, which is an N-element
* array with slots that look like:
* We also initialize a 32-bit prefetch queue, containing dword-aligned values; the queue is
* an array of dwords indexed by a masked regLIP; for example, a queue of 4 dwords is indexed
* by "regLIP & 0xC"; we use a sparse array to avoid right-shifting the index, like so:
*
* 0: [tag, b] <-- iPrefetchTail
* 1: [tag, b]
* 2: [ -1, 0] <-- iPrefetchHead (eg, when cbPrefetchQueued == 2)
* ...
* 7: [ -1, 0]
* 0: [dword]
* 4: [dword]
* 8: [dword]
* 12: [dword]
*
* where tag is the linear address of the byte that's been prefetched, and b is the
* value of the byte. N is currently 8 (PREFETCH.ARRAY), but it can be any power-of-two
* that is equal to or greater than (PREFETCH.QUEUE), the effective size of the prefetch
* queue (6 on an 8086, 4 on an 8088; currently hard-coded to the latter). All slots
* are initialized to [-1, 0] when preallocating the prefetch queue, but those initial
* values are quickly overwritten and never seen again.
* The actual regLIP mask is X86CPU.PREFETCH.IP_MASK; ie, (X86CPU.PREFETCH.LENGTH - 1) & ~0x3.
*
* iPrefetchTail is the index (0-7) of the next prefetched byte to be returned to the CPU,
* and iPrefetchHead is the index (0-7) of the next slot to be filled. The prefetch queue
* is empty IFF the two indexes are equal and IFF cbPrefetchQueued is zero. cbPrefetchQueued
* is simply the number of bytes between the tail and the head (from 0 to PREFETCH.QUEUE).
* On refilling, the queue is always filled to capacity, and cbPrefetch is set to its maximum
* value (eg, a value from 16 to 13, depending on whether "regLIP & 0x3" is 0, 1, 2 or 3).
*
* cbPrefetchValid indicates how many bytes behind iPrefetchHead are still valid, allowing us
* to "rewind" the tail up to that many bytes. For example, let's imagine that we prefetched
* 2 bytes, and then we immediately consumed both bytes, leaving iPrefetchTail == iPrefetchHead
* again; however, those previous 2 bytes are still valid, and if, for example, we wanted to
* rewind the IP by 2 (which we might want to do in the case of a repeated string instruction),
* we could rewind the prefetch queue tail as well.
* When a byte is requested from the queue, the dword is extracted from index "regLIP & 0xC"
* and then shifted by 0, 8, 16, or 24, depending on whether "regLIP & 0x3" is 0, 1, 2 or 3
* (ie, "(regLIP & 0x3) << 3").
*
* Corresponding to iPrefetchHead is addrPrefetchHead; both are incremented in lock-step.
* Whenever the prefetch queue is flushed, it's typically because a new, non-incremental
* regLIP has been set, so flushPrefetch() expects to receive that address.
*
* If the prefetch queue does not contain any (or enough) bytes to satisfy a getBytePrefetch()
* or getShortPrefetch() request, we force the queue to be filled with the necessary number
* of bytes first.
* TODO: Consider how/whether to simulate an effective prefetch queue size of 4 bytes for an 8088,
* 6 bytes for an 8086, 12 for an 80386, etc.
*
* @this {X86CPU}
* @param {Array} aMemBlocks
@ -620,12 +495,8 @@ X86CPU.prototype.initMemory = function(aMemBlocks, nBlockShift)
this.nBlockTotal = aMemBlocks.length;
this.nBlockMask = this.nBlockTotal - 1;
if (PREFETCH) {
this.nBusCycles = 0;
this.aPrefetch = new Array(X86CPU.PREFETCH.ARRAY);
for (var i = 0; i < X86CPU.PREFETCH.ARRAY; i++) {
this.aPrefetch[i] = 0;
}
this.flushPrefetch(0);
// this.nBusCycles = 0;
this.adwPrefetch = new Array(X86CPU.PREFETCH.LENGTH);
}
};
@ -785,8 +656,10 @@ X86CPU.prototype.enablePageBlocks = function()
this.aMemBlocks[iBlock] = this.blockUnpaged;
}
/*
* We also need a special "empty" Memory block that mapPageBlock() can pass back to callers
* whenever a valid block cannot be found for an UNPAGED block.
* We also use a special "empty" Memory block that mapPageBlock() can pass back to callers
* whenever a valid block cannot be found for an UNPAGED block. Under normal conditions,
* an invalid block will trigger a fault, so memEmpty will never actually be returned, but
* if the Debugger is suppressing faults or calling probeAddr(), returning memEmpty is helpful.
*/
this.memEmpty = new Memory();
} else {
@ -821,13 +694,13 @@ X86CPU.prototype.enablePageBlocks = function()
*
* Also, addrPDE, addrPTE and addrPhys do not need any offsets added to them, because we immediately shift
* the offset portion of those addresses out. But for now, at least for debugging and documentation purposes,
* my preference is to perform full address calculations.
* my preference is to include the offset in the address calculations.
*
* Besides, this should not be a performance-critical function; it's normally called only once per UNPAGED
* page. Obviously, if CR3 is constantly being updated, that will trigger repeated calls to enablePageBlocks(),
* which will perform our equivalent of a TLB flush (ie, resetting all PAGED blocks back to UNPAGED blocks).
* That would hurt our performance, but it would hurt performance on a real machine as well, so let's see
* what real-world scenarios we run into.
* That would hurt our performance, but it would hurt performance on a real machine as well, so presumably
* CR3 updates will be minimal.
*
* @this {X86CPU}
* @param {number} addr is a linear address
@ -1448,10 +1321,6 @@ X86CPU.prototype.resetRegs = function()
*/
this.intFlags = X86.INTFLAG.NONE;
this.setCSIP(0, 0xffff); // this should be called before the first setPS() call, in part so that CPL will be set
if (!I386) this.resetSizes();
if (BACKTRACK) {
/*
* Initialize the backtrack indexes for all registers to zero. And while, yes, it IS possible
@ -1489,23 +1358,28 @@ X86CPU.prototype.resetRegs = function()
}
/*
* Assorted 80286-specific registers. The GDTR and IDTR registers are stored as the following pieces:
*
* GDTR: addrGDT (24 bits) and addrGDTLimit (24 bits)
* IDTR: addrIDT (24 bits) and addrIDTLimit (24 bits)
*
* while the LDTR and TR are stored as special segment registers: segLDT and segTSS.
*
* So, yes, our GDTR and IDTR "registers" differ from other segment registers in that we do NOT record
* the 16-bit limit specified by the LGDT or LIDT instructions; instead, we immediately calculate the limiting
* address, and record that instead.
*
* In addition to different CS:IP reset values, the CS base address must be set to the top of the 16Mb
* address space rather than the top of the first 1Mb (which is why the MODEL_5170 ROM must be addressable
* at both 0x0F0000 and 0xFF0000; see the ROM component's "alias" parameter).
* Set the initial CS:IP appropriate for the processor; this should be done before the first setPS() call,
* in part so that CPL will be set properly.
*/
if (this.model >= X86.MODEL_80286) {
if (this.model < X86.MODEL_80286) {
this.setCSIP(0, 0xffff);
} else {
/*
* Assorted 80286-specific registers. The GDTR and IDTR registers are stored as the following pieces:
*
* GDTR: addrGDT (24 bits) and addrGDTLimit (24 bits)
* IDTR: addrIDT (24 bits) and addrIDTLimit (24 bits)
*
* while the LDTR and TR are stored as special segment registers: segLDT and segTSS.
*
* So, yes, our GDTR and IDTR "registers" differ from other segment registers in that we do NOT record
* the 16-bit limit specified by the LGDT or LIDT instructions; instead, we immediately calculate the limiting
* address, and record that instead.
*
* In addition to different CS:IP reset values, the CS base address must be set to the top of the 16Mb
* address space rather than the top of the first 1Mb (which is why the MODEL_5170 ROM must be addressable
* at both 0x0F0000 and 0xFF0000; see the ROM component's "alias" parameter).
*
* TODO: Verify what the 80286 actually sets addrGDT and addrGDTLimit to on reset (or if it leaves them alone).
*/
this.addrGDT = 0; this.addrGDTLimit = 0xffff; // GDTR
@ -1573,7 +1447,7 @@ X86CPU.prototype.setAddrSize = function(size)
X86CPU.prototype.updateAddrSize = function()
{
if (!I386) {
this.getAddr = this.getShort;
this.getAddr = (PREFETCH? this.getShortPrefetch : this.getShort);
this.aOpModRegByte = X86ModB.aOpModReg;
this.aOpModMemByte = X86ModB.aOpModMem;
this.aOpModGrpByte = X86ModB.aOpModGrp;
@ -1582,7 +1456,7 @@ X86CPU.prototype.updateAddrSize = function()
this.aOpModGrpWord = X86ModW.aOpModGrp;
} else {
if (this.sizeAddr == 2) {
this.getAddr = this.getShort;
this.getAddr = (PREFETCH? this.getShortPrefetch : this.getShort);
this.aOpModRegByte = X86ModB16.aOpModReg;
this.aOpModMemByte = X86ModB16.aOpModMem;
this.aOpModGrpByte = X86ModB16.aOpModGrp;
@ -1590,7 +1464,7 @@ X86CPU.prototype.updateAddrSize = function()
this.aOpModMemWord = X86ModW16.aOpModMem;
this.aOpModGrpWord = X86ModW16.aOpModGrp;
} else {
this.getAddr = this.getLong;
this.getAddr = (PREFETCH? this.getLongPrefetch : this.getLong);
this.aOpModRegByte = X86ModB32.aOpModReg;
this.aOpModMemByte = X86ModB32.aOpModMem;
this.aOpModGrpByte = X86ModB32.aOpModGrp;
@ -1864,8 +1738,8 @@ X86CPU.prototype.checkDebugRegisters = function(fEnable)
* 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 which "read" accesses are
* are actually "exec" accesses.
* "prefetch" model, that would also be a good time to include a signal to this function indicating which "read"
* accesses are are actually "exec" accesses.
*
* @this {X86CPU}
* @param {number} addr
@ -2356,19 +2230,34 @@ X86CPU.prototype.getIP = function()
/**
* setIP(off)
*
* With the addition of flushPrefetch(), this function should only be called
* for non-incremental IP updates; setIP(this.getIP()+1) is no longer appropriate.
*
* In fact, for performance reasons, it's preferable to increment regLIP yourself,
* but you can also call advanceIP() if speed is not important.
*
* @this {X86CPU}
* @param {number} off
*/
X86CPU.prototype.setIP = function(off)
{
this.regLIP = (this.segCS.base + (off & (I386? this.maskData : 0xffff)))|0;
if (PREFETCH) this.flushPrefetch(this.regLIP);
if (PREFETCH) this.refillPrefetch();
};
/**
* setLIP(addr)
*
* @this {X86CPU}
* @param {number} addr
*/
X86CPU.prototype.setLIP = function(addr)
{
this.regLIP = addr|0;
this.regLIPLimit = (this.segCS.base + this.segCS.limit)|0;
this.nCPL = this.segCS.cpl; // cache the current CPL where it's more convenient
if (I386) this.resetSizes();
/*
* Here, we need to additionally test whether the prefetch buffer (adwPrefetch) has been allocated yet,
* because when resetRegs() is first called, the Bus hasn't been initialized yet, so there's nothing to fetch.
*
* We'll allocate the prefetch buffer when the Bus calls initMemory().
*/
if (PREFETCH && this.adwPrefetch) this.refillPrefetch();
};
/**
@ -2397,14 +2286,7 @@ X86CPU.prototype.setCSIP = function(off, sel, fCall)
*/
var base = this.segCS.loadCode(off, sel, fCall);
if (base !== X86.ADDR_INVALID) {
/*
* TODO: Should this code be factored into a setLIP() function? The other primary client would be fnINT().
*/
if (I386) this.resetSizes();
this.regLIP = (base + (this.segCS.offIP & (I386? this.maskData : 0xffff)))|0;
this.regLIPLimit = (base + this.segCS.limit)|0;
this.nCPL = this.segCS.cpl; // cache the current CPL where it's more convenient
if (PREFETCH) this.flushPrefetch(this.regLIP);
this.setLIP(base + (this.segCS.offIP & (I386? this.segCS.maskData : 0xffff)));
return this.segCS.fStackSwitch;
}
return null;
@ -2473,21 +2355,38 @@ X86CPU.prototype.advanceIP = function(inc)
};
/**
* rewindIP(dec)
* resetIP(dec)
*
* This "rewinds" IP to the beginning of the current instruction (eg, an instruction with a REP prefix)
*
* @this {X86CPU}
* @param {number} dec (negative)
*/
X86CPU.prototype.rewindIP = function(dec)
X86CPU.prototype.resetIP = function(dec)
{
// DEBUG: this.assert(dec < 0);
this.regLIP = (this.regLIP + dec)|0;
/*
* Since rewindIP() is used only for discrete "intra-instruction" IP adjustments, there should be no need
* to perform all the same limit checks as advanceIP().
*/
if (PREFETCH) this.advancePrefetch(dec);
if (BUGS_8086) {
this.regLIP = (this.regLIP + dec)|0;
/*
* This assertion is intended to fail if/when we encounter a "buggy" instruction (see BUGS_8086)
*/
this.assert(this.regLIP == this.opLIP);
} else {
if (PREFETCH) {
this.cbPrefetch += this.regLIP - this.opLIP;
this.regLIP = this.opLIP;
/*
* If "rewinding" produces a prefetch total greater than the allocated amount, then we must have
* refilled the queue somewhere in the middle of the rewound instruction, so we need to refill the
* queue all over again; otherwise, the next repetition may fetch future data instead of past data.
*
* That's the bad news; the good news is that this extra refill should only hurt performance of the
* first repetition.
*/
if (this.cbPrefetch > X86CPU.PREFETCH.LENGTH) this.refillPrefetch();
} else {
this.regLIP = this.opLIP;
}
}
};
/**
@ -3202,11 +3101,11 @@ X86CPU.prototype.setBinding = function(sHTMLType, sBinding, control)
* probeAddr(addr, size, fLinear)
*
* Used by the Debugger to probe addresses without risk of triggering a page fault, and by internal
* functions, like fnFaultMessage(), that also need to avoid triggering faults, since they're not part
* of standard CPU operation.
* functions, like fnFaultMessage(), that must also avoid triggering faults, since they're not part of
* standard CPU operation.
*
* NOTE: If the size parameter is used, then the caller is required to provide a valid size (1, 2 or 4)
* and ensure that the data is contained entirely with the requested block.
* and ensure that the data is contained entirely with the requested block (which we assert below).
*
* @this {X86CPU}
* @param {number} addr is a linear address
@ -3223,6 +3122,7 @@ X86CPU.prototype.probeAddr = function(addr, size, fLinear)
}
if (block) {
var off = addr & this.nBlockLimit;
this.assert(off + (size || 1) <= this.nBlockSize);
switch(size) {
default:
return block.readByteDirect(off, addr);
@ -3232,6 +3132,12 @@ X86CPU.prototype.probeAddr = function(addr, size, fLinear)
return block.readLongDirect(off, addr);
}
}
/*
* Since the Bus component initializes all unused portions of physical address space with an empty
* block, we have also written mapPageBlock() to return an empty block (memEmpty) whenever there is
* no valid mapping. So if we ever end up here, this may represent a hole that needs plugging.
*/
this.assert(false);
return null;
};
@ -3699,159 +3605,116 @@ X86CPU.prototype.setSOWord = function(seg, off, w)
};
/**
* getBytePrefetch(addr)
*
* Return the next byte from the prefetch queue, prefetching it now if necessary.
* getBytePrefetch()
*
* @this {X86CPU}
* @param {number} addr is a linear address
* @return {number} byte (8-bit) value at that address
* @return {number} byte (8-bit) value at regLIP
*/
X86CPU.prototype.getBytePrefetch = function(addr)
X86CPU.prototype.getBytePrefetch = function()
{
if (this.opFlags & X86.OPFLAG.NOREAD) return 0;
var b;
if (!this.cbPrefetchQueued) {
if (MAXDEBUG) {
this.printMessage(" getBytePrefetch[" + this.iPrefetchTail + "]: filling");
this.assert(addr == this.addrPrefetchHead, "X86CPU.getBytePrefetch(" + str.toHex(addr) + "): invalid head address (" + str.toHex(this.addrPrefetchHead) + ")");
this.assert(this.iPrefetchTail == this.iPrefetchHead, "X86CPU.getBytePrefetch(" + str.toHex(addr) + "): head (" + this.iPrefetchHead + ") does not match tail (" + this.iPrefetchTail + ")");
}
this.fillPrefetch(1);
this.nBusCycles += 4;
/*
* This code effectively inlines this.fillPrefetch(1), but without queueing the byte, so it's an optimization
* with side-effects we may not want, and in any case, while it seemed to improve Safari's performance slightly,
* it did nothing for the oddball Chrome performance I'm seeing with PREFETCH enabled.
*
* b = this.aMemBlocks[(addr & this.nMemMask) >>> this.nBlockShift].readByte(addr & this.nBlockLimit, addr);
* this.nBusCycles += 4;
* this.cbPrefetchValid = 0;
* this.addrPrefetchHead = (addr + 1) & this.nMemMask;
* return b;
*/
if (!this.cbPrefetch) {
this.refillPrefetch();
if (!this.cbPrefetch) return this.getByte(this.regLIP);
}
b = this.aPrefetch[this.iPrefetchTail] & 0xff;
if (MAXDEBUG) {
this.printMessage(" getBytePrefetch[" + this.iPrefetchTail + "]: " + str.toHex(addr) + ":" + str.toHexByte(b));
this.assert(addr == (this.aPrefetch[this.iPrefetchTail] >> 8), "X86CPU.getBytePrefetch(" + str.toHex(addr) + "): invalid tail address (" + str.toHex(this.aPrefetch[this.iPrefetchTail] >> 8) + ")");
}
this.iPrefetchTail = (this.iPrefetchTail + 1) & X86CPU.PREFETCH.MASK;
this.cbPrefetchQueued--;
var b = (this.adwPrefetch[this.regLIP & X86CPU.PREFETCH.IP_MASK] >> ((this.regLIP & 0x3) << 3)) & 0xff;
this.assert(b === this.getByte(this.regLIP));
this.cbPrefetch--;
return b;
};
/**
* getShortPrefetch(addr)
*
* Return the next short from the prefetch queue. There are 3 cases to consider:
*
* 1) Both bytes have been prefetched; no bytes need be fetched from memory
* 2) Only the low byte has been prefetched; the high byte must be fetched from memory
* 3) Neither byte has been prefetched; both bytes must be fetched from memory
*
* However, since we want to mirror getBytePrefetch's behavior of fetching all bytes through
* the prefetch queue, we're taking the easy way out and simply calling getBytePrefetch() twice.
* getShortPrefetch()
*
* @this {X86CPU}
* @param {number} addr is a linear address
* @return {number} short (16-bit) value at that address
* @return {number} short (16-bit) value at regLIP
*/
X86CPU.prototype.getShortPrefetch = function(addr)
X86CPU.prototype.getShortPrefetch = function()
{
return this.getBytePrefetch(addr) | (this.getBytePrefetch(addr + 1) << 8);
};
/**
* getLongPrefetch(addr)
*
* Return the next long from the prefetch queue. Similar to getShortPrefetch(), we take the
* easy way out and call getShortPrefetch() twice.
*
* @this {X86CPU}
* @param {number} addr is a linear address
* @return {number} long (32-bit) value at that address
*/
X86CPU.prototype.getLongPrefetch = function(addr)
{
return this.getShortPrefetch(addr) | (this.getShortPrefetch(addr + 2) << 16);
};
/**
* getWordPrefetch(addr)
*
* @this {X86CPU}
* @param {number} addr is a linear address
* @return {number} short (16-bit) or long (32-bit value as appropriate
*/
X86CPU.prototype.getWordPrefetch = function(addr)
{
return (I386 && this.sizeData == 4? this.getLongPrefetch(addr) : this.getShortPrefetch(addr));
};
/**
* fillPrefetch(n)
*
* Fill the prefetch queue with n instruction bytes.
*
* @this {X86CPU}
* @param {number} n is the number of instruction bytes to fetch
*/
X86CPU.prototype.fillPrefetch = function(n)
{
while (n-- > 0 && this.cbPrefetchQueued < X86CPU.PREFETCH.QUEUE) {
var addr = this.addrPrefetchHead;
var b = this.aMemBlocks[(addr & this.nMemMask) >>> this.nBlockShift].readByte(addr & this.nBlockLimit, addr);
this.aPrefetch[this.iPrefetchHead] = b | (addr << 8);
if (MAXDEBUG) this.printMessage(" fillPrefetch[" + this.iPrefetchHead + "]: " + str.toHex(addr) + ":" + str.toHexByte(b));
this.addrPrefetchHead = (addr + 1) & this.nMemMask;
this.iPrefetchHead = (this.iPrefetchHead + 1) & X86CPU.PREFETCH.MASK;
this.cbPrefetchQueued++;
/*
* We could probably allow cbPrefetchValid to grow as large as X86CPU.PREFETCH.ARRAY-1, but I'm not
* sure there's any advantage to that; certainly the tiny values we expect to see from advancePrefetch()
* wouldn't justify that.
*/
if (this.cbPrefetchValid < X86CPU.PREFETCH.QUEUE) this.cbPrefetchValid++;
if (this.cbPrefetch < 2) {
this.refillPrefetch();
if (this.cbPrefetch < 2) {
this.cbPrefetch = 0;
return this.getShort(this.regLIP);
}
}
var shift = (this.regLIP & 0x3) << 3;
var w = (this.adwPrefetch[this.regLIP & X86CPU.PREFETCH.IP_MASK] >>> shift) & 0xffff;
if (shift > 16) w |= (this.adwPrefetch[(this.regLIP + 4) & X86CPU.PREFETCH.IP_MASK] & 0xff) << 8;
this.assert(w === this.getShort(this.regLIP));
this.cbPrefetch -= 2;
return w;
};
/**
* flushPrefetch(addr)
*
* Empty the prefetch queue.
* getLongPrefetch()
*
* @this {X86CPU}
* @param {number} addr is a linear address of the current program counter (regLIP)
* @return {number} long (32-bit) value at regLIP
*/
X86CPU.prototype.flushPrefetch = function(addr)
X86CPU.prototype.getLongPrefetch = function()
{
this.addrPrefetchHead = addr;
this.iPrefetchTail = this.iPrefetchHead = this.cbPrefetchQueued = this.cbPrefetchValid = 0;
if (MAXDEBUG && addr !== undefined) this.printMessage(" flushPrefetch[-]: " + str.toHex(addr));
if (this.cbPrefetch < 4) {
this.refillPrefetch();
if (this.cbPrefetch < 4) {
this.cbPrefetch = 0;
return this.getLong(this.regLIP);
}
}
var shift = (this.regLIP & 0x3) << 3;
var l = (this.adwPrefetch[this.regLIP & X86CPU.PREFETCH.IP_MASK] >>> shift)|0;
if (shift) l |= this.adwPrefetch[(this.regLIP + 4) & X86CPU.PREFETCH.IP_MASK] << (32 - shift);
this.assert(l === this.getLong(this.regLIP));
this.cbPrefetch -= 4;
return l;
};
/**
* advancePrefetch(inc)
*
* Advance the prefetch queue tail. This is used, for example, in cases where the IP is rewound
* to the start of a repeated string instruction (ie, a string instruction with a REP and possibly
* other prefixes).
*
* If a negative increment takes us beyond what's still valid in the prefetch queue, or if a positive
* increment takes us beyond what's been queued so far, then we simply flush the queue.
* getWordPrefetch()
*
* @this {X86CPU}
* @param {number} inc (may be +/-)
* @return {number} short (16-bit) or long (32-bit) value as appropriate
*/
X86CPU.prototype.advancePrefetch = function(inc)
X86CPU.prototype.getWordPrefetch = function()
{
if (inc < 0 && this.cbPrefetchQueued - inc <= this.cbPrefetchValid || inc > 0 && inc < this.cbPrefetchQueued) {
this.iPrefetchTail = (this.iPrefetchTail + inc) & X86CPU.PREFETCH.MASK;
this.cbPrefetchQueued -= inc;
return (I386 && this.sizeData == 4? this.getLongPrefetch() : this.getShortPrefetch());
};
/**
* refillPrefetch()
*
* This function is similar to probeAddr() in that must NOT trigger a fault, because prefetching
* inherently runs the risk of fetching more bytes that may actually be executed. Also, to keep it
* simple, we limit prefetching to whatever bytes (if any) are available in the current page. If the
* page is not present, or there are insufficient bytes in the current page to completely fill the
* queue, then the caller must request byte(s) "the old-fashioned way", to ensure proper fault handling.
*
* For example, if getShortPrefetch() finds there are only 0 or 1 bytes in the prefetch queue, and
* if it is unable to obtain any more bytes via refillPrefetch(), then getShortPrefetch() must call
* getShort(this.regLIP) (which is also what would be called if PREFETCH was disabled completely).
*
* @this {X86CPU}
*/
X86CPU.prototype.refillPrefetch = function()
{
var aBlocks = this.aMemBlocks;
var regLIP = this.regLIP & ~0x3;
var block = aBlocks[(regLIP & this.nMemMask) >>> this.nBlockShift];
if (block && block.type == Memory.TYPE.UNPAGED) {
block = this.mapPageBlock(regLIP, false, true);
if (block === this.memEmpty) block = null;
}
if (block) {
var off = regLIP & this.nBlockLimit;
var cbMax = this.nBlockSize - off;
if (cbMax > X86CPU.PREFETCH.LENGTH) cbMax = X86CPU.PREFETCH.LENGTH;
for (var i = 0; i < cbMax; i += 4) {
this.adwPrefetch[regLIP & X86CPU.PREFETCH.IP_MASK] = block.readLongDirect(off, regLIP);
off += 4; regLIP += 4;
}
this.cbPrefetch = i - (this.regLIP & 0x3);
// this.nBusCycles += 4;
} else {
this.flushPrefetch(this.regLIP);
if (MAXDEBUG) this.printMessage("advancePrefetch(" + inc + "): flushed");
this.cbPrefetch = 0;
}
};
@ -3863,7 +3726,7 @@ X86CPU.prototype.advancePrefetch = function(inc)
*/
X86CPU.prototype.getIPByte = function()
{
var b = (PREFETCH? this.getBytePrefetch(this.regLIP) : this.getByte(this.regLIP));
var b = (PREFETCH? this.getBytePrefetch() : this.getByte(this.regLIP));
if (BACKTRACK) this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMem0);
this.advanceIP(1);
return b;
@ -3877,7 +3740,7 @@ X86CPU.prototype.getIPByte = function()
*/
X86CPU.prototype.getIPShort = function()
{
var w = (PREFETCH? this.getShortPrefetch(this.regLIP) : this.getShort(this.regLIP));
var w = (PREFETCH? this.getShortPrefetch() : this.getShort(this.regLIP));
if (BACKTRACK) {
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMem0);
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMem1);
@ -3894,7 +3757,7 @@ X86CPU.prototype.getIPShort = function()
*/
X86CPU.prototype.getIPLong = function()
{
var l = (PREFETCH? this.getLongPrefetch(this.regLIP) : this.getLong(this.regLIP));
var l = (PREFETCH? this.getLongPrefetch() : this.getLong(this.regLIP));
if (BACKTRACK) {
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMem0);
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMem1);
@ -3913,10 +3776,7 @@ X86CPU.prototype.getIPLong = function()
*/
X86CPU.prototype.getIPAddr = function()
{
/*
* TODO: Add PREFETCH support to this function
*/
var w = this.getAddr(this.regLIP);
var w = (PREFETCH? this.getAddr() : this.getAddr(this.regLIP));
if (BACKTRACK) {
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMem0);
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMem1);
@ -3933,7 +3793,7 @@ X86CPU.prototype.getIPAddr = function()
*/
X86CPU.prototype.getIPWord = function()
{
var w = (PREFETCH? this.getWordPrefetch(this.regLIP) : this.getWord(this.regLIP));
var w = (PREFETCH? this.getWordPrefetch() : this.getWord(this.regLIP));
if (BACKTRACK) {
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMem0);
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMem1);
@ -3950,7 +3810,7 @@ X86CPU.prototype.getIPWord = function()
*/
X86CPU.prototype.getIPDisp = function()
{
var w = ((PREFETCH? this.getBytePrefetch(this.regLIP) : this.getByte(this.regLIP)) << 24) >> 24;
var w = ((PREFETCH? this.getBytePrefetch() : this.getByte(this.regLIP)) << 24) >> 24;
if (BACKTRACK) this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMem0);
this.advanceIP(1);
return w;
@ -3965,7 +3825,7 @@ X86CPU.prototype.getIPDisp = function()
*/
X86CPU.prototype.getSIBAddr = function(mod)
{
var b = PREFETCH? this.getBytePrefetch(this.regLIP) : this.getByte(this.regLIP);
var b = PREFETCH? this.getBytePrefetch() : this.getByte(this.regLIP);
if (BACKTRACK) this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMem0);
this.advanceIP(1);
return X86ModSIB.aOpModSIB[b].call(this, mod);
@ -4208,11 +4068,15 @@ X86CPU.prototype.checkINTR = function()
iPriority = 1 - iPriority;
}
}
/*
* As long as the ChipSet component isn't calling setDMA(), we don't need to test INTFLAG.DMA...
*
if (this.intFlags & X86.INTFLAG.DMA) {
if (!this.chipset.checkDMA()) {
this.intFlags &= ~X86.INTFLAG.DMA;
}
}
*/
return false;
};
@ -4500,8 +4364,6 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
this.opFlags = 0;
/*
* DEBUG || PREFETCH:
*
if (DEBUG || PREFETCH) {
this.nBusCycles = 0;
this.nSnapCycles = this.nStepCycles;
@ -4511,14 +4373,15 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
this.aOps[this.getIPByte()].call(this);
/*
* DEBUG || PREFETCH:
*
if (PREFETCH) {
var nSpareCycles = (this.nSnapCycles - this.nStepCycles) - this.nBusCycles;
if (nSpareCycles >= 4) {
this.fillPrefetch(nSpareCycles >> 2); // for every 4 spare cycles, fetch 1 instruction byte
}
}
*/
/*
if (DEBUG) {
//
// Make sure that every instruction is assessing a cycle cost, and that the cost is a net positive.

View file

@ -1362,9 +1362,6 @@ X86.fnINCw = function(dst, src)
* how to load GDT and LDT descriptors, whereas interrupts must use setCS.loadIDT(), which deals exclusively
* with IDT descriptors.
*
* This means we must take care to replicate critical features of setCSIP(); ie, updating regLIP and regLIPLimit,
* resetting default operand and address sizes, and flushing the prefetch queue AFTER calling loadIDT().
*
* @this {X86CPU}
* @param {number} nIDT
* @param {number|null} [nError]
@ -1386,14 +1383,7 @@ X86.fnINT = function(nIDT, nError, nCycles)
this.pushWord(oldIP);
if (nError != null) this.pushWord(nError);
this.nFault = -1;
/*
* TODO: Should this code be factored into a setLIP() function? The other primary client would be setCSIP().
*/
if (I386) this.resetSizes();
this.regLIP = addr;
this.regLIPLimit = (this.segCS.base + this.segCS.limit)|0;
this.nCPL = this.segCS.cpl; // cache the current CPL where it's more convenient
if (PREFETCH) this.flushPrefetch(this.regLIP);
this.setLIP(addr);
}
};

View file

@ -1598,12 +1598,7 @@ X86.opINSb = function INSb()
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
this.nStepCycles -= nCycles;
if (nReps) {
if (BUGS_8086) {
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.resetIP(-2);
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -1656,12 +1651,7 @@ X86.opINSw = function INSw()
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
this.nStepCycles -= nCycles;
if (nReps) {
if (BUGS_8086) {
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.resetIP(-2);
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -1710,12 +1700,7 @@ X86.opOUTSb = function OUTSb()
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
this.nStepCycles -= nCycles;
if (nReps) {
if (BUGS_8086) {
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.resetIP(-2);
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -1767,12 +1752,7 @@ X86.opOUTSw = function OUTSw()
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
this.nStepCycles -= nCycles;
if (nReps) {
if (BUGS_8086) {
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.resetIP(-2);
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2769,12 +2749,7 @@ X86.opMOVSb = function MOVSb()
this.nStepCycles -= nCycles;
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
if (nReps) {
if (BUGS_8086) {
this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.resetIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2812,12 +2787,7 @@ X86.opMOVSw = function MOVSw()
this.nStepCycles -= nCycles;
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
if (nReps) {
if (BUGS_8086) {
this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.resetIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2865,12 +2835,7 @@ X86.opCMPSb = function CMPSb()
* two values are equal, we must continue.
*/
if (nReps && this.getZF() == (this.opPrefixes & X86.OPFLAG.REPZ)) {
if (BUGS_8086) {
this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.resetIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2918,12 +2883,7 @@ X86.opCMPSw = function CMPSw()
* two values are equal, we must continue.
*/
if (nReps && this.getZF() == (this.opPrefixes & X86.OPFLAG.REPZ)) {
if (BUGS_8086) {
this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.resetIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -2984,12 +2944,7 @@ X86.opSTOSb = function STOSb()
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
this.nStepCycles -= nCycles;
if (nReps) {
if (BUGS_8086) {
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.resetIP(-2);
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -3030,12 +2985,7 @@ X86.opSTOSw = function STOSw()
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
this.nStepCycles -= nCycles;
if (nReps) {
if (BUGS_8086) {
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.resetIP(-2);
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -3073,12 +3023,7 @@ X86.opLODSb = function LODSb()
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
this.nStepCycles -= nCycles;
if (nReps) {
if (BUGS_8086) {
this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.resetIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -3118,12 +3063,7 @@ X86.opLODSw = function LODSw()
this.regECX = (this.regECX & ~maskAddr) | ((this.regECX - nDelta) & maskAddr);
this.nStepCycles -= nCycles;
if (nReps) {
if (BUGS_8086) {
this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.resetIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -3167,12 +3107,7 @@ X86.opSCASb = function SCASb()
* two values are equal, we must continue.
*/
if (nReps && this.getZF() == (this.opPrefixes & X86.OPFLAG.REPZ)) {
if (BUGS_8086) {
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.resetIP(-2);
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -3216,12 +3151,7 @@ X86.opSCASw = function SCASw()
* two values are equal, we must continue.
*/
if (nReps && this.getZF() == (this.opPrefixes & X86.OPFLAG.REPZ)) {
if (BUGS_8086) {
this.rewindIP(-2); // this instruction does not support multiple overrides
this.assert(this.regLIP == this.opLIP);
} else {
this.regLIP = this.opLIP;
}
this.resetIP(-2);
this.opFlags |= X86.OPFLAG.REPEAT;
}
}
@ -4169,7 +4099,7 @@ X86.opHLT = function HLT()
* on the theory that whoever's using the Debugger would like to see HLTs.
*/
if (DEBUGGER && this.dbg && this.messageEnabled(Messages.HALT)) {
this.rewindIP(-1); // this is purely for the Debugger's benefit, to show the HLT
this.resetIP(-1); // this is purely for the Debugger's benefit, to show the HLT
this.dbg.stopCPU();
return;
}
@ -4178,7 +4108,7 @@ X86.opHLT = function HLT()
* in the water (we have no NMI generation mechanism at the moment).
*/
if (!this.getIF()) {
if (DEBUGGER && this.dbg) this.rewindIP(-1);
if (DEBUGGER && this.dbg) this.resetIP(-1);
this.stopCPU();
}
};