Revamped dump extensions to accept argument arrays, added probeDesc() for Debugger-safe segment descriptor loading

This commit is contained in:
Jeff Parsons 2015-09-03 09:07:40 -07:00
commit ee2f5ff761
9 changed files with 340 additions and 219 deletions

View file

@ -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;