A few Debugger fixes whilst tracking down TSS issues
This commit is contained in:
parent
a28fa95d25
commit
30d0d22e41
3 changed files with 235 additions and 198 deletions
|
|
@ -1424,7 +1424,7 @@ if (DEBUGGER) {
|
||||||
* should be done only as a last resort.
|
* should be done only as a last resort.
|
||||||
*
|
*
|
||||||
* @param {number|null|undefined} sel
|
* @param {number|null|undefined} sel
|
||||||
* @return {X86Seg} seg
|
* @return {X86Seg|null} seg
|
||||||
*/
|
*/
|
||||||
Debugger.prototype.getSegment = function(sel)
|
Debugger.prototype.getSegment = function(sel)
|
||||||
{
|
{
|
||||||
|
|
@ -1436,6 +1436,7 @@ if (DEBUGGER) {
|
||||||
if (sel === this.cpu.getFS()) return this.cpu.segFS;
|
if (sel === this.cpu.getFS()) return this.cpu.segFS;
|
||||||
if (sel === this.cpu.getGS()) return this.cpu.segGS;
|
if (sel === this.cpu.getGS()) return this.cpu.segGS;
|
||||||
}
|
}
|
||||||
|
if (this.nBreakSuppress) return null;
|
||||||
var seg = new X86Seg(this.cpu, X86Seg.ID.DEBUG, "DBG");
|
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
|
* 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;
|
var addr = dbgAddr.addr;
|
||||||
if (addr == null) {
|
if (addr == null) {
|
||||||
|
addr = X86.ADDR_INVALID;
|
||||||
var seg = this.getSegment(dbgAddr.sel);
|
var seg = this.getSegment(dbgAddr.sel);
|
||||||
if (!fWrite) {
|
if (seg) {
|
||||||
addr = seg.checkRead(dbgAddr.off, cb || 1, true);
|
if (!fWrite) {
|
||||||
} else {
|
addr = seg.checkRead(dbgAddr.off, cb || 1, true);
|
||||||
addr = seg.checkWrite(dbgAddr.off, cb || 1, true);
|
} else {
|
||||||
|
addr = seg.checkWrite(dbgAddr.off, cb || 1, true);
|
||||||
|
}
|
||||||
|
dbgAddr.addr = addr;
|
||||||
}
|
}
|
||||||
dbgAddr.addr = addr;
|
|
||||||
}
|
}
|
||||||
return addr;
|
return addr;
|
||||||
};
|
};
|
||||||
|
|
@ -1639,8 +1643,8 @@ if (DEBUGGER) {
|
||||||
Debugger.prototype.checkLimit = function(dbgAddr)
|
Debugger.prototype.checkLimit = function(dbgAddr)
|
||||||
{
|
{
|
||||||
if (dbgAddr.sel != null) {
|
if (dbgAddr.sel != null) {
|
||||||
var limit = this.getSegment(dbgAddr.sel).limit;
|
var seg = this.getSegment(dbgAddr.sel);
|
||||||
if (dbgAddr.off > limit) {
|
if (!seg || dbgAddr.off > seg.limit) {
|
||||||
dbgAddr.off = 0;
|
dbgAddr.off = 0;
|
||||||
dbgAddr.addr = null;
|
dbgAddr.addr = null;
|
||||||
}
|
}
|
||||||
|
|
@ -1816,8 +1820,8 @@ if (DEBUGGER) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var seg = this.getSegment(sel);
|
var seg = this.getSegment(sel);
|
||||||
|
this.println("dumpDesc(" + str.toHexWord(seg? seg.sel : sel) + "): %" + str.toHex(seg? seg.addrDesc : null, this.cchAddr));
|
||||||
this.println("dumpDesc(" + str.toHexWord(seg.sel) + "): %" + str.toHex(seg.addrDesc, this.cchAddr));
|
if (!seg) return;
|
||||||
|
|
||||||
var sType;
|
var sType;
|
||||||
var fGate = false;
|
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
|
* 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.
|
* 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);
|
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 = "";
|
var sDump = "";
|
||||||
for (var sField in Debugger.aTSSFields) {
|
for (var sField in Debugger.aTSSFields) {
|
||||||
|
|
@ -2829,6 +2895,12 @@ if (DEBUGGER) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.aBreakWrite = ["write"];
|
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.
|
* or history data (see checkInstruction), since we might not actually execute the current instruction.
|
||||||
*/
|
*/
|
||||||
var fBreak = false;
|
var fBreak = false;
|
||||||
addr = this.mapBreakpoint(addr);
|
if (!this.nBreakSuppress++) {
|
||||||
for (var i = 1; i < aBreak.length; i++) {
|
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
|
* 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,
|
* 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).
|
* unless this is a breakpoint on a linear address (as indicated by a -1 offset).
|
||||||
*/
|
*/
|
||||||
if (dbgAddrBreak.off != -1) dbgAddrBreak.addr = null;
|
if (dbgAddrBreak.off != -1) dbgAddrBreak.addr = null;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We used to calculate the linear address of the breakpoint at the time the
|
* 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)
|
* 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).
|
* would still work as intended if the mode changed later (eg, to protected-mode).
|
||||||
*
|
*
|
||||||
* However, that created difficulties setting protected-mode breakpoints in segments
|
* However, that created difficulties setting protected-mode breakpoints in segments
|
||||||
* that might not be defined yet, or that could move in physical memory.
|
* 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,
|
* 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.
|
* use the physical address of the real-mode memory location instead.
|
||||||
*/
|
*/
|
||||||
if (addr == this.mapBreakpoint(this.getAddr(dbgAddrBreak))) {
|
if (addr == this.mapBreakpoint(this.getAddr(dbgAddrBreak))) {
|
||||||
if (dbgAddrBreak.fTempBreak) {
|
if (dbgAddrBreak.fTempBreak) {
|
||||||
this.findBreakpoint(aBreak, dbgAddrBreak, true);
|
this.findBreakpoint(aBreak, dbgAddrBreak, true);
|
||||||
} else if (!fTemp) {
|
} else if (!fTemp) {
|
||||||
this.println("breakpoint hit: " + this.hexAddr(dbgAddrBreak) + " (" + aBreak[0] + ")");
|
this.println("breakpoint hit: " + this.hexAddr(dbgAddrBreak) + " (" + aBreak[0] + ")");
|
||||||
|
}
|
||||||
|
fBreak = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
fBreak = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.nBreakSuppress--;
|
||||||
return fBreak;
|
return fBreak;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -4167,9 +4242,10 @@ if (DEBUGGER) {
|
||||||
sDumpers += ",state,symbols";
|
sDumpers += ",state,symbols";
|
||||||
this.println("\ndump commands:");
|
this.println("\ndump commands:");
|
||||||
this.println("\tdb [a] [#] dump # bytes at address a");
|
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("\tdw [a] [#] dump # words at address a");
|
||||||
this.println("\tdd [a] [#] dump # dwords 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);
|
if (sDumpers.length) this.println("dump extensions:\n\t" + sDumpers);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -4181,6 +4257,10 @@ if (DEBUGGER) {
|
||||||
this.dumpSymbols();
|
this.dumpSymbols();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (sCmd == "dh") {
|
||||||
|
this.dumpHistory(sAddr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (sCmd == "ds") { // transform a "ds" command into a "d desc" command
|
if (sCmd == "ds") { // transform a "ds" command into a "d desc" command
|
||||||
sCmd = 'd';
|
sCmd = 'd';
|
||||||
sLen = sAddr;
|
sLen = sAddr;
|
||||||
|
|
@ -4326,57 +4406,7 @@ if (DEBUGGER) {
|
||||||
this.stopCPU();
|
this.stopCPU();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var sMore = "";
|
this.dumpHistory(sCount);
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -3802,11 +3802,10 @@ X86.opHLT = function HLT()
|
||||||
this.intFlags |= X86.INTFLAG.HALT;
|
this.intFlags |= X86.INTFLAG.HALT;
|
||||||
this.nStepCycles -= 2;
|
this.nStepCycles -= 2;
|
||||||
/*
|
/*
|
||||||
* If a Debugger is present AND Debugger checks are enabled (eg, one or more breakpoints are set,
|
* If a Debugger is present and the HALT message category is enabled, then we REALLY halt the CPU,
|
||||||
* or the global DEBUG flag is set, etc), then we REALLY halt the CPU, on the theory that whoever's
|
* on the theory that whoever's using the Debugger would like to see HLTs.
|
||||||
* 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.advanceIP(-1); // this is purely for the Debugger's benefit, to show the HLT
|
||||||
this.stopCPU();
|
this.stopCPU();
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ if (typeof module !== 'undefined') {
|
||||||
* @property {number} sel
|
* @property {number} sel
|
||||||
* @property {number} limit (in protected-mode, this comes from descriptor word 0x0)
|
* @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} 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)
|
* @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
|
* 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;
|
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)
|
* loadAcc(sel, fGDT)
|
||||||
*
|
*
|
||||||
|
|
@ -567,7 +471,7 @@ X86Seg.prototype.loadDesc6 = function(addrDesc, sel)
|
||||||
this.addrDesc = addrDesc;
|
this.addrDesc = addrDesc;
|
||||||
this.updateMode(true);
|
this.updateMode(true);
|
||||||
|
|
||||||
this.messageSeg(sel, base, limit, acc);
|
this.messageSeg(sel, base, limit, this.type);
|
||||||
|
|
||||||
return base;
|
return base;
|
||||||
};
|
};
|
||||||
|
|
@ -650,6 +554,14 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
|
||||||
}
|
}
|
||||||
fGate = false;
|
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) {
|
else if (type == X86.DESC.ACC.TYPE.GATE_CALL) {
|
||||||
fGate = true;
|
fGate = true;
|
||||||
regPSMask = ~0;
|
regPSMask = ~0;
|
||||||
|
|
@ -807,10 +719,106 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
|
||||||
this.updateMode(true);
|
this.updateMode(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!fSuppress) this.messageSeg(sel, base, limit, acc, ext);
|
if (!fSuppress) this.messageSeg(sel, base, limit, type, ext);
|
||||||
return base;
|
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)
|
* 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}
|
* @this {X86Seg}
|
||||||
* @param {number} sel
|
* @param {number} sel
|
||||||
* @param {number} base
|
* @param {number} base
|
||||||
* @param {number} limit
|
* @param {number} limit
|
||||||
* @param {number} acc
|
* @param {number} type
|
||||||
* @param {number} [ext]
|
* @param {number} [ext]
|
||||||
*/
|
*/
|
||||||
X86Seg.prototype.messageSeg = function(sel, base, limit, acc, ext)
|
X86Seg.prototype.messageSeg = function(sel, base, limit, type, ext)
|
||||||
{
|
{
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(Messages.SEG)) {
|
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(Messages.SEG)) {
|
||||||
var ch = (this.sName.length < 3? " " : "");
|
var ch = (this.sName.length < 3? " " : "");
|
||||||
var sDPL = " dpl=" + this.dpl;
|
var sDPL = " dpl=" + this.dpl;
|
||||||
if (this.id == X86Seg.ID.CODE) sDPL += " cpl=" + this.cpl;
|
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));
|
this.cpu.assert(/* base !== X86.ADDR_INVALID && */ (this.cpu.model >= X86.MODEL_80386 || !ext || ext == X86.DESC.EXT.AVAIL));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue