Assorted 80386 tweaks
This commit is contained in:
parent
c9e303600d
commit
f5c0930f83
8 changed files with 255 additions and 232 deletions
|
|
@ -5,11 +5,9 @@ Assembling a detailed and accurate history of the 80386, including a complete li
|
|||
problems were fixed by a later stepping, seems virtually impossible at this late date.
|
||||
|
||||
I won't make the attempt here, either. Using information from various sources, I'll start with an overview
|
||||
of the steppings (revision levels), including how each stepping was externally marked and internally identified,
|
||||
along with lists of associated errata, then move on to more detailed errata information (from Intel's own documents),
|
||||
and end with a summary of undocumented 80386 instructions.
|
||||
|
||||
Until further notice, this document is a work-in-progress.
|
||||
of the steppings, including how each stepping was externally marked and internally identified, along with lists
|
||||
of associated errata, then move on to more detailed errata information (from Intel's own documents), and end
|
||||
with a summary of undocumented 80386 instructions.
|
||||
|
||||
### Steppings
|
||||
|
||||
|
|
@ -378,7 +376,6 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
fields are correctly stored. Note that coprocessor error-handling routines are the only routines possibly
|
||||
affected. Also note that the problem does not occur in PROTECTED mode programs (since no opcode is saved
|
||||
by FSAVE or FSTENV in that case).
|
||||
|
||||
**Workaround**: In REAL mode or in VIRTUAL 8086 mode, the instruction linear address field can be used to
|
||||
read the opcode from memory. Note that the two bytes fetched need to be swapped to yield the image that
|
||||
FSAVE and FSTENV normally stores.
|
||||
|
|
@ -386,7 +383,6 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
**Problem**: If either of the last two bytes of an FSAVE or an FSTENV operand are for any reason not writeable,
|
||||
or either of the last two bytes of an FRESTOR or FLDENV are for any reason not readable, the instruction
|
||||
is not restartable.
|
||||
|
||||
**Workaround**: This does not not affect typical systems with reasonably-assigned page access rights.
|
||||
In an obscure situation where this problem arises, a workaround is to avoid having the operand of these
|
||||
instructions span a page boundary. This can be accomplished by aligning these operands on any 128-byte boundary.
|
||||
|
|
@ -395,13 +391,11 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
of maximum size (i.e. 0FFFFh for a 16-bit segment or 0FFFFFFFFh for a 32-bit segment) or within 108 bytes of
|
||||
maximum size, thus wrapping around to offset 0 of the segment. Since a wraparound situation is very abnormal
|
||||
for a compiler or programmer to create, this does not affect a typical system.
|
||||
|
||||
Formally, the 80386 architecture does not permit an operand (coprocessor operands included) to wrap around
|
||||
the end of a segment. If the user issues such an instruction nonetheless in a Protected Mode system, and
|
||||
the operand starts and ends in valid, present pages of a segment, BUT spans through an invalid or inaccessible
|
||||
page, the coprocessor may be put in an indeterminate state. In such cases, an FCLEX or FINIT instruction needs
|
||||
to be executed before any other coprocessor instruction is issued.
|
||||
|
||||
**Workaround**: In Real Mode, this is not a problem since protection is not enabled. In Protected Mode,
|
||||
this problem is avoided simply by not creating coprocessor operands which wrap around the end of the segment,
|
||||
or by aligning the base of all segments on page boundaries.
|
||||
|
|
@ -411,7 +405,6 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
Furthermore, if the Double Fault entry in the IDT is a trap gate, a shutdown results. In a related topic,
|
||||
if the TSS Fault entry in the IDT is invalid for any reason (e.g. bad AR byte), then instead of a Double Fault
|
||||
(exception 8), a shutdown results.
|
||||
|
||||
**Workaround**: A working system, one that creates TSS segments of adequate size to hold the processor state
|
||||
(44 bytes for the TSS of a 16-bit task, 104 bytes for the TSS of a 32-bit task), will not encounter any problems
|
||||
here. A working system should also provide a valid gate (interrupt, trap, or task gate) in the IDT for exception 8.
|
||||
|
|
@ -422,37 +415,29 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
next even-numbered iteration. If the REP MOVS ends with an odd number of iterations, and single-stepping or data
|
||||
breakpoints are enabled, then a single-step trap or data breakpoint trap on the final iteration will properly occur
|
||||
after the final, odd-numbered iteration.
|
||||
|
||||
**Workaround**: When using the Trap Flag or data breakpoints with a debugger utility, this minor variation of
|
||||
REP MOVS must be accepted, unless an effort is made to have the debugger emulate the REP MOVS rather than actually
|
||||
execute it.
|
||||
6. Task Switch to Virtual 8086 Mode Doesn't Update Prefetch Limit
|
||||
**Problem**: When a task switch to Virtual 8086 Mode is performed, the prefetch limit is not updated to become 0FFFFh,
|
||||
but instead remains at its previous value.
|
||||
|
||||
**Workaround**: Use the IRET instruction to transfer to Virtual 8086 Mode. Using IRET is the preferred method for
|
||||
most instances, especially when the master OS dispatches a Virtual 8086 Mode program, because IRET can cause the
|
||||
transition without a task switch.
|
||||
7. Wrong Register Size for String Instructions in Mixed 16/32-bit Addressing Systems
|
||||
**Problem**: If certain string and loop instructions are followed by instructions that either:
|
||||
|
||||
1) use a different address size (that is, if either the string instruction or the following instruction
|
||||
uses an address size prefix), or
|
||||
|
||||
2) reference the stack (e.g. PUSH/POP/CALL/RET) and the "B" bit in the SS descriptor is different from the address size used by the string
|
||||
instructions,
|
||||
|
||||
2) reference the stack (e.g. PUSH/POP/CALL/RET) and the "B" bit in the SS descriptor is different from the address
|
||||
size used by the string instructions,
|
||||
then one or more of [E]CX, [E]SI, or [E]DI is not updated properly. The size of the register (16 vs. 32) is
|
||||
taken from the following instruction rather than from the string or loop instruction. This could result in
|
||||
updating only the lower 16 bits of a 32-bit register, or in updating all 32 bits of a register being used as
|
||||
16 bits. The instructions (and registers) affected by this are:
|
||||
|
||||
MOVS ([E]DI), REP MOVS ([E]SI), STOS ([E]DI), INS ([E]DI), and REP INS ([E]CX).
|
||||
|
||||
**Workaround**: No workaround is necessary if all code is 16-bit or if all code is 32-bit. The problem only
|
||||
occurs if instructions with different address sizes are mixed together, or if a code segment of one size is used
|
||||
with a stack segment of the other size.
|
||||
|
||||
In a system which mixes address sizes, add a NOP after each of the above instructions and ensure that the NOP
|
||||
has the same address size as the string/loop (i.e., if the string/loop instruction includes an address prefix,
|
||||
place the same address prefix before the NOP; conversely, if the string/loop instruction does not have an address
|
||||
|
|
@ -464,19 +449,15 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
old code segment. This can allow execution beyond the end of the new segment without triggering a segment limit
|
||||
violation. Or it can result in a spurious GP fault if the old and new segments overlap, and a prefetch occurs
|
||||
beyond the limit of the old segment.
|
||||
|
||||
Note that the prefetch limit is checked on the linear address, not by comparing IP to 0FFFFh.
|
||||
|
||||
**Workaround**: All existing 8086 programs use only 16-bit addressing, and thus will not execute code at offsets
|
||||
greater than 0FFFFh from the code segment base. Thus the lack of detection of walking off the end of a code segment
|
||||
should not impact working 8086 programs.
|
||||
|
||||
A workaround to the spurious GP fault, if it occurs, is to simply IRET back to the faulting instruction, since the
|
||||
IRET will correctly set the prefetch limit. If the fault handler has control of the single-step function, a very
|
||||
simple workaround is to attempt to single-step the faulting instruction. If the single-step succeeded, the handler
|
||||
could clear the fault, turn off single-stepping, and IRET. If a GP fault occurred attempting to single-step the
|
||||
instruction, a "real" GP fault is the cause.
|
||||
|
||||
If the fault handler cannot access the single-stepping function, it still can check for "real" GP faults which must
|
||||
be emulated by the master OS, for example, I/O instructions that need to be emulated, CLI/STI instructions that must
|
||||
be emulated, etc. If none of these faults are recognized, the fault handler can assume this errata caused the GP fault
|
||||
|
|
@ -484,7 +465,6 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
9. Page Fault Error Code on Stack Not Reliable
|
||||
**Problem**: When a Page Fault (exception 14) occurs, the 3 defined bits in the error code may be unreliable
|
||||
if a certain sequence of prefetch is happening at the same time.
|
||||
|
||||
**Workaround**: Although the page fault error code pushed onto the page fault handler's stack can be unreliable,
|
||||
as described, the page fault linear address stored in register CR2 is always correct. The page fault handler should
|
||||
refer to the page fault linear address in CR2 to access the corresponding page table entry and thereby determine
|
||||
|
|
@ -494,23 +474,18 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
or accessing coprocessor ports (I/O addresses 800000F8h-800000FFh) as a result of executing coprocessor opcodes,
|
||||
can generate incorrect I/O addresses if paging is enabled and the corresponding linear memory address is marked
|
||||
"present" and "dirty."
|
||||
|
||||
Furthermore, when paging has been enabled and is then turned off, paging translation continues to occur for memory
|
||||
or I/O cycles (I/O as described above) to linear addresses still stored in the TLB, but paging does not occur for
|
||||
linear addresses that result in a TLB miss.
|
||||
|
||||
**Workaround**: Unless paging is used, this item is not a problem. If paging is used but all I/O ports are below
|
||||
00001000h (as in a PC-DOS system), then I/O is no problem.
|
||||
|
||||
If paging is used and I/O ports exist in the range 0000l000h-0000FFFFh, then either have the memory pages at those
|
||||
linear addresses marked "not present" (to avoid having those pages table entries cached in the TLB), or if "present,"
|
||||
have those pages mapped such that bits 12-15 of the physical address equal bits 12-15 of the linear address.
|
||||
Alternatively, re-assign any I/O ports in the range 00001000h-0000FFFFh to below 00001000h.
|
||||
|
||||
If paging is used and the coprocessor is also used, then have the memory page at linear address 80000xxxh either
|
||||
marked "not present" (to avoid having that page table entry cached in the TLB), or if "present," have the page
|
||||
mapped such that bit 31 (the most significant bit) of that page's physical address is a 1.
|
||||
|
||||
To completely disable 80386 paging when paging was previously enabled, the 80386 TLB should be flushed immediately
|
||||
after resetting the~PG bit in CRO. The TLB can be flushed, you recall, by writing a Page Table Directory base address
|
||||
to register CR3.
|
||||
|
|
@ -521,7 +496,6 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
But in the case of a REP INS instruction, ECX is not updated correctly and is 0FFFFFFFFh (or CX is 0FFFFh in case of
|
||||
16-bit operations). It should be noted that the REP INS executes the correct number of iterations and EDI (or DI)
|
||||
is updated properly.
|
||||
|
||||
**Workaround**: After a REP INS instruction, do not rely on ECX (or CX) being zero. Hence, a new count (if any)
|
||||
should be MOVed into ECX, rather than being ADDed into ECX.
|
||||
12. NMI Doesn't Always Bring Chip Out of Shutdown in Obscure Condition with Paging Enabled
|
||||
|
|
@ -530,51 +504,37 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
and a TLB miss occurs when accessing the null descriptor slot, the chip enters shutdown as it should in this case.
|
||||
In this specific case however, an incoming NMI will not be able to bring the 386 out of shutdown. In this specific
|
||||
case, only reset will bring the 386 out of shutdown.
|
||||
|
||||
**Workaround**: Ensure that the IDT gate for the Double Fault Handler has a non-null selectors for CS, and that
|
||||
SS of the destination level is also non-null.
|
||||
13. HOLD Input During Protected Mode Interlevel IRET when Paging is Enabled
|
||||
**Problem**: Under specific situations involving paging and the page privilege bits, the HOLD input, and a RET
|
||||
or IRET instruction performing an inter-level return to level 3, a problem can develop. These situations can be
|
||||
avoided by the workarounds given.
|
||||
|
||||
The first situation, when the inner level stack (levels 0, 1, and 2) is not dword aligned (or not word aligned
|
||||
in the case of a 16-bit [I]RET), requires that several conditions occur simultaneously:
|
||||
|
||||
1) Paging must be enabled, and the page table and directory entries for the inner level stacks must be marked
|
||||
as supervisor access only.
|
||||
|
||||
2) The software must execute an inter-level RET or IRET to a Protected Mode program at privilege level 3.
|
||||
An inter-level IRET to Virtual 8086 Mode does not exhibit this problem. An inter-level RET or IRET to level 1
|
||||
or 2 does not exhibit this problem.
|
||||
|
||||
3) The inner level stack must be unaligned to a dword boundary (word boundary for a 16-bit [I]RET).
|
||||
|
||||
When the first situation occurs, a page fault (exception 14) occurs spuriously, indicating a page level
|
||||
protection violation during a "user" level read of the inner level stack.
|
||||
|
||||
The second situation, whether or not the inner level stack is dword aligned (or word aligned in the case of a
|
||||
16-bit [I]RET), also requires that several conditions occur simultaneously:
|
||||
|
||||
1) Paging must be enabled, and the page table and directory entries for the inner level stacks must be marked
|
||||
as supervisor access only.
|
||||
|
||||
2) The software must execute an inter-level RET or IRET to a Protected Mode program at privilege level 3.
|
||||
An inter-level IRET to Virtual 8086 Mode does not exhibit this problem. An inter-level RET or IRET to level 1
|
||||
or 2 does not exhibit this problem.
|
||||
|
||||
3) The bus HOLD input must be asserted during the read, cycle which pops ESP (or SP) off the inner stack as a
|
||||
result of a RET or IRET instruction.
|
||||
|
||||
When the second situation occurs, no exception is generated, but the processor will drive an incorrect physical
|
||||
address during the read cycle in which SS is popped from the inner level stack.
|
||||
|
||||
**Workarounds**: A software workaround to both situations is to mark all pages which contain the inner level
|
||||
stacks as user readable. This prevents either the first or second situation from occurring. The segmentation
|
||||
system can be used to prevent user access to the linear addresses containing the inner-level stacks.
|
||||
|
||||
A workaround if not using the HOLD input is merely to keep the inner-level stacks aligned.
|
||||
|
||||
A Hardware workaround if using the HOLD input but not using the software workaround above is the following:
|
||||
Since the problem occurs during the first cycle after a locked cycle to read the CS descriptor, a hardware
|
||||
workaround is to prevent a HOLD request from hitting the processor during bus cycle following a LOCKed cycle.
|
||||
|
|
@ -588,7 +548,6 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
perform a stack operation, such as PUSH or POP (see exact list below), the value of the [E]SP register may be
|
||||
incorrect after the stack operation. Note that stack operations resulting from interrupts or exceptions following
|
||||
LSL do update [E]SP correctly.
|
||||
|
||||
**Workaround**: Do not immediately follow the Protected Mode LSL instruction with any of the following stack
|
||||
operation instructions: IRET (intra-task), POPA, POPF, POP (mem, reg, seg-reg), RET (intrasegment or intersegment),
|
||||
CALL (direct intrasegment, direct intersegment, indirect intrasegment via reg), ENTER, PUSHA, PUSHF, PUSH (mem,
|
||||
|
|
@ -600,7 +559,6 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
**Problem**: The Protected Mode instructions LSL, LAR, VERR or VERW executed with a null selector (i.e. bits
|
||||
15 through 2 of the selector set to zero) as the operand will operate on the descriptor at entry 0 of the GDT
|
||||
instead of unconditionally clearing the ZF flag.
|
||||
|
||||
**Workaround**: The "null descriptor" (i.e. the descriptor at entry 0 of the GDT) should be initialized to all
|
||||
zeroes. If the "null descriptor" is initialized to all zeroes (i.e. an invalid value), the access made by these
|
||||
instructions to the "null descriptor" will fail (since these instructions only operate on valid descriptors).
|
||||
|
|
@ -610,18 +568,15 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
16. "Not Present" LDT in VM86 Task Raises Wrong Exception
|
||||
**Problem**: A task switch to a VM86 task that has a "not present" LDT descriptor will cause a Segment Not Present
|
||||
fault (exception 11) rather than an Invalid TSS fault (exception 10).
|
||||
|
||||
**Workaround**: The simplest workaround is to use a NULL selector for the LDT in a VM86 task, since the LDT is
|
||||
not used when executing in Virtual 86 mode. However, if an interrupt or exception occurs, the processor will switch
|
||||
out of Virtual 86 mode, into protected mode to handle the interrupt, without switching tasks. Thus, the operating
|
||||
system should be structured so that all Interrupt and Trap gates active when executing a VM86 task reference segments
|
||||
in the GDT.
|
||||
|
||||
If an LDT must be supplied for a task that executes in Virtual 86 mode, there are several easy workarounds. One
|
||||
is to ensure that LDT segments are never marked "not present" in their segment descriptors. Paging is not affected
|
||||
by this errata. LDT segments can be paged out and marked "not present" in their page descriptors in systems which
|
||||
use paging.
|
||||
|
||||
If the operating system must mark the LDT segment descriptor "not present", the "not present" (exception 11)
|
||||
handler must be able to handle the case of a "not present" LDT during a task switch. The "not present" exception
|
||||
is reported with the LDT selector as the error code and with the VM bit set to 1 in the EFLAGS image of the caller.
|
||||
|
|
@ -632,20 +587,14 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
and the second byte is located on a page or segment which would create a fault, then the processor will hang when
|
||||
it tries to signal the fault. The processor remains stopped until an interrupt, NMI, or RESET occurs. This errata
|
||||
applies only to coprocessor instructions in systems which use virtual memory.
|
||||
|
||||
**Workaround**: In virtual memory systems, the time-slice or watchdog timer provides an easy workaround, since a
|
||||
timer interrupt will always cause the processor to begin interrupt processing. The timer routine should test the
|
||||
following conditions to determine if this errata was encountered.
|
||||
|
||||
1) The saved CS:EIP must point within 8 bytes of the end of a page.
|
||||
|
||||
2) The last byte within the page must contain an ESC opcode.
|
||||
|
||||
3) All bytes between the saved CS:EIP and the ESC opcode must contain valid prefix opcodes (segment override 26h,
|
||||
2Eh, 36h, 3Eh, 64h, 65h, address size override 67h, operand size override 66h).
|
||||
|
||||
4) The next page is not present, or not accessable.
|
||||
|
||||
If all four conditions are true, then the timer routine can assume this errata was encountered, and signal a page
|
||||
fault, which will clear the condition. This workaround should be placed in the Operating System, so that applications
|
||||
programs are unaffected.
|
||||
|
|
@ -653,7 +602,6 @@ Here's what the world knew about 80386 problems in the B1 stepping, as of Decemb
|
|||
**Problem**: If a second page fault occurs, while the processor is attempting to enter the service routine for the
|
||||
first, then the processor will invoke the page fault (exception 14) handler a second time, rather than the double
|
||||
fault (exception 8) handler. A subsequent fault, though, will lead to shutdown.
|
||||
|
||||
**Workaround**: No workaround is necessary in a working system.
|
||||
|
||||
An errata update dated March 26, 1987, produced internally by IBM rather than Intel, noted two additional
|
||||
|
|
|
|||
|
|
@ -1134,6 +1134,7 @@ CPU.prototype.yieldCPU = function()
|
|||
this.aCounts.nCyclesNextYield = 0; // this will break us out of runCPU(), once we break out of stepCPU()
|
||||
this.nBurstCycles -= this.nStepCycles;
|
||||
this.nStepCycles = 0; // this will break us out of stepCPU()
|
||||
if (DEBUG) this.nSnapCycles = this.nBurstCycles;
|
||||
/*
|
||||
* The Debugger calls yieldCPU() after every message() to ensure browser responsiveness, but it looks
|
||||
* odd for those messages to show CPU state changes but for the CPU's own status display to not (ditto
|
||||
|
|
|
|||
|
|
@ -4115,9 +4115,11 @@ if (DEBUGGER) {
|
|||
/*
|
||||
* We must create a new aAddr from the address we obtained from aHistory, because
|
||||
* aAddr was a reference, not a copy, and we don't want getInstruction() modifying the original.
|
||||
*
|
||||
* TODO: By using a new address for each iteration, history dumps fail to disassemble 32-bit overrides properly.
|
||||
*/
|
||||
aAddr = this.newAddr(aAddr[0], aAddr[1], aAddr[2]);
|
||||
this.println(this.getInstruction(aAddr, "history", -n));
|
||||
this.println(this.getInstruction(aAddr, "history", n));
|
||||
if (++iHistory == aHistory.length) iHistory = 0;
|
||||
this.nextHistory = --n;
|
||||
cLines--;
|
||||
|
|
@ -4384,7 +4386,7 @@ if (DEBUGGER) {
|
|||
if (sCategory !== undefined) {
|
||||
var bitsMessage = 0;
|
||||
if (sCategory == "all") {
|
||||
bitsMessage = (0xffffffff|0) & ~(Messages.HALT | Messages.LOG);
|
||||
bitsMessage = (0xffffffff|0) & ~(Messages.HALT | Messages.KEYS | Messages.LOG);
|
||||
sCategory = null;
|
||||
} else if (sCategory == "on") {
|
||||
fCriteria = true;
|
||||
|
|
@ -4980,8 +4982,8 @@ if (DEBUGGER) {
|
|||
* aAddr[5] to true whenever the address size is 32-bit. Initially, both fields must be set to match
|
||||
* the size of the current code segment.
|
||||
*/
|
||||
aAddr[4] = (this.cpu.dataSize == 4);
|
||||
aAddr[5] = (this.cpu.addrSize == 4);
|
||||
aAddr[4] = (this.cpu.segCS.dataSize == 4);
|
||||
aAddr[5] = (this.cpu.segCS.addrSize == 4);
|
||||
fInitSize = false;
|
||||
}
|
||||
if (this.isPrefixIns(bOpcode)) {
|
||||
|
|
|
|||
|
|
@ -2287,9 +2287,6 @@ Video.prototype.captureTouch = function()
|
|||
*/
|
||||
Video.prototype.onFocusChange = function(fFocus)
|
||||
{
|
||||
if (this.fHasFocus != fFocus && DEBUG && this.messageEnabled()) {
|
||||
this.printMessage("onFocusChange(" + (fFocus? "true" : "false") + ")", true);
|
||||
}
|
||||
/*
|
||||
* As per http://stackoverflow.com/questions/6740253/disable-scrolling-when-changing-focus-form-elements-ipad-web-app,
|
||||
* I decided to try this work-around to prevent the webpage from scrolling around whenever the canvas is given
|
||||
|
|
|
|||
|
|
@ -2863,7 +2863,7 @@ X86CPU.prototype.getLongPrefetch = function(addr)
|
|||
*/
|
||||
X86CPU.prototype.getWordPrefetch = function(addr)
|
||||
{
|
||||
return (I386 && this.addrSize == 4? this.getLongPrefetch(addr) : this.getShortPrefetch(addr));
|
||||
return (I386 && this.dataSize == 4? this.getLongPrefetch(addr) : this.getShortPrefetch(addr));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -3017,7 +3017,7 @@ X86CPU.prototype.getIPWord = function()
|
|||
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMemLo);
|
||||
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMemHi);
|
||||
}
|
||||
this.regLIP += this.addrSize;
|
||||
this.regLIP += this.dataSize;
|
||||
if (this.regLIP > this.regLIPLimit) {
|
||||
this.setIP(this.regLIP - this.segCS.base);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1337,16 +1337,13 @@ X86.fnRCLb = function RCLb(dst, src)
|
|||
{
|
||||
var result = dst;
|
||||
var flagsIn = (DEBUG? this.getPS() : 0);
|
||||
if (src) {
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
var carry = this.getCarry();
|
||||
var shift = (src & this.nShiftCountMask) % 9;
|
||||
shift %= 9;
|
||||
if (!shift) {
|
||||
carry <<= 7;
|
||||
} else {
|
||||
/*
|
||||
* shift is 1-8, which means the new carry will come from the dst bit
|
||||
* at position 7-0. To force it into position 7, left shift by (shift - 1).
|
||||
*/
|
||||
result = ((dst << shift) | (carry << (shift - 1)) | (dst >> (9 - shift))) & 0xff;
|
||||
carry = dst << (shift - 1);
|
||||
}
|
||||
|
|
@ -1357,6 +1354,8 @@ X86.fnRCLb = function RCLb(dst, src)
|
|||
};
|
||||
|
||||
/**
|
||||
* fnRCLw(dst, src)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src (1 or CL)
|
||||
|
|
@ -1366,16 +1365,13 @@ X86.fnRCLw = function RCLw(dst, src)
|
|||
{
|
||||
var result = dst;
|
||||
var flagsIn = (DEBUG? this.getPS() : 0);
|
||||
if (src) {
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
var carry = this.getCarry();
|
||||
var shift = (src & this.nShiftCountMask) % 17;
|
||||
shift %= 17;
|
||||
if (!shift) {
|
||||
carry <<= 15;
|
||||
} else {
|
||||
/*
|
||||
* shift is 1-16, which means the new carry will come from the dst bit
|
||||
* at position 15-0. To force it into position 15, left shift by (shift - 1).
|
||||
*/
|
||||
result = ((dst << shift) | (carry << (shift - 1)) | (dst >> (17 - shift))) & 0xffff;
|
||||
carry = dst << (shift - 1);
|
||||
}
|
||||
|
|
@ -1385,6 +1381,29 @@ X86.fnRCLw = function RCLw(dst, src)
|
|||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* fnRCLd(dst, src)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src (1 or CL)
|
||||
* @return {number}
|
||||
*/
|
||||
X86.fnRCLd = function RCLd(dst, src)
|
||||
{
|
||||
var result = dst;
|
||||
var flagsIn = (DEBUG? this.getPS() : 0);
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
var carry = this.getCarry();
|
||||
result = (dst << shift) | (carry << (shift - 1)) | (dst >>> (32 - shift));
|
||||
carry = dst << (shift - 1);
|
||||
X86.setRotateResult.call(this, result, carry, X86.RESULT.DWORD);
|
||||
}
|
||||
if (DEBUG && DEBUGGER) this.traceLog('RCLD', dst, src, flagsIn, this.getPS(), result);
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* fnRCRb(dst, src)
|
||||
*
|
||||
|
|
@ -1397,16 +1416,13 @@ X86.fnRCRb = function RCRb(dst, src)
|
|||
{
|
||||
var result = dst;
|
||||
var flagsIn = (DEBUG? this.getPS() : 0);
|
||||
if (src) {
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
var carry = this.getCarry();
|
||||
var shift = (src & this.nShiftCountMask) % 9;
|
||||
shift %= 9;
|
||||
if (!shift) {
|
||||
carry <<= 7;
|
||||
} else {
|
||||
/*
|
||||
* shift is 1-8, which means the new carry will come from the dst bit
|
||||
* at position 0-7. To force it into position 7, left shift by (8 - shift).
|
||||
*/
|
||||
result = ((dst >> shift) | (carry << (8 - shift)) | (dst << (9 - shift))) & 0xff;
|
||||
carry = dst << (8 - shift);
|
||||
}
|
||||
|
|
@ -1428,16 +1444,13 @@ X86.fnRCRw = function RCRw(dst, src)
|
|||
{
|
||||
var result = dst;
|
||||
var flagsIn = (DEBUG? this.getPS() : 0);
|
||||
if (src) {
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
var carry = this.getCarry();
|
||||
var shift = (src & this.nShiftCountMask) % 17;
|
||||
shift %= 17;
|
||||
if (!shift) {
|
||||
carry <<= 15;
|
||||
} else {
|
||||
/*
|
||||
* shift is 1-16, which means the new carry will come from the dst bit
|
||||
* at position 0-15. To force it into position 15, left shift by (16 - shift).
|
||||
*/
|
||||
result = ((dst >> shift) | (carry << (16 - shift)) | (dst << (17 - shift))) & 0xffff;
|
||||
carry = dst << (16 - shift);
|
||||
}
|
||||
|
|
@ -1447,6 +1460,29 @@ X86.fnRCRw = function RCRw(dst, src)
|
|||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* fnRCRd(dst, src)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src (1 or CL)
|
||||
* @return {number}
|
||||
*/
|
||||
X86.fnRCRd = function RCRd(dst, src)
|
||||
{
|
||||
var result = dst;
|
||||
var flagsIn = (DEBUG? this.getPS() : 0);
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
var carry = this.getCarry();
|
||||
result = (dst >>> shift) | (carry << (32 - shift)) | (dst << (33 - shift));
|
||||
carry = dst << (32 - shift);
|
||||
X86.setRotateResult.call(this, result, carry, X86.RESULT.DWORD);
|
||||
}
|
||||
if (DEBUG && DEBUGGER) this.traceLog('RCRD', dst, src, flagsIn, this.getPS(), result);
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* fnRETF(n)
|
||||
*
|
||||
|
|
@ -1497,23 +1533,13 @@ X86.fnROLb = function ROLb(dst, src)
|
|||
{
|
||||
var result = dst;
|
||||
var flagsIn = (DEBUG? this.getPS() : 0);
|
||||
if (src) {
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
var carry;
|
||||
/*
|
||||
* The following mask obviates the need to use nShiftCountMask.
|
||||
*/
|
||||
var shift = src & 0x7;
|
||||
shift &= 0x7;
|
||||
if (!shift) {
|
||||
/*
|
||||
* shift is 8, which means the new carry will come from the dst bit
|
||||
* at position 0.
|
||||
*/
|
||||
carry = dst << 7;
|
||||
} else {
|
||||
/*
|
||||
* shift is 1-7, which means the new carry will come from the dst bit
|
||||
* at position 7-1. To force it into position 7, left shift by (shift - 1).
|
||||
*/
|
||||
result = ((dst << shift) | (dst >> (8 - shift))) & 0xff;
|
||||
carry = dst << (shift - 1);
|
||||
}
|
||||
|
|
@ -1535,19 +1561,13 @@ X86.fnROLw = function ROLw(dst, src)
|
|||
{
|
||||
var result = dst;
|
||||
var flagsIn = (DEBUG? this.getPS() : 0);
|
||||
if (src) {
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
var carry;
|
||||
/*
|
||||
* The following mask obviates the need to use nShiftCountMask.
|
||||
*/
|
||||
var shift = src & 0xf;
|
||||
shift &= 0xf;
|
||||
if (!shift) {
|
||||
carry = dst << 15;
|
||||
} else {
|
||||
/*
|
||||
* shift is 1-15, which means the new carry will come from the dst bit
|
||||
* at position 15-1. To force it into position 15, left shift by (shift - 1).
|
||||
*/
|
||||
result = ((dst << shift) | (dst >> (16 - shift))) & 0xffff;
|
||||
carry = dst << (shift - 1);
|
||||
}
|
||||
|
|
@ -1569,22 +1589,13 @@ X86.fnRORb = function RORb(dst, src)
|
|||
{
|
||||
var result = dst;
|
||||
var flagsIn = (DEBUG? this.getPS() : 0);
|
||||
if (src) {
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
var carry;
|
||||
/*
|
||||
* The following mask obviates the need to use nShiftCountMask.
|
||||
*/
|
||||
var shift = src & 0x7;
|
||||
shift &= 0x7;
|
||||
if (!shift) {
|
||||
/*
|
||||
* shift is 8, which means the new carry will come from the dst bit at position 7.
|
||||
*/
|
||||
carry = dst;
|
||||
} else {
|
||||
/*
|
||||
* shift is 1-7, which means the new carry will come from the dst bit
|
||||
* at position 0-6. To force it into position 7, left shift by (8 - shift).
|
||||
*/
|
||||
result = ((dst >> shift) | (dst << (8 - shift))) & 0xff;
|
||||
carry = dst << (8 - shift);
|
||||
}
|
||||
|
|
@ -1606,22 +1617,13 @@ X86.fnRORw = function RORw(dst, src)
|
|||
{
|
||||
var result = dst;
|
||||
var flagsIn = (DEBUG? this.getPS() : 0);
|
||||
if (src) {
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
var carry;
|
||||
/*
|
||||
* The following mask obviates the need to use nShiftCountMask.
|
||||
*/
|
||||
var shift = src & 0xf;
|
||||
shift &= 0xf;
|
||||
if (!shift) {
|
||||
/*
|
||||
* shift is 16, which means the new carry will come from dst bit 15.
|
||||
*/
|
||||
carry = dst;
|
||||
} else {
|
||||
/*
|
||||
* shift is 1-15, which means the new carry will come from the dst bit
|
||||
* at position 0-14. To force it into position 15, left shift by (16 - shift).
|
||||
*/
|
||||
result = ((dst >> shift) | (dst << (16 - shift))) & 0xffff;
|
||||
carry = dst << (16 - shift);
|
||||
}
|
||||
|
|
@ -1645,12 +1647,10 @@ X86.fnRORw = function RORw(dst, src)
|
|||
*/
|
||||
X86.fnSARb = function SARb(dst, src)
|
||||
{
|
||||
if (src) {
|
||||
/*
|
||||
* The following comparison obviates the need to mask src with nShiftCountMask.
|
||||
*/
|
||||
if (src > 8) src = 9;
|
||||
var temp = ((dst << 24) >> 24) >> (src - 1);
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
if (shift > 8) shift = 9;
|
||||
var temp = ((dst << 24) >> 24) >> (shift - 1);
|
||||
dst = (temp >> 1) & 0xff;
|
||||
this.setLogicResult(dst, X86.RESULT.BYTE, temp & 0x1);
|
||||
}
|
||||
|
|
@ -1671,12 +1671,10 @@ X86.fnSARb = function SARb(dst, src)
|
|||
*/
|
||||
X86.fnSARw = function SARw(dst, src)
|
||||
{
|
||||
if (src) {
|
||||
/*
|
||||
* The following comparison obviates the need to mask src with nShiftCountMask.
|
||||
*/
|
||||
if (src > 16) src = 17;
|
||||
var temp = ((dst << 16) >> 16) >> (src - 1);
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
if (shift > 16) shift = 17;
|
||||
var temp = ((dst << 16) >> 16) >> (shift - 1);
|
||||
dst = (temp >> 1) & 0xffff;
|
||||
this.setLogicResult(dst, X86.RESULT.WORD, temp & 0x1);
|
||||
}
|
||||
|
|
@ -1796,15 +1794,13 @@ X86.fnSHLb = function SHLb(dst, src)
|
|||
{
|
||||
var result = dst;
|
||||
var flagsIn = (DEBUG? this.getPS() : 0);
|
||||
if (src) {
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
var carry = 0;
|
||||
/*
|
||||
* The following comparison obviates the need to mask src with nShiftCountMask.
|
||||
*/
|
||||
if (src > 8) {
|
||||
if (shift > 8) {
|
||||
result = 0;
|
||||
} else {
|
||||
carry = dst << (src - 1);
|
||||
carry = dst << (shift - 1);
|
||||
result = (carry << 1) & 0xff;
|
||||
}
|
||||
this.setLogicResult(result, X86.RESULT.BYTE, carry & X86.RESULT.BYTE, (result ^ carry) & X86.RESULT.BYTE);
|
||||
|
|
@ -1829,15 +1825,13 @@ X86.fnSHLw = function SHLw(dst, src)
|
|||
{
|
||||
var result = dst;
|
||||
var flagsIn = (DEBUG? this.getPS() : 0);
|
||||
if (src) {
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
var carry = 0;
|
||||
/*
|
||||
* The following comparison obviates the need to mask src with nShiftCountMask.
|
||||
*/
|
||||
if (src > 16) {
|
||||
if (shift > 16) {
|
||||
result = 0;
|
||||
} else {
|
||||
carry = dst << (src - 1);
|
||||
carry = dst << (shift - 1);
|
||||
result = (carry << 1) & 0xffff;
|
||||
}
|
||||
this.setLogicResult(result, X86.RESULT.WORD, carry & X86.RESULT.WORD, (result ^ carry) & X86.RESULT.WORD);
|
||||
|
|
@ -1860,11 +1854,9 @@ X86.fnSHLw = function SHLw(dst, src)
|
|||
*/
|
||||
X86.fnSHRb = function SHRb(dst, src)
|
||||
{
|
||||
if (src) {
|
||||
/*
|
||||
* The following comparison obviates the need to mask src with nShiftCountMask.
|
||||
*/
|
||||
var temp = (src > 8? 0 : (dst >> (src - 1)));
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
var temp = (shift > 8? 0 : (dst >> (shift - 1)));
|
||||
dst = (temp >> 1) & 0xff;
|
||||
this.setLogicResult(dst, X86.RESULT.BYTE, temp & 0x1, dst & X86.RESULT.BYTE);
|
||||
}
|
||||
|
|
@ -1885,11 +1877,9 @@ X86.fnSHRb = function SHRb(dst, src)
|
|||
*/
|
||||
X86.fnSHRw = function SHRw(dst, src)
|
||||
{
|
||||
if (src) {
|
||||
/*
|
||||
* The following comparison obviates the need to mask src with nShiftCountMask.
|
||||
*/
|
||||
var temp = (src > 16? 0 : (dst >> (src - 1)));
|
||||
var shift = src & this.nShiftCountMask;
|
||||
if (shift) {
|
||||
var temp = (shift > 16? 0 : (dst >> (shift - 1)));
|
||||
dst = (temp >> 1) & 0xffff;
|
||||
this.setLogicResult(dst, X86.RESULT.WORD, temp & 0x1, dst & X86.RESULT.WORD);
|
||||
}
|
||||
|
|
@ -2323,6 +2313,8 @@ X86.fnXORw = function XORw(dst, src)
|
|||
};
|
||||
|
||||
/**
|
||||
* fnXORd(dst, src)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src
|
||||
|
|
@ -2334,6 +2326,21 @@ X86.fnXORd = function XORd(dst, src)
|
|||
return this.setLogicResult(dst ^ src, X86.RESULT.DWORD);
|
||||
};
|
||||
|
||||
/**
|
||||
* fnTBD(dst, src)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src
|
||||
* @return {number}
|
||||
*/
|
||||
X86.fnTBD = function TBD(dst, src)
|
||||
{
|
||||
this.printMessage("unimplemented 80386 opcode", true);
|
||||
this.stopCPU();
|
||||
return dst;
|
||||
};
|
||||
|
||||
/**
|
||||
* setRotateResult(result, carry, size)
|
||||
*
|
||||
|
|
@ -2355,43 +2362,57 @@ X86.setRotateResult = function(result, carry, size)
|
|||
};
|
||||
|
||||
/**
|
||||
* fnGRPCount1()
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @return {number}
|
||||
*/
|
||||
X86.fnGRPCount1 = function() {
|
||||
X86.fnGRPCount1 = function()
|
||||
{
|
||||
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 2 : this.CYCLES.nOpCyclesShift1M);
|
||||
return 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* fnGRPCountCL()
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @return {number}
|
||||
*/
|
||||
X86.fnGRPCountCL = function() {
|
||||
var count = this.regECX & this.nShiftCountMask;
|
||||
X86.fnGRPCountCL = function()
|
||||
{
|
||||
var count = this.regECX & 0xff;
|
||||
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.CYCLES.nOpCyclesShiftCR : this.CYCLES.nOpCyclesShiftCM) + (count << this.CYCLES.nOpCyclesShiftCS);
|
||||
return count;
|
||||
};
|
||||
|
||||
/**
|
||||
* fnGRPCountImm()
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @return {number}
|
||||
*/
|
||||
X86.fnGRPCountImm = function() {
|
||||
X86.fnGRPCountImm = function()
|
||||
{
|
||||
var count = this.getIPByte();
|
||||
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? this.CYCLES.nOpCyclesShiftCR : this.CYCLES.nOpCyclesShiftCM) + (count << this.CYCLES.nOpCyclesShiftCS);
|
||||
return count;
|
||||
};
|
||||
|
||||
/**
|
||||
* fnGRPSrcNone()
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @return {number|null}
|
||||
*/
|
||||
X86.fnGRPSrcNone = function() {
|
||||
X86.fnGRPSrcNone = function()
|
||||
{
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* fnGRPFault(dst, src)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src
|
||||
|
|
@ -2404,6 +2425,8 @@ X86.fnGRPFault = function(dst, src)
|
|||
};
|
||||
|
||||
/**
|
||||
* fnGRPInvalid(dst, src)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src
|
||||
|
|
@ -2416,6 +2439,8 @@ X86.fnGRPInvalid = function(dst, src)
|
|||
};
|
||||
|
||||
/**
|
||||
* fnGRPUndefined(dst, src)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src
|
||||
|
|
|
|||
|
|
@ -1632,12 +1632,20 @@ X86.opINSw = function INSw()
|
|||
}
|
||||
if (nReps--) {
|
||||
var addrFrom = this.regLIP - nDelta - 1;
|
||||
var w = this.bus.checkPortInputNotify(this.regEDX, addrFrom);
|
||||
if (BACKTRACK) this.backTrack.btiMemLo = this.backTrack.btiIO;
|
||||
w |= (this.bus.checkPortInputNotify(this.regEDX, addrFrom) << 8);
|
||||
if (BACKTRACK) this.backTrack.btiMemHi = this.backTrack.btiIO;
|
||||
var w = 0, shift = 0;
|
||||
for (var n = 0; n < this.dataSize; n++) {
|
||||
w |= this.bus.checkPortInputNotify(this.regEDX, addrFrom) << shift;
|
||||
shift += 8;
|
||||
if (BACKTRACK) {
|
||||
if (!n) {
|
||||
this.backTrack.btiMemLo = this.backTrack.btiIO;
|
||||
} else if (n == 1) {
|
||||
this.backTrack.btiMemHi = this.backTrack.btiIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setSOWord(this.segES, this.regEDI & this.addrMask, w);
|
||||
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -2 : 2)) & this.addrMask);
|
||||
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize)) & this.addrMask);
|
||||
this.nStepCycles -= nCycles;
|
||||
this.regECX -= nDelta;
|
||||
if (nReps) {
|
||||
|
|
@ -1727,14 +1735,21 @@ X86.opOUTSw = function OUTSw()
|
|||
}
|
||||
if (nReps--) {
|
||||
var w = this.getSOWord(this.segDS, this.regESI & this.addrMask);
|
||||
this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + ((this.regPS & X86.PS.DF)? -2 : 2)) & this.addrMask);
|
||||
this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize)) & this.addrMask);
|
||||
this.nStepCycles -= nCycles;
|
||||
this.regECX -= nDelta;
|
||||
var addrFrom = this.regLIP - nDelta - 1;
|
||||
if (BACKTRACK) this.backTrack.btiIO = this.backTrack.btiMemLo;
|
||||
this.bus.checkPortOutputNotify(this.regEDX, w & 0xff, addrFrom);
|
||||
if (BACKTRACK) this.backTrack.btiIO = this.backTrack.btiMemHi;
|
||||
this.bus.checkPortOutputNotify(this.regEDX, w >> 8, addrFrom);
|
||||
var addrFrom = this.regLIP - nDelta - 1, shift = 0;
|
||||
for (var n = 0; n < this.dataSize; n++) {
|
||||
if (BACKTRACK) {
|
||||
if (!n) {
|
||||
this.backTrack.btiIO = this.backTrack.btiMemLo;
|
||||
} else if (n == 1) {
|
||||
this.backTrack.btiIO = this.backTrack.btiMemHi;
|
||||
}
|
||||
}
|
||||
this.bus.checkPortOutputNotify(this.regEDX, (w >> shift) & 0xff, addrFrom);
|
||||
shift += 8;
|
||||
}
|
||||
if (nReps) {
|
||||
if (BUGS_8086) {
|
||||
this.advanceIP(-2); // this instruction does not support segment overrides
|
||||
|
|
@ -2542,15 +2557,15 @@ X86.opPOPF = function POPF()
|
|||
X86.opSAHF = function SAHF()
|
||||
{
|
||||
/*
|
||||
* NOTE: While it make LOOK more efficient to do this:
|
||||
* NOTE: While it make seem more efficient to do this:
|
||||
*
|
||||
* this.setPS((this.getPS() & ~X86.PS.SAHF) | ((this.regEAX >> 8) & X86.PS.SAHF));
|
||||
*
|
||||
* the call to getPS() forces all the "indirect" flags to be resolved first, and then the call
|
||||
* to setPS() forces them all to be recalculated, so on balance, the code below is probably more
|
||||
* efficient, and may also avoid some unexpected side-effects of slamming the entire PS register.
|
||||
* getPS() forces any "cached" flags to be resolved first, and setPS() must do extra work above
|
||||
* and beyond setting the arithmetic and logical flags, so on balance, the code below may be more
|
||||
* efficient, and may also avoid unexpected side-effects of updating the entire PS register.
|
||||
*/
|
||||
var ah = this.regEAX >> 8;
|
||||
var ah = (this.regEAX >> 8) & 0xff;
|
||||
if (ah & X86.PS.CF) this.setCF(); else this.clearCF();
|
||||
if (ah & X86.PS.PF) this.setPF(); else this.clearPF();
|
||||
if (ah & X86.PS.AF) this.setAF(); else this.clearAF();
|
||||
|
|
@ -2567,7 +2582,7 @@ X86.opSAHF = function SAHF()
|
|||
*/
|
||||
X86.opLAHF = function LAHF()
|
||||
{
|
||||
this.regEAX = (this.regEAX & 0xff) | (this.getPS() & X86.PS.SAHF) << 8;
|
||||
this.regEAX = (this.regEAX & ~0xff00) | (this.getPS() & X86.PS.SAHF) << 8;
|
||||
this.nStepCycles -= this.CYCLES.nOpCyclesLAHF;
|
||||
};
|
||||
|
||||
|
|
@ -2681,7 +2696,7 @@ X86.opMOVSw = function MOVSw()
|
|||
if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.CYCLES.nOpCyclesMovSr0;
|
||||
}
|
||||
if (nReps--) {
|
||||
var nInc = ((this.regPS & X86.PS.DF)? -2 : 2);
|
||||
var nInc = ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize);
|
||||
this.setSOWord(this.segES, this.regEDI & this.addrMask, this.getSOWord(this.segData, this.regESI));
|
||||
this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + nInc) & this.addrMask);
|
||||
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + nInc) & this.addrMask);
|
||||
|
|
@ -2761,7 +2776,7 @@ X86.opCMPSw = function CMPSw()
|
|||
if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.CYCLES.nOpCyclesCmpSr0;
|
||||
}
|
||||
if (nReps--) {
|
||||
var nInc = ((this.regPS & X86.PS.DF)? -2 : 2);
|
||||
var nInc = ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize);
|
||||
var wDst = this.getEAWord(this.segData, this.regESI & this.addrMask);
|
||||
var wSrc = this.modEAWord(this.segES, this.regEDI & this.addrMask);
|
||||
X86.fnCMPw.call(this, wDst, wSrc);
|
||||
|
|
@ -2830,9 +2845,6 @@ X86.opSTOSb = function STOSb()
|
|||
if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.CYCLES.nOpCyclesStoSr0;
|
||||
}
|
||||
if (nReps--) {
|
||||
/*
|
||||
* NOTE: We rely on setSOByte() to truncate regEAX to 8 bits; if setSOByte() changes, mask AX below.
|
||||
*/
|
||||
if (BACKTRACK) this.backTrack.btiMemLo = this.backTrack.btiAL;
|
||||
this.setSOByte(this.segES, this.regEDI & this.addrMask, this.regEAX);
|
||||
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -1 : 1)) & this.addrMask);
|
||||
|
|
@ -2877,7 +2889,7 @@ X86.opSTOSw = function STOSw()
|
|||
this.backTrack.btiMemLo = this.backTrack.btiAL; this.backTrack.btiMemHi = this.backTrack.btiAH;
|
||||
}
|
||||
this.setSOWord(this.segES, this.regEDI & this.addrMask, this.regEAX);
|
||||
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -2 : 2)) & this.addrMask);
|
||||
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize)) & this.addrMask);
|
||||
this.nStepCycles -= nCycles;
|
||||
this.regECX -= nDelta;
|
||||
if (nReps) {
|
||||
|
|
@ -2943,11 +2955,11 @@ X86.opLODSw = function LODSw()
|
|||
if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.CYCLES.nOpCyclesLodSr0;
|
||||
}
|
||||
if (nReps--) {
|
||||
this.regEAX = this.getSOWord(this.segData, this.regESI & this.addrMask);
|
||||
this.regEAX = (this.regEAX & ~this.dataMask) | this.getSOWord(this.segData, this.regESI & this.addrMask);
|
||||
if (BACKTRACK) {
|
||||
this.backTrack.btiAL = this.backTrack.btiMemLo; this.backTrack.btiAH = this.backTrack.btiMemHi;
|
||||
}
|
||||
this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + ((this.regPS & X86.PS.DF)? -2 : 2)) & this.addrMask);
|
||||
this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize)) & this.addrMask);
|
||||
this.nStepCycles -= nCycles;
|
||||
this.regECX -= nDelta;
|
||||
if (nReps) {
|
||||
|
|
@ -3020,8 +3032,8 @@ X86.opSCASw = function SCASw()
|
|||
if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.CYCLES.nOpCyclesScaSr0;
|
||||
}
|
||||
if (nReps--) {
|
||||
X86.fnCMPw.call(this, this.regEAX, this.modEAWord(this.segES, this.regEDI & this.addrMask));
|
||||
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -2 : 2)) & this.addrMask);
|
||||
X86.fnCMPw.call(this, this.regEAX & this.dataMask, this.modEAWord(this.segES, this.regEDI & this.addrMask));
|
||||
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize)) & this.addrMask);
|
||||
/*
|
||||
* NOTE: As long as we're calling opGrpCMPb(), all our cycle times must be reduced by nOpCyclesArithRM
|
||||
*/
|
||||
|
|
@ -3269,6 +3281,16 @@ X86.opGrp2wi = function GRP2wi()
|
|||
this.aOpModGrpWord[this.getIPByte()].call(this, X86.aOpGrp2w, X86.fnGRPCountImm);
|
||||
};
|
||||
|
||||
/**
|
||||
* op=0xC1 (GRP2 dword,imm16) (80186/80188 and up)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
X86.opGrp2di = function GRP2di()
|
||||
{
|
||||
this.aOpModGrpWord[this.getIPByte()].call(this, X86.aOpGrp2d, X86.fnGRPCountImm);
|
||||
};
|
||||
|
||||
/**
|
||||
* op=0xC2 (RET n)
|
||||
*
|
||||
|
|
@ -3502,6 +3524,16 @@ X86.opGrp2w1 = function GRP2w1()
|
|||
this.aOpModGrpWord[this.getIPByte()].call(this, X86.aOpGrp2w, X86.fnGRPCount1);
|
||||
};
|
||||
|
||||
/**
|
||||
* op=0xD1 (GRP2 dword,1)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
X86.opGrp2d1 = function GRP2d1()
|
||||
{
|
||||
this.aOpModGrpWord[this.getIPByte()].call(this, X86.aOpGrp2d, X86.fnGRPCount1);
|
||||
};
|
||||
|
||||
/**
|
||||
* op=0xD2 (GRP2 byte,CL)
|
||||
*
|
||||
|
|
@ -3522,6 +3554,16 @@ X86.opGrp2wCL = function GRP2wCL()
|
|||
this.aOpModGrpWord[this.getIPByte()].call(this, X86.aOpGrp2w, X86.fnGRPCountCL);
|
||||
};
|
||||
|
||||
/**
|
||||
* op=0xD3 (GRP2 dword,CL)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
X86.opGrp2dCL = function GRP2dCL()
|
||||
{
|
||||
this.aOpModGrpWord[this.getIPByte()].call(this, X86.aOpGrp2d, X86.fnGRPCountCL);
|
||||
};
|
||||
|
||||
/**
|
||||
* op=0xD4 0x0A (AAM)
|
||||
*
|
||||
|
|
@ -4098,11 +4140,11 @@ X86.opUndefined = function()
|
|||
};
|
||||
|
||||
/**
|
||||
* opTBDd()
|
||||
* opTBD()
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
X86.opTBDd = function()
|
||||
X86.opTBD = function()
|
||||
{
|
||||
this.printMessage("unimplemented 80386 opcode", true);
|
||||
this.stopCPU();
|
||||
|
|
@ -4268,6 +4310,11 @@ X86.aOpGrp2w = [
|
|||
X86.fnSHLw, X86.fnSHRw, X86.fnGRPUndefined, X86.fnSARw // 0xD1/0xD3(reg=0x4-0x7)
|
||||
];
|
||||
|
||||
X86.aOpGrp2d = [
|
||||
X86.fnTBD, X86.fnTBD, X86.fnRCLd, X86.fnRCRd, // 0xD1/0xD3(reg=0x0-0x3)
|
||||
X86.fnTBD, X86.fnTBD, X86.fnGRPUndefined, X86.fnTBD // 0xD1/0xD3(reg=0x4-0x7)
|
||||
];
|
||||
|
||||
X86.aOpGrp3b = [
|
||||
X86.fnTEST8, X86.fnGRPUndefined, X86.fnNOTb, X86.fnNEGb, // 0xF6(reg=0x0-0x3)
|
||||
X86.fnMULb, X86.fnIMULb, X86.fnDIVb, X86.fnIDIVb // 0xF6(reg=0x4-0x7)
|
||||
|
|
@ -4290,32 +4337,35 @@ X86.aOpGrp4w = [
|
|||
|
||||
if (I386) {
|
||||
/*
|
||||
* Until we have *d() forms of all *w() opcode handlers, we need to put in placeholders (ie, opTBDd())
|
||||
* Until we have *d() forms of all *w() opcode handlers, we need to put in placeholders (ie, opTBD())
|
||||
*/
|
||||
X86.aOpsD = {
|
||||
0x01: X86.opTBDd, // opADDmd()
|
||||
0x03: X86.opTBDd, // opADDrd()
|
||||
0x05: X86.opTBDd, // opADDAXd()
|
||||
0x09: X86.opTBDd, // opORmd()
|
||||
0x0B: X86.opTBDd, // opORrd()
|
||||
0x0D: X86.opTBDd, // opORAXd()
|
||||
0x11: X86.opTBDd, // opADCmd()
|
||||
0x13: X86.opTBDd, // opADCrd()
|
||||
0x15: X86.opTBDd, // opADCAXd()
|
||||
0x19: X86.opTBDd, // opSBBmd()
|
||||
0x1B: X86.opTBDd, // opSBBrd()
|
||||
0x1D: X86.opTBDd, // opSBBAXd()
|
||||
0x01: X86.opTBD, // opADDmd()
|
||||
0x03: X86.opTBD, // opADDrd()
|
||||
0x05: X86.opTBD, // opADDAXd()
|
||||
0x09: X86.opTBD, // opORmd()
|
||||
0x0B: X86.opTBD, // opORrd()
|
||||
0x0D: X86.opTBD, // opORAXd()
|
||||
0x11: X86.opTBD, // opADCmd()
|
||||
0x13: X86.opTBD, // opADCrd()
|
||||
0x15: X86.opTBD, // opADCAXd()
|
||||
0x19: X86.opTBD, // opSBBmd()
|
||||
0x1B: X86.opTBD, // opSBBrd()
|
||||
0x1D: X86.opTBD, // opSBBAXd()
|
||||
0x21: X86.opANDmd,
|
||||
0x23: X86.opANDrd,
|
||||
0x25: X86.opANDAXd,
|
||||
0x29: X86.opTBDd, // opSUBmd()
|
||||
0x2B: X86.opTBDd, // opSUBrd()
|
||||
0x2D: X86.opTBDd, // opSUBAXd()
|
||||
0x31: X86.opTBDd, // opXORmd()
|
||||
0x29: X86.opTBD, // opSUBmd()
|
||||
0x2B: X86.opTBD, // opSUBrd()
|
||||
0x2D: X86.opTBD, // opSUBAXd()
|
||||
0x31: X86.opTBD, // opXORmd()
|
||||
0x33: X86.opXORrd,
|
||||
0x35: X86.opTBDd, // opXORAXd()
|
||||
0x39: X86.opTBDd, // opCMPmd()
|
||||
0x3B: X86.opTBDd, // opCMPrd()
|
||||
0x3D: X86.opTBDd // opCMPAXd()
|
||||
0x35: X86.opTBD, // opXORAXd()
|
||||
0x39: X86.opTBD, // opCMPmd()
|
||||
0x3B: X86.opTBD, // opCMPrd()
|
||||
0x3D: X86.opTBD, // opCMPAXd()
|
||||
0xC1: X86.opGrp2di,
|
||||
0xD1: X86.opGrp2d1,
|
||||
0xD3: X86.opGrp2dCL
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ X86Seg.loadIDTReal = function loadIDTReal(nIDT)
|
|||
var addrIDT = cpu.addrIDT + (nIDT << 2);
|
||||
var off = cpu.getShort(addrIDT);
|
||||
cpu.regPS &= ~(X86.PS.TF | X86.PS.IF);
|
||||
return this.load(cpu.getShort(addrIDT + 2)) + off;
|
||||
return (this.load(cpu.getShort(addrIDT + 2)) + off)|0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -254,7 +254,7 @@ X86Seg.loadIDTProt = function loadIDTProt(nIDT)
|
|||
*/
|
||||
X86Seg.checkReadReal = function checkReadReal(off, cb, fSuppress)
|
||||
{
|
||||
return (this.base + off) | 0;
|
||||
return (this.base + off)|0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -271,7 +271,7 @@ X86Seg.checkReadReal = function checkReadReal(off, cb, fSuppress)
|
|||
*/
|
||||
X86Seg.checkWriteReal = function checkWriteReal(off, cb, fSuppress)
|
||||
{
|
||||
return (this.base + off) | 0;
|
||||
return (this.base + off)|0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -286,7 +286,7 @@ X86Seg.checkWriteReal = function checkWriteReal(off, cb, fSuppress)
|
|||
X86Seg.checkReadProt = function checkReadProt(off, cb, fSuppress)
|
||||
{
|
||||
if (off + cb <= this.limit) {
|
||||
return this.base + off;
|
||||
return (this.base + off)|0;
|
||||
}
|
||||
return X86Seg.checkReadProtDisallowed.call(this, off, cb, fSuppress);
|
||||
};
|
||||
|
|
@ -303,7 +303,7 @@ X86Seg.checkReadProt = function checkReadProt(off, cb, fSuppress)
|
|||
X86Seg.checkReadProtDown = function checkReadProtDown(off, cb, fSuppress)
|
||||
{
|
||||
if (off + cb > this.limit) {
|
||||
return this.base + off;
|
||||
return (this.base + off)|0;
|
||||
}
|
||||
return X86Seg.checkReadProtDisallowed.call(this, off, cb, fSuppress);
|
||||
};
|
||||
|
|
@ -337,7 +337,7 @@ X86Seg.checkReadProtDisallowed = function checkReadProtDisallowed(off, cb, fSupp
|
|||
X86Seg.checkWriteProt = function checkWriteProt(off, cb, fSuppress)
|
||||
{
|
||||
if (off + cb <= this.limit) {
|
||||
return this.base + off;
|
||||
return (this.base + off)|0;
|
||||
}
|
||||
return X86Seg.checkWriteProtDisallowed.call(this, off, cb, fSuppress);
|
||||
};
|
||||
|
|
@ -354,7 +354,7 @@ X86Seg.checkWriteProt = function checkWriteProt(off, cb, fSuppress)
|
|||
X86Seg.checkWriteProtDown = function checkWriteProtDown(off, cb, fSuppress)
|
||||
{
|
||||
if (off + cb > this.limit) {
|
||||
return this.base + off;
|
||||
return (this.base + off)|0;
|
||||
}
|
||||
return X86Seg.checkWriteProtDisallowed.call(this, off, cb, fSuppress);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue