v1.18.9: Added loading of 16-bit Windows binary symbol data (for non-COMPILED BACKTRACK configs only)

This commit is contained in:
Jeff Parsons 2015-08-18 15:47:51 -07:00
commit 989407ca4a
9 changed files with 4176 additions and 3681 deletions

View file

@ -1131,13 +1131,15 @@ Bus.prototype.getBackTrackObjectFromAddr = function(addr)
};
/**
* getBackTrackInfo(bti)
* getBackTrackInfo(bti, fSymbol, fNearest)
*
* @this {Bus}
* @param {number} bti
* @param {boolean} [fSymbol] (true to return only symbol)
* @param {boolean} [fNearest] (true to return nearest symbol)
* @return {string|null}
*/
Bus.prototype.getBackTrackInfo = function(bti)
Bus.prototype.getBackTrackInfo = function(bti, fSymbol, fNearest)
{
if (BACKTRACK) {
var bto = this.getBackTrackObject(bti);
@ -1146,9 +1148,11 @@ Bus.prototype.getBackTrackInfo = function(bti)
var file = bto.obj.file;
if (file) {
this.assert(!bto.off);
return file.sName + '[' + str.toHexLong(bto.obj.offFile + off) + ']';
return file.getSymbol(bto.obj.offFile + off, fNearest);
}
if (!fSymbol || fNearest) {
return bto.obj.idComponent + '+' + str.toHexLong(bto.off + off);
}
return bto.obj.idComponent + '[' + str.toHexLong(bto.off + off) + ']';
}
}
return null;
@ -1166,6 +1170,19 @@ Bus.prototype.getBackTrackInfoFromAddr = function(addr)
return BACKTRACK? this.getBackTrackInfo(this.readBackTrack(addr)) : null;
};
/**
* getSymbol(addr, fNearest)
*
* @this {Bus}
* @param {number} addr
* @param {boolean} [fNearest] (true to return nearest symbol)
* @return {string|null}
*/
Bus.prototype.getSymbol = function(addr, fNearest)
{
return BACKTRACK? this.getBackTrackInfo(this.readBackTrack(addr), true, fNearest) : null;
};
/**
* saveMemory()
*

View file

@ -3689,8 +3689,9 @@ if (DEBUGGER) {
disp = this.getWord(dbgAddr, true);
}
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));
sOperand = aSymbol[0] || str.toHex(offset, dbgAddr.fData32? 8: 4);
if (aSymbol[0]) sOperand += " (" + aSymbol[0] + ")";
}
else if (typeMode == Debugger.TYPE_IMPREG) {
sOperand = this.getRegOperand((type & Debugger.TYPE_IREG) >> 8, type, dbgAddr);
@ -3754,6 +3755,7 @@ if (DEBUGGER) {
{
var sOperand = ' ';
var typeSize = type & Debugger.TYPE_SIZE;
switch (typeSize) {
case Debugger.TYPE_BYTE:
/*
@ -3779,7 +3781,10 @@ if (DEBUGGER) {
sOperand = str.toHex(this.getShort(dbgAddr, 2), 4);
break;
case Debugger.TYPE_FARP:
sOperand = this.hexAddr(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);
var aSymbol = this.findSymbolAtAddr(dbgAddr);
if (aSymbol[0]) sOperand += " (" + aSymbol[0] + ")";
break;
default:
sOperand = "imm(" + str.toHexWord(type) + ')';
@ -4714,7 +4719,7 @@ if (DEBUGGER) {
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {boolean} [fNearest]
* @return {Array|null} where [0] == symbol name, [1] == symbol value, [2] == any annotation, and [3] == any associated comment
* @return {Array} where [0] == symbol name, [1] == symbol value, [2] == any annotation, and [3] == any associated comment
*/
Debugger.prototype.findSymbolAtAddr = function(dbgAddr, fNearest)
{
@ -4742,6 +4747,13 @@ if (DEBUGGER) {
break;
}
}
if (!aSymbol.length) {
var sSymbol = this.bus.getSymbol(addr, true);
if (sSymbol) {
aSymbol.push(sSymbol);
aSymbol.push(addr);
}
}
return aSymbol;
};
@ -5084,7 +5096,7 @@ if (DEBUGGER) {
if (BACKTRACK && sCmd == "di") {
var addr = this.getAddr(dbgAddr);
sDump += '%' + str.toHex(addr) + ": ";
var sInfo = this.bus.getBackTrackInfoFromAddr(addr);
var sInfo = this.bus.getSymbol(addr, true);
sDump += sInfo || "no information";
}
else {
@ -6221,7 +6233,7 @@ if (DEBUGGER) {
}
}
if (!sCall) break;
sCall = str.pad(sCall, 50) + ";stack=" + this.hexAddr(dbgAddrStack) + " return=" + this.hexAddr(dbgAddrCall);
sCall = str.pad(sCall, 50) + " ;stack=" + this.hexAddr(dbgAddrStack) + " return=" + this.hexAddr(dbgAddrCall);
this.println(sCall);
cFrames++;
}
@ -6347,10 +6359,12 @@ if (DEBUGGER) {
var sComment = (nSequence != null? "cycles" : null);
var aSymbol = this.findSymbolAtAddr(dbgAddr);
if (aSymbol[0]) {
var sLabel = aSymbol[0] + ':';
if (aSymbol[2]) sLabel += ' ' + aSymbol[2];
this.println(sLabel);
if (aSymbol[0] && n) {
if (!cLines && n || aSymbol[0].indexOf('+') < 0) {
var sLabel = aSymbol[0] + ':';
if (aSymbol[2]) sLabel += ' ' + aSymbol[2];
this.println(sLabel);
}
}
if (aSymbol[3]) {
@ -6453,7 +6467,7 @@ if (DEBUGGER) {
sCmd = "a " + this.hexAddr(this.dbgAddrAssemble) + ' ' + sCmd;
}
var asArgs = sCmd.split(' ');
var asArgs = sCmd.replace(/ +/g, ' ').split(' ');
var ch0 = asArgs[0].charAt(0).toLowerCase();
switch (ch0) {

View file

@ -229,16 +229,479 @@ function Disk(controller, drive, mode)
}
/**
* @typedef {{
* sPath: string,
* sName: string,
* bAttr: number,
* cbSize: number,
* apba: Array.<number>,
* disk: Disk
* }}
* The default number of milliseconds to wait before writing a dirty sector back to a remote disk image
*
* @const {number}
*/
var FileInfo;
Disk.REMOTE_WRITE_DELAY = 2000; // 2-second delay
/*
* A global disk count, used to form unique Disk component IDs (totally optional; for debugging purposes only)
*/
Disk.nDisks = 0;
Component.subclass(Disk);
/**
* FileInfo(disk, sPath, sName, bAttr, cbSize, apba)
*
* To the basic file information below, loadSymbols() may also add:
*
* sModule
* sDescription
* aSegments[]
*
* which is indexed by 1-based segment numbers, where each aSegments[] element is an object
* containing:
*
* offStart (file-relative offset of start of segment data)
* offEnd (file-relative offset of end of segment data)
* aEntries[]
*
* where aEntries is an array indexed by 1-based ordinals, where each aEntries[] element contains:
*
* [offset, symbol]
*
* where offset is relative to the segment's offStart value, and symbol is a string describing the
* entry.
*
* There will always be an offset at index 0 of an aEntries[] element, but some error or incomplete
* symbolic information could result in a missing symbol at index 1, because symbol name processing is
* separate from entry table processing.
*
* @constructor
* @param {Disk} disk
* @param {string} sPath
* @param {string} sName
* @param {number} bAttr
* @param {number} cbSize
* @param {Array.<number>} apba
*/
function FileInfo(disk, sPath, sName, bAttr, cbSize, apba)
{
this.disk = disk;
this.sPath = sPath;
this.sName = sName;
this.bAttr = bAttr;
this.cbSize = cbSize;
this.apba = apba;
}
/*
* Original (aka "Old") Executable MS-DOS File Format
*
* Relocation entries are pairs of 16-bit words:
*
* wOffset
* wSegment
*
* I've noticed that a "PKLITE" EXE may have a oeRelocOffset of 0x52, where the word at 0x001C is 0x210F and the
* bytes from 0x001E through 0x0051 are:
*
* "PKLITE Copr. 1990-92 PKWARE Inc. All Rights Reserved"
*
* Other EXEs have a oeRelocOffset of 0x1E, which begs the question: what is the word at 0x001C typically used for?
*
* It was not uncommon for there to be wasted space in the header; even an EXE with, say, 20 (0x14) entries would
* likely have a wHeaderParas value of 0x20, which is 512 (0x200) bytes. The desire, no doubt, was to align the
* start of the EXE segment(s) to a traditional sector boundary.
*/
FileInfo.OE = {
SIG: 0x5A4D,
oeSignature: [0x0000, 2], // "MZ" (0x4D,0x5A)
oeLastBytes: [0x0002, 2], // 0-511 (0 means the entire last block is used)
oeBlocks: [0x0004, 2], // number of blocks in the file
oeRelocEntries: [0x0006, 2], // number of relocation entries in the header
oeHeaderParas: [0x0008, 2], // number of (16-byte) paragraphs in the header
oeExtraParas: [0x000A, 2], // minimum number of additional paragraphs required at load-time
oeMaxParas: [0x000C, 2], // maximum number of additional paragraphs required at load-time
oeSSRel: [0x000E, 2], // relative value of SS
oeSPInit: [0x0010, 2], // initial value of SP
oeChecksum: [0x0012, 2], // checksum if non-zero (sum of all words, including this, should be zero)
oeIPInit: [0x0014, 2], // initial value of IP
oeCSRel: [0x0016, 2], // relative value of CS
oeRelocOffset: [0x0018, 2], // offset of first relocation item
oeOverlay: [0x001A, 2], // overlay number (normally zero, implying main program)
/*
* The following fields are accommodated by the NE format, but they were actually defined by "the DOS 4.0 group"
* as extensions to the OE format.
*/
oeDOS40Bits: [0x0020, 2], // DOS 4.0 behavior bits
oeUnusedBits: [0x0022, 2], // unused behavior bits
/*
* If oeRelocOffset (0x0018) is 0x40, then the file is considered an NE (New Executable) MS-DOS file, and
* the offset of the NE header (from the start of the file) is a 32-bit value stored at 0x003C. Note that early
* versions of Windows (aka "DOS 2.0 Windows") originally defined the NE header offset as a 16-bit value stored
* at 0x003E. And before that, it may have been a 16-bit value stored at 0x0024, which would have been immediately
* after the "behavior bits" fields shown above).
*/
oeNEHeader: [0x003C, 4], // offset from start of file to NE header
NE_SIG: 0x40
};
/*
* New Executable MS-DOS File Format
*
* Unless otherwise specified, all *Offset fields are relative to the start of the NE header, and all *Size fields
* are in bytes.
*/
FileInfo.NE = {
SIG: 0x454E,
neSignature: [0x0000, 2], // "NE" (0x4E,0x45)
neLinkerVer: [0x0002, 2], // (low byte is version, high byte is revision)
neETOffset: [0x0004, 2], // Entry Table offset
neETSize: [0x0006, 2], // Entry Table size
neChecksum: [0x0008, 4], // checksum (sum of all DWORDs in the file, excluding this one)
neFlags: [0x000C, 2],
neDataSeg: [0x000E, 2],
neHeapSize: [0x0010, 2],
neStackSize: [0x0012, 2],
neCSIP: [0x0014, 4],
neSSSP: [0x0018, 4],
neSTEntries: [0x001C, 2], // Segment Table entries
neMRTEntries: [0x001E, 2], // Module Reference Table entries
neNRNTSize: [0x0020, 2], // Non-Resident Name Table size
neSTOffset: [0x0022, 2], // Segment Table offset
neRTOffset: [0x0024, 2], // Resource Table offset
neRNTOffset: [0x0026, 2], // Resident Name Table offset
neMRTOffset: [0x0028, 2], // Module Reference Table offset
neINTOffset: [0x002A, 2], // Imported Names Table offset
neNRNTOffset: [0x002C, 4], // Non-Resident Name Table offset (relative to start of file)
neETMovable: [0x0030, 2], // number of movable entries in the Entry Table
neSegOffShift: [0x0032, 2], // logical sector alignment shift count, log(base 2) of the segment sector size (default 9)
/*
* Fields after this point are post "DOS 2.0 Windows"...
*/
neRTEntries: [0x0034, 2], // Resource Table entries
neEXEType: [0x0036, 1] // executable type (0x02 for Windows)
/*
* 0x37 through 0x3F is reserved.
*/
};
/**
* loadValue(offset, length)
*
* @this {FileInfo}
* @param {number} offset
* @param {number} [length] (1, 2 or 4 bytes; default is 2)
* @return {number|undefined}
*/
FileInfo.prototype.loadValue = function(offset, length)
{
var l;
length = length || 2;
var iSector = offset >> 9;
var offSector = offset & 0x1ff;
var sector = this.disk.getSector(this.apba[iSector]);
if (sector) {
/*
* If the read is wholly contained within a sector, read it with one call.
*/
if (offSector + length <= sector['length']) {
return this.disk.getSectorData(sector, offSector, length);
}
/*
* The spans a sector boundary, so we just call ourselves one byte at a time.
*/
l = 0;
var shift = 0;
while (length--) {
l |= this.loadValue(offset++, 1) << shift;
shift += 8;
}
}
return l;
};
/**
* loadString(offset, length)
*
* @this {FileInfo}
* @param {number} offset
* @param {number} [length] (if omitted, then string must be zero-terminated)
* @return {string}
*/
FileInfo.prototype.loadString = function(offset, length)
{
var s = "";
if (!length) length = -1;
while (length--) {
var b = this.loadValue(offset++, 1);
if (!b) break;
s += String.fromCharCode(b);
}
return s;
};
/**
* loadField(aField, offset)
*
* @this {FileInfo}
* @param {Array.<number>} aField
* @param {number} [offset] (0 if not specified)
* @return {number|undefined}
*/
FileInfo.prototype.loadField = function(aField, offset)
{
return this.loadValue(aField[0] + (offset || 0), aField[1]);
};
/**
* loadSegmentTable(offEntries, nEntries, nSegOffShift)
*
* @this {FileInfo}
* @param {number} offEntries
* @param {number} nEntries
* @param {number} nSegOffShift
*/
FileInfo.prototype.loadSegmentTable = function(offEntries, nEntries, nSegOffShift)
{
/*
* Read the Segment Table entries now.
*/
var iSegment = 1;
this.aSegments = [];
this.aOrdinals = []; // this is an optional array for quick ordinal-to-segment lookup
if (MAXDEBUG) this.disk.println("loadSegmentTable(" + this.sPath + "," + str.toHexLong(offEntries) + "," + str.toHexWord(nEntries) + ")");
while (nEntries--) {
var offSegment = this.loadValue(offEntries) << nSegOffShift;
if (offSegment) {
var lenSegment = this.loadValue(offEntries + 2) || 0x10000; // 0 means 64K
if (MAXDEBUG) this.disk.println("segment " + iSegment + ": offStart=" + str.toHexLong(offSegment) + " offEnd=" + str.toHexLong(offSegment + lenSegment));
this.aSegments[iSegment++] = {offStart: offSegment, offEnd: offSegment + lenSegment - 1, aEntries: []};
}
offEntries += 8;
}
};
/**
* loadEntryTable(offEntries, offEntriesEnd)
*
* @this {FileInfo}
* @param {number} offEntries
* @param {number} offEntriesEnd
*/
FileInfo.prototype.loadEntryTable = function(offEntries, offEntriesEnd)
{
var iOrdinal = 1;
if (MAXDEBUG) this.disk.println("loadEntryTable(" + str.toHexLong(offEntries) + "," + str.toHexLong(offEntriesEnd) + ")");
while (offEntries < offEntriesEnd) {
var w = this.loadValue(offEntries);
var bEntries = w & 0xff;
if (!bEntries) break;
var bSegment = w >> 8, iSegment;
if (MAXDEBUG) this.disk.println("bundle for segment " + bSegment + ": " + bEntries + " entries @" + str.toHex(offEntries));
offEntries += 2;
/*
* bSegment 0x00 means all the entries spanned by bEntries are unused, so move on.
*/
if (!bSegment) {
iOrdinal += bEntries;
continue;
}
while (bEntries--) {
/*
* bSegment 0x01-0xFE means the next 3 bytes describe a fixed segment entry; the next
* byte contains flags indicating exported (0x1) and/or global/shared (0x2) data, and the
* next word is the offset within the segment.
*/
var offEntry;
var offDebug = offEntries;
var bFlags = this.loadValue(offEntries, 1);
if (bSegment <= 0xFE) {
iSegment = bSegment;
offEntry = this.loadValue(offEntries + 1);
offEntries += 3;
} else {
/*
* bSegment 0xFF means a movable segment entry, which is 6 bytes long: flags byte (which
* we've already read), an INT 0x3F (0xCD,0x3F), a 1-byte segment number, and a 2-byte offset.
*/
iSegment = this.loadValue(offEntries + 3, 1);
offEntry = this.loadValue(offEntries + 4);
offEntries += 6;
}
if (!this.aSegments[iSegment]) {
if (MAXDEBUG) this.disk.println("invalid segment: " + iSegment);
break;
}
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;
iOrdinal++;
}
}
};
/**
* loadNameTable(aField, offset)
*
* NOTE: If offset is omitted, we assume we're reading the Resident Name Table, and therefore
* the first name is the module name; otherwise, we assume it is the Non-Resident Name Table, and
* that the first name is the module description.
*
* @this {FileInfo}
* @param {number} offEntries
* @param {number} [offEntriesEnd] (if omitted, then the table must be null-terminated)
*/
FileInfo.prototype.loadNameTable = function(offEntries, offEntriesEnd)
{
var cNames = 0;
if (MAXDEBUG) this.disk.println("loadNameTable(" + str.toHexLong(offEntries) + (offEntriesEnd? ("," + str.toHexLong(offEntriesEnd)) : "") + ")");
while (!offEntriesEnd || offEntries < offEntriesEnd) {
var offDebug = offEntries;
var bLength = this.loadValue(offEntries, 1);
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)
offEntries += 1 + bLength;
if (!cNames) {
if (!offEntriesEnd) {
this.sModule = sSymbol;
} else {
this.sDescription = sSymbol;
}
}
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));
} else {
if (MAXDEBUG) this.disk.println(this.sPath + ": cannot find ordinal " + iOrdinal + " @" + str.toHex(offDebug));
}
}
offEntries += 2;
cNames++;
}
};
/**
* loadSymbols()
*
* For files with NE headers, extract all available symbolic information from the file.
*
* @this {FileInfo}
*/
FileInfo.prototype.loadSymbols = function()
{
if (!str.endsWith(this.sName, ".EXE") && !str.endsWith(this.sName, ".DLL") && !str.endsWith(this.sName, ".DRV")) {
return;
}
if (this.loadField(FileInfo.OE.oeSignature) != FileInfo.OE.SIG) {
return;
}
if (this.loadField(FileInfo.OE.oeRelocOffset) != FileInfo.OE.NE_SIG) {
return;
}
var offNEHeader = this.loadField(FileInfo.OE.oeNEHeader);
if (this.loadField(FileInfo.NE.neSignature, offNEHeader) != FileInfo.NE.SIG) {
return;
}
var nEntries = this.loadField(FileInfo.NE.neSTEntries, offNEHeader);
var offEntries = this.loadField(FileInfo.NE.neSTOffset, offNEHeader);
var nSegOffShift = this.loadField(FileInfo.NE.neSegOffShift, offNEHeader);
if (offEntries && nEntries) {
this.loadSegmentTable(offEntries + offNEHeader, nEntries, nSegOffShift || 0);
}
offEntries = this.loadField(FileInfo.NE.neETOffset, offNEHeader);
var cbEntries = this.loadField(FileInfo.NE.neETSize, offNEHeader);
if (offEntries && cbEntries) {
this.loadEntryTable(offEntries += offNEHeader, offEntries + cbEntries);
}
/*
* Time to walk the Resident Name Table and update the corresponding ordinals.
*/
offEntries = this.loadField(FileInfo.NE.neRNTOffset, offNEHeader);
if (offEntries) {
this.loadNameTable(offEntries + offNEHeader);
}
/*
* Ditto for the Non-Resident Name Table, which for some reason, uses a file-relative offset rather than
* an NE header-relative offset, and which is both sized AND null-terminated; we check both terminating
* conditions to be safe.
*/
offEntries = this.loadField(FileInfo.NE.neNRNTOffset, offNEHeader);
cbEntries = this.loadField(FileInfo.NE.neNRNTSize, offNEHeader);
if (offEntries && cbEntries) {
this.loadNameTable(offEntries, offEntries + cbEntries);
}
};
/**
* getSymbol(off, fNearest)
*
* @this {FileInfo}
* @param {number} off (offset relative to start of file)
* @param {boolean} [fNearest] (true to return nearest symbol if a segment with symbols is found)
* @return {string} symbol corresponding to file offset (of the file name + offset if no symbol found)
*/
FileInfo.prototype.getSymbol = function(off, fNearest)
{
var sSymbol = null;
if (this.aSegments) {
for (var iSegment in this.aSegments) {
var segment = this.aSegments[iSegment];
if (off >= segment.offStart && off <= segment.offEnd) {
/*
* This is the one and only segment we need to check, so we can make off segment-relative now.
*/
off -= segment.offStart;
/*
* To support fNearest, save the entry where (off - entry[0]) yields the smallest positive result.
*/
var cbNearest = off, entryNearest;
for (var iEntry in segment.aEntries) {
var entry = segment.aEntries[iEntry];
var cb = off - entry[0];
if (!cb) {
sSymbol = this.sModule + '!' + entry[1];
break;
}
if (fNearest && cb > 0 && cb < cbNearest) {
entryNearest = entry;
cbNearest = cb;
}
}
if (!sSymbol && entryNearest) {
sSymbol = this.sModule + '!' + entryNearest[1] + "+" + str.toHexWord(cbNearest);
}
break;
}
}
}
return sSymbol || this.sName + '[' + str.toHexLong(off) + ']';
};
/**
* Every Sector object (once loaded and fully parsed) should have ALL of the following named properties:
@ -293,20 +756,6 @@ var SectorData;
* @property {function(nErrorCode:number,fAsync:boolean)} 5 contains done
*/
/**
* The default number of milliseconds to wait before writing a dirty sector back to a remote disk image
*
* @const {number}
*/
Disk.REMOTE_WRITE_DELAY = 2000; // 2-second delay
/*
* A global disk count, used to form unique Disk component IDs
*/
Disk.nDisks = 0;
Component.subclass(Disk);
/**
* initBus(cmp, bus, cpu, dbg)
*
@ -885,8 +1334,14 @@ Disk.prototype.doneLoad = function(sDiskFile, sDiskData, nErrorCode, sDiskPath)
/**
* buildFileTable()
*
* This function builds a complete file table from the (first) FAT volume found on the current disk, and
* then updates all the sector objects to point back to the corresponding file. Used for BACKTRACK support.
* This function builds (or rebuilds) a complete file table from the (first) FAT volume found on the current
* disk, and then updates all the sector objects to point back to the corresponding file. Used for BACKTRACK
* support, and like BACKTRACK support, this is an expensive operation, in terms of both time and memory, so
* it should only be called when a disk is mounted or has been modified (eg, by applying deltas from a saved
* machine state).
*
* More recently, the FileInfo objects in the table have been enhanced to include debugging information if
* the file is an EXE or DLL, which we determine merely by checking the file extension.
*
* Note that while most of the methods in this module use CHS-style parameters, because our primary clients
* are old disk controllers that deal exclusively with cylinder/head/sector values, here we use 0-based
@ -897,14 +1352,15 @@ Disk.prototype.doneLoad = function(sDiskFile, sDiskData, nErrorCode, sDiskPath)
* of PCjs, what we call PBA numbers are what those controllers would later call LBA numbers.
*
* @this {Disk}
* @return {Array.<FileInfo>|undefined}
*/
Disk.prototype.buildFileTable = function()
{
if (BACKTRACK) {
var i, off, dir = {};
var i, off, dir = {}, iSector;
if (this.aFileTable) {
if (this.aFileTable && this.aFileTable.length) {
/*
* In order for buildFileTable() to rebuild an existing table (eg, after deltas have been
* applied), we need to zap any and all existing file table references in the sector data.
@ -912,7 +1368,7 @@ Disk.prototype.buildFileTable = function()
var aDiskData = this.aDiskData;
for (var iCylinder = 0; iCylinder < aDiskData.length; iCylinder++) {
for (var iHead = 0; iHead < aDiskData[iCylinder].length; iHead++) {
for (var iSector = 0; iSector < aDiskData[iCylinder][iHead].length; iSector++) {
for (iSector = 0; iSector < aDiskData[iCylinder][iHead].length; iSector++) {
var sector = aDiskData[iCylinder][iHead][iSector];
if (sector) {
delete sector['file'];
@ -1071,12 +1527,14 @@ Disk.prototype.buildFileTable = function()
for (i = 0; i < this.aFileTable.length; i++) {
var file = this.aFileTable[i];
off = 0;
for (var iSector = 0; iSector < file.apba.length; iSector++) {
for (iSector = 0; iSector < file.apba.length; iSector++) {
this.updateSector(file, file.apba[iSector], off);
off += this.cbSector;
}
file.loadSymbols();
}
}
return this.aFileTable;
};
/**
@ -1090,6 +1548,7 @@ Disk.prototype.buildFileTable = function()
*/
Disk.prototype.getDir = function(dir, sDisk, sDir, apba)
{
var file;
var iStart = this.aFileTable.length;
var nEntriesPerSector = (dir.cbSector / DiskAPI.DIRENT.LENGTH) | 0;
@ -1110,14 +1569,15 @@ Disk.prototype.getDir = function(dir, sDisk, sDir, apba)
this.printMessage('"' + sPath + '" size=' + dir.cbSize + ' cluster=' + dir.iCluster + ' sectors=' + JSON.stringify(dir.apba));
if (dir.apba.length) this.printMessage(this.dumpSector(this.getSector(dir.apba[0]), dir.apba[0], sPath));
}
this.aFileTable.push({sPath: sPath, sName: dir.sName, bAttr: dir.bAttr, cbSize: dir.cbSize, apba: dir.apba, disk: this});
file = new FileInfo(this, sPath, dir.sName, dir.bAttr, dir.cbSize, dir.apba);
this.aFileTable.push(file);
}
}
var iEnd = this.aFileTable.length;
for (var i = iStart; i < iEnd; i++) {
var file = this.aFileTable[i];
file = this.aFileTable[i];
if (file.bAttr & DiskAPI.ATTR.SUBDIR && file.apba.length) this.getDir(dir, sDisk, sDir + "\\" + file.sName, file.apba);
}
};
@ -1316,6 +1776,8 @@ Disk.prototype.updateSector = function(file, pba, off)
/**
* getSectorData(sector, off, len)
*
* WARNING: This function is restricted to reading data contained ENTIRELY within the specified sector.
*
* NOTE: Yes, this function is not the most efficient way to read a byte/word/dword value from within a sector,
* but given the different states a sector may be in, it's certainly the simplest and safest, and since this is
* only used by buildFileTable() and its progeny, it's not clear that we need to be superfast anyway.
@ -1345,6 +1807,8 @@ Disk.prototype.getSectorData = function(sector, off, len)
/**
* getSectorString(sector, off, len)
*
* WARNING: This function is restricted to reading a string contained ENTIRELY within the specified sector.
*
* @this {Disk}
* @param {Object} sector
* @param {number} off (byte offset)

View file

@ -1890,7 +1890,7 @@ FDC.prototype.doCmd = function()
n = 0;
drive.resCode = FDC.REG_DATA.RES.NONE;
if (drive.disk && (drive.sector = drive.disk.seek(drive.bCylinder, drive.bHead, drive.bSector))) {
n = (drive.sector.length >> 8);
n = (drive.sector['length'] >> 8);
} else {
/*
* TODO: Determine the appropriate response code(s) for the possible errors that can occur here.