diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index f4f95b728..e03256a2c 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -113,7 +113,7 @@ function Debugger(parmsDbg) * call and simply represents the number of cycles performed by the last run of instructions. */ 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); @@ -627,7 +627,7 @@ if (DEBUGGER) { }; Debugger.TRACE_LIMIT = 100000; - Debugger.HISTORY_LIMIT = 100000; + Debugger.HISTORY_LIMIT = DEBUG? 100000 : 10000; /* * Opcode 0x0F has a distinguished history: @@ -1963,6 +1963,39 @@ if (DEBUGGER) { 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) * @@ -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} - * @param {string} [sCount] is the 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} [sPrev] is a (decimal) number of instructions to rewind to (default is 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 = ""; - cLines = cLines || 10; var cHistory = 0; var iHistory = this.iOpcodeHistory; var aHistory = this.aOpcodeHistory; + if (aHistory.length) { - var n = (sCount === undefined? this.nextHistory : +sCount); // warning: decimal instead of hex conversion - if (isNaN(n)) - n = cLines; - else + var nPrev = +sPrev || this.nextHistory; + var nLines = +sLines || 10; + + if (isNaN(nPrev)) { + nPrev = nLines; + } else { 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 (aHistory[aHistory.length - 1][1] != null) { - iHistory += aHistory.length; - } else { - n = iHistory + n; + /* + * If the dbgAddr of the last aHistory element contains a valid selector, wrap around. + */ + if (aHistory[aHistory.length - 1].sel == null) { + nPrev = iHistory + nPrev; 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) * 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. */ var fData32 = null, fAddr32 = null; - while (cLines > 0 && iHistory != this.iOpcodeHistory) { + + while (nLines > 0 && iHistory != this.iOpcodeHistory) { + var dbgAddr = aHistory[iHistory++]; if (dbgAddr.sel == null) break; + /* * 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. */ 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() * will have automatically disassembled additional bytes, so skip additional history entries. @@ -2125,13 +2185,14 @@ if (DEBUGGER) { if (!dbgAddr.cOverrides) { fData32 = fAddr32 = null; } 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; } + if (iHistory >= aHistory.length) iHistory = 0; - this.nextHistory = n; + this.nextHistory = nPrev; cHistory++; - cLines--; + nLines--; } /* * See comments above. @@ -2139,6 +2200,7 @@ if (DEBUGGER) { * this.nSuppressBreaks--; */ } + if (!cHistory) { this.println("no " + sMore + "history available"); this.nextHistory = undefined; @@ -2258,9 +2320,14 @@ if (DEBUGGER) { * @return {number} register index, or -1 if not found */ Debugger.prototype.getRegIndex = function(sReg, off) { - off = off || 0; - var i = usr.indexOf(Debugger.REGS, sReg.substr(off, 3).toUpperCase()); - if (i < 0) i = usr.indexOf(Debugger.REGS, sReg.substr(off, 2).toUpperCase()); + var i; + sReg = sReg.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; }; @@ -2806,7 +2873,7 @@ if (DEBUGGER) { this.nCycles += nCyclesStep; this.cpu.addCycles(nCyclesStep, true); this.cpu.updateChecksum(nCyclesStep); - this.cInstructions++; + this.cOpcodes++; } } catch (e) { @@ -2933,7 +3000,7 @@ if (DEBUGGER) { Debugger.prototype.reset = function(fQuiet) { this.historyInit(); - this.cInstructions = 0; + this.cOpcodes = 0; this.sMessagePrev = null; this.nCycles = 0; 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); sStopped += " ("; if (this.checksEnabled()) { - sStopped += this.cInstructions + " ops, "; - this.cInstructions = 0; // remove this line if you want to maintain a longer total + sStopped += this.cOpcodes + " opcodes, "; + this.cOpcodes = 0; // remove this line if you want to maintain a longer total } sStopped += this.nCycles + " cycles, " + msTotal + " ms, " + nCyclesPerSecond + " hz)"; if (MAXDEBUG && this.chipset) { @@ -3114,7 +3181,7 @@ if (DEBUGGER) { * well, OK, and a few other things now, like enabling Messages.INT messages. */ if (nState >= 0 && this.aaOpcodeCounts.length) { - this.cInstructions++; + this.cOpcodes++; var bOpcode = this.cpu.probeAddr(addr); if (bOpcode != null) { this.aaOpcodeCounts[bOpcode][1]++; @@ -3713,7 +3780,7 @@ if (DEBUGGER) { } offset = (dbgAddr.off + disp) & (dbgAddr.fData32? -1 : 0xffff); 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] + ")"; } else if (typeMode == Debugger.TYPE_IMPREG) { @@ -3806,7 +3873,7 @@ if (DEBUGGER) { case Debugger.TYPE_FARP: dbgAddr = this.newAddr(this.getWord(dbgAddr, true), this.getShort(dbgAddr, 2), null, dbgAddr.fProt, dbgAddr.fData32, dbgAddr.fAddr32); sOperand = this.hexAddr(dbgAddr); - var aSymbol = this.findSymbolAtAddr(dbgAddr); + var aSymbol = this.findSymbol(dbgAddr); if (aSymbol[0]) sOperand += " (" + aSymbol[0] + ")"; break; 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 * 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 {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} [fQuiet] * @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 off = dbgAddrNext.off, sel = dbgAddrNext.sel, addr = dbgAddrNext.addr; + if (fQuiet) fPrint = false; + if (sAddr !== undefined) { if (sAddr.charAt(0) == '%') { @@ -4230,16 +4300,16 @@ if (DEBUGGER) { var iColon = sAddr.indexOf(':'); if (iColon < 0) { if (sel != null) { - off = this.parseExpression(sAddr); + off = this.parseExpression(sAddr, fPrint); addr = null; } else { - addr = this.parseExpression(sAddr); + addr = this.parseExpression(sAddr, fPrint); if (addr == null) off = null; } } else { - sel = this.parseExpression(sAddr.substring(0, iColon)); - off = this.parseExpression(sAddr.substring(iColon + 1)); + sel = this.parseExpression(sAddr.substring(0, iColon), fPrint); + off = this.parseExpression(sAddr.substring(iColon + 1), fPrint); addr = null; } } @@ -4385,7 +4455,7 @@ if (DEBUGGER) { * * @this {Debugger} * @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 */ Debugger.prototype.parseExpression = function(sExp, fPrint) @@ -4413,7 +4483,7 @@ if (DEBUGGER) { fError = true; break; } - var v = this.parseValue(s); + var v = this.parseValue(s, null, fPrint === false); if (v === undefined) { fError = true; fPrint = false; @@ -4442,7 +4512,7 @@ if (DEBUGGER) { }; /** - * parseValue(sValue, sName) + * parseValue(sValue, sName, fQuiet) * * @this {Debugger} * @param {string|undefined} sValue @@ -4603,16 +4673,16 @@ if (DEBUGGER) { * * [addr, size, aSymbols, aOffsetPairs] * - * There are two basic symbol operations: findSymbolAddr(), which takes a string and attempts to match it - * to a non-anonymous symbol with a matching offset ('o') property, and findSymbolAtAddr(), which takes an - * address and finds the symbol, if any, at that address. + * There are two basic symbol operations: findSymbol(), which takes an address and finds the symbol, if any, + * at that address, and findSymbolAddr(), which takes a string and attempts to match it to a non-anonymous + * 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. * * 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 - * 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. * * @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) * @@ -4731,59 +4850,10 @@ if (DEBUGGER) { 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) * - * Helper function for findSymbolAtAddr(). + * Helper function for findSymbol(). * * @param {number} iTable * @param {number} iOffset @@ -5108,40 +5178,38 @@ if (DEBUGGER) { } if (sCmd == "dh") { - this.dumpHistory(sAddr, cb); + this.dumpHistory(sAddr, sLen); + return; + } + + if (sCmd == "di") { + var sInfo = this.dumpInfo(sAddr); + this.println(sInfo); return; } var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_DATA); - if (!dbgAddr) return; + if (!dbgAddr || dbgAddr.sel == null && dbgAddr.addr == null) return; var sDump = ""; - if (BACKTRACK && sCmd == "di") { - var addr = this.getAddr(dbgAddr); - sDump += '%' + str.toHex(addr) + ": "; - var sInfo = this.bus.getSymbol(addr, true); - sDump += sInfo || "no information"; - } - else { - var cLines = (((cb || 128) + 15) >> 4) || 1; - var size = (sCmd == "dd"? 4 : (sCmd == "dw"? 2 : 1)); - for (var iLine = 0; iLine < cLines; iLine++) { - var data = 0, iByte = 0; - var sData = "", sChars = ""; - sAddr = this.hexAddr(dbgAddr); - 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) : '.'); + var cLines = (((cb || 128) + 15) >> 4) || 1; + var size = (sCmd == "dd"? 4 : (sCmd == "dw"? 2 : 1)); + for (var iLine = 0; iLine < cLines; iLine++) { + var data = 0, iByte = 0; + var sData = "", sChars = ""; + sAddr = this.hexAddr(dbgAddr); + 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; } - if (sDump) sDump += '\n'; - sDump += sAddr + " " + sData + ' ' + sChars; + sChars += (b >= 32 && b < 128? String.fromCharCode(b) : '.'); } + if (sDump) sDump += '\n'; + sDump += sAddr + " " + sData + ' ' + sChars; } if (sDump) this.println(sDump); @@ -5418,7 +5486,7 @@ if (DEBUGGER) { sSymbol = sSymbol? (sSymbol + ": ") : ""; 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) { var nDelta, sDelta; 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 * read succeeds or not. */ - var dc = this .fdc; + var dc = this.fdc; if (iDrive >= 2 && this.hdc) { iDrive -= 2; dc = this.hdc; @@ -6380,7 +6448,7 @@ if (DEBUGGER) { var addr = dbgAddr.addr; var nSequence = (this.isBusy(false) || this.nStep)? this.nCycles : null; var sComment = (nSequence != null? "cycles" : null); - var aSymbol = this.findSymbolAtAddr(dbgAddr); + var aSymbol = this.findSymbol(dbgAddr); if (aSymbol[0] && n) { if (!cLines && n || aSymbol[0].indexOf('+') < 0) { @@ -6395,7 +6463,7 @@ if (DEBUGGER) { 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), @@ -6404,7 +6472,7 @@ if (DEBUGGER) { */ if (!dbgAddr.fComplete && !n) n++; - this.println(sIns); + this.println(sInstruction); this.dbgAddrNextCode = dbgAddr; cb -= dbgAddr.addr - addr; cLines++; diff --git a/modules/pcjs/lib/disk.js b/modules/pcjs/lib/disk.js index 69a59e901..d0ce46678 100644 --- a/modules/pcjs/lib/disk.js +++ b/modules/pcjs/lib/disk.js @@ -477,6 +477,29 @@ FileInfo.prototype.loadSegmentTable = function(offEntries, nEntries, nSegOffShif } 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 (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]; - - if (MAXDEBUG) this.disk.println("ordinal " + iOrdinal + ": segment=" + iSegment + " offset=" + str.toHexLong(offEntry) + " @" + str.toHex(offDebug)); - - this.aOrdinals[iOrdinal] = iSegment; + this.aOrdinals[iOrdinal] = [iSegment, offEntry]; iOrdinal++; } } @@ -571,7 +592,7 @@ FileInfo.prototype.loadNameTable = function(offEntries, offEntriesEnd) if (!bLength) break; 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; if (!cNames) { @@ -583,14 +604,19 @@ FileInfo.prototype.loadNameTable = function(offEntries, offEntriesEnd) } else { var iOrdinal = this.loadValue(offEntries); - var iSegment = this.aOrdinals[iOrdinal]; - if (iSegment !== undefined) { - var aEntries = this.aSegments[iSegment].aEntries[iOrdinal]; - 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)); + var tuple = this.aOrdinals[iOrdinal]; + if (tuple) { + var iSegment = tuple[0]; // tuple[0] is the segment number and tuple[1] is the corresponding offEntry + if (this.aSegments[iSegment]) { + var aEntries = this.aSegments[iSegment].aEntries[iOrdinal]; + 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 { - 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; @@ -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; }; +/** + * 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) *