Revamped dump extensions to accept argument arrays, added probeDesc() for Debugger-safe segment descriptor loading
This commit is contained in:
parent
4bdbb5b085
commit
ee2f5ff761
9 changed files with 340 additions and 219 deletions
|
|
@ -1060,8 +1060,8 @@ ChipSet.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
dbg.messageDump(Messages.PIC, function onDumpPIC() {
|
||||
chipset.dumpPIC();
|
||||
});
|
||||
dbg.messageDump(Messages.TIMER, function onDumpTimer(sParm) {
|
||||
chipset.dumpTimer(sParm);
|
||||
dbg.messageDump(Messages.TIMER, function onDumpTimer(asArgs) {
|
||||
chipset.dumpTimer(asArgs);
|
||||
});
|
||||
dbg.messageDump(Messages.CMOS, function onDumpCMOS() {
|
||||
chipset.dumpCMOS();
|
||||
|
|
@ -2344,16 +2344,17 @@ ChipSet.prototype.dumpPIC = function()
|
|||
};
|
||||
|
||||
/**
|
||||
* dumpTimer(sParm)
|
||||
* dumpTimer(asArgs)
|
||||
*
|
||||
* Use "d timer" to dump all timers, or "d timer n" to dump only timer n.
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {string} [sParm]
|
||||
* @param {Array.<string>} asArgs
|
||||
*/
|
||||
ChipSet.prototype.dumpTimer = function(sParm)
|
||||
ChipSet.prototype.dumpTimer = function(asArgs)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
var sParm = asArgs[0];
|
||||
var nTimer = (sParm? +sParm : null);
|
||||
for (var iTimer = 0; iTimer < this.aTimers.length; iTimer++) {
|
||||
if (nTimer != null && iTimer != nTimer) continue;
|
||||
|
|
@ -2909,7 +2910,7 @@ ChipSet.prototype.advanceDMA = function(channel, fInit)
|
|||
channel.sAddrDebug = str.toHex(addr >> 4, 4) + ":" + str.toHex(addr & 0xf, 4);
|
||||
if (this.messageEnabled(this.messageBitsDMA(iDMAChannel)) && channel.type != ChipSet.DMA_MODE.TYPE_WRITE) {
|
||||
this.printMessage("advanceDMA(" + iDMAChannel + ") transferring " + channel.cbDebug + " bytes from " + channel.sAddrDebug, true);
|
||||
this.dbg.doDump("db", channel.sAddrDebug, 'l', channel.cbDebug);
|
||||
this.dbg.doDump(["db", channel.sAddrDebug, 'l', channel.cbDebug]);
|
||||
}
|
||||
}
|
||||
if (channel.type == ChipSet.DMA_MODE.TYPE_WRITE) {
|
||||
|
|
@ -3024,7 +3025,7 @@ ChipSet.prototype.updateDMA = function(channel)
|
|||
|
||||
if (DEBUG && this.messageEnabled(this.messageBitsDMA(iDMAChannel)) && channel.type == ChipSet.DMA_MODE.TYPE_WRITE && channel.sAddrDebug) {
|
||||
this.printMessage("updateDMA(" + iDMAChannel + ") transferred " + channel.cbDebug + " bytes to " + channel.sAddrDebug, true);
|
||||
this.dbg.doDump("db", channel.sAddrDebug, 'l', channel.cbDebug);
|
||||
this.dbg.doDump(["db", channel.sAddrDebug, 'l', channel.cbDebug]);
|
||||
}
|
||||
|
||||
if (channel.done) {
|
||||
|
|
@ -3172,7 +3173,7 @@ ChipSet.prototype.outPICLo = function(iPIC, bOut, addrFrom)
|
|||
this.checkIRR();
|
||||
} else {
|
||||
if (DEBUG && this.messageEnabled(Messages.PIC | Messages.WARN)) {
|
||||
this.printMessage("outPIC" + iPIC + '(' + str.toHexByte(pic.port) + "): unexpected EOI command, IRQ " + nIRQ + " not in service", true);
|
||||
this.printMessage("outPIC" + iPIC + '(' + str.toHexByte(pic.port) + "): unexpected EOI command, IRQ " + nIRQ + " not in service", true, true);
|
||||
if (!SAMPLER && MAXDEBUG) this.dbg.stopCPU();
|
||||
}
|
||||
}
|
||||
|
|
@ -4966,7 +4967,7 @@ ChipSet.prototype.outCoprocReset = function(port, bOut, addrFrom)
|
|||
ChipSet.prototype.intBIOSRTC = function(addr)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
if (this.messageEnabled(Messages.RTC) && this.dbg.messageInt(Interrupts.RTC, addr)) {
|
||||
if (this.messageEnabled(Messages.INT) && this.dbg.messageInt(Interrupts.RTC, addr)) {
|
||||
/*
|
||||
* By computing AH now, we get the incoming AH value; if we computed it below, along with
|
||||
* the rest of the register values, we'd get the outgoing AH value, which is not what we want.
|
||||
|
|
|
|||
|
|
@ -259,12 +259,19 @@ if (DEBUGGER) {
|
|||
0x13: Messages.FDC,
|
||||
0x15: Messages.CHIPSET,
|
||||
0x16: Messages.KEYBOARD,
|
||||
// 0x1a: Messages.RTC, // ChipSet contains its own custom messageInt() handler for the RTC
|
||||
0x1c: Messages.TIMER,
|
||||
// 0x1A: Messages.RTC, // ChipSet contains its own custom messageInt() handler for the RTC
|
||||
0x1C: Messages.TIMER,
|
||||
0x21: Messages.DOS,
|
||||
0x33: Messages.MOUSE
|
||||
};
|
||||
|
||||
/*
|
||||
* Information regarding "annoying" interrupts (which aren't annoying so much as too frequent);
|
||||
* note that some of these can still be enabled if you really want them (eg, RTC can be turned on
|
||||
* with RTC messages, ALT_TIMER with TIMER messages, etc).
|
||||
*/
|
||||
Debugger.INT_ANNOYING = [Interrupts.RTC, Interrupts.ALT_TIMER, Interrupts.DOS_IDLE, Interrupts.DOS_NETBIOS, Interrupts.ALT_VIDEO];
|
||||
|
||||
Debugger.COMMANDS = {
|
||||
'?': "help/print",
|
||||
'a [#]': "assemble",
|
||||
|
|
@ -1258,11 +1265,11 @@ 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); });
|
||||
this.messageDump(Messages.BUS, function onDumpBus(asArgs) { dbg.dumpBus(asArgs); });
|
||||
this.messageDump(Messages.MEM, function onDumpMem(asArgs) { dbg.dumpMem(asArgs); });
|
||||
this.messageDump(Messages.DESC, function onDumpDesc(asArgs) { dbg.dumpDesc(asArgs); });
|
||||
this.messageDump(Messages.TSS, function onDumpTSS(asArgs) { dbg.dumpTSS(asArgs); });
|
||||
this.messageDump(Messages.DOS, function onDumpDOS(asArgs) { dbg.dumpDOS(asArgs); });
|
||||
|
||||
if (Interrupts.WINDBG.ENABLED) {
|
||||
this.fWinDbg = null;
|
||||
|
|
@ -1633,7 +1640,7 @@ if (DEBUGGER) {
|
|||
seg.limit = 0xffff; // although an ACTUAL real-mode segment load would not modify the limit,
|
||||
seg.offMax = 0x10000; // proper segDebugger operation requires that we update the limit ourselves
|
||||
} else {
|
||||
seg.loadProt(sel);
|
||||
seg.probeDesc(sel);
|
||||
}
|
||||
return seg;
|
||||
};
|
||||
|
|
@ -1659,6 +1666,9 @@ if (DEBUGGER) {
|
|||
if (addr == null) {
|
||||
addr = X86.ADDR_INVALID;
|
||||
if (dbgAddr) {
|
||||
/*
|
||||
* TODO: We should try to cache the seg inside dbgAddr, to avoid unnecessary calls to getSegment().
|
||||
*/
|
||||
var seg = this.getSegment(dbgAddr.sel, dbgAddr.type);
|
||||
if (seg) {
|
||||
if (!fWrite) {
|
||||
|
|
@ -1987,18 +1997,19 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* dumpDOS(sMCB)
|
||||
* dumpDOS(asArgs)
|
||||
*
|
||||
* Dumps DOS MCBs (Memory Control Blocks).
|
||||
*
|
||||
* TODO: Add some code to detect the current version of DOS (if any) and locate the first MCB automatically.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} [sMCB]
|
||||
* @param {Array.<string>} asArgs
|
||||
*/
|
||||
Debugger.prototype.dumpDOS = function(sMCB)
|
||||
Debugger.prototype.dumpDOS = function(asArgs)
|
||||
{
|
||||
var mcb;
|
||||
var sMCB = asArgs[0];
|
||||
if (sMCB) {
|
||||
mcb = this.parseValue(sMCB);
|
||||
}
|
||||
|
|
@ -2044,35 +2055,37 @@ if (DEBUGGER) {
|
|||
|
||||
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]);
|
||||
if (block.type !== Memory.TYPE.NONE) {
|
||||
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)
|
||||
* dumpBus(asArgs)
|
||||
*
|
||||
* Dumps Bus allocations.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} [sAddr] (optional block address)
|
||||
* @param {Array.<string>} asArgs (asArgs[0] is an optional block address)
|
||||
*/
|
||||
Debugger.prototype.dumpBus = function(sAddr)
|
||||
Debugger.prototype.dumpBus = function(asArgs)
|
||||
{
|
||||
this.dumpBlocks(this.cpu.aBusBlocks, sAddr);
|
||||
this.dumpBlocks(this.cpu.aBusBlocks, asArgs[0]);
|
||||
};
|
||||
|
||||
/**
|
||||
* dumpInfo(sAddr)
|
||||
* dumpInfo(asArgs)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string|undefined} sAddr
|
||||
* @param {Array.<string>} asArgs
|
||||
*/
|
||||
Debugger.prototype.dumpInfo = function(sAddr)
|
||||
Debugger.prototype.dumpInfo = function(asArgs)
|
||||
{
|
||||
var sInfo = "no information";
|
||||
if (BACKTRACK) {
|
||||
var sAddr = asArgs[0];
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR.CODE, true, true);
|
||||
if (dbgAddr) {
|
||||
var addr = this.getAddr(dbgAddr);
|
||||
|
|
@ -2097,21 +2110,21 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* dumpMem(sAddr)
|
||||
* dumpMem(asArgs)
|
||||
*
|
||||
* Dumps page allocations.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} [sAddr] (optional block address)
|
||||
* @param {Array.<string>} asArgs (asArgs[0] is an optional block address)
|
||||
*/
|
||||
Debugger.prototype.dumpMem = function(sAddr)
|
||||
Debugger.prototype.dumpMem = function(asArgs)
|
||||
{
|
||||
var aBlocks = this.cpu.aMemBlocks;
|
||||
if (aBlocks === this.cpu.aBusBlocks) {
|
||||
this.println("paging not enabled");
|
||||
return;
|
||||
}
|
||||
this.dumpBlocks(aBlocks, sAddr);
|
||||
this.dumpBlocks(aBlocks, asArgs[0]);
|
||||
};
|
||||
|
||||
Debugger.SYSDESCS = {
|
||||
|
|
@ -2130,23 +2143,25 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* dumpDesc(s)
|
||||
* dumpDesc(asArgs)
|
||||
*
|
||||
* Dumps a descriptor for the given selector.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} [s]
|
||||
* @param {Array.<string>} asArgs
|
||||
*/
|
||||
Debugger.prototype.dumpDesc = function(s)
|
||||
Debugger.prototype.dumpDesc = function(asArgs)
|
||||
{
|
||||
if (!s) {
|
||||
var sSel = asArgs[0];
|
||||
|
||||
if (!sSel) {
|
||||
this.println("no selector");
|
||||
return;
|
||||
}
|
||||
|
||||
var sel = this.parseValue(s);
|
||||
var sel = this.parseValue(sSel);
|
||||
if (sel === undefined) {
|
||||
this.println("invalid selector: " + s);
|
||||
this.println("invalid selector: " + sSel);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2360,22 +2375,24 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* dumpTSS(s)
|
||||
* dumpTSS(asArgs)
|
||||
*
|
||||
* This dumps a TSS using the given selector. If none is specified, the current TR is used.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} [s]
|
||||
* @param {Array.<string>} asArgs
|
||||
*/
|
||||
Debugger.prototype.dumpTSS = function(s)
|
||||
Debugger.prototype.dumpTSS = function(asArgs)
|
||||
{
|
||||
var seg;
|
||||
if (!s) {
|
||||
var sSel = asArgs[0];
|
||||
|
||||
if (!sSel) {
|
||||
seg = this.cpu.segTSS;
|
||||
} else {
|
||||
var sel = this.parseValue(s);
|
||||
var sel = this.parseValue(sSel);
|
||||
if (sel === undefined) {
|
||||
this.println("invalid task selector: " + s);
|
||||
this.println("invalid task selector: " + sSel);
|
||||
return;
|
||||
}
|
||||
seg = this.getSegment(sel, Debugger.ADDR.PROT);
|
||||
|
|
@ -2449,7 +2466,7 @@ if (DEBUGGER) {
|
|||
*
|
||||
* @this {Debugger}
|
||||
* @param {number} bitMessage is one Messages category flag
|
||||
* @param {function(string)} fnDumper is a function the Debugger can use to dump data for that category
|
||||
* @param {function(Array.<string>)} fnDumper is a function the Debugger can use to dump data for that category
|
||||
* @return {boolean} true if successfully registered, false if not
|
||||
*/
|
||||
Debugger.prototype.messageDump = function(bitMessage, fnDumper)
|
||||
|
|
@ -2765,7 +2782,7 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.message = function(sMessage, fAddress)
|
||||
{
|
||||
if (fAddress) {
|
||||
sMessage += " @" + this.hexOffset(this.cpu.getIP(), this.cpu.getCS()) + " (%" + str.toHex(this.cpu.regLIP, 6) + ")";
|
||||
sMessage += " @" + this.hexOffset(this.cpu.getIP(), this.cpu.getCS()) + " (%" + str.toHex(this.cpu.regLIP) + ")";
|
||||
}
|
||||
|
||||
if (this.sMessagePrev && sMessage == this.sMessagePrev) return;
|
||||
|
|
@ -2801,17 +2818,39 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.messageInt = function(nInt, addr, fForce)
|
||||
{
|
||||
var fMessage, AH, DL;
|
||||
if (fForce) {
|
||||
fMessage = true;
|
||||
} else {
|
||||
fMessage = this.messageEnabled(Messages.CPU) && nInt != Interrupts.DOS_IDLE /* 0x28 */ && nInt != Interrupts.DOS_NETBIOS /* 0x2A */;
|
||||
var nCategory = Debugger.INT_MESSAGES[nInt];
|
||||
if (nCategory) {
|
||||
if (this.messageEnabled(nCategory)) {
|
||||
fMessage = true;
|
||||
} else {
|
||||
fMessage = (nCategory == Messages.FDC && this.messageEnabled(nCategory = Messages.HDC));
|
||||
var AH, DL;
|
||||
var fMessage = fForce;
|
||||
|
||||
/*
|
||||
* We currently arrive here only because the CPU has already determined that INT messages are enabled,
|
||||
* or because the ChipSet's RTC interrupt handler has already determined that INT messages are enabled.
|
||||
*
|
||||
* But software interrupts are very common, so we generally require additional categories to be enabled;
|
||||
* unless the caller has set fForce, we check those additional categories now.
|
||||
*/
|
||||
if (!fMessage) {
|
||||
/*
|
||||
* Display all software interrupts if CPU messages are enabled (and it's not an "annoying" interrupt);
|
||||
* note that in some cases, even "annoying" interrupts can be turned with an extra message category.
|
||||
*/
|
||||
fMessage = this.messageEnabled(Messages.CPU) && Debugger.INT_ANNOYING.indexOf(nInt) < 0;
|
||||
if (!fMessage) {
|
||||
/*
|
||||
* Alternatively, display this software interrupt if its corresponding message category is enabled.
|
||||
*/
|
||||
var nCategory = Debugger.INT_MESSAGES[nInt];
|
||||
if (nCategory) {
|
||||
if (this.messageEnabled(nCategory)) {
|
||||
fMessage = true;
|
||||
} else {
|
||||
/*
|
||||
* Alternatively, display this FDC interrupt if HDC messages are enabled (since they share
|
||||
* a common software interrupt). Normally, an HDC BIOS will copy the original DISK (0x13)
|
||||
* vector to the ALT_DISK (0x40) vector, but it's a nuisance having to check different
|
||||
* interrupts in different configurations for the same frickin' functionality, so we don't.
|
||||
*/
|
||||
fMessage = (nCategory == Messages.FDC && this.messageEnabled(nCategory = Messages.HDC));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3890,7 +3929,7 @@ if (DEBUGGER) {
|
|||
}
|
||||
|
||||
var typeCPU = null;
|
||||
var fNonPrefix = true;
|
||||
var fComplete = true;
|
||||
|
||||
for (var iOperand = 1; iOperand <= cOperands; iOperand++) {
|
||||
|
||||
|
|
@ -3906,7 +3945,7 @@ if (DEBUGGER) {
|
|||
continue;
|
||||
}
|
||||
if (typeSize == Debugger.TYPE_PREFIX) {
|
||||
fNonPrefix = false;
|
||||
fComplete = false;
|
||||
continue;
|
||||
}
|
||||
var typeMode = type & Debugger.TYPE_MODE;
|
||||
|
|
@ -4004,7 +4043,7 @@ if (DEBUGGER) {
|
|||
sComment = Debugger.CPUS[typeCPU] + " CPU only";
|
||||
}
|
||||
|
||||
if (sComment && fNonPrefix) {
|
||||
if (sComment && fComplete) {
|
||||
sLine = str.pad(sLine, dbgAddrIns.fAddr32? 74 : 56) + ';' + sComment;
|
||||
if (!this.cpu.aFlags.fChecksum) {
|
||||
sLine += (nSequence != null? '=' + nSequence.toString() : "");
|
||||
|
|
@ -4014,7 +4053,7 @@ if (DEBUGGER) {
|
|||
}
|
||||
}
|
||||
|
||||
this.initAddrSize(dbgAddr, fNonPrefix, cOverrides);
|
||||
this.initAddrSize(dbgAddr, fComplete, cOverrides);
|
||||
return sLine;
|
||||
};
|
||||
|
||||
|
|
@ -5340,21 +5379,22 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* doDump(sCmd, sAddr, sLen, sBytes)
|
||||
* doDump(asArgs)
|
||||
*
|
||||
* sLen is interpreted as a number of bytes, in hex, which we convert to the appropriate number of lines,
|
||||
* because we always display whole lines. If sLen is omitted/undefined, sLen defaults to 0x80 (128.) bytes,
|
||||
* which normally translates to 8 lines.
|
||||
* The length parameter is interpreted as a number of bytes, in hex, which we convert to the appropriate number
|
||||
* of lines, because we always display whole lines. If the length is omitted/undefined, it defaults to 0x80 (128.)
|
||||
* bytes, which normally translates to 8 lines.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} sCmd
|
||||
* @param {string|undefined} sAddr
|
||||
* @param {string|undefined} [sLen] (# of bytes to dump, in hex; default is 0x80)
|
||||
* @param {string|undefined} [sBytes] (this is checked only if sLen was an 'l', in honor of old DEBUG.COM syntax)
|
||||
* @param {Array.<string>} asArgs (formerly sCmd, [sAddr], [sLen] and [sBytes])
|
||||
*/
|
||||
Debugger.prototype.doDump = function(sCmd, sAddr, sLen, sBytes)
|
||||
Debugger.prototype.doDump = function(asArgs)
|
||||
{
|
||||
var m;
|
||||
var sCmd = asArgs[0];
|
||||
var sAddr = asArgs[1];
|
||||
var sLen = asArgs[2];
|
||||
var sBytes = asArgs[3];
|
||||
|
||||
if (sAddr == '?') {
|
||||
var sDumpers = "";
|
||||
|
|
@ -5420,9 +5460,8 @@ if (DEBUGGER) {
|
|||
}
|
||||
|
||||
if (sCmd == "ds") { // transform a "ds" command into a "d desc" command
|
||||
sCmd = "d";
|
||||
sLen = sAddr;
|
||||
sAddr = "desc";
|
||||
sCmd = 'd';
|
||||
asArgs = [sCmd, "desc", sAddr];
|
||||
}
|
||||
|
||||
if (sCmd == 'd') {
|
||||
|
|
@ -5430,7 +5469,9 @@ if (DEBUGGER) {
|
|||
if (sAddr == m) {
|
||||
var fnDumper = this.afnDumpers[m];
|
||||
if (fnDumper) {
|
||||
fnDumper(sLen);
|
||||
asArgs.shift();
|
||||
asArgs.shift();
|
||||
fnDumper(asArgs);
|
||||
} else {
|
||||
this.println("no dump registered for " + sAddr);
|
||||
}
|
||||
|
|
@ -5448,7 +5489,8 @@ if (DEBUGGER) {
|
|||
}
|
||||
|
||||
if (sCmd == "di") {
|
||||
var sInfo = this.dumpInfo(sAddr);
|
||||
asArgs.shift();
|
||||
var sInfo = this.dumpInfo(asArgs);
|
||||
this.println(sInfo);
|
||||
return;
|
||||
}
|
||||
|
|
@ -6403,11 +6445,6 @@ if (DEBUGGER) {
|
|||
/**
|
||||
* doRun(sAddr, sOptions, fQuiet)
|
||||
*
|
||||
* NOTE: We assume that whenever we're being called with fQuiet set to true, that we're being
|
||||
* called in the context of checkBreakpoint(), and therefore the CPU is already running, and
|
||||
* therefore we can skip the runCPU() call -- because if we don't, then breakpoint commands that
|
||||
* include the "g" command will produce unwanted "noise".
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} sAddr
|
||||
* @param {string} [sOptions]
|
||||
|
|
@ -6668,36 +6705,38 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* initAddrSize(dbgAddr, fNonPrefix, cOverrides)
|
||||
* initAddrSize(dbgAddr, fComplete, cOverrides)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {DbgAddr} dbgAddr
|
||||
* @param {boolean} fNonPrefix
|
||||
* @param {boolean} fComplete
|
||||
* @param {number} [cOverrides]
|
||||
*/
|
||||
Debugger.prototype.initAddrSize = function(dbgAddr, fNonPrefix, cOverrides)
|
||||
Debugger.prototype.initAddrSize = function(dbgAddr, fComplete, cOverrides)
|
||||
{
|
||||
/*
|
||||
* Use cOverrides to record whether we previously processed any OPERAND or ADDRESS overrides.
|
||||
* We use dbgAddr.fComplete to record whether or not the caller (ie, getInstruction())
|
||||
* processed a complete instruction.
|
||||
*/
|
||||
dbgAddr.cOverrides = cOverrides || 0;
|
||||
dbgAddr.fComplete = fComplete;
|
||||
/*
|
||||
* For proper disassembly of instructions preceded by an OPERAND (0x66) size prefix, we set
|
||||
* dbgAddr.fData32 to true whenever the operand size is 32-bit; similarly, for an ADDRESS (0x67)
|
||||
* size prefix, we set dbgAddr.fAddr32 to true whenever the address size is 32-bit. Initially,
|
||||
* both fields must be set to match the size of the current code segment.
|
||||
* size prefix, we set dbgAddr.fAddr32 to true whenever the address size is 32-bit.
|
||||
*
|
||||
* Initially (and every time we've processed a complete instruction), both fields must be
|
||||
* set to their original value.
|
||||
*/
|
||||
if (fNonPrefix) {
|
||||
if (fComplete) {
|
||||
if (dbgAddr.fData32Orig != null) dbgAddr.fData32 = dbgAddr.fData32Orig;
|
||||
if (dbgAddr.fAddr32Orig != null) dbgAddr.fAddr32 = dbgAddr.fAddr32Orig;
|
||||
dbgAddr.fData32Orig = dbgAddr.fData32;
|
||||
dbgAddr.fAddr32Orig = dbgAddr.fAddr32;
|
||||
}
|
||||
/*
|
||||
* We also use dbgAddr.fComplete to record whether the caller (ie, getInstruction()) is reporting that
|
||||
* it processed a complete instruction (ie, a non-prefix) or not.
|
||||
* Use cOverrides to record whether we previously processed any OPERAND or ADDRESS overrides.
|
||||
*/
|
||||
dbgAddr.fComplete = fNonPrefix;
|
||||
dbgAddr.cOverrides = cOverrides || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -6727,10 +6766,10 @@ if (DEBUGGER) {
|
|||
|
||||
if (n === undefined) n = 1;
|
||||
|
||||
var dbgAddrEnd, cb = 0x100;
|
||||
var cb = 0x100;
|
||||
if (sAddrEnd !== undefined) {
|
||||
|
||||
dbgAddrEnd = this.parseAddr(sAddrEnd, Debugger.ADDR.CODE);
|
||||
var dbgAddrEnd = this.parseAddr(sAddrEnd, Debugger.ADDR.CODE);
|
||||
if (!dbgAddrEnd || dbgAddrEnd.off < dbgAddr.off) return;
|
||||
|
||||
cb = dbgAddrEnd.off - dbgAddr.off;
|
||||
|
|
@ -6745,9 +6784,6 @@ if (DEBUGGER) {
|
|||
}
|
||||
n = -1;
|
||||
}
|
||||
else {
|
||||
dbgAddrEnd = this.newAddr(this.maskReg, dbgAddr.sel, this.bus.nBusLimit, dbgAddr.type, dbgAddr.fData32, dbgAddr.fAddr32);
|
||||
}
|
||||
|
||||
var cLines = 0;
|
||||
this.initAddrSize(dbgAddr, true);
|
||||
|
|
@ -6925,7 +6961,7 @@ if (DEBUGGER) {
|
|||
break;
|
||||
}
|
||||
this.shiftArgs(asArgs);
|
||||
this.doDump(asArgs[0], asArgs[1], asArgs[2], asArgs[3]);
|
||||
this.doDump(asArgs);
|
||||
break;
|
||||
case 'e':
|
||||
if (asArgs[0] == "else") break;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ if (typeof module !== 'undefined') {
|
|||
var web = require("../../shared/lib/weblib");
|
||||
var DiskAPI = require("../../shared/lib/diskapi");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var Interrupts = require("./interrupts");
|
||||
var Messages = require("./messages");
|
||||
var ChipSet = require("./chipset");
|
||||
var Disk = require("./disk");
|
||||
|
|
@ -470,44 +471,6 @@ if (DEBUG) {
|
|||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* HDC BIOS interrupts, functions, and other parameters
|
||||
*
|
||||
* When the HDC BIOS overwrites the ROM BIOS INT 0x13 address, it saves the original INT 0x13 address
|
||||
* in the INT 0x40 vector.
|
||||
*/
|
||||
HDC.BIOS = {
|
||||
INT_DISK: 0x13,
|
||||
INT_DISKETTE: 0x40
|
||||
};
|
||||
|
||||
/*
|
||||
* NOTE: These are useful values for reference, but they're not actually used for anything at the moment.
|
||||
*/
|
||||
HDC.BIOS.DISK_CMD = {
|
||||
RESET: 0x00,
|
||||
GET_STATUS: 0x01,
|
||||
READ_SECTORS: 0x02,
|
||||
WRITE_SECTORS: 0x03,
|
||||
VERIFY_SECTORS: 0x04,
|
||||
FORMAT_TRK: 0x05,
|
||||
FORMAT_BAD: 0x06,
|
||||
FORMAT_DRIVE: 0x07,
|
||||
GET_DRIVEPARMS: 0x08,
|
||||
SET_DRIVEPARMS: 0x09,
|
||||
READ_LONG: 0x0A,
|
||||
WRITE_LONG: 0x0B,
|
||||
SEEK: 0x0C,
|
||||
ALT_RESET: 0x0D,
|
||||
READ_BUFFER: 0x0E,
|
||||
WRITE_BUFFER: 0x0F,
|
||||
TEST_READY: 0x10,
|
||||
RECALIBRATE: 0x11,
|
||||
RAM_DIAGNOSTIC: 0x12,
|
||||
DRV_DIAGNOSTIC: 0x13,
|
||||
CTL_DIAGNOSTIC: 0x14
|
||||
};
|
||||
|
||||
/**
|
||||
* setBinding(sHTMLType, sBinding, control)
|
||||
*
|
||||
|
|
@ -550,8 +513,8 @@ HDC.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
bus.addPortInputTable(this, this.fATC? HDC.aATCPortInput : HDC.aXTCPortInput);
|
||||
bus.addPortOutputTable(this, this.fATC? HDC.aATCPortOutput : HDC.aXTCPortOutput);
|
||||
|
||||
cpu.addIntNotify(HDC.BIOS.INT_DISK, this.intBIOSDisk.bind(this));
|
||||
cpu.addIntNotify(HDC.BIOS.INT_DISKETTE, this.intBIOSDiskette.bind(this));
|
||||
cpu.addIntNotify(Interrupts.DISK, this.intBIOSDisk.bind(this));
|
||||
cpu.addIntNotify(Interrupts.ALT_DISK, this.intBIOSDiskette.bind(this));
|
||||
|
||||
/*
|
||||
* The following code used to be performed in the HDC constructor, but now we need to wait for information
|
||||
|
|
|
|||
|
|
@ -54,11 +54,13 @@ var Interrupts = {
|
|||
CASSETTE: 0x15,
|
||||
KBD: 0x16,
|
||||
RTC: 0x1A,
|
||||
TIMER_TICK: 0x1C,
|
||||
ALT_TIMER: 0x1C, // invoked by the BIOS timer interrupt handler (vector 0x08)
|
||||
DOS: 0x21,
|
||||
DOS_IDLE: 0x28,
|
||||
DOS_NETBIOS:0x2A,
|
||||
MOUSE: 0x33,
|
||||
ALT_DISK: 0x40, // HDC BIOS saves original FDC BIOS vector here
|
||||
ALT_VIDEO: 0x6D, // IBM VGA BIOS saves original video BIOS vector here
|
||||
WINDBG: { // Windows Debugger protected-mode interface
|
||||
VECTOR: 0x41,
|
||||
IS_LOADED: 0x004F, // AX command
|
||||
|
|
@ -104,6 +106,33 @@ if (DEBUGGER) {
|
|||
0x16: "get drive %DL change line status",
|
||||
0x17: "set drive %DL DASD type",
|
||||
0x18: "set drive %DL media type"
|
||||
/*
|
||||
* Here's an additional function reference, previously in the HDC component, but moved here
|
||||
* because our components are hardware emulations, not BIOS emulations, so this information is
|
||||
* really only of interest to the Debugger (or the casual observer).
|
||||
*
|
||||
* RESET: 0x00,
|
||||
* GET_STATUS: 0x01,
|
||||
* READ_SECTORS: 0x02,
|
||||
* WRITE_SECTORS: 0x03,
|
||||
* VERIFY_SECTORS: 0x04,
|
||||
* FORMAT_TRK: 0x05,
|
||||
* FORMAT_BAD: 0x06,
|
||||
* FORMAT_DRIVE: 0x07,
|
||||
* GET_DRIVEPARMS: 0x08,
|
||||
* SET_DRIVEPARMS: 0x09,
|
||||
* READ_LONG: 0x0A,
|
||||
* WRITE_LONG: 0x0B,
|
||||
* SEEK: 0x0C,
|
||||
* ALT_RESET: 0x0D,
|
||||
* READ_BUFFER: 0x0E,
|
||||
* WRITE_BUFFER: 0x0F,
|
||||
* TEST_READY: 0x10,
|
||||
* RECALIBRATE: 0x11,
|
||||
* RAM_DIAGNOSTIC: 0x12,
|
||||
* DRV_DIAGNOSTIC: 0x13,
|
||||
* CTL_DIAGNOSTIC: 0x14
|
||||
*/
|
||||
};
|
||||
Interrupts.FUNCS[Interrupts.CASSETTE] = {
|
||||
0x80: "open device",
|
||||
|
|
|
|||
|
|
@ -2496,38 +2496,38 @@ Card.prototype.dumpVideoCard = function()
|
|||
};
|
||||
|
||||
/**
|
||||
* dumpVideoBuffer(sParm)
|
||||
* dumpVideoBuffer(asArgs)
|
||||
*
|
||||
* Rather than requiring sParm to ALWAYS be a frame buffer address OR a frame buffer offset,
|
||||
* we'll just make a guess as to what the user intended and support BOTH; basically if the value
|
||||
* is less than the frame buffer address, we'll assume it's an offset.
|
||||
* Rather than requiring the first parameter to ALWAYS be a frame buffer address OR a frame buffer
|
||||
* offset, we'll just make a guess as to what the user intended and support BOTH; basically, if the
|
||||
* value is less than the frame buffer address, we'll assume it's an offset.
|
||||
*
|
||||
* Also, we allow some special options to be encoded in sParm: 'l' followed by a number means
|
||||
* Also, we allow some special options to be encoded in asArgs: 'l' followed by a number means
|
||||
* print that many rows of data. 'n' followed by a number (1-8) means print only that number of
|
||||
* memory locations per row, and then adjust the starting address of the next row by the number
|
||||
* of bytes per row (or whatever is specified by the 'w' option) so that the dump reflects a
|
||||
* rectangular chunk of video data. Finally, if sParm contains 'p' followed by a number (0-3),
|
||||
* rectangular chunk of video data. Finally, if asArgs contains 'p' followed by a number (0-3),
|
||||
* we display only the bits from that plane for each memory location, in binary instead of hex.
|
||||
*
|
||||
* For example, assuming a standard VGA frame buffer with 640x480 pixels across 38400 (0x9600) memory
|
||||
* locations, the following command will dump a vertical swath of bits from plane 0 that is 32 (0x20)
|
||||
* rows tall and 8 columns wide, from roughly the center of the screen (0x4B00 + 0x28 - 2 = 0x4B26).
|
||||
*
|
||||
* d video 4b26l20n8p0
|
||||
* d video 4b26 l20 n8 p0
|
||||
*
|
||||
* Subsequent commands that omit a starting address or offset will continue where the last dump
|
||||
* left off; eg:
|
||||
*
|
||||
* d video n8p0
|
||||
* d video n8 p0
|
||||
*
|
||||
* To dump a chunk of off-screen memory starting at 0x9600, where the Windows VGA driver typically
|
||||
* stores a copy of the video memory containing the current mouse pointer:
|
||||
*
|
||||
* d video 9600l20n5w5p0
|
||||
* d video 9600 l20 n5 w5 p0
|
||||
*
|
||||
* Alternatively, you could use decimal values:
|
||||
*
|
||||
* d video 9600l32.n5.w5.p0.
|
||||
* d video 9600 l32. n5. w5. p0.
|
||||
*
|
||||
* NOTE: If these commands look suspiciously like weird Hayes modem command strings, trust me,
|
||||
* that is ENTIRELY coincidental (but mildly amusing).
|
||||
|
|
@ -2535,49 +2535,58 @@ Card.prototype.dumpVideoCard = function()
|
|||
* TODO: Make these options more general-purpose (it currently assumes a conventional VGA planar layout).
|
||||
*
|
||||
* @this {Card}
|
||||
* @param {string} sParm
|
||||
* @param {Array.<string>} asArgs
|
||||
*/
|
||||
Card.prototype.dumpVideoBuffer = function(sParm)
|
||||
Card.prototype.dumpVideoBuffer = function(asArgs)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
if (!this.adwMemory) {
|
||||
this.dbg.println("no buffer");
|
||||
return;
|
||||
}
|
||||
var i, idw, fColAdjust = false;
|
||||
|
||||
var i, j, idw, fColAdjust = false;
|
||||
var l = 8, n = 8, p = -1, w = this.video.nCols >> 3;
|
||||
|
||||
var a = sParm.split(/([lnpw])/);
|
||||
for (i = 0; i < a.length; i++) {
|
||||
for (i = 0; i < asArgs.length; i++) {
|
||||
|
||||
var s = asArgs[i];
|
||||
if (!i) {
|
||||
if (a[0].length) {
|
||||
idw = str.parseInt(a[0]);
|
||||
idw = str.parseInt(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
var ch = s.charAt(0);
|
||||
j = str.parseInt(s.substr(1));
|
||||
|
||||
switch(ch) {
|
||||
case 'l':
|
||||
l = j;
|
||||
break;
|
||||
case 'n':
|
||||
if (j >= 1 && j <= 8) {
|
||||
n = j;
|
||||
fColAdjust = true;
|
||||
}
|
||||
} else {
|
||||
var j = str.parseInt(a[i+1]);
|
||||
if (a[i] == 'l') {
|
||||
l = j;
|
||||
}
|
||||
else if (a[i] == 'n') {
|
||||
if (j >= 1 && j <= 8) {
|
||||
n = j;
|
||||
fColAdjust = true;
|
||||
}
|
||||
}
|
||||
else if (a[i] == 'p') {
|
||||
if (j >= 0 && j <= 3) p = j;
|
||||
}
|
||||
else if (a[i] == 'w') {
|
||||
if (j < w) w = j;
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
case 'p':
|
||||
if (j >= 0 && j <= 3) p = j;
|
||||
break;
|
||||
case 'w':
|
||||
if (j < w) w = j;
|
||||
break;
|
||||
default:
|
||||
this.dbg.println("unrecognized argument: " + s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idw === undefined) {
|
||||
idw = this.prevDump || 0;
|
||||
} else if (idw >= this.addrBuffer) {
|
||||
idw -= this.addrBuffer;
|
||||
}
|
||||
|
||||
var sDump = "";
|
||||
for (i = 0; i < l; i++) {
|
||||
var sData = str.toHex(this.addrBuffer + idw) + ":";
|
||||
|
|
@ -2589,6 +2598,7 @@ Card.prototype.dumpVideoBuffer = function(sParm)
|
|||
if (sDump) sDump += "\n";
|
||||
sDump += sData;
|
||||
}
|
||||
|
||||
if (sDump) this.dbg.println(sDump);
|
||||
this.prevDump = idw;
|
||||
}
|
||||
|
|
@ -2793,8 +2803,8 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
|
||||
if (DEBUGGER && dbg) {
|
||||
var video = this;
|
||||
dbg.messageDump(Messages.VIDEO, function onDumpVideo(sParm) {
|
||||
video.dumpVideo(sParm);
|
||||
dbg.messageDump(Messages.VIDEO, function onDumpVideo(asArgs) {
|
||||
video.dumpVideo(asArgs);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -6804,20 +6814,20 @@ Video.prototype.inCardStatus = function(card, addrFrom)
|
|||
};
|
||||
|
||||
/**
|
||||
* dumpVideo(sParm)
|
||||
* dumpVideo(asArgs)
|
||||
*
|
||||
* @this {Video}
|
||||
* @param {string} [sParm]
|
||||
* @param {Array.<string>} asArgs
|
||||
*/
|
||||
Video.prototype.dumpVideo = function(sParm)
|
||||
Video.prototype.dumpVideo = function(asArgs)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
if (!this.cardActive) {
|
||||
this.dbg.println("no active video card");
|
||||
return;
|
||||
}
|
||||
if (sParm) {
|
||||
this.cardActive.dumpVideoBuffer(sParm);
|
||||
if (asArgs[0]) {
|
||||
this.cardActive.dumpVideoBuffer(asArgs);
|
||||
return;
|
||||
}
|
||||
this.dbg.println("BIOSMODE: " + str.toHexByte(this.nMode));
|
||||
|
|
|
|||
|
|
@ -132,10 +132,9 @@ function X86CPU(parmsCPU)
|
|||
|
||||
/*
|
||||
* List of software interrupt notification functions: aIntNotify is an array, indexed by
|
||||
* interrupt number, of 2-element sub-arrays that, in turn, contain:
|
||||
* interrupt number, where each element contains:
|
||||
*
|
||||
* [0]: registered component
|
||||
* [1]: registered function to call for every software interrupt
|
||||
* registered function to call for every software interrupt
|
||||
*
|
||||
* The registered function is called with the linear address (LIP) following the software interrupt;
|
||||
* if any function returns false, the software interrupt will be skipped (presumed to be emulated),
|
||||
|
|
@ -1068,7 +1067,7 @@ X86CPU.prototype.initProcessor = function()
|
|||
this.PS_SET = X86.PS.BIT1; // on the 80286, only BIT1 of Processor Status (flags) is always set
|
||||
this.PS_DIRECT |= X86.PS.IOPL.MASK | X86.PS.NT;
|
||||
|
||||
this.OPFLAG_NOINTR_8086 = 0; // used with instructions that should *not* set NOINTR on an 80286 (eg, non-SS segment loads)
|
||||
this.OPFLAG_NOINTR_8086 = 0; // for instructions that do *not* set NOINTR on an 80286 (eg, non-SS segment loads)
|
||||
|
||||
this.aOps[0x0F] = X86.op0F;
|
||||
this.aOps0F = X86.aOps0F.slice();
|
||||
|
|
@ -1080,13 +1079,7 @@ X86CPU.prototype.initProcessor = function()
|
|||
|
||||
if (I386 && this.model >= X86.MODEL_80386) {
|
||||
var bOpcode;
|
||||
/*
|
||||
* TODO: Determine if the Nested Task (PS.NT) flag should really be cleared in real-mode on an 80386
|
||||
* (we already know based on the OS/2 CPU test discussed in setPS() that it can't be set in real-mode
|
||||
* on an 80286); for now, we assume that it should remain clear on all CPUs, to avoid any unexpected
|
||||
* nested-task weirdness in real-mode.
|
||||
*/
|
||||
this.PS_CLEAR_RM = X86.PS.NT;
|
||||
this.PS_CLEAR_RM = 0; // NOTE: This allows the 80386 to modify X86.PS.NT in real-mode (which is presumably OK)
|
||||
this.PS_DIRECT |= X86.PS.RF | X86.PS.VM;
|
||||
this.aOps[X86.OPCODE.FS] = X86.opFS; // 0x64
|
||||
this.aOps[X86.OPCODE.GS] = X86.opGS; // 0x65
|
||||
|
|
@ -1635,7 +1628,7 @@ X86CPU.prototype.checkIntNotify = function(nInt)
|
|||
var aNotify = this.aIntNotify[nInt];
|
||||
if (aNotify !== undefined) {
|
||||
for (var i = 0; i < aNotify.length; i++) {
|
||||
if (!aNotify[i](aNotify[i][0], this.regLIP)) {
|
||||
if (!aNotify[i](this.regLIP)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1882,7 +1875,7 @@ X86CPU.prototype.setProtMode = function(fProt, fV86)
|
|||
if (fV86 === undefined) {
|
||||
fV86 = !!(this.regPS & X86.PS.VM);
|
||||
}
|
||||
if (!fProt != !(this.regCR0 & X86.CR0.MSW.PE) && this.messageEnabled()) {
|
||||
if (DEBUG && (!fProt != !(this.regCR0 & X86.CR0.MSW.PE) || fV86 != !!(this.regPS & X86.PS.VM)) && this.messageEnabled()) {
|
||||
this.printMessage("CPU switching to " + (fProt? (fV86? "v86" : "protected") : "real") + "-mode", this.bitsMessage, true);
|
||||
}
|
||||
this.aOpGrp6 = (fProt && !fV86? X86.aOpGrp6Prot : X86.aOpGrp6Real);
|
||||
|
|
@ -2994,13 +2987,10 @@ X86CPU.prototype.setPS = function(regPS, cpl)
|
|||
* pops 0xF000 into the flags is able to set *any* of flag bits 12-15: if it can, then OS/2 declares
|
||||
* the CPU an 80386.
|
||||
*
|
||||
* So, if the CPU is an 80286, we zero incoming bits 12-14 in real-mode (bit 15 is never allowed to
|
||||
* be modified, so there's no need to mask it). And if the CPU is an 80386, we zero only bit 14 (PS.NT),
|
||||
* allowing the IOPL bits to change; however, that should not affect any real-mode operations, since
|
||||
* CPL will always be zero, making IOPL irrelevant.
|
||||
*
|
||||
* It's still an open question whether an 80386 should also clear the Nested Task (PS.NT) flag in
|
||||
* real-mode; if not, then initProcessor() should set PS_CLEAR_RM to zero.
|
||||
* So, if the CPU is an 80286, we clear incoming bits 12-14 in real-mode (bit 15 is never allowed to
|
||||
* be modified, so there's no need to mask it). And if the CPU is an 80386, no bits are automatically
|
||||
* cleared in real-mode (PS_CLEAR_RM is zero); although that allows the IOPL bits to change, it doesn't
|
||||
* affect real-mode operation, since CPL is always zero, making IOPL irrelevant.
|
||||
*/
|
||||
if (!(this.regCR0 & X86.CR0.MSW.PE)) regPS &= ~this.PS_CLEAR_RM;
|
||||
|
||||
|
|
@ -3147,24 +3137,40 @@ X86CPU.prototype.setBinding = function(sHTMLType, sBinding, control)
|
|||
};
|
||||
|
||||
/**
|
||||
* probeAddr(addr)
|
||||
* probeAddr(addr, size)
|
||||
*
|
||||
* Used by the Debugger to probe addresses without risk of triggering a page fault, and by internal
|
||||
* functions, like fnFaultMessage(), that also need to avoid triggering faults, since they're not part
|
||||
* of standard CPU operation.
|
||||
*
|
||||
* NOTE: If the size parameter is used, then the caller is required to provide a valid size (1, 2 or 4)
|
||||
* and ensure that the data is contained entirely with the requested block.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} addr is a linear address
|
||||
* @param {number} [size] is a length (default is 1)
|
||||
* @return {number|null} byte (8-bit) value at that address, or null if invalid
|
||||
*/
|
||||
X86CPU.prototype.probeAddr = function(addr)
|
||||
X86CPU.prototype.probeAddr = function(addr, size)
|
||||
{
|
||||
var block = this.aMemBlocks[(addr & this.nMemMask) >>> this.nBlockShift];
|
||||
if (block.type == Memory.TYPE.UNPAGED) {
|
||||
block = this.mapPageBlock(addr, false, true);
|
||||
if (!block) return null;
|
||||
if (block) {
|
||||
if (block.type == Memory.TYPE.UNPAGED) {
|
||||
block = this.mapPageBlock(addr, false, true);
|
||||
}
|
||||
}
|
||||
return block.readByteDirect(addr & this.nBlockLimit, addr);
|
||||
if (block) {
|
||||
var off = addr & this.nBlockLimit;
|
||||
switch(size) {
|
||||
default:
|
||||
return block.readByteDirect(off, addr);
|
||||
case 2:
|
||||
return block.readShortDirect(off, addr);
|
||||
case 4:
|
||||
return block.readLongDirect(off, addr);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3225,6 +3225,11 @@ X86.fnSLDT = function SLDT(dst, src)
|
|||
/**
|
||||
* fnSMSW(dst, src)
|
||||
*
|
||||
* TODO: I've seen a claim that SMSW can be used with an operand size override to obtain the entire CR0.
|
||||
* I don't dispute that, and since I don't mask the return value, that should be possible here; however, it
|
||||
* should still be confirmed on real hardware at some point. Note that this differs from LMSW, which is
|
||||
* REQUIRED to mask the source operand.
|
||||
*
|
||||
* op=0x0F,0x01,reg=0x4 (GRP7:SMSW)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
|
|
|
|||
|
|
@ -2475,17 +2475,23 @@ X86.opPUSHF = function PUSHF()
|
|||
var regPS = this.getPS();
|
||||
if (I386) {
|
||||
if ((regPS & X86.PS.VM) && this.nIOPL < 3) {
|
||||
if (DEBUG) this.printMessage("PUSHF in v86-mode (IOPL < 3)", this.bitsMessage, true);
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* It doesn't matter whether this is PUSHF or PUSHFD: the VM and RF flags are never pushed, so
|
||||
* we can always clear them.
|
||||
* we should always clear them. NOTE: This contradicts what the "INTEL 80386 PROGRAMMER'S REFERENCE
|
||||
* MANUAL 1986" says on page 81 (which we assume is wrong):
|
||||
*
|
||||
* SYSTEMS FLAGS (INCLUDING THE IOPL FIELD, AND THE VM, RF, AND IF FLAGS) ARE PUSHED AND ARE
|
||||
* VISIBLE TO APPLICATIONS PROGRAMS. HOWEVER, WHEN AN APPLICATIONS PROGRAM POPS THE FLAGS,
|
||||
* THESE ITEMS ARE NOT CHANGED, REGARDLESS OF THE VALUES POPPED INTO THEM.
|
||||
*
|
||||
* This does, however, beg the question: how does code running in V86-mode detect that's in V86-mode
|
||||
* and not real-mode? By using the SMSW instruction and checking the PE (protected-mode enabled) bit.
|
||||
* The SMSW instruction returns a subset of the CR0 bits, and unlike the MOV reg,CR0 instruction, is
|
||||
* allowed in V86-mode.
|
||||
* allowed in V86-mode. See fnSMSW() for more information.
|
||||
*/
|
||||
regPS &= ~(X86.PS.VM | X86.PS.RF);
|
||||
}
|
||||
|
|
@ -2504,6 +2510,7 @@ X86.opPOPF = function POPF()
|
|||
* TODO: Consider swapping out this function whenever setProtMode() changes the mode to V86-mode.
|
||||
*/
|
||||
if (I386 && (this.regPS & X86.PS.VM) && this.nIOPL < 3) {
|
||||
if (DEBUG) this.printMessage("POPF in v86-mode (IOPL < 3)", this.bitsMessage, true);
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
|
|
@ -3439,6 +3446,7 @@ X86.opINT3 = function INT3()
|
|||
* TODO: Consider swapping out this function whenever setProtMode() changes the mode to V86-mode.
|
||||
*/
|
||||
if (I386 && (this.regPS & X86.PS.VM) && this.nIOPL < 3) {
|
||||
if (DEBUG) this.printMessage("INT 0x03 in v86-mode (IOPL < 3)", this.bitsMessage, true);
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
|
|
@ -3478,14 +3486,15 @@ X86.opINT3 = function INT3()
|
|||
*/
|
||||
X86.opINTn = function INTn()
|
||||
{
|
||||
var nInt = this.getIPByte();
|
||||
/*
|
||||
* TODO: Consider swapping out this function whenever setProtMode() changes the mode to V86-mode.
|
||||
*/
|
||||
if (I386 && (this.regPS & X86.PS.VM) && this.nIOPL < 3) {
|
||||
if (DEBUG && this.messageEnabled()) this.printMessage("INT " + str.toHexByte(nInt) + " in v86-mode (IOPL < 3)", true, true);
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
var nInt = this.getIPByte();
|
||||
/*
|
||||
* checkIntNotify() checks for any notification handlers registered via addIntNotify(), calls them,
|
||||
* and returns false ONLY if a notification handler returned false (ie, requesting the interrupt be skipped).
|
||||
|
|
@ -3509,6 +3518,7 @@ X86.opINTO = function INTO()
|
|||
* TODO: Consider swapping out this function whenever setProtMode() changes the mode to V86-mode.
|
||||
*/
|
||||
if (I386 && (this.regPS & X86.PS.VM) && this.nIOPL < 3) {
|
||||
if (DEBUG) this.printMessage("INTO in v86-mode (IOPL < 3)", this.bitsMessage, true);
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
|
|
@ -3529,6 +3539,7 @@ X86.opIRET = function IRET()
|
|||
* TODO: Consider swapping out this function whenever setProtMode() changes the mode to V86-mode.
|
||||
*/
|
||||
if (I386 && (this.regPS & X86.PS.VM) && this.nIOPL < 3) {
|
||||
if (DEBUG) this.printMessage("IRET in v86-mode (IOPL < 3)", this.bitsMessage, true);
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
|
|
@ -4131,6 +4142,7 @@ X86.opCLI = function CLI()
|
|||
* and in V86-mode, CPL is always 3.
|
||||
*/
|
||||
if (this.nCPL > this.nIOPL) {
|
||||
if (DEBUG && (this.regPS & X86.PS.VM)) this.printMessage("CLI in v86-mode (IOPL < 3)", this.bitsMessage, true);
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
|
|
@ -4150,6 +4162,7 @@ X86.opSTI = function STI()
|
|||
* and in V86-mode, CPL is always 3.
|
||||
*/
|
||||
if (this.nCPL > this.nIOPL) {
|
||||
if (DEBUG && (this.regPS & X86.PS.VM)) this.printMessage("STI in v86-mode (IOPL < 3)", this.bitsMessage, true);
|
||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -225,11 +225,11 @@ X86Seg.prototype.loadProt = function loadProt(sel, fProbe)
|
|||
/*
|
||||
* The ROM BIOS POST executes some test code in protected-mode without properly initializing the LDT,
|
||||
* which has no bearing on the ROM's own code, because it never loads any LDT selectors, but if at the same
|
||||
* time our Debugger attempts to validate a selector in one of its breakpoints, that could cause some
|
||||
* grief here. We avoid that grief by skipping segment lookup if the descriptor table being referenced is zero
|
||||
* AND the Debugger's ID.DBG segment register is being used.
|
||||
* time our Debugger attempts to validate a selector in one of its breakpoints, that could cause some grief.
|
||||
*
|
||||
* Fortunately, the Debugger now has its own interface, probeDesc(), so that should no longer be a concern.
|
||||
*/
|
||||
if (addrDT || this.id != X86Seg.ID.DBG) {
|
||||
if (addrDT) {
|
||||
var addrDesc = (addrDT + (sel & X86.SEL.MASK))|0;
|
||||
if ((addrDTLimit - addrDesc)|0 >= 7) {
|
||||
/*
|
||||
|
|
@ -238,7 +238,7 @@ X86Seg.prototype.loadProt = function loadProt(sel, fProbe)
|
|||
* starting with a 15-cycle difference. Obviously the difference will vary with the instruction,
|
||||
* and will be much greater whenever the load fails.
|
||||
*/
|
||||
if (this.id != X86Seg.ID.DBG) cpu.nStepCycles -= 15;
|
||||
cpu.nStepCycles -= 15;
|
||||
return this.loadDesc8(addrDesc, sel, fProbe);
|
||||
}
|
||||
if (this.id < X86Seg.ID.VER) {
|
||||
|
|
@ -740,7 +740,6 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
|
|||
if (!I386 || !(type & X86.DESC.ACC.NONSEG_386)) {
|
||||
offSP = (cplNew << 2) + X86.TSS286.CPL0_SP;
|
||||
lenSP = 2;
|
||||
cpu.assert(!(regPS & X86.PS.VM));
|
||||
} else {
|
||||
offSP = (cplNew << 2) + X86.TSS386.CPL0_ESP;
|
||||
lenSP = 4;
|
||||
|
|
@ -923,8 +922,9 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
|
|||
|
||||
default:
|
||||
/*
|
||||
* The only other case should be X86Seg.ID.DBG, for which we do nothing.
|
||||
* The only other case used to be X86Seg.ID.DBG, but the Debugger uses probeDesc() now, so we should never get here.
|
||||
*/
|
||||
cpu.assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -946,7 +946,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
|
|||
this.ext = ext;
|
||||
this.addrDesc = addrDesc;
|
||||
/*
|
||||
* A quick recap of what updateMode(fLoad=true, fProt=true, fV86=false) actually updates next:
|
||||
* A quick recap of what updateMode(fLoad=true, fProt=true, fV86=false) actually updates:
|
||||
*
|
||||
* cpl
|
||||
* dpl
|
||||
|
|
@ -968,6 +968,64 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
|
|||
return base;
|
||||
};
|
||||
|
||||
/**
|
||||
* probeDesc(sel)
|
||||
*
|
||||
* This is a neutered version of loadProt() designed for the Debugger.
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} sel
|
||||
* @return {number} base address of selected segment, or X86.ADDR_INVALID if error
|
||||
*/
|
||||
X86Seg.prototype.probeDesc = function(sel)
|
||||
{
|
||||
var addrDT;
|
||||
var addrDTLimit;
|
||||
var cpu = this.cpu;
|
||||
|
||||
sel &= 0xffff;
|
||||
|
||||
if (!(sel & X86.SEL.LDT)) {
|
||||
addrDT = cpu.addrGDT;
|
||||
addrDTLimit = cpu.addrGDTLimit;
|
||||
} else {
|
||||
addrDT = cpu.segLDT.base;
|
||||
addrDTLimit = (addrDT + cpu.segLDT.limit)|0;
|
||||
}
|
||||
|
||||
var addrDesc = (addrDT + (sel & X86.SEL.MASK))|0;
|
||||
|
||||
if ((addrDTLimit - addrDesc)|0 >= 7) {
|
||||
|
||||
/*
|
||||
* Load the descriptor from memory using probeAddr().
|
||||
*/
|
||||
var limit = cpu.probeAddr(addrDesc + X86.DESC.LIMIT.OFFSET, 2);
|
||||
var acc = cpu.probeAddr(addrDesc + X86.DESC.ACC.OFFSET, 2);
|
||||
var type = (acc & X86.DESC.ACC.TYPE.MASK);
|
||||
var base = cpu.probeAddr(addrDesc + X86.DESC.BASE.OFFSET, 2) | ((acc & X86.DESC.ACC.BASE1623) << 16);
|
||||
var ext = cpu.probeAddr(addrDesc + X86.DESC.EXT.OFFSET, 2);
|
||||
|
||||
if (I386 && cpu.model >= X86.MODEL_80386) {
|
||||
base |= (ext & X86.DESC.EXT.BASE2431) << 16;
|
||||
limit |= (ext & X86.DESC.EXT.LIMIT1619) << 16;
|
||||
if (ext & X86.DESC.EXT.LIMITPAGES) limit = (limit << 12) | 0xfff;
|
||||
}
|
||||
|
||||
this.sel = sel;
|
||||
this.base = base;
|
||||
this.limit = limit;
|
||||
this.offMax = (limit >>> 0) + 1;
|
||||
this.acc = acc;
|
||||
this.type = type;
|
||||
this.ext = ext;
|
||||
this.addrDesc = addrDesc;
|
||||
this.updateMode(true, true, false);
|
||||
return base;
|
||||
}
|
||||
return X86.ADDR_INVALID;
|
||||
};
|
||||
|
||||
/**
|
||||
* switchTSS(selNew, fNest)
|
||||
*
|
||||
|
|
@ -1408,7 +1466,7 @@ X86Seg.prototype.updateMode = function(fLoad, fProt, fV86)
|
|||
X86Seg.prototype.messageSeg = function(sel, base, limit, type, ext)
|
||||
{
|
||||
if (DEBUG) {
|
||||
if (DEBUGGER && this.id != X86Seg.ID.DBG && this.dbg && this.dbg.messageEnabled(Messages.SEG)) {
|
||||
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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue