diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index 3e94f6060..263e92d66 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -1424,7 +1424,7 @@ if (DEBUGGER) { * should be done only as a last resort. * * @param {number|null|undefined} sel - * @return {X86Seg} seg + * @return {X86Seg|null} seg */ Debugger.prototype.getSegment = function(sel) { @@ -1436,6 +1436,7 @@ if (DEBUGGER) { if (sel === this.cpu.getFS()) return this.cpu.segFS; if (sel === this.cpu.getGS()) return this.cpu.segGS; } + if (this.nBreakSuppress) return null; var seg = new X86Seg(this.cpu, X86Seg.ID.DEBUG, "DBG"); /* * Note the load() function's fSuppress parameter, which the Debugger should ALWAYS set to true @@ -1466,13 +1467,16 @@ if (DEBUGGER) { */ var addr = dbgAddr.addr; if (addr == null) { + addr = X86.ADDR_INVALID; var seg = this.getSegment(dbgAddr.sel); - if (!fWrite) { - addr = seg.checkRead(dbgAddr.off, cb || 1, true); - } else { - addr = seg.checkWrite(dbgAddr.off, cb || 1, true); + if (seg) { + if (!fWrite) { + addr = seg.checkRead(dbgAddr.off, cb || 1, true); + } else { + addr = seg.checkWrite(dbgAddr.off, cb || 1, true); + } + dbgAddr.addr = addr; } - dbgAddr.addr = addr; } return addr; }; @@ -1639,8 +1643,8 @@ if (DEBUGGER) { Debugger.prototype.checkLimit = function(dbgAddr) { if (dbgAddr.sel != null) { - var limit = this.getSegment(dbgAddr.sel).limit; - if (dbgAddr.off > limit) { + var seg = this.getSegment(dbgAddr.sel); + if (!seg || dbgAddr.off > seg.limit) { dbgAddr.off = 0; dbgAddr.addr = null; } @@ -1816,8 +1820,8 @@ if (DEBUGGER) { } var seg = this.getSegment(sel); - - this.println("dumpDesc(" + str.toHexWord(seg.sel) + "): %" + str.toHex(seg.addrDesc, this.cchAddr)); + this.println("dumpDesc(" + str.toHexWord(seg? seg.sel : sel) + "): %" + str.toHex(seg? seg.addrDesc : null, this.cchAddr)); + if (!seg) return; var sType; var fGate = false; @@ -1879,7 +1883,68 @@ if (DEBUGGER) { * been incorporated into the limit and base properties of the segment register; all we care about here * are whether EXT contains any of the AVAIL (0x10), BIG (0x40) or LIMITPAGES (0x80) bits. */ - this.println(sDump + " dpl=" + str.toHexByte(seg.dpl) + " type=" + str.toHexByte(seg.type >> 8) + " (" + sType + ")" + " ext=" + str.toHexWord(seg.ext & ~(X86.DESC.EXT.LIMIT1619 | X86.DESC.EXT.BASE2431))); + this.println(sDump + " type=" + str.toHexByte(seg.type >> 8) + " (" + sType + ")" + " ext=" + str.toHexWord(seg.ext & ~(X86.DESC.EXT.LIMIT1619 | X86.DESC.EXT.BASE2431)) + " dpl=" + str.toHexByte(seg.dpl)); + }; + + /** + * dumpHistory(sCount) + * + * @this {Debugger} + * @param {string|undefined} sCount is the number of instructions to rewind to (default is 10) + */ + Debugger.prototype.dumpHistory = function(sCount) + { + var sMore = ""; + var cLines = 10; + var iHistory = this.iOpcodeHistory; + var aHistory = this.aOpcodeHistory; + if (aHistory.length) { + var n = (sCount === undefined? this.nextHistory : +sCount); + if (isNaN(n)) + n = cLines; + else + sMore = "more "; + if (n > aHistory.length) { + this.println("note: only " + aHistory.length + " available"); + n = aHistory.length; + } + iHistory -= n; + if (iHistory < 0) { + if (aHistory[aHistory.length - 1][1] != null) { + iHistory += aHistory.length; + } else { + n = iHistory + n; + iHistory = 0; + } + } + if (sCount !== undefined) { + this.println(n + " instructions earlier:"); + } + while (cLines && iHistory != this.iOpcodeHistory) { + var dbgAddr = aHistory[iHistory++]; + if (dbgAddr.sel == null) break; + /* + * We must create a new dbgAddr from the address we obtained from aHistory, because dbgAddr + * was a reference, not a copy, and we don't want getInstruction() modifying the original. + */ + dbgAddr = this.newAddr(dbgAddr.off, dbgAddr.sel, dbgAddr.addr); + this.println(this.getInstruction(dbgAddr, "history", n--)); + /* + * If there was an OPERAND or ADDRESS override on the previous instruction, getInstruction() + * will have automatically disassembled the next instruction, so skip one more history entry. + */ + if (dbgAddr.fOverride) { + iHistory++; n--; + } + if (iHistory >= aHistory.length) iHistory = 0; + this.nextHistory = n; + cLines--; + } + } + if (cLines == 10) { + this.println("no " + sMore + "history available"); + this.nextHistory = undefined; + } }; /** @@ -1904,7 +1969,8 @@ if (DEBUGGER) { seg = this.getSegment(sel); } - this.println("dumpTSS(" + str.toHexWord(seg.sel) + "): %" + str.toHex(seg.base, this.cchAddr)); + this.println("dumpTSS(" + str.toHexWord(seg? seg.sel : sel) + "): %" + str.toHex(seg? seg.base : null, this.cchAddr)); + if (!seg) return; var sDump = ""; for (var sField in Debugger.aTSSFields) { @@ -2829,6 +2895,12 @@ if (DEBUGGER) { } } this.aBreakWrite = ["write"]; + /* + * nBreakSuppress ensures we can't get into an infinite loop where a breakpoint lookup requires + * reading a segment descriptor via getSegment(), and that triggers more memory reads, which triggers + * more breakpoint checks. + */ + this.nBreakSuppress = 0; }; /** @@ -3017,39 +3089,42 @@ if (DEBUGGER) { * or history data (see checkInstruction), since we might not actually execute the current instruction. */ var fBreak = false; - addr = this.mapBreakpoint(addr); - for (var i = 1; i < aBreak.length; i++) { + if (!this.nBreakSuppress++) { + addr = this.mapBreakpoint(addr); + for (var i = 1; i < aBreak.length; i++) { - var dbgAddrBreak = aBreak[i]; + var dbgAddrBreak = aBreak[i]; - /* - * We need to zap the linear address field of the breakpoint address before - * calling getAddr(), to force it to recalculate the linear address every time, - * unless this is a breakpoint on a linear address (as indicated by a -1 offset). - */ - if (dbgAddrBreak.off != -1) dbgAddrBreak.addr = null; + /* + * We need to zap the linear address field of the breakpoint address before + * calling getAddr(), to force it to recalculate the linear address every time, + * unless this is a breakpoint on a linear address (as indicated by a -1 offset). + */ + if (dbgAddrBreak.off != -1) dbgAddrBreak.addr = null; - /* - * We used to calculate the linear address of the breakpoint at the time the - * breakpoint was added, so that a breakpoint set in one mode (eg, in real-mode) - * would still work as intended if the mode changed later (eg, to protected-mode). - * - * However, that created difficulties setting protected-mode breakpoints in segments - * that might not be defined yet, or that could move in physical memory. - * - * If you want to create a real-mode breakpoint that will break regardless of mode, - * use the physical address of the real-mode memory location instead. - */ - if (addr == this.mapBreakpoint(this.getAddr(dbgAddrBreak))) { - if (dbgAddrBreak.fTempBreak) { - this.findBreakpoint(aBreak, dbgAddrBreak, true); - } else if (!fTemp) { - this.println("breakpoint hit: " + this.hexAddr(dbgAddrBreak) + " (" + aBreak[0] + ")"); + /* + * We used to calculate the linear address of the breakpoint at the time the + * breakpoint was added, so that a breakpoint set in one mode (eg, in real-mode) + * would still work as intended if the mode changed later (eg, to protected-mode). + * + * However, that created difficulties setting protected-mode breakpoints in segments + * that might not be defined yet, or that could move in physical memory. + * + * If you want to create a real-mode breakpoint that will break regardless of mode, + * use the physical address of the real-mode memory location instead. + */ + if (addr == this.mapBreakpoint(this.getAddr(dbgAddrBreak))) { + if (dbgAddrBreak.fTempBreak) { + this.findBreakpoint(aBreak, dbgAddrBreak, true); + } else if (!fTemp) { + this.println("breakpoint hit: " + this.hexAddr(dbgAddrBreak) + " (" + aBreak[0] + ")"); + } + fBreak = true; + break; } - fBreak = true; - break; } } + this.nBreakSuppress--; return fBreak; }; @@ -4167,9 +4242,10 @@ if (DEBUGGER) { sDumpers += ",state,symbols"; this.println("\ndump commands:"); this.println("\tdb [a] [#] dump # bytes at address a"); - if (BACKTRACK) this.println("\tdi [a] dump backtrack info at address a"); this.println("\tdw [a] [#] dump # words at address a"); this.println("\tdd [a] [#] dump # dwords at address a"); + this.println("\tdh [#] dump # instructions prior"); + if (BACKTRACK) this.println("\tdi [a] dump backtrack info at address a"); if (sDumpers.length) this.println("dump extensions:\n\t" + sDumpers); return; } @@ -4181,6 +4257,10 @@ if (DEBUGGER) { this.dumpSymbols(); return; } + if (sCmd == "dh") { + this.dumpHistory(sAddr); + return; + } if (sCmd == "ds") { // transform a "ds" command into a "d desc" command sCmd = 'd'; sLen = sAddr; @@ -4326,57 +4406,7 @@ if (DEBUGGER) { this.stopCPU(); return; } - var sMore = ""; - var cLines = 10; - var iHistory = this.iOpcodeHistory; - var aHistory = this.aOpcodeHistory; - if (aHistory.length) { - var n = (sCount === undefined? this.nextHistory : +sCount); - if (isNaN(n)) - n = cLines; - else - sMore = "more "; - if (n > aHistory.length) { - this.println("note: only " + aHistory.length + " available"); - n = aHistory.length; - } - iHistory -= n; - if (iHistory < 0) { - if (aHistory[aHistory.length - 1][1] != null) { - iHistory += aHistory.length; - } else { - n = iHistory + n; - iHistory = 0; - } - } - if (sCount !== undefined) { - this.println(n + " instructions earlier:"); - } - while (cLines && iHistory != this.iOpcodeHistory) { - var dbgAddr = aHistory[iHistory++]; - if (dbgAddr.sel == null) break; - /* - * We must create a new dbgAddr from the address we obtained from aHistory, because dbgAddr - * was a reference, not a copy, and we don't want getInstruction() modifying the original. - */ - dbgAddr = this.newAddr(dbgAddr.off, dbgAddr.sel, dbgAddr.addr); - this.println(this.getInstruction(dbgAddr, "history", n--)); - /* - * If there was an OPERAND or ADDRESS override on the previous instruction, getInstruction() - * will have automatically disassembled the next instruction, so skip one more history entry. - */ - if (dbgAddr.fOverride) { - iHistory++; n--; - } - if (iHistory >= aHistory.length) iHistory = 0; - this.nextHistory = n; - cLines--; - } - } - if (cLines == 10) { - this.println("no " + sMore + "history available"); - this.nextHistory = undefined; - } + this.dumpHistory(sCount); }; /** diff --git a/modules/pcjs/lib/x86ops.js b/modules/pcjs/lib/x86ops.js index cabaae027..ae74b1e06 100644 --- a/modules/pcjs/lib/x86ops.js +++ b/modules/pcjs/lib/x86ops.js @@ -3802,11 +3802,10 @@ X86.opHLT = function HLT() this.intFlags |= X86.INTFLAG.HALT; this.nStepCycles -= 2; /* - * If a Debugger is present AND Debugger checks are enabled (eg, one or more breakpoints are set, - * or the global DEBUG flag is set, etc), then we REALLY halt the CPU, on the theory that whoever's - * using the Debugger would like to see HLTs. + * If a Debugger is present and the HALT message category is enabled, then we REALLY halt the CPU, + * on the theory that whoever's using the Debugger would like to see HLTs. */ - if (DEBUGGER && this.dbg && this.dbg.checksEnabled(true)) { + if (DEBUGGER && this.dbg && this.messageEnabled(Messages.HALT)) { this.advanceIP(-1); // this is purely for the Debugger's benefit, to show the HLT this.stopCPU(); return; diff --git a/modules/pcjs/lib/x86seg.js b/modules/pcjs/lib/x86seg.js index 1eeac1ece..d17f0a545 100644 --- a/modules/pcjs/lib/x86seg.js +++ b/modules/pcjs/lib/x86seg.js @@ -44,7 +44,7 @@ if (typeof module !== 'undefined') { * @property {number} sel * @property {number} limit (in protected-mode, this comes from descriptor word 0x0) * @property {number} base (in protected-mode, this comes from descriptor word 0x2) - * @property {number} acc (in protected-mode, this comes from descriptor word 0x4, masked with 0xff00; bits 0-7 supplement base bits 16-23) + * @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) * * TODO: Determine what good, if any, these class annotations are for either an IDE like WebStorm or a tool like @@ -409,102 +409,6 @@ X86Seg.prototype.checkWriteProtDisallowed = function checkWriteProtDisallowed(of return X86.ADDR_INVALID; }; -/** - * switchTSS(selNew, fNest) - * - * Implements TSS (Task State Segment) task switching. - * - * NOTES: This typically occurs during double-fault processing, because the IDT entry for DF_FAULT normally - * contains a task gate. Interestingly, if we force a GP_FAULT to occur at a sufficiently early point in the - * OS/2 1.0 initialization code, OS/2 does a nice job of displaying the GP fault and then shutting down: - * - * 0090:067B FB STI - * 0090:067C EBFD JMP 067B - * - * but it may not have yet reprogrammed the master PIC to re-vector hardware interrupts to IDT entries 0x50-0x57, - * so when the next timer interrupt (IRQ 0) occurs, it vectors through IDT entry 0x08, which is the DF_FAULT - * vector. A spurious double-fault is generated, and a clean shutdown turns into a messy crash. - * - * Of course, that all could have been avoided if IBM had heeded Intel's advice and not used Intel-reserved IDT - * entries for PC interrupts. - * - * TODO: Add 80386 TSS support (including CR3 support). - * - * @this {X86Seg} - * @param {number} selNew - * @param {boolean} fNest is true if nesting, false if un-nesting - * @return {boolean} true if successful, false if error - */ -X86Seg.prototype.switchTSS = function switchTSS(selNew, fNest) -{ - var cpu = this.cpu; - cpu.assert(this === cpu.segCS); - - var addrOld = cpu.segTSS.base; - var cplOld = this.cpl; - var selOld = cpu.segTSS.sel; - if (!fNest) { - if (cpu.segTSS.type != X86.DESC.ACC.TYPE.TSS_BUSY) { - X86.fnFault.call(cpu, X86.EXCEPTION.TS_FAULT, selNew, true); - return false; - } - cpu.setShort(cpu.segTSS.addrDesc + X86.DESC.ACC.OFFSET, (cpu.segTSS.acc & ~X86.DESC.ACC.TYPE.TSS_BUSY) | X86.DESC.ACC.TYPE.TSS); - } - if (cpu.segTSS.load(selNew) === X86.ADDR_INVALID) { - return false; - } - var addrNew = cpu.segTSS.base; - if (DEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(Messages.TSS)) { - this.dbg.message((fNest? "Task switch" : "Task return") + ": TR " + str.toHexWord(selOld) + " (%" + str.toHex(addrOld, 6) + "), new TR " + str.toHexWord(selNew) + " (%" + str.toHex(addrNew, 6) + ")"); - } - if (fNest) { - if (cpu.segTSS.type == X86.DESC.ACC.TYPE.TSS_BUSY) { - X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, selNew, true); - return false; - } - cpu.setShort(cpu.segTSS.addrDesc + X86.DESC.ACC.OFFSET, cpu.segTSS.acc |= X86.DESC.ACC.TYPE.TSS_BUSY); - cpu.segTSS.type = X86.DESC.ACC.TYPE.TSS_BUSY; - } - cpu.setShort(addrOld + X86.TSS.TASK_IP, cpu.getIP()); - cpu.setShort(addrOld + X86.TSS.TASK_PS, cpu.getPS()); - cpu.setShort(addrOld + X86.TSS.TASK_AX, cpu.regEAX); - cpu.setShort(addrOld + X86.TSS.TASK_CX, cpu.regECX); - cpu.setShort(addrOld + X86.TSS.TASK_DX, cpu.regEDX); - cpu.setShort(addrOld + X86.TSS.TASK_BX, cpu.regEBX); - cpu.setShort(addrOld + X86.TSS.TASK_SP, cpu.getSP()); - cpu.setShort(addrOld + X86.TSS.TASK_BP, cpu.regEBP); - cpu.setShort(addrOld + X86.TSS.TASK_SI, cpu.regESI); - cpu.setShort(addrOld + X86.TSS.TASK_DI, cpu.regEDI); - cpu.setShort(addrOld + X86.TSS.TASK_ES, cpu.segES.sel); - cpu.setShort(addrOld + X86.TSS.TASK_CS, cpu.segCS.sel); - cpu.setShort(addrOld + X86.TSS.TASK_SS, cpu.segSS.sel); - cpu.setShort(addrOld + X86.TSS.TASK_DS, cpu.segDS.sel); - var offSS = X86.TSS.TASK_SS; - var offSP = X86.TSS.TASK_SP; - cpu.setPS(cpu.getShort(addrNew + X86.TSS.TASK_PS) | (fNest? X86.PS.NT : 0)); - cpu.assert(!fNest || !!(cpu.regPS & X86.PS.NT)); - cpu.regEAX = cpu.getShort(addrNew + X86.TSS.TASK_AX); - cpu.regECX = cpu.getShort(addrNew + X86.TSS.TASK_CX); - cpu.regEDX = cpu.getShort(addrNew + X86.TSS.TASK_DX); - cpu.regEBX = cpu.getShort(addrNew + X86.TSS.TASK_BX); - cpu.regEBP = cpu.getShort(addrNew + X86.TSS.TASK_BP); - cpu.regESI = cpu.getShort(addrNew + X86.TSS.TASK_SI); - cpu.regEDI = cpu.getShort(addrNew + X86.TSS.TASK_DI); - cpu.segES.load(cpu.getShort(addrNew + X86.TSS.TASK_ES)); - cpu.segDS.load(cpu.getShort(addrNew + X86.TSS.TASK_DS)); - cpu.setCSIP(cpu.getShort(addrNew + X86.TSS.TASK_IP), cpu.getShort(addrNew + X86.TSS.TASK_CS)); - if (this.cpl < cplOld) { - offSP = (this.cpl << 2) + X86.TSS.CPL0_SP; - offSS = offSP + 2; - } - cpu.setSS(cpu.getShort(addrNew + offSS), true); - cpu.setSP(cpu.getShort(addrNew + offSP)); - cpu.segLDT.load(cpu.getShort(addrNew + X86.TSS.TASK_LDT)); - if (fNest) cpu.setShort(addrNew + X86.TSS.PREV_TSS, selOld); - cpu.regCR0 |= X86.CR0.MSW.TS; - return true; -}; - /** * loadAcc(sel, fGDT) * @@ -567,7 +471,7 @@ X86Seg.prototype.loadDesc6 = function(addrDesc, sel) this.addrDesc = addrDesc; this.updateMode(true); - this.messageSeg(sel, base, limit, acc); + this.messageSeg(sel, base, limit, this.type); return base; }; @@ -650,6 +554,14 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress) } fGate = false; } + else if (type == X86.DESC.ACC.TYPE.TSS) { + if (!this.switchTSS(sel, true)) { + base = addrDesc = X86.ADDR_INVALID; + break; + } + if (DEBUG) cpu.stopCPU(); + return this.base; + } else if (type == X86.DESC.ACC.TYPE.GATE_CALL) { fGate = true; regPSMask = ~0; @@ -807,10 +719,106 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress) this.updateMode(true); break; } - if (!fSuppress) this.messageSeg(sel, base, limit, acc, ext); + if (!fSuppress) this.messageSeg(sel, base, limit, type, ext); return base; }; +/** + * switchTSS(selNew, fNest) + * + * Implements TSS (Task State Segment) task switching. + * + * NOTES: This typically occurs during double-fault processing, because the IDT entry for DF_FAULT normally + * contains a task gate. Interestingly, if we force a GP_FAULT to occur at a sufficiently early point in the + * OS/2 1.0 initialization code, OS/2 does a nice job of displaying the GP fault and then shutting down: + * + * 0090:067B FB STI + * 0090:067C EBFD JMP 067B + * + * but it may not have yet reprogrammed the master PIC to re-vector hardware interrupts to IDT entries 0x50-0x57, + * so when the next timer interrupt (IRQ 0) occurs, it vectors through IDT entry 0x08, which is the DF_FAULT + * vector. A spurious double-fault is generated, and a clean shutdown turns into a messy crash. + * + * Of course, that all could have been avoided if IBM had heeded Intel's advice and not used Intel-reserved IDT + * entries for PC interrupts. + * + * TODO: Add 80386 TSS support (including CR3 support). + * + * @this {X86Seg} + * @param {number} selNew + * @param {boolean} fNest is true if nesting, false if un-nesting + * @return {boolean} true if successful, false if error + */ +X86Seg.prototype.switchTSS = function switchTSS(selNew, fNest) +{ + var cpu = this.cpu; + cpu.assert(this === cpu.segCS); + + var addrOld = cpu.segTSS.base; + var cplOld = this.cpl; + var selOld = cpu.segTSS.sel; + if (!fNest) { + if (cpu.segTSS.type != X86.DESC.ACC.TYPE.TSS_BUSY) { + X86.fnFault.call(cpu, X86.EXCEPTION.TS_FAULT, selNew, true); + return false; + } + cpu.setShort(cpu.segTSS.addrDesc + X86.DESC.ACC.OFFSET, (cpu.segTSS.acc & ~X86.DESC.ACC.TYPE.TSS_BUSY) | X86.DESC.ACC.TYPE.TSS); + } + if (cpu.segTSS.load(selNew) === X86.ADDR_INVALID) { + return false; + } + var addrNew = cpu.segTSS.base; + if (DEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(Messages.TSS)) { + this.dbg.message((fNest? "Task switch" : "Task return") + ": TR " + str.toHexWord(selOld) + " (%" + str.toHex(addrOld, 6) + "), new TR " + str.toHexWord(selNew) + " (%" + str.toHex(addrNew, 6) + ")"); + } + if (fNest) { + if (cpu.segTSS.type == X86.DESC.ACC.TYPE.TSS_BUSY) { + X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, selNew, true); + return false; + } + cpu.setShort(cpu.segTSS.addrDesc + X86.DESC.ACC.OFFSET, cpu.segTSS.acc |= X86.DESC.ACC.TYPE.TSS_BUSY); + cpu.segTSS.type = X86.DESC.ACC.TYPE.TSS_BUSY; + } + cpu.setShort(addrOld + X86.TSS.TASK_IP, cpu.getIP()); + cpu.setShort(addrOld + X86.TSS.TASK_PS, cpu.getPS()); + cpu.setShort(addrOld + X86.TSS.TASK_AX, cpu.regEAX); + cpu.setShort(addrOld + X86.TSS.TASK_CX, cpu.regECX); + cpu.setShort(addrOld + X86.TSS.TASK_DX, cpu.regEDX); + cpu.setShort(addrOld + X86.TSS.TASK_BX, cpu.regEBX); + cpu.setShort(addrOld + X86.TSS.TASK_SP, cpu.getSP()); + cpu.setShort(addrOld + X86.TSS.TASK_BP, cpu.regEBP); + cpu.setShort(addrOld + X86.TSS.TASK_SI, cpu.regESI); + cpu.setShort(addrOld + X86.TSS.TASK_DI, cpu.regEDI); + cpu.setShort(addrOld + X86.TSS.TASK_ES, cpu.segES.sel); + cpu.setShort(addrOld + X86.TSS.TASK_CS, cpu.segCS.sel); + cpu.setShort(addrOld + X86.TSS.TASK_SS, cpu.segSS.sel); + cpu.setShort(addrOld + X86.TSS.TASK_DS, cpu.segDS.sel); + var offSS = X86.TSS.TASK_SS; + var offSP = X86.TSS.TASK_SP; + cpu.setPS(cpu.getShort(addrNew + X86.TSS.TASK_PS) | (fNest? X86.PS.NT : 0)); + cpu.assert(!fNest || !!(cpu.regPS & X86.PS.NT)); + cpu.regEAX = cpu.getShort(addrNew + X86.TSS.TASK_AX); + cpu.regECX = cpu.getShort(addrNew + X86.TSS.TASK_CX); + cpu.regEDX = cpu.getShort(addrNew + X86.TSS.TASK_DX); + cpu.regEBX = cpu.getShort(addrNew + X86.TSS.TASK_BX); + cpu.regEBP = cpu.getShort(addrNew + X86.TSS.TASK_BP); + cpu.regESI = cpu.getShort(addrNew + X86.TSS.TASK_SI); + cpu.regEDI = cpu.getShort(addrNew + X86.TSS.TASK_DI); + cpu.segES.load(cpu.getShort(addrNew + X86.TSS.TASK_ES)); + cpu.segDS.load(cpu.getShort(addrNew + X86.TSS.TASK_DS)); + cpu.setCSIP(cpu.getShort(addrNew + X86.TSS.TASK_IP), cpu.getShort(addrNew + X86.TSS.TASK_CS)); + if (this.cpl < cplOld) { + offSP = (this.cpl << 2) + X86.TSS.CPL0_SP; + offSS = offSP + 2; + } + cpu.setSS(cpu.getShort(addrNew + offSS), true); + cpu.setSP(cpu.getShort(addrNew + offSP)); + cpu.segLDT.load(cpu.getShort(addrNew + X86.TSS.TASK_LDT)); + if (fNest) cpu.setShort(addrNew + X86.TSS.PREV_TSS, selOld); + cpu.regCR0 |= X86.CR0.MSW.TS; + return true; +}; + /** * setBase(addr) * @@ -1008,23 +1016,23 @@ X86Seg.prototype.updateMode = function(fLoad, fProt) }; /** - * messageSeg(sel, base, limit, acc, ext) + * messageSeg(sel, base, limit, type, ext) * * @this {X86Seg} * @param {number} sel * @param {number} base * @param {number} limit - * @param {number} acc + * @param {number} type * @param {number} [ext] */ -X86Seg.prototype.messageSeg = function(sel, base, limit, acc, ext) +X86Seg.prototype.messageSeg = function(sel, base, limit, type, ext) { if (DEBUG) { if (DEBUGGER && this.dbg && this.dbg.messageEnabled(Messages.SEG)) { var ch = (this.sName.length < 3? " " : ""); var sDPL = " dpl=" + this.dpl; if (this.id == X86Seg.ID.CODE) sDPL += " cpl=" + this.cpl; - this.dbg.message("loadSeg(" + this.sName + "):" + ch + "sel=" + str.toHexWord(sel) + " base=" + str.toHex(base) + " limit=" + str.toHexWord(limit) + " acc=" + str.toHexWord(acc) + sDPL); + this.dbg.message("loadSeg(" + this.sName + "):" + ch + "sel=" + str.toHexWord(sel) + " base=" + str.toHex(base) + " limit=" + str.toHexWord(limit) + " type=" + str.toHexWord(type) + sDPL, true); } this.cpu.assert(/* base !== X86.ADDR_INVALID && */ (this.cpu.model >= X86.MODEL_80386 || !ext || ext == X86.DESC.EXT.AVAIL)); }