More symbol support

This commit is contained in:
Jeff Parsons 2015-08-19 21:18:04 -07:00
commit 1d448f14ee
2 changed files with 269 additions and 141 deletions

View file

@ -113,7 +113,7 @@ function Debugger(parmsDbg)
* call and simply represents the number of cycles performed by the last run of instructions. * call and simply represents the number of cycles performed by the last run of instructions.
*/ */
this.nCycles = -1; this.nCycles = -1;
this.cInstructions = -1; this.cOpcodes = -1;
/* /*
* Default number of hex chars in a register and a linear address (ie, for real-mode); * Default number of hex chars in a register and a linear address (ie, for real-mode);
@ -627,7 +627,7 @@ if (DEBUGGER) {
}; };
Debugger.TRACE_LIMIT = 100000; Debugger.TRACE_LIMIT = 100000;
Debugger.HISTORY_LIMIT = 100000; Debugger.HISTORY_LIMIT = DEBUG? 100000 : 10000;
/* /*
* Opcode 0x0F has a distinguished history: * Opcode 0x0F has a distinguished history:
@ -1963,6 +1963,39 @@ if (DEBUGGER) {
this.dumpBlocks(this.cpu.aBusBlocks, sAddr); this.dumpBlocks(this.cpu.aBusBlocks, sAddr);
}; };
/**
* dumpInfo(sAddr)
*
* @this {Debugger}
* @param {string|undefined} sAddr
*/
Debugger.prototype.dumpInfo = function(sAddr)
{
var sInfo = "no information";
if (BACKTRACK) {
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_CODE, true, true);
if (dbgAddr) {
var addr = this.getAddr(dbgAddr);
sInfo = '%' + str.toHex(addr) + ": " + (this.bus.getSymbol(addr, true) || sInfo);
} else {
var component, componentPrev = null;
while (component = this.cmp.getComponentByType("Disk", componentPrev)) {
var aInfo = component.getSymbolInfo(sAddr);
if (aInfo.length) {
sInfo = "";
for (var i in aInfo) {
var a = aInfo[i];
if (sInfo) sInfo += '\n';
sInfo += a[0] + ": " + a[1] + ' ' + str.toHex(a[2], 4) + ':' + str.toHex(a[3], 4) + " len " + str.toHexWord(a[4]);
}
}
componentPrev = component;
}
}
}
return sInfo;
};
/** /**
* dumpMem(sAddr) * dumpMem(sAddr)
* *
@ -2061,41 +2094,61 @@ if (DEBUGGER) {
}; };
/** /**
* dumpHistory(sCount, cLines) * dumpHistory(sPrev, sLines)
*
* If sLines is not a number, it can be a instruction filter. However, for the moment, the only
* supported filter is "call", which filters the history buffer for all CALL and RET instructions
* from the specified previous point forward.
* *
* @this {Debugger} * @this {Debugger}
* @param {string} [sCount] is the number of instructions to rewind to (default is 10) * @param {string} [sPrev] is a (decimal) number of instructions to rewind to (default is 10)
* @param {number} [cLines] is the number of instructions to print (default is, again, 10) * @param {string} [sLines] is a (decimal) number of instructions to print (default is, again, 10)
*/ */
Debugger.prototype.dumpHistory = function(sCount, cLines) Debugger.prototype.dumpHistory = function(sPrev, sLines)
{ {
var sMore = ""; var sMore = "";
cLines = cLines || 10;
var cHistory = 0; var cHistory = 0;
var iHistory = this.iOpcodeHistory; var iHistory = this.iOpcodeHistory;
var aHistory = this.aOpcodeHistory; var aHistory = this.aOpcodeHistory;
if (aHistory.length) { if (aHistory.length) {
var n = (sCount === undefined? this.nextHistory : +sCount); // warning: decimal instead of hex conversion var nPrev = +sPrev || this.nextHistory;
if (isNaN(n)) var nLines = +sLines || 10;
n = cLines;
else if (isNaN(nPrev)) {
nPrev = nLines;
} else {
sMore = "more "; sMore = "more ";
if (n > aHistory.length) {
this.println("note: only " + aHistory.length + " available");
n = aHistory.length;
} }
iHistory -= n;
if (nPrev > aHistory.length) {
this.println("note: only " + aHistory.length + " available");
nPrev = aHistory.length;
}
iHistory -= nPrev;
if (iHistory < 0) { if (iHistory < 0) {
if (aHistory[aHistory.length - 1][1] != null) { /*
iHistory += aHistory.length; * If the dbgAddr of the last aHistory element contains a valid selector, wrap around.
} else { */
n = iHistory + n; if (aHistory[aHistory.length - 1].sel == null) {
nPrev = iHistory + nPrev;
iHistory = 0; iHistory = 0;
} else {
iHistory += aHistory.length;
} }
} }
if (sCount !== undefined) {
this.println(n + " instructions earlier:"); var aFilters = [];
if (sLines == "call") {
nLines = 100000;
aFilters = ["CALL"];
} }
if (sPrev !== undefined) {
this.println(nPrev + " instructions earlier:");
}
/* /*
* TODO: The following is necessary to prevent dumpHistory() from causing additional (or worse, recursive) * TODO: The following is necessary to prevent dumpHistory() from causing additional (or worse, recursive)
* faults due to segmented addresses that are no longer valid, but the only alternative is to dramatically * faults due to segmented addresses that are no longer valid, but the only alternative is to dramatically
@ -2109,15 +2162,22 @@ if (DEBUGGER) {
* If you re-enable this protection, be sure to re-enable the decrement below, too. * If you re-enable this protection, be sure to re-enable the decrement below, too.
*/ */
var fData32 = null, fAddr32 = null; var fData32 = null, fAddr32 = null;
while (cLines > 0 && iHistory != this.iOpcodeHistory) {
while (nLines > 0 && iHistory != this.iOpcodeHistory) {
var dbgAddr = aHistory[iHistory++]; var dbgAddr = aHistory[iHistory++];
if (dbgAddr.sel == null) break; if (dbgAddr.sel == null) break;
/* /*
* We must create a new dbgAddr from the address in aHistory, because dbgAddr was * We must create a new dbgAddr from the address in aHistory, because dbgAddr was
* a reference, not a copy, and we don't want getInstruction() modifying the original. * a reference, not a copy, and we don't want getInstruction() modifying the original.
*/ */
dbgAddr = this.newAddr(dbgAddr.off, dbgAddr.sel, dbgAddr.addr, dbgAddr.fProt, fData32 == null? dbgAddr.fData32 : fData32, fAddr32 == null? dbgAddr.fAddr32 : fAddr32); dbgAddr = this.newAddr(dbgAddr.off, dbgAddr.sel, dbgAddr.addr, dbgAddr.fProt, fData32 == null? dbgAddr.fData32 : fData32, fAddr32 == null? dbgAddr.fAddr32 : fAddr32);
this.println(this.getInstruction(dbgAddr, "history", n--)); var sInstruction = this.getInstruction(dbgAddr, "history", nPrev--);
if (!aFilters.length || sInstruction.indexOf(aFilters[0]) >= 0) {
this.println(sInstruction);
}
/* /*
* If there were OPERAND or ADDRESS overrides on the previous instruction, getInstruction() * If there were OPERAND or ADDRESS overrides on the previous instruction, getInstruction()
* will have automatically disassembled additional bytes, so skip additional history entries. * will have automatically disassembled additional bytes, so skip additional history entries.
@ -2125,13 +2185,14 @@ if (DEBUGGER) {
if (!dbgAddr.cOverrides) { if (!dbgAddr.cOverrides) {
fData32 = fAddr32 = null; fData32 = fAddr32 = null;
} else { } else {
iHistory += dbgAddr.cOverrides; cLines -= dbgAddr.cOverrides; n -= dbgAddr.cOverrides; iHistory += dbgAddr.cOverrides; nLines -= dbgAddr.cOverrides; nPrev -= dbgAddr.cOverrides;
fData32 = dbgAddr.fData32; fAddr32 = dbgAddr.fAddr32; fData32 = dbgAddr.fData32; fAddr32 = dbgAddr.fAddr32;
} }
if (iHistory >= aHistory.length) iHistory = 0; if (iHistory >= aHistory.length) iHistory = 0;
this.nextHistory = n; this.nextHistory = nPrev;
cHistory++; cHistory++;
cLines--; nLines--;
} }
/* /*
* See comments above. * See comments above.
@ -2139,6 +2200,7 @@ if (DEBUGGER) {
* this.nSuppressBreaks--; * this.nSuppressBreaks--;
*/ */
} }
if (!cHistory) { if (!cHistory) {
this.println("no " + sMore + "history available"); this.println("no " + sMore + "history available");
this.nextHistory = undefined; this.nextHistory = undefined;
@ -2258,9 +2320,14 @@ if (DEBUGGER) {
* @return {number} register index, or -1 if not found * @return {number} register index, or -1 if not found
*/ */
Debugger.prototype.getRegIndex = function(sReg, off) { Debugger.prototype.getRegIndex = function(sReg, off) {
off = off || 0; var i;
var i = usr.indexOf(Debugger.REGS, sReg.substr(off, 3).toUpperCase()); sReg = sReg.toUpperCase();
if (i < 0) i = usr.indexOf(Debugger.REGS, sReg.substr(off, 2).toUpperCase()); if (off == null) {
i = usr.indexOf(Debugger.REGS, sReg);
} else {
i = usr.indexOf(Debugger.REGS, sReg.substr(off, 3));
if (i < 0) i = usr.indexOf(Debugger.REGS, sReg.substr(off, 2));
}
return i; return i;
}; };
@ -2806,7 +2873,7 @@ if (DEBUGGER) {
this.nCycles += nCyclesStep; this.nCycles += nCyclesStep;
this.cpu.addCycles(nCyclesStep, true); this.cpu.addCycles(nCyclesStep, true);
this.cpu.updateChecksum(nCyclesStep); this.cpu.updateChecksum(nCyclesStep);
this.cInstructions++; this.cOpcodes++;
} }
} }
catch (e) { catch (e) {
@ -2933,7 +3000,7 @@ if (DEBUGGER) {
Debugger.prototype.reset = function(fQuiet) Debugger.prototype.reset = function(fQuiet)
{ {
this.historyInit(); this.historyInit();
this.cInstructions = 0; this.cOpcodes = 0;
this.sMessagePrev = null; this.sMessagePrev = null;
this.nCycles = 0; this.nCycles = 0;
this.dbgAddrNextCode = this.newAddr(this.cpu.getIP(), this.cpu.getCS()); this.dbgAddrNextCode = this.newAddr(this.cpu.getIP(), this.cpu.getCS());
@ -3025,8 +3092,8 @@ if (DEBUGGER) {
var nCyclesPerSecond = (msTotal > 0? Math.round(this.nCycles * 1000 / msTotal) : 0); var nCyclesPerSecond = (msTotal > 0? Math.round(this.nCycles * 1000 / msTotal) : 0);
sStopped += " ("; sStopped += " (";
if (this.checksEnabled()) { if (this.checksEnabled()) {
sStopped += this.cInstructions + " ops, "; sStopped += this.cOpcodes + " opcodes, ";
this.cInstructions = 0; // remove this line if you want to maintain a longer total this.cOpcodes = 0; // remove this line if you want to maintain a longer total
} }
sStopped += this.nCycles + " cycles, " + msTotal + " ms, " + nCyclesPerSecond + " hz)"; sStopped += this.nCycles + " cycles, " + msTotal + " ms, " + nCyclesPerSecond + " hz)";
if (MAXDEBUG && this.chipset) { if (MAXDEBUG && this.chipset) {
@ -3114,7 +3181,7 @@ if (DEBUGGER) {
* well, OK, and a few other things now, like enabling Messages.INT messages. * well, OK, and a few other things now, like enabling Messages.INT messages.
*/ */
if (nState >= 0 && this.aaOpcodeCounts.length) { if (nState >= 0 && this.aaOpcodeCounts.length) {
this.cInstructions++; this.cOpcodes++;
var bOpcode = this.cpu.probeAddr(addr); var bOpcode = this.cpu.probeAddr(addr);
if (bOpcode != null) { if (bOpcode != null) {
this.aaOpcodeCounts[bOpcode][1]++; this.aaOpcodeCounts[bOpcode][1]++;
@ -3713,7 +3780,7 @@ if (DEBUGGER) {
} }
offset = (dbgAddr.off + disp) & (dbgAddr.fData32? -1 : 0xffff); offset = (dbgAddr.off + disp) & (dbgAddr.fData32? -1 : 0xffff);
sOperand = str.toHex(offset, dbgAddr.fData32? 8: 4); sOperand = str.toHex(offset, dbgAddr.fData32? 8: 4);
var aSymbol = this.findSymbolAtAddr(this.newAddr(offset, dbgAddr.sel)); var aSymbol = this.findSymbol(this.newAddr(offset, dbgAddr.sel));
if (aSymbol[0]) sOperand += " (" + aSymbol[0] + ")"; if (aSymbol[0]) sOperand += " (" + aSymbol[0] + ")";
} }
else if (typeMode == Debugger.TYPE_IMPREG) { else if (typeMode == Debugger.TYPE_IMPREG) {
@ -3806,7 +3873,7 @@ if (DEBUGGER) {
case Debugger.TYPE_FARP: case Debugger.TYPE_FARP:
dbgAddr = this.newAddr(this.getWord(dbgAddr, true), this.getShort(dbgAddr, 2), null, dbgAddr.fProt, dbgAddr.fData32, dbgAddr.fAddr32); dbgAddr = this.newAddr(this.getWord(dbgAddr, true), this.getShort(dbgAddr, 2), null, dbgAddr.fProt, dbgAddr.fData32, dbgAddr.fAddr32);
sOperand = this.hexAddr(dbgAddr); sOperand = this.hexAddr(dbgAddr);
var aSymbol = this.findSymbolAtAddr(dbgAddr); var aSymbol = this.findSymbol(dbgAddr);
if (aSymbol[0]) sOperand += " (" + aSymbol[0] + ")"; if (aSymbol[0]) sOperand += " (" + aSymbol[0] + ")";
break; break;
default: default:
@ -4182,7 +4249,7 @@ if (DEBUGGER) {
}; };
/** /**
* parseAddr(sAddr, type, fNoChecks) * parseAddr(sAddr, type, fNoChecks, fQuiet)
* *
* As discussed above, dbgAddr variables contain one or more of: off, sel, and addr. They represent * As discussed above, dbgAddr variables contain one or more of: off, sel, and addr. They represent
* a segmented address (sel:off) when sel is defined or a linear address (addr) when sel is undefined * a segmented address (sel:off) when sel is defined or a linear address (addr) when sel is undefined
@ -4207,14 +4274,17 @@ if (DEBUGGER) {
* @param {string|undefined} sAddr * @param {string|undefined} sAddr
* @param {number|undefined} [type] is the address segment type, in case sAddr doesn't specify a segment * @param {number|undefined} [type] is the address segment type, in case sAddr doesn't specify a segment
* @param {boolean} [fNoChecks] (eg, true when setting breakpoints that may not be valid now, but will be later) * @param {boolean} [fNoChecks] (eg, true when setting breakpoints that may not be valid now, but will be later)
* @param {boolean} [fQuiet]
* @return {DbgAddr|null|undefined} * @return {DbgAddr|null|undefined}
*/ */
Debugger.prototype.parseAddr = function(sAddr, type, fNoChecks) Debugger.prototype.parseAddr = function(sAddr, type, fNoChecks, fQuiet)
{ {
var dbgAddr; var dbgAddr, fPrint;
var dbgAddrNext = (type === Debugger.ADDR_CODE? this.dbgAddrNextCode : this.dbgAddrNextData); var dbgAddrNext = (type === Debugger.ADDR_CODE? this.dbgAddrNextCode : this.dbgAddrNextData);
var off = dbgAddrNext.off, sel = dbgAddrNext.sel, addr = dbgAddrNext.addr; var off = dbgAddrNext.off, sel = dbgAddrNext.sel, addr = dbgAddrNext.addr;
if (fQuiet) fPrint = false;
if (sAddr !== undefined) { if (sAddr !== undefined) {
if (sAddr.charAt(0) == '%') { if (sAddr.charAt(0) == '%') {
@ -4230,16 +4300,16 @@ if (DEBUGGER) {
var iColon = sAddr.indexOf(':'); var iColon = sAddr.indexOf(':');
if (iColon < 0) { if (iColon < 0) {
if (sel != null) { if (sel != null) {
off = this.parseExpression(sAddr); off = this.parseExpression(sAddr, fPrint);
addr = null; addr = null;
} else { } else {
addr = this.parseExpression(sAddr); addr = this.parseExpression(sAddr, fPrint);
if (addr == null) off = null; if (addr == null) off = null;
} }
} }
else { else {
sel = this.parseExpression(sAddr.substring(0, iColon)); sel = this.parseExpression(sAddr.substring(0, iColon), fPrint);
off = this.parseExpression(sAddr.substring(iColon + 1)); off = this.parseExpression(sAddr.substring(iColon + 1), fPrint);
addr = null; addr = null;
} }
} }
@ -4385,7 +4455,7 @@ if (DEBUGGER) {
* *
* @this {Debugger} * @this {Debugger}
* @param {string|undefined} sExp * @param {string|undefined} sExp
* @param {boolean} [fPrint] is true to print all resolved values * @param {boolean} [fPrint] is true to print all resolved values, false for quiet parsing
* @return {number|undefined} numeric value, or undefined if sExp contains any undefined or invalid values * @return {number|undefined} numeric value, or undefined if sExp contains any undefined or invalid values
*/ */
Debugger.prototype.parseExpression = function(sExp, fPrint) Debugger.prototype.parseExpression = function(sExp, fPrint)
@ -4413,7 +4483,7 @@ if (DEBUGGER) {
fError = true; fError = true;
break; break;
} }
var v = this.parseValue(s); var v = this.parseValue(s, null, fPrint === false);
if (v === undefined) { if (v === undefined) {
fError = true; fError = true;
fPrint = false; fPrint = false;
@ -4442,7 +4512,7 @@ if (DEBUGGER) {
}; };
/** /**
* parseValue(sValue, sName) * parseValue(sValue, sName, fQuiet)
* *
* @this {Debugger} * @this {Debugger}
* @param {string|undefined} sValue * @param {string|undefined} sValue
@ -4603,16 +4673,16 @@ if (DEBUGGER) {
* *
* [addr, size, aSymbols, aOffsetPairs] * [addr, size, aSymbols, aOffsetPairs]
* *
* There are two basic symbol operations: findSymbolAddr(), which takes a string and attempts to match it * There are two basic symbol operations: findSymbol(), which takes an address and finds the symbol, if any,
* to a non-anonymous symbol with a matching offset ('o') property, and findSymbolAtAddr(), which takes an * at that address, and findSymbolAddr(), which takes a string and attempts to match it to a non-anonymous
* address and finds the symbol, if any, at that address. * symbol with a matching offset ('o') property.
* *
* To implement findSymbolAtAddr() efficiently, addSymbols() creates an array of [offset, sSymbol] pairs * To implement findSymbol() efficiently, addSymbols() creates an array of [offset, sSymbol] pairs
* (aOffsetPairs), one pair for each symbol that corresponds to an offset within the specified address space. * (aOffsetPairs), one pair for each symbol that corresponds to an offset within the specified address space.
* *
* We guarantee the elements of aOffsetPairs are in offset order, because we build it using binaryInsert(); * We guarantee the elements of aOffsetPairs are in offset order, because we build it using binaryInsert();
* it's quite likely that the MAP file already ordered all its symbols in offset order, but since they're * it's quite likely that the MAP file already ordered all its symbols in offset order, but since they're
* hand-edited files, we can't assume that. This ensures that findSymbolAtAddr()'s binarySearch() will operate * hand-edited files, we can't assume that. This ensures that findSymbol()'s binarySearch() will operate
* properly. * properly.
* *
* @this {Debugger} * @this {Debugger}
@ -4689,6 +4759,55 @@ if (DEBUGGER) {
} }
}; };
/**
* findSymbol(dbgAddr, fNearest)
*
* Search aSymbolTable for dbgAddr, and return an Array for the corresponding symbol (empty if not found).
*
* If fNearest is true, and no exact match was found, then the Array returned will contain TWO sets of
* entries: [0]-[3] will refer to closest preceding symbol, and [4]-[7] will refer to the closest subsequent symbol.
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {boolean} [fNearest]
* @return {Array} where [0] == symbol name, [1] == symbol value, [2] == any annotation, and [3] == any associated comment
*/
Debugger.prototype.findSymbol = function(dbgAddr, fNearest)
{
var aSymbol = [];
var addr = this.getAddr(dbgAddr);
for (var iTable = 0; iTable < this.aSymbolTable.length; iTable++) {
var addrSymbol = this.aSymbolTable[iTable][0];
var sizeSymbol = this.aSymbolTable[iTable][1];
if (addr >= addrSymbol && addr < addrSymbol + sizeSymbol) {
var offset = dbgAddr.off;
var aOffsetPairs = this.aSymbolTable[iTable][3];
var fnComparePairs = function(p1, p2)
{
return p1[0] > p2[0]? 1 : p1[0] < p2[0]? -1 : 0;
};
var result = usr.binarySearch(aOffsetPairs, [offset], fnComparePairs);
if (result >= 0) {
this.returnSymbol(iTable, result, aSymbol);
}
else if (fNearest) {
result = ~result;
this.returnSymbol(iTable, result-1, aSymbol);
this.returnSymbol(iTable, result, aSymbol);
}
break;
}
}
if (!aSymbol.length) {
var sSymbol = this.bus.getSymbol(addr, true);
if (sSymbol) {
aSymbol.push(sSymbol);
aSymbol.push(addr);
}
}
return aSymbol;
};
/** /**
* findSymbolAddr(sSymbol) * findSymbolAddr(sSymbol)
* *
@ -4731,59 +4850,10 @@ if (DEBUGGER) {
return dbgAddr; return dbgAddr;
}; };
/**
* findSymbolAtAddr(dbgAddr, fNearest)
*
* Search aSymbolTable for dbgAddr, and return an Array for the corresponding symbol (empty if not found).
*
* If fNearest is true, and no exact match was found, then the Array returned will contain TWO sets of
* entries: [0]-[3] will refer to closest preceding symbol, and [4]-[7] will refer to the closest subsequent symbol.
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {boolean} [fNearest]
* @return {Array} where [0] == symbol name, [1] == symbol value, [2] == any annotation, and [3] == any associated comment
*/
Debugger.prototype.findSymbolAtAddr = function(dbgAddr, fNearest)
{
var aSymbol = [];
var addr = this.getAddr(dbgAddr);
for (var iTable = 0; iTable < this.aSymbolTable.length; iTable++) {
var addrSymbol = this.aSymbolTable[iTable][0];
var sizeSymbol = this.aSymbolTable[iTable][1];
if (addr >= addrSymbol && addr < addrSymbol + sizeSymbol) {
var offset = dbgAddr.off;
var aOffsetPairs = this.aSymbolTable[iTable][3];
var fnComparePairs = function(p1, p2)
{
return p1[0] > p2[0]? 1 : p1[0] < p2[0]? -1 : 0;
};
var result = usr.binarySearch(aOffsetPairs, [offset], fnComparePairs);
if (result >= 0) {
this.returnSymbol(iTable, result, aSymbol);
}
else if (fNearest) {
result = ~result;
this.returnSymbol(iTable, result-1, aSymbol);
this.returnSymbol(iTable, result, aSymbol);
}
break;
}
}
if (!aSymbol.length) {
var sSymbol = this.bus.getSymbol(addr, true);
if (sSymbol) {
aSymbol.push(sSymbol);
aSymbol.push(addr);
}
}
return aSymbol;
};
/** /**
* returnSymbol(iTable, iOffset, aSymbol) * returnSymbol(iTable, iOffset, aSymbol)
* *
* Helper function for findSymbolAtAddr(). * Helper function for findSymbol().
* *
* @param {number} iTable * @param {number} iTable
* @param {number} iOffset * @param {number} iOffset
@ -5108,40 +5178,38 @@ if (DEBUGGER) {
} }
if (sCmd == "dh") { if (sCmd == "dh") {
this.dumpHistory(sAddr, cb); this.dumpHistory(sAddr, sLen);
return;
}
if (sCmd == "di") {
var sInfo = this.dumpInfo(sAddr);
this.println(sInfo);
return; return;
} }
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_DATA); var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_DATA);
if (!dbgAddr) return; if (!dbgAddr || dbgAddr.sel == null && dbgAddr.addr == null) return;
var sDump = ""; var sDump = "";
if (BACKTRACK && sCmd == "di") { var cLines = (((cb || 128) + 15) >> 4) || 1;
var addr = this.getAddr(dbgAddr); var size = (sCmd == "dd"? 4 : (sCmd == "dw"? 2 : 1));
sDump += '%' + str.toHex(addr) + ": "; for (var iLine = 0; iLine < cLines; iLine++) {
var sInfo = this.bus.getSymbol(addr, true); var data = 0, iByte = 0;
sDump += sInfo || "no information"; var sData = "", sChars = "";
} sAddr = this.hexAddr(dbgAddr);
else { for (var i = 0; i < 16; i++) {
var cLines = (((cb || 128) + 15) >> 4) || 1; var b = this.getByte(dbgAddr, 1);
var size = (sCmd == "dd"? 4 : (sCmd == "dw"? 2 : 1)); data |= (b << (iByte++ << 3));
for (var iLine = 0; iLine < cLines; iLine++) { if (iByte == size) {
var data = 0, iByte = 0; sData += str.toHex(data, size * 2);
var sData = "", sChars = ""; sData += (size == 1? (i == 7? '-' : ' ') : " ");
sAddr = this.hexAddr(dbgAddr); data = iByte = 0;
for (var i = 0; i < 16; i++) {
var b = this.getByte(dbgAddr, 1);
data |= (b << (iByte++ << 3));
if (iByte == size) {
sData += str.toHex(data, size * 2);
sData += (size == 1? (i == 7? '-' : ' ') : " ");
data = iByte = 0;
}
sChars += (b >= 32 && b < 128? String.fromCharCode(b) : '.');
} }
if (sDump) sDump += '\n'; sChars += (b >= 32 && b < 128? String.fromCharCode(b) : '.');
sDump += sAddr + " " + sData + ' ' + sChars;
} }
if (sDump) sDump += '\n';
sDump += sAddr + " " + sData + ' ' + sChars;
} }
if (sDump) this.println(sDump); if (sDump) this.println(sDump);
@ -5418,7 +5486,7 @@ if (DEBUGGER) {
sSymbol = sSymbol? (sSymbol + ": ") : ""; sSymbol = sSymbol? (sSymbol + ": ") : "";
this.println(sSymbol + this.hexAddr(dbgAddr) + " (%" + str.toHex(addr, this.cchAddr) + ')'); this.println(sSymbol + this.hexAddr(dbgAddr) + " (%" + str.toHex(addr, this.cchAddr) + ')');
var aSymbol = this.findSymbolAtAddr(dbgAddr, true); var aSymbol = this.findSymbol(dbgAddr, true);
if (aSymbol.length) { if (aSymbol.length) {
var nDelta, sDelta; var nDelta, sDelta;
if (aSymbol[0]) { if (aSymbol[0]) {
@ -5496,7 +5564,7 @@ if (DEBUGGER) {
* this point (ie, if the disk is uninitialized and unformatted), but that will only affect whether the * this point (ie, if the disk is uninitialized and unformatted), but that will only affect whether the
* read succeeds or not. * read succeeds or not.
*/ */
var dc = this .fdc; var dc = this.fdc;
if (iDrive >= 2 && this.hdc) { if (iDrive >= 2 && this.hdc) {
iDrive -= 2; iDrive -= 2;
dc = this.hdc; dc = this.hdc;
@ -6380,7 +6448,7 @@ if (DEBUGGER) {
var addr = dbgAddr.addr; var addr = dbgAddr.addr;
var nSequence = (this.isBusy(false) || this.nStep)? this.nCycles : null; var nSequence = (this.isBusy(false) || this.nStep)? this.nCycles : null;
var sComment = (nSequence != null? "cycles" : null); var sComment = (nSequence != null? "cycles" : null);
var aSymbol = this.findSymbolAtAddr(dbgAddr); var aSymbol = this.findSymbol(dbgAddr);
if (aSymbol[0] && n) { if (aSymbol[0] && n) {
if (!cLines && n || aSymbol[0].indexOf('+') < 0) { if (!cLines && n || aSymbol[0].indexOf('+') < 0) {
@ -6395,7 +6463,7 @@ if (DEBUGGER) {
nSequence = null; nSequence = null;
} }
var sIns = this.getInstruction(dbgAddr, sComment, nSequence); var sInstruction = this.getInstruction(dbgAddr, sComment, nSequence);
/* /*
* If getInstruction() reported that it did not yet process a complete instruction (via dbgAddr.fComplete), * If getInstruction() reported that it did not yet process a complete instruction (via dbgAddr.fComplete),
@ -6404,7 +6472,7 @@ if (DEBUGGER) {
*/ */
if (!dbgAddr.fComplete && !n) n++; if (!dbgAddr.fComplete && !n) n++;
this.println(sIns); this.println(sInstruction);
this.dbgAddrNextCode = dbgAddr; this.dbgAddrNextCode = dbgAddr;
cb -= dbgAddr.addr - addr; cb -= dbgAddr.addr - addr;
cLines++; cLines++;

View file

@ -477,6 +477,29 @@ FileInfo.prototype.loadSegmentTable = function(offEntries, nEntries, nSegOffShif
} }
offEntries += 8; offEntries += 8;
} }
/*
* Although not documented (at least not in any of the early Windows "New Executable" documents I've seen),
* the Entry Table may also contain entries whose bSegment field is 0xFE, which doesn't correspond to a valid
* segment number. That pseudo-segment number appears to be reserved for constants. Here are some examples
* from a 3.1-vintage KRNL386.EXE:
*
* cannot find segment 254 (offset 0xF000) for symbol __ROMBIOS with ordinal 173
* cannot find segment 254 (offset 0x0000) for symbol __0000H with ordinal 183
* cannot find segment 254 (offset 0x0040) for symbol __0040H with ordinal 193
* cannot find segment 254 (offset 0x0008) for symbol __AHINCR with ordinal 114
* cannot find segment 254 (offset 0x0003) for symbol __AHSHIFT with ordinal 113
* cannot find segment 254 (offset 0xA000) for symbol __A000H with ordinal 174
* cannot find segment 254 (offset 0xB000) for symbol __B000H with ordinal 181
* cannot find segment 254 (offset 0xC000) for symbol __C000H with ordinal 195
* cannot find segment 254 (offset 0xB800) for symbol __B800H with ordinal 182
* cannot find segment 254 (offset 0xD000) for symbol __D000H with ordinal 179
* cannot find segment 254 (offset 0xE000) for symbol __E000H with ordinal 190
* cannot find segment 254 (offset 0xF000) for symbol __F000H with ordinal 194
* cannot find segment 254 (offset 0x0001) for symbol __WINFLAGS with ordinal 178
*
* The simplest way to handle those Entry Table entries is creating an additional (fake) aSegments table entry.
*/
this.aSegments[0xFE] = {offStart: 0, offEnd: 0, aEntries: []};
}; };
/** /**
@ -535,13 +558,11 @@ FileInfo.prototype.loadEntryTable = function(offEntries, offEntriesEnd)
} }
if (!this.aSegments[iSegment]) { if (!this.aSegments[iSegment]) {
if (MAXDEBUG) this.disk.println("invalid segment: " + iSegment); if (MAXDEBUG) this.disk.println("invalid segment: " + iSegment);
break; } else {
this.aSegments[iSegment].aEntries[iOrdinal] = [offEntry];
if (MAXDEBUG) this.disk.println("ordinal " + iOrdinal + ": segment=" + iSegment + " offset=" + str.toHexLong(offEntry) + " @" + str.toHex(offDebug));
} }
this.aSegments[iSegment].aEntries[iOrdinal] = [offEntry]; this.aOrdinals[iOrdinal] = [iSegment, offEntry];
if (MAXDEBUG) this.disk.println("ordinal " + iOrdinal + ": segment=" + iSegment + " offset=" + str.toHexLong(offEntry) + " @" + str.toHex(offDebug));
this.aOrdinals[iOrdinal] = iSegment;
iOrdinal++; iOrdinal++;
} }
} }
@ -571,7 +592,7 @@ FileInfo.prototype.loadNameTable = function(offEntries, offEntriesEnd)
if (!bLength) break; if (!bLength) break;
var sSymbol = this.loadString(offEntries + 1, bLength); var sSymbol = this.loadString(offEntries + 1, bLength);
if (!sSymbol) break; // an error must have occurred (this is not a natural way to end) if (!sSymbol) break; // an error must have occurred (this is not a natural way to end)
offEntries += 1 + bLength; offEntries += 1 + bLength;
if (!cNames) { if (!cNames) {
@ -583,14 +604,19 @@ FileInfo.prototype.loadNameTable = function(offEntries, offEntriesEnd)
} }
else { else {
var iOrdinal = this.loadValue(offEntries); var iOrdinal = this.loadValue(offEntries);
var iSegment = this.aOrdinals[iOrdinal]; var tuple = this.aOrdinals[iOrdinal];
if (iSegment !== undefined) { if (tuple) {
var aEntries = this.aSegments[iSegment].aEntries[iOrdinal]; var iSegment = tuple[0]; // tuple[0] is the segment number and tuple[1] is the corresponding offEntry
this.disk.assert(aEntries && aEntries.length == 1); if (this.aSegments[iSegment]) {
aEntries.push(sSymbol); var aEntries = this.aSegments[iSegment].aEntries[iOrdinal];
if (MAXDEBUG) this.disk.println("segment " + iSegment + " offset " + str.toHexWord(aEntries[0]) + " ordinal " + iOrdinal + ": " + sSymbol + " @" + str.toHex(offDebug)); this.disk.assert(aEntries && aEntries.length == 1);
aEntries.push(sSymbol);
if (MAXDEBUG) this.disk.println("segment " + iSegment + " offset " + str.toHexWord(aEntries[0]) + " ordinal " + iOrdinal + ": " + sSymbol + " @" + str.toHex(offDebug));
} else {
if (MAXDEBUG) this.disk.println(this.sPath + ": cannot find segment " + iSegment + " (offset " + str.toHexWord(tuple[1]) + ") for symbol " + sSymbol + " with ordinal " + iOrdinal + " @" + str.toHex(offDebug));
}
} else { } else {
if (MAXDEBUG) this.disk.println(this.sPath + ": cannot find ordinal " + iOrdinal + " @" + str.toHex(offDebug)); if (MAXDEBUG) this.disk.println(this.sPath + ": cannot find ordinal " + iOrdinal + " for symbol " + sSymbol + " @" + str.toHex(offDebug));
} }
} }
offEntries += 2; offEntries += 2;
@ -700,7 +726,7 @@ FileInfo.prototype.getSymbol = function(off, fNearest)
} }
} }
} }
return sSymbol || this.sName + '[' + str.toHexLong(off) + ']'; return sSymbol || this.sName + '+' + str.toHexLong(off);
}; };
/** /**
@ -1537,6 +1563,40 @@ Disk.prototype.buildFileTable = function()
return this.aFileTable; return this.aFileTable;
}; };
/**
* getSymbolInfo(sSymbol)
*
* For all whole or partial symbol matches, return them in an Array of entries:
*
* [symbol, file name, segment number, segment offset, segment size].
*
* TODO: This function has many limitations (ie, slow, case-sensitive), but it gets the job done for now.
*
* @this {Disk}
* @param {string} sSymbol
* @return {Array}
*/
Disk.prototype.getSymbolInfo = function(sSymbol)
{
var aInfo = [];
if (this.aFileTable) {
var sSymbolUpper = sSymbol.toUpperCase();
for (var iFile = 0; iFile < this.aFileTable.length; iFile++) {
var file = this.aFileTable[iFile];
for (var iSegment in file.aSegments) {
var segment = file.aSegments[iSegment];
for (var iEntry in segment.aEntries) {
var entry = segment.aEntries[iEntry];
if (entry[1] && entry[1].indexOf(sSymbolUpper) >= 0) {
aInfo.push([entry[1], file.sName, iSegment, entry[0], segment.offEnd - segment.offStart]);
}
}
}
}
}
return aInfo;
};
/** /**
* getDir(dir, sDisk, sDir, apba) * getDir(dir, sDisk, sDir, apba)
* *