diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index 890bd6299..25cc4c482 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -1316,6 +1316,7 @@ if (DEBUGGER) { } this.messageDump(Messages.BUS, function onDumpBus(s) { dbg.dumpBus(s); }); + this.messageDump(Messages.MEM, function onDumpMem(s) { dbg.dumpMem(s); }); this.messageDump(Messages.DESC, function onDumpDesc(s) { dbg.dumpDesc(s); }); this.messageDump(Messages.TSS, function onDumpTSS(s) { dbg.dumpTSS(s); }); this.messageDump(Messages.DOS, function onDumpDOS(s) { dbg.dumpDOS(s); }); @@ -1695,8 +1696,8 @@ if (DEBUGGER) { * hexOffset(off, sel, fAddr32) * * @this {Debugger} - * @param {number|null} [off] - * @param {number|null} [sel] + * @param {number|null|undefined} [off] + * @param {number|null|undefined} [sel] * @param {boolean} [fAddr32] is true for 32-bit ADDRESS size * @return {string} the hex representation of off (or sel:off) */ @@ -1723,7 +1724,7 @@ if (DEBUGGER) { /** * getSZ(dbgAddr, cchMax) * - * Get zero-terminated (aka "ASCIIZ") string from dbgAddr. It also stops at the first '$', in case this is + * Gets zero-terminated (aka "ASCIIZ") string from dbgAddr. It also stops at the first '$', in case this is * a '$'-terminated string -- mainly because I'm lazy and didn't feel like writing a separate get() function. * Yes, a zero-terminated string containing a '$' will be prematurely terminated, and no, I don't care. * @@ -1745,35 +1746,34 @@ if (DEBUGGER) { }; /** - * dumpDOS(s) + * dumpDOS(sMCB) * - * This dumps DOS MCBs (Memory Control Blocks). + * Dumps DOS MCBs (Memory Control Blocks). + * + * TODO: Add some code to detect the running version of DOS (if any) and locate the first MCB automatically. * * @this {Debugger} - * @param {string} [s] + * @param {string} [sMCB] */ - Debugger.prototype.dumpDOS = function(s) + Debugger.prototype.dumpDOS = function(sMCB) { - if (!s) { - this.println("no MCB"); + var mcb; + if (sMCB) { + mcb = this.parseValue(sMCB); + } + if (mcb === undefined) { + this.println("invalid MCB"); return; } - - this.println("dumpDOS(" + s + ")"); - - /* - * If s is provided and str.parseInt(s) succeeds, then we assume it represents a starting - * MCB (Memory Control Block) segment, and we dump the corresponding blocks. - */ - var sel = this.parseValue(s); - while (sel) { - var dbgAddr = this.newAddr(0, sel); + this.println("dumpMCB(" + str.toHexWord(mcb) + ")"); + while (mcb) { + var dbgAddr = this.newAddr(0, mcb); var bSig = this.getByte(dbgAddr, 1); var wPID = this.getShort(dbgAddr, 2); var wParas = this.getShort(dbgAddr, 5); if (bSig != 0x4D && bSig != 0x5A) break; - this.println(this.hexOffset(0, sel) + ": '" + String.fromCharCode(bSig) + "' PID=" + str.toHexWord(wPID) + " LEN=" + str.toHexWord(wParas) + ' "' + this.getSZ(dbgAddr, 8) + '"'); - sel += 1 + wParas; + this.println(this.hexOffset(0, mcb) + ": '" + String.fromCharCode(bSig) + "' PID=" + str.toHexWord(wPID) + " LEN=" + str.toHexWord(wParas) + ' "' + this.getSZ(dbgAddr, 8) + '"'); + mcb += 1 + wParas; } }; @@ -1832,24 +1832,65 @@ if (DEBUGGER) { }; /** - * dumpBus(s) - * - * This dumps Bus allocations. + * dumpBlocks(aBlocks, sAddr) * * @this {Debugger} - * @param {string} [s] + * @param {Array} aBlocks + * @param {string} [sAddr] (optional block address) */ - Debugger.prototype.dumpBus = function(s) + Debugger.prototype.dumpBlocks = function(aBlocks, sAddr) { + var i = 0, n = aBlocks.length; + this.println("id physaddr blkaddr used size type"); this.println("-------- --------- -------- ------ ------ ----"); - for (var i = 0; i < this.cpu.aMemBlocks.length; i++) { - var block = this.cpu.aBusBlocks[i]; + + if (sAddr) { + var addr = this.parseValue(sAddr); + if (addr !== undefined) { + i = addr >>> this.cpu.nBlockShift; + n = 1; + } + } + while (n--) { + var block = aBlocks[i]; if (block.type === Memory.TYPE.NONE) continue; this.println(str.toHex(block.id) + " %" + str.toHex(i << this.cpu.nBlockShift) + ": " + str.toHex(block.addr) + " " + str.toHexWord(block.used) + " " + str.toHexWord(block.size) + " " + Memory.TYPE.NAMES[block.type]); + i++; } }; + /** + * dumpBus(sAddr) + * + * Dumps Bus allocations. + * + * @this {Debugger} + * @param {string} [sAddr] (optional block address) + */ + Debugger.prototype.dumpBus = function(sAddr) + { + this.dumpBlocks(this.cpu.aBusBlocks, sAddr); + }; + + /** + * dumpMem(sAddr) + * + * Dumps page allocations. + * + * @this {Debugger} + * @param {string} [sAddr] (optional block address) + */ + Debugger.prototype.dumpMem = function(sAddr) + { + var aBlocks = this.cpu.aMemBlocks; + if (aBlocks === this.cpu.aBusBlocks) { + this.println("paging not enabled"); + return; + } + this.dumpBlocks(aBlocks, sAddr); + }; + Debugger.SYSDESCS = { 0x0100: ["tss286", false], 0x0200: ["ldt", false], @@ -1868,7 +1909,7 @@ if (DEBUGGER) { /** * dumpDesc(s) * - * This dumps a descriptor for the given selector. + * Dumps a descriptor for the given selector. * * @this {Debugger} * @param {string} [s] @@ -2058,12 +2099,12 @@ if (DEBUGGER) { var iPort = 0; off = (v >>> 16); /* - * We arbitrarily cut the IOPM dump off at port 0x3FF, because we're not interested in anything above that. + * We arbitrarily cut the IOPM dump off at port 0x3FF; we're not currently interested in anything above that. */ while (off < seg.offMax && iPort < 0x3ff) { addr = seg.base + off; v = this.cpu.probeAddr(addr) | (this.cpu.probeAddr(addr + 1) << 8); - sDump += "\nports " + str.toHexWord(iPort) + '-' + str.toHexWord(iPort+15) + ": " + str.toBinBytes(v, 2); + sDump += "\n" + str.toHexWord(off) + " ports " + str.toHexWord(iPort) + '-' + str.toHexWord(iPort+15) + ": " + str.toBinBytes(v, 2); iPort += 16; off += 2; } @@ -2615,7 +2656,7 @@ if (DEBUGGER) { */ if (fUpdateCPU !== false) this.cpu.updateCPU(); - this.updateStatus(fRegs || false, false); + this.updateStatus(fRegs || false); return (this.nCycles > 0); }; @@ -2631,16 +2672,14 @@ if (DEBUGGER) { }; /** - * updateStatus(fRegs, fCompact) + * updateStatus(fRegs) * * @this {Debugger} * @param {boolean} [fRegs] (default is true) - * @param {boolean} [fCompact] (default is true) */ - Debugger.prototype.updateStatus = function(fRegs, fCompact) + Debugger.prototype.updateStatus = function(fRegs) { if (fRegs === undefined) fRegs = true; - if (fCompact === undefined) fCompact = true; this.dbgAddrNextCode = this.newAddr(this.cpu.getIP(), this.cpu.getCS()); /* @@ -2651,7 +2690,7 @@ if (DEBUGGER) { if (!fRegs || this.fProcStep == 1) this.doUnassemble(); else { - this.doRegisters(null, fCompact); + this.doRegisters(null); } }; @@ -2862,7 +2901,7 @@ if (DEBUGGER) { } this.println(sStopped); } - this.updateStatus(true, this.fProcStep != 2); + this.updateStatus(true); this.setFocus(); this.clearTempBreakpoint(this.cpu.regLIP); } @@ -3053,6 +3092,8 @@ if (DEBUGGER) { */ Debugger.prototype.addBreakpoint = function(aBreak, dbgAddr, fTemp) { + var fSuccess = false; + this.nSuppressBreaks++; if (!this.findBreakpoint(aBreak, dbgAddr)) { dbgAddr.fTempBreak = fTemp; aBreak.push(dbgAddr); @@ -3071,9 +3112,10 @@ if (DEBUGGER) { this.println("breakpoint enabled: " + this.hexAddr(dbgAddr) + " (" + aBreak[0] + ")"); } if (!fTemp) this.historyInit(); - return true; + fSuccess = true; } - return false; + this.nSuppressBreaks--; + return fSuccess; }; /** @@ -3091,7 +3133,8 @@ if (DEBUGGER) { var addr = this.mapBreakpoint(this.getAddr(dbgAddr)); for (var i = 1; i < aBreak.length; i++) { var dbgAddrBreak = aBreak[i]; - if (addr == this.mapBreakpoint(this.getAddr(dbgAddrBreak))) { + if (addr != X86.ADDR_INVALID && addr == this.mapBreakpoint(this.getAddr(dbgAddrBreak)) || + addr == X86.ADDR_INVALID && dbgAddr.sel == dbgAddrBreak.sel && dbgAddr.off == dbgAddrBreak.off) { fFound = true; if (fRemove) { aBreak.splice(i, 1); @@ -3206,8 +3249,10 @@ if (DEBUGGER) { * because any CS-based breakpoint you set immediately after a CPU reset will have a physical address * in the top 16Mb, yet after the first inter-segment JMP, you will be running in the first 1Mb. */ - var mask = (this.maskAddr & ~0xffff); - if ((addr & mask) == mask) addr &= 0x000fffff; + if (addr != X86.ADDR_INVALID) { + var mask = (this.maskAddr & ~0xffff); + if ((addr & mask) == mask) addr &= 0x000fffff; + } return addr; }; @@ -5148,13 +5193,12 @@ if (DEBUGGER) { }; /** - * doRegisters(asArgs, fCompact) + * doRegisters(asArgs) * * @this {Debugger} * @param {Array.} [asArgs] - * @param {boolean} [fCompact] */ - Debugger.prototype.doRegisters = function(asArgs, fCompact) + Debugger.prototype.doRegisters = function(asArgs) { if (asArgs && asArgs[1] == "?") { this.println("register commands:"); @@ -5378,7 +5422,6 @@ if (DEBUGGER) { } this.cpu.updateCPU(); this.println("updated registers:"); - fCompact = true; } } @@ -5448,7 +5491,7 @@ if (DEBUGGER) { this.fProcStep = fProcStep; this.incAddr(dbgAddr, 1); break; - case X86.OPCODE.INTn: + case X86.OPCODE.INTN: case X86.OPCODE.LOOPNZ: case X86.OPCODE.LOOPZ: case X86.OPCODE.LOOP: diff --git a/modules/pcjs/lib/memory.js b/modules/pcjs/lib/memory.js index c6a0e0d32..33efe5199 100644 --- a/modules/pcjs/lib/memory.js +++ b/modules/pcjs/lib/memory.js @@ -491,13 +491,11 @@ Memory.prototype = { * @return {Memory} */ getPageBlock: function(addr, fWrite) { - var block = this.cpu.mapPageBlock(addr, fWrite); /* - * If mapPageBlock() fails -- which can easily happen if the page is not present or has insufficient - * privileges -- then a fault will be triggered and block will be null. We still have to return a block, - * but it will be our old "unpaged" self. + * Even when mapPageBlock() fails (ie, when the page is not present or has insufficient privileges), it + * will trigger a fault (since we don't set fSuppress), but it will still return a block (ie, an empty block). */ - return block || this; + return this.cpu.mapPageBlock(addr, fWrite); }, /** * setPhysBlock(blockPhys, blockPDE, offPDE, blockPTE, offPTE) diff --git a/modules/pcjs/lib/x86.js b/modules/pcjs/lib/x86.js index 74f4b23f2..d2c949993 100644 --- a/modules/pcjs/lib/x86.js +++ b/modules/pcjs/lib/x86.js @@ -382,6 +382,7 @@ var X86 = { NOREAD: 0x0001, NOWRITE: 0x0002, NOINTR: 0x0004, // indicates a segreg has been set, or a prefix, or an STI (delay INTR acknowledgement) + FAULT: 0x0008, // indicates a fault occurred during the current instruction SEG: 0x0010, // segment override LOCK: 0x0020, // lock prefix REPZ: 0x0040, // repeat while Z (NOTE: this value MUST match PS.ZF; see opCMPSb/opCMPSw/opSCASb/opSCASw) @@ -440,7 +441,7 @@ var X86 = { SCASB: 0xAE, // opSCASb() SCASW: 0xAF, // opSCASw() INT3: 0xCC, // opINT3() - INTn: 0xCD, // opINTn() + INTN: 0xCD, // opINTn() INTO: 0xCE, // opINTO() LOOPNZ: 0xE0, // opLOOPNZ() LOOPZ: 0xE1, // opLOOPZ() diff --git a/modules/pcjs/lib/x86cpu.js b/modules/pcjs/lib/x86cpu.js index a983c200d..5f7d377bc 100644 --- a/modules/pcjs/lib/x86cpu.js +++ b/modules/pcjs/lib/x86cpu.js @@ -677,7 +677,7 @@ X86CPU.prototype.setAddressMask = function(nBusMask) * of the Bus block array, to simulate paging. Whenever the CPU turns paging off, disablePageBlocks() * must be called to restore our copy of the Bus block array to its original (physical) mapping. * - * This also requires PAGEBLOCKS be enabled, ensuring that the Bus is configured with a 4Kb block size. + * This also requires PAGEBLOCKS be enabled, to ensure the Bus is configured with a 4Kb block size. * * The first time this function is called, aMemBlocks and aBusBlocks are identical, so aMemBlocks is * reinitialized with special UNPAGED Memory blocks that know how to perform page directory/page table @@ -690,7 +690,7 @@ X86CPU.prototype.setAddressMask = function(nBusMask) X86CPU.prototype.enablePageBlocks = function() { if (!PAGEBLOCKS) { - this.setError("PAGEBLOCK support required"); + this.setError("PAGEBLOCKS support required"); return; } if (this.aMemBlocks === this.aBusBlocks) { @@ -699,6 +699,11 @@ X86CPU.prototype.enablePageBlocks = function() for (var iBlock = 0; iBlock < this.nBlockTotal; iBlock++) { 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. + */ + this.memEmpty = new Memory(); } else { for (var i = 0; i < this.aBlocksPaged.length; i++) { this.aMemBlocks[this.aBlocksPaged[i]] = this.blockUnpaged; @@ -708,7 +713,7 @@ X86CPU.prototype.enablePageBlocks = function() }; /** - * mapPageBlock(addr, fWrite) + * mapPageBlock(addr, fWrite, fSuppress) * * Locate the corresponding physical PDE, PTE and memory blocks for the given linear address, and then * upgrade the block from an UNPAGED Memory block to a new PAGED Memory block; all future accesses to @@ -737,7 +742,7 @@ X86CPU.prototype.enablePageBlocks = function() * @param {number} addr is a linear address * @param {boolean} fWrite (true if called for a write, false if for a read) * @param {boolean} [fSuppress] (true if any faults, remapping, etc should be suppressed) - * @return {Memory|null} + * @return {Memory} */ X86CPU.prototype.mapPageBlock = function(addr, fWrite, fSuppress) { @@ -753,12 +758,12 @@ X86CPU.prototype.mapPageBlock = function(addr, fWrite, fSuppress) if (!(pde & X86.PTE.PRESENT)) { if (!fSuppress) X86.fnPageFault.call(this, addr, false, fWrite); - return null; + return this.memEmpty; } if (!(pde & X86.PTE.USER) && this.segCS.cpl == 3) { if (!fSuppress) X86.fnPageFault.call(this, addr, true, fWrite); - return null; + return this.memEmpty; } var offPTE = (addr & X86.LADDR.PTE.MASK) >>> X86.LADDR.PTE.SHIFT; @@ -773,12 +778,12 @@ X86CPU.prototype.mapPageBlock = function(addr, fWrite, fSuppress) if (!(pte & X86.PTE.PRESENT) && !fSuppress) { if (!fSuppress) X86.fnPageFault.call(this, addr, false, fWrite); - return null; + return this.memEmpty; } if (!(pte & X86.PTE.USER) && this.segCS.cpl == 3) { if (!fSuppress) X86.fnPageFault.call(this, addr, true, fWrite); - return null; + return this.memEmpty; } var addrPhys = (pte & X86.PTE.FRAME) + (addr & X86.LADDR.OFFSET); @@ -816,6 +821,7 @@ X86CPU.prototype.disablePageBlocks = function() this.aMemBlocks = this.aBusBlocks; this.blockUnpaged = null; this.aBlocksPaged = null; + this.memEmpty = null; } }; @@ -2756,7 +2762,7 @@ X86CPU.prototype.setPS = function(regPS, cpl) X86CPU.prototype.checkIOPM = function(port, nPorts) { var bitsPorts = 0; - if (I386 && (this.regCR0 & X86.CR0.MSW.PE) && this.segCS.cpl > this.nIOPL && this.segTSS.addrIOPM) { + if (I386 && (this.regCR0 & X86.CR0.MSW.PE) && (this.segCS.cpl > this.nIOPL || (this.regPS & X86.PS.VM)) && this.segTSS.addrIOPM) { var offIOPM = port >>> 3; var addrIOPM = this.segTSS.addrIOPM + offIOPM; bitsPorts = ((1 << nPorts) - 1) << (port & 0x7); @@ -2767,7 +2773,12 @@ X86CPU.prototype.checkIOPM = function(port, nPorts) addrIOPM++; } } - return !bitsPorts; + if (bitsPorts) { + if (this.messageEnabled(Messages.PORT)) this.printMessage("checkIOPM(" + str.toHexWord(port) + "," + nPorts + "): trapped", true, true); + X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0, false); + return false; + } + return true; }; /** diff --git a/modules/pcjs/lib/x86func.js b/modules/pcjs/lib/x86func.js index 234b7564c..5c48d48f0 100644 --- a/modules/pcjs/lib/x86func.js +++ b/modules/pcjs/lib/x86func.js @@ -3679,11 +3679,13 @@ X86.fnSrcNone = function SrcNone() * @this {X86CPU} * @param {number} nFault * @param {number} [nError] (if omitted, no error code will be pushed) - * @param {boolean} [fHalt] will halt the CPU if true *and* a Debugger is loaded + * @param {boolean} [fHalt] true to halt the CPU (if the Debugger is loaded), false to not, undefined if "it depends" * @param {number} [nCycles] cycle count to pass through to fnINT(), if any */ X86.fnFault = function(nFault, nError, fHalt, nCycles) { + this.opFlags |= X86.OPFLAG.FAULT; + if (!this.aFlags.fComplete) { this.printMessage("Fault " + str.toHexByte(nFault) + " blocked by Debugger", Messages.WARN); this.setIP(this.opLIP - this.segCS.base); @@ -3774,7 +3776,7 @@ X86.fnPageFault = function(addr, fPresent, fWrite) * @this {X86CPU} * @param {number} nFault * @param {number} [nError] (if omitted, no error code will be reported) - * @param {boolean} [fHalt] true if the CPU should always be halted, false if "it depends" + * @param {boolean} [fHalt] true to halt the CPU (if the Debugger is loaded), false to not, undefined if "it depends" * @return {boolean|undefined} true to block the fault (often desirable when fHalt is true), otherwise dispatch it */ X86.fnFaultMessage = function(nFault, nError, fHalt) @@ -3798,15 +3800,31 @@ X86.fnFaultMessage = function(nFault, nError, fHalt) */ if (bOpcode == X86.OPCODE.INT3 && !this.addrIDTLimit) { fHalt = false; - bitsMessage |= Messages.CPU; } /* - * Windows 3.00 (and other versions of enhanced-mode Windows) use an ARPL in V86-mode to switch out - * of V86-mode; we don't need to report the UD_FAULT by default. + * There are a number of V86-mode exceptions we don't need to know about. For starters, Windows 3.00 + * (and other versions of enhanced-mode Windows) use an ARPL in V86-mode to switch out of V86-mode, so + * we can ignore those UD_FAULTs. + * + * Ditto for software interrupts, which will generate a GP_FAULT when the interrupt number (eg, 0x6D) + * exceeds the protected-mode IDT's limit (eg, a limit of 0x2FF yields a maximum interrupt number of 0x5F). + * Windows doesn't really care if its IDT is too small, because no matter what, it has to simulate all + * software interrupts in V86-mode (they will also generate a GP_FAULT if IOPL < 3, and even if IOPL == 3, + * only the protected-mode IDT handler gets to run). */ - if (bOpcode == X86.OPCODE.ARPL && (this.regPS & X86.PS.VM)) { - fHalt = false; + if ((this.regPS & X86.PS.VM)) { + if (nFault == X86.EXCEPTION.UD_FAULT && bOpcode == X86.OPCODE.ARPL || + nFault == X86.EXCEPTION.GP_FAULT && bOpcode == X86.OPCODE.INTN) { + fHalt = false; + } + } + + /* + * If fHalt has been explicitly set to false, we also take that as a cue to disable fault messages + * (which you can override by turning on CPU messages). + */ + if (fHalt === false) { bitsMessage |= Messages.CPU; } @@ -3822,16 +3840,13 @@ X86.fnFaultMessage = function(nFault, nError, fHalt) /* * However, the foregoing notwithstanding, if MESSAGE.HALT is enabled along with all the other required * MESSAGE bits, then we want to halt regardless. - * - * TODO: Eventually remove the code below that halts on all MODEL_80386 GP_FAULTs and PG_FAULTs; this is - * just to make it easier to catch bad faults on DeskPro 386 configurations. */ - if (DEBUGGER && this.model == X86.MODEL_80386 && (nFault == X86.EXCEPTION.GP_FAULT || nFault == X86.EXCEPTION.PG_FAULT) || this.messageEnabled(bitsMessage | Messages.HALT)) { + if (this.messageEnabled(bitsMessage | Messages.HALT)) { fHalt = true; } if (this.messageEnabled(bitsMessage) || fHalt) { - var sMessage = (fHalt? '\n' : '') + "Fault " + str.toHexByte(nFault) + (nError != null? " (" + str.toHexWord(nError) + ")" : "") + " on opcode " + str.toHexByte(bOpcode) + " at " + this.dbg.hexOffset(this.getIP(), this.getCS()) + " (%" + str.toHex(this.regLIP, 6) + ")"; + var sMessage = "Fault " + str.toHexByte(nFault) + (nError != null? " (" + str.toHexWord(nError) + ")" : "") + " on opcode " + str.toHexByte(bOpcode) + " at " + this.dbg.hexOffset(this.getIP(), this.getCS()) + " (%" + str.toHex(this.regLIP, 6) + ")"; var fRunning = this.aFlags.fRunning; if (this.printMessage(sMessage, bitsMessage)) { if (fHalt) { diff --git a/modules/pcjs/lib/x86ops.js b/modules/pcjs/lib/x86ops.js index 495bb2f87..73cf52808 100644 --- a/modules/pcjs/lib/x86ops.js +++ b/modules/pcjs/lib/x86ops.js @@ -1503,13 +1503,11 @@ X86.opINSb = function INSb() if (nReps--) { var port = this.regEDX & 0xffff; - if (!this.checkIOPM(port, 1)) { - X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); - return; - } + if (!this.checkIOPM(port, 1)) return; var b = this.bus.checkPortInputNotify(port, this.regLIP - nDelta - 1); - if (BACKTRACK) this.backTrack.btiMemLo = this.backTrack.btiIO; this.setSOByte(this.segES, this.regEDI & this.addrMask, b); + if (this.opFlags & X86.OPFLAG.FAULT) return; + if (BACKTRACK) this.backTrack.btiMemLo = this.backTrack.btiIO; this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -1 : 1)) & this.addrMask); this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask); this.nStepCycles -= nCycles; @@ -1556,10 +1554,7 @@ X86.opINSw = function INSw() var addrFrom = this.regLIP - nDelta - 1; var w = 0, shift = 0; var port = this.regEDX & 0xffff; - if (!this.checkIOPM(port, 1)) { - X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); - return; - } + if (!this.checkIOPM(port, 1)) return; for (var n = 0; n < this.dataSize; n++) { w |= this.bus.checkPortInputNotify(port, addrFrom) << shift; shift += 8; @@ -1572,6 +1567,7 @@ X86.opINSw = function INSw() } } this.setSOWord(this.segES, this.regEDI & this.addrMask, w); + if (this.opFlags & X86.OPFLAG.FAULT) return; this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize)) & this.addrMask); this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask); this.nStepCycles -= nCycles; @@ -1615,11 +1611,9 @@ X86.opOUTSb = function OUTSb() } if (nReps--) { var port = this.regEDX & 0xffff; - if (!this.checkIOPM(port, 1)) { - X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); - return; - } + if (!this.checkIOPM(port, 1)) return; var b = this.getSOByte(this.segDS, this.regESI & this.addrMask); + if (this.opFlags & X86.OPFLAG.FAULT) return; if (BACKTRACK) this.backTrack.btiIO = this.backTrack.btiMemLo; this.bus.checkPortOutputNotify(port, b, this.regLIP - nDelta - 1); this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + ((this.regPS & X86.PS.DF)? -1 : 1)) & this.addrMask); @@ -1665,12 +1659,10 @@ X86.opOUTSw = function OUTSw() } if (nReps--) { var w = this.getSOWord(this.segDS, this.regESI & this.addrMask); + if (this.opFlags & X86.OPFLAG.FAULT) return; var addrFrom = this.regLIP - nDelta - 1, shift = 0; var port = this.regEDX & 0xffff; - if (!this.checkIOPM(port, 1)) { - X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); - return; - } + if (!this.checkIOPM(port, 1)) return; for (var n = 0; n < this.dataSize; n++) { if (BACKTRACK) { if (!n) { @@ -2609,8 +2601,9 @@ X86.opMOVSb = function MOVSb() if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesMovSr0; } if (nReps--) { - var nInc = ((this.regPS & X86.PS.DF)? -1 : 1); this.setSOByte(this.segES, this.regEDI & this.addrMask, this.getSOByte(this.segData, this.regESI & this.addrMask)); + if (this.opFlags & X86.OPFLAG.FAULT) return; + var nInc = ((this.regPS & X86.PS.DF)? -1 : 1); this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + nInc) & this.addrMask); this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + nInc) & this.addrMask); this.nStepCycles -= nCycles; @@ -2644,8 +2637,9 @@ X86.opMOVSw = function MOVSw() if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesMovSr0; } if (nReps--) { - 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.addrMask)); + if (this.opFlags & X86.OPFLAG.FAULT) return; + var nInc = ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize); this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + nInc) & this.addrMask); this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + nInc) & this.addrMask); this.nStepCycles -= nCycles; @@ -2679,17 +2673,18 @@ X86.opCMPSb = function CMPSb() if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesCmpSr0; } if (nReps--) { - var nInc = ((this.regPS & X86.PS.DF)? -1 : 1); var bDst = this.getEAByte(this.segData, this.regESI & this.addrMask); var bSrc = this.modEAByte(this.segES, this.regEDI & this.addrMask); + if (this.opFlags & X86.OPFLAG.FAULT) return; X86.fnCMPb.call(this, bDst, bSrc); + var nInc = ((this.regPS & X86.PS.DF)? -1 : 1); this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + nInc) & this.addrMask); this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + nInc) & this.addrMask); + this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask); /* * NOTE: As long as we're calling fnCMPb(), all our cycle times must be reduced by nOpCyclesArithRM */ this.nStepCycles -= nCycles - this.cycleCounts.nOpCyclesArithRM; - this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask); /* * Repetition continues while ZF matches bit 0 of the REP prefix. getZF() returns 0x40 if ZF is * set, and OP_REPZ (which represents the REP prefix whose bit 0 is set) is 0x40 as well, so when those @@ -2724,17 +2719,18 @@ X86.opCMPSw = function CMPSw() if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesCmpSr0; } if (nReps--) { - 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); + if (this.opFlags & X86.OPFLAG.FAULT) return; X86.fnCMPw.call(this, wDst, wSrc); + var nInc = ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize); this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + nInc) & this.addrMask); this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + nInc) & this.addrMask); + this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask); /* * NOTE: As long as we're calling fnCMPw(), all our cycle times must be reduced by nOpCyclesArithRM */ this.nStepCycles -= nCycles - this.cycleCounts.nOpCyclesArithRM; - this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask); /* * Repetition continues while ZF matches bit 0 of the REP prefix. getZF() returns 0x40 if ZF is * set, and OP_REPZ (which represents the REP prefix whose bit 0 is set) is 0x40 as well, so when those @@ -2793,11 +2789,12 @@ X86.opSTOSb = function STOSb() if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesStoSr0; } if (nReps--) { - if (BACKTRACK) this.backTrack.btiMemLo = this.backTrack.btiAL; this.setSOByte(this.segES, this.regEDI & this.addrMask, this.regEAX); + if (this.opFlags & X86.OPFLAG.FAULT) return; + if (BACKTRACK) this.backTrack.btiMemLo = this.backTrack.btiAL; this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -1 : 1)) & this.addrMask); - this.nStepCycles -= nCycles; this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask); + this.nStepCycles -= nCycles; if (nReps) { if (BUGS_8086) { this.rewindIP(-2); // this instruction does not support multiple overrides @@ -2829,17 +2826,14 @@ X86.opSTOSw = function STOSw() if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesStoSr0; } if (nReps--) { - /* - * NOTE: Storing a word imposes another 4-cycle penalty on the 8088, so consider that - * if you think the cycle times here are too high. - */ + this.setSOWord(this.segES, this.regEDI & this.addrMask, this.regEAX); + if (this.opFlags & X86.OPFLAG.FAULT) return; if (BACKTRACK) { 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)? -this.dataSize : this.dataSize)) & this.addrMask); - this.nStepCycles -= nCycles; this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask); + this.nStepCycles -= nCycles; if (nReps) { if (BUGS_8086) { this.rewindIP(-2); // this instruction does not support multiple overrides @@ -2869,11 +2863,13 @@ X86.opLODSb = function LODSb() if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesLodSr0; } if (nReps--) { - this.regEAX = (this.regEAX & ~0xff) | this.getSOByte(this.segData, this.regESI & this.addrMask); + var b = this.getSOByte(this.segData, this.regESI & this.addrMask); + if (this.opFlags & X86.OPFLAG.FAULT) return; + this.regEAX = (this.regEAX & ~0xff) | b; if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiMemLo; this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + ((this.regPS & X86.PS.DF)? -1 : 1)) & this.addrMask); - this.nStepCycles -= nCycles; this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask); + this.nStepCycles -= nCycles; if (nReps) { if (BUGS_8086) { this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2)); @@ -2903,13 +2899,15 @@ X86.opLODSw = function LODSw() if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesLodSr0; } if (nReps--) { - this.regEAX = (this.regEAX & ~this.dataMask) | this.getSOWord(this.segData, this.regESI & this.addrMask); + var w = this.getSOWord(this.segData, this.regESI & this.addrMask); + if (this.opFlags & X86.OPFLAG.FAULT) return; + this.regEAX = (this.regEAX & ~this.dataMask) | w; 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)? -this.dataSize : this.dataSize)) & this.addrMask); - this.nStepCycles -= nCycles; this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask); + this.nStepCycles -= nCycles; if (nReps) { if (BUGS_8086) { this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2)); @@ -2940,12 +2938,13 @@ X86.opSCASb = function SCASb() } if (nReps--) { X86.fnCMPb.call(this, this.regEAX & 0xff, this.modEAByte(this.segES, this.regEDI & this.addrMask)); + if (this.opFlags & X86.OPFLAG.FAULT) return; this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -1 : 1)) & this.addrMask); + this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask); /* * NOTE: As long as we're calling fnCMPb(), all our cycle times must be reduced by nOpCyclesArithRM */ this.nStepCycles -= nCycles - this.cycleCounts.nOpCyclesArithRM; - this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask); /* * Repetition continues while ZF matches bit 0 of the REP prefix. getZF() returns 0x40 if ZF is * set, and OP_REPZ (which represents the REP prefix whose bit 0 is set) is 0x40 as well, so when those @@ -2981,12 +2980,13 @@ X86.opSCASw = function SCASw() } if (nReps--) { X86.fnCMPw.call(this, this.regEAX & this.dataMask, this.modEAWord(this.segES, this.regEDI & this.addrMask)); + if (this.opFlags & X86.OPFLAG.FAULT) return; this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize)) & this.addrMask); + this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask); /* * NOTE: As long as we're calling fnCMPw(), all our cycle times must be reduced by nOpCyclesArithRM */ this.nStepCycles -= nCycles - this.cycleCounts.nOpCyclesArithRM; - this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask); /* * Repetition continues while ZF matches bit 0 of the REP prefix. getZF() returns 0x40 if ZF is * set, and OP_REPZ (which represents the REP prefix whose bit 0 is set) is 0x40 as well, so when those @@ -3692,10 +3692,7 @@ X86.opJCXZ = function JCXZ() X86.opINb = function INb() { var port = this.getIPByte(); - if (!this.checkIOPM(port, 1)) { - X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); - return; - } + if (!this.checkIOPM(port, 1)) return; this.regEAX = (this.regEAX & ~0xff) | this.bus.checkPortInputNotify(port, this.regLIP - 2); if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiIO; this.nStepCycles -= this.cycleCounts.nOpCyclesInP; @@ -3709,10 +3706,7 @@ X86.opINb = function INb() X86.opINw = function INw() { var port = this.getIPByte(); - if (!this.checkIOPM(port, 2)) { - X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); - return; - } + if (!this.checkIOPM(port, 2)) return; this.regEAX = this.bus.checkPortInputNotify(port, this.regLIP - 2); if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiIO; /* @@ -3733,10 +3727,7 @@ X86.opINw = function INw() X86.opOUTb = function OUTb() { var port = this.getIPByte(); - if (!this.checkIOPM(port, 1)) { - X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); - return; - } + if (!this.checkIOPM(port, 1)) return; this.bus.checkPortOutputNotify(port, this.regEAX & 0xff, this.regLIP - 2); this.nStepCycles -= this.cycleCounts.nOpCyclesOutP; }; @@ -3749,10 +3740,7 @@ X86.opOUTb = function OUTb() X86.opOUTw = function OUTw() { var port = this.getIPByte(); - if (!this.checkIOPM(port, 2)) { - X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); - return; - } + if (!this.checkIOPM(port, 2)) return; this.bus.checkPortOutputNotify(port, this.regEAX & 0xff, this.regLIP - 2); /* * TODO: Specs are clear that bits 8-15 of the port address for the FIRST byte of I/O will be zero, but @@ -3824,10 +3812,7 @@ X86.opJMPs = function JMPs() X86.opINDXb = function INDXb() { var port = this.regEDX & 0xffff; - if (!this.checkIOPM(port, 1)) { - X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); - return; - } + if (!this.checkIOPM(port, 1)) return; this.regEAX = (this.regEAX & ~0xff) | this.bus.checkPortInputNotify(port, this.regLIP - 1); if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiIO; this.nStepCycles -= this.cycleCounts.nOpCyclesInDX; @@ -3841,10 +3826,7 @@ X86.opINDXb = function INDXb() X86.opINDXw = function INDXw() { var port = this.regEDX & 0xffff; - if (!this.checkIOPM(port, 2)) { - X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); - return; - } + if (!this.checkIOPM(port, 2)) return; this.regEAX = this.bus.checkPortInputNotify(port, this.regLIP - 1); if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiIO; this.regEAX |= (this.bus.checkPortInputNotify((port + 1) & 0xffff, this.regLIP - 1) << 8); @@ -3860,10 +3842,7 @@ X86.opINDXw = function INDXw() X86.opOUTDXb = function OUTDXb() { var port = this.regEDX & 0xffff; - if (!this.checkIOPM(port, 1)) { - X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); - return; - } + if (!this.checkIOPM(port, 1)) return; if (BACKTRACK) this.backTrack.btiIO = this.backTrack.btiAL; this.bus.checkPortOutputNotify(port, this.regEAX & 0xff, this.regLIP - 1); this.nStepCycles -= this.cycleCounts.nOpCyclesOutDX; @@ -3877,10 +3856,7 @@ X86.opOUTDXb = function OUTDXb() X86.opOUTDXw = function OUTDXw() { var port = this.regEDX & 0xffff; - if (!this.checkIOPM(port, 2)) { - X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); - return; - } + if (!this.checkIOPM(port, 2)) return; if (BACKTRACK) this.backTrack.btiIO = this.backTrack.btiAL; this.bus.checkPortOutputNotify(port, this.regEAX & 0xff, this.regLIP - 1); if (BACKTRACK) this.backTrack.btiIO = this.backTrack.btiAH; diff --git a/modules/pcjs/lib/x86seg.js b/modules/pcjs/lib/x86seg.js index c732408be..fe20ce270 100644 --- a/modules/pcjs/lib/x86seg.js +++ b/modules/pcjs/lib/x86seg.js @@ -60,6 +60,7 @@ if (typeof module !== 'undefined') { * @property {number} base (in protected-mode, this comes from descriptor word 0x2) * @property {number} acc (in protected-mode, this comes from descriptor word 0x4; bits 0-7 supplement base bits 16-23) * @property {number} ext (in protected-mode, this is descriptor word 0x6, 80386 only; supplements limit bits 16-19 and base bits 24-31) + * @property {number} type (this is a subset of acc, using X86.DESC.ACC.TYPE.MASK) * * TODO: Determine what good, if any, these class annotations are for either an IDE like WebStorm or a tool like * the Closure Compiler. More importantly, what good do they do at runtime? Is it better to simply ensure that all @@ -763,7 +764,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress) * last valid address in segTSS.addrIOPMLimit. */ if (typeTSS == X86.DESC.ACC.TYPE.TSS386) { - this.addrIOPM = (base + cpu.getShort(base + X86.TSS386.TASK_IOPM))|0; + this.addrIOPM = (base + cpu.getShort(base + X86.TSS386.TASK_IOPM + 2))|0; this.addrIOPMLimit = (base + this.limit)|0; } }