Working prefetch support (speeds up 80386 operation when paging)
This commit is contained in:
parent
922ca42571
commit
526da018d0
5 changed files with 256 additions and 458 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue