Started integrating Windows Debugger segment load info with PCjs Debugger symbol tables
This commit is contained in:
parent
b3f1f288d3
commit
1d0b7974fc
19 changed files with 342 additions and 347 deletions
|
|
@ -155,13 +155,14 @@ function Debugger(parmsDbg)
|
|||
this.dbgAddrAssemble = this.newAddr();
|
||||
|
||||
/*
|
||||
* aSymbolTable is an array of 4-element arrays, one per ROM or other chunk of address space.
|
||||
* Each 4-element arrays contains:
|
||||
* aSymbolTable is an array of 5-element arrays, one per ROM or other chunk of address space.
|
||||
* Each 5-element arrays contains:
|
||||
*
|
||||
* [0]: addr
|
||||
* [1]: size
|
||||
* [2]: aSymbols
|
||||
* [3]: aOffsetPairs
|
||||
* [0]: sel
|
||||
* [1]: addr
|
||||
* [2]: len
|
||||
* [3]: aSymbols
|
||||
* [4]: aOffsetPairs
|
||||
*
|
||||
* See addSymbols() for more details, since that's how callers add sets of symbols to the table.
|
||||
*/
|
||||
|
|
@ -293,6 +294,7 @@ if (DEBUGGER) {
|
|||
'u [#]': "unassemble",
|
||||
'x': "execution options",
|
||||
'if': "eval expression",
|
||||
"ln": "list nearest symbol(s)",
|
||||
'mouse': "mouse action", // syntax: mouse {action} {delta} (eg, mouse x 10, mouse click 0, etc)
|
||||
'print': "print expression",
|
||||
'reset': "reset machine",
|
||||
|
|
@ -1284,6 +1286,71 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
if (Interrupts.WINDBG.ENABLED || Interrupts.WINDBGRM.ENABLED) {
|
||||
/**
|
||||
* addSegmentInfo(dbgAddr, nSeg, sel, fCode, fPrint)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {DbgAddr} dbgAddr (address of module name)
|
||||
* @param {number} nSeg (logical segment number)
|
||||
* @param {number} sel (current selector)
|
||||
* @param {boolean} fCode (true if code segment, false if data segment)
|
||||
* @param {boolean} [fPrint]
|
||||
*/
|
||||
Debugger.prototype.addSegmentInfo = function(dbgAddr, nSeg, sel, fCode, fPrint)
|
||||
{
|
||||
var sModule = this.getSZ(dbgAddr);
|
||||
var seg = this.getSegment(sel);
|
||||
var len = seg? seg.limit + 1 : 0;
|
||||
var sSection = (fCode? "_CODE" : "_DATA") + str.toHex(nSeg, 2);
|
||||
if (fPrint) {
|
||||
this.println(sModule + "!undefined " + (fCode? "code" : "data") + '(' + str.toHex(nSeg, 4) + ")=#" + str.toHex(sel, 4) + " len " + str.toHex(len));
|
||||
}
|
||||
var aSymbols = {};
|
||||
var off = 0;
|
||||
aSymbols[sModule + sSection] = off;
|
||||
this.addSymbols(sel, off, len, aSymbols);
|
||||
};
|
||||
|
||||
/**
|
||||
* addSectionInfo(dbgAddr, fCode, fPrint)
|
||||
*
|
||||
* dbgAddr -> D386_Device_Params structure:
|
||||
* DD_logical_seg dw ? ; logical segment # from map
|
||||
* DD_actual_sel dw ? ; actual selector value
|
||||
* DD_base dd ? ; linear address offset for start of segment
|
||||
* DD_length dd ? ; actual length of segment
|
||||
* DD_name df ? ; 16:32 ptr to null terminated device name
|
||||
* DD_sym_name df ? ; 16:32 ptr to null terminated symbolic module name (i.e. Win386)
|
||||
* DD_alias_sel dw ? ; alias selector value (0 = none)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {DbgAddr} dbgAddr (address of D386_Device_Params)
|
||||
* @param {boolean} fCode (true if code section, false if data section)
|
||||
* @param {boolean} [fPrint]
|
||||
*/
|
||||
Debugger.prototype.addSectionInfo = function(dbgAddr, fCode, fPrint)
|
||||
{
|
||||
var nSeg = this.getShort(dbgAddr, 2);
|
||||
var sel = this.getShort(dbgAddr, 2);
|
||||
var off = this.getLong(dbgAddr, 4);
|
||||
var len = this.getLong(dbgAddr, 4);
|
||||
var dbgAddrDevice = this.newAddr(this.getLong(dbgAddr, 4), this.getShort(dbgAddr, 2));
|
||||
var dbgAddrModule = this.newAddr(this.getLong(dbgAddr, 4), this.getShort(dbgAddr, 2));
|
||||
sel = this.getShort(dbgAddr, 2) || sel;
|
||||
var sModule = this.getSZ(dbgAddrModule).toUpperCase();
|
||||
var sDevice = this.getSZ(dbgAddrDevice).toUpperCase();
|
||||
var sSection = (fCode? "_CODE" : "_DATA") + str.toHex(nSeg, 2);
|
||||
if (fPrint) {
|
||||
/*
|
||||
* Mimic WDEB386 output
|
||||
*/
|
||||
this.println(sModule + '!' + sDevice + "!undefined " + (fCode? "code" : "data") + '(' + str.toHex(nSeg, 4) + ")=" + str.toHex(sel, 4) + ':' + str.toHex(off) + " len " + str.toHex(len));
|
||||
}
|
||||
var aSymbols = {};
|
||||
aSymbols[sDevice + sSection] = off;
|
||||
this.addSymbols(sel, off, len, aSymbols);
|
||||
};
|
||||
|
||||
/**
|
||||
* intWindowsDebugger()
|
||||
*
|
||||
|
|
@ -1345,17 +1412,11 @@ if (DEBUGGER) {
|
|||
case Interrupts.WINDBG.IS_LOADED:
|
||||
if (this.fWinDbg) {
|
||||
cpu.regEAX = (cpu.regEAX & ~0xffff) | Interrupts.WINDBG.LOADED;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Interrupts.WINDBG.LOAD_SEG:
|
||||
if (this.fWinDbg) {
|
||||
sModule = this.getSZ(this.newAddr(DI, ES));
|
||||
limit = (seg = this.getSegment(CX))? seg.limit : 0;
|
||||
this.println(sModule + "!undefined " + (!(SI & 0x1)? "code" : "data") + '(' + str.toHex(BX+1, 4) + ")=#" + str.toHex(CX, 4) + " len " + str.toHex(limit+1));
|
||||
return false;
|
||||
}
|
||||
this.addSegmentInfo(this.newAddr(DI, ES), BX+1, CX, !(SI & 0x1), this.fWinDbg);
|
||||
break;
|
||||
|
||||
case Interrupts.WINDBG.LOAD_SEG32:
|
||||
|
|
@ -1363,52 +1424,19 @@ if (DEBUGGER) {
|
|||
* SI == segment type:
|
||||
* 0x0 code selector
|
||||
* 0x1 data selector
|
||||
* DX:EBX -> D386_Device_Params structure (see dumpD386DeviceParms() for details)
|
||||
* DX:EBX -> D386_Device_Params structure (see addSectionInfo() for details)
|
||||
*/
|
||||
if (this.fWinDbg) {
|
||||
this.println(this.dumpD386DeviceParms(this.newAddr(cpu.regEBX, DX), !SI));
|
||||
return false;
|
||||
}
|
||||
this.addSectionInfo(this.newAddr(cpu.regEBX, DX), !SI, this.fWinDbg);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (this.fWinDbg) {
|
||||
this.println("INT 0x41: " + str.toHexWord(AX));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* dumpD386DeviceParms(dbgAddr, fCode)
|
||||
*
|
||||
* dbgAddr -> D386_Device_Params structure:
|
||||
* DD_logical_seg dw ? ; logical segment # from map
|
||||
* DD_actual_sel dw ? ; actual selector value
|
||||
* DD_base dd ? ; linear address offset for start of segment
|
||||
* DD_length dd ? ; actual length of segment
|
||||
* DD_name df ? ; 16:32 ptr to null terminated device name
|
||||
* DD_sym_name df ? ; 16:32 ptr to null terminated symbolic module name (i.e. Win386)
|
||||
* DD_alias_sel dw ? ; alias selector value (0 = none)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {DbgAddr} dbgAddr
|
||||
* @param {boolean} fCode
|
||||
* @return {string}
|
||||
*/
|
||||
Debugger.prototype.dumpD386DeviceParms = function(dbgAddr, fCode)
|
||||
{
|
||||
var nSeg = this.getShort(dbgAddr, 2);
|
||||
var sel = this.getShort(dbgAddr, 2);
|
||||
var off = this.getLong(dbgAddr, 4);
|
||||
var len = this.getLong(dbgAddr, 4);
|
||||
var dbgAddrDevice = this.newAddr(this.getLong(dbgAddr, 4), this.getShort(dbgAddr, 2));
|
||||
var dbgAddrModule = this.newAddr(this.getLong(dbgAddr, 4), this.getShort(dbgAddr, 2));
|
||||
var selAlias = this.getShort(dbgAddr, 2) || sel;
|
||||
return this.getSZ(dbgAddrModule) + '!' + this.getSZ(dbgAddrDevice) + "!undefined " + (fCode? "code" : "data") + '(' + str.toHex(nSeg, 4) + ")=" + str.toHex(selAlias, 4) + ':' + str.toHex(off) + " len " + str.toHex(len);
|
||||
return !this.fWinDbg;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1482,7 +1510,6 @@ if (DEBUGGER) {
|
|||
case Interrupts.WINDBGRM.IS_LOADED:
|
||||
if (this.fWinDbgRM) {
|
||||
cpu.regEAX = (cpu.regEAX & ~0xffff) | Interrupts.WINDBGRM.LOADED;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -1493,7 +1520,6 @@ if (DEBUGGER) {
|
|||
cpu.regEDI = a[0];
|
||||
cpu.setES(a[1]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -1525,27 +1551,23 @@ if (DEBUGGER) {
|
|||
* AL == segment type:
|
||||
* 0x80 device driver code seg
|
||||
* 0x81 device driver data seg
|
||||
* ES:DI -> D386_Device_Params structure (see dumpD386DeviceParms() for details)
|
||||
* ES:DI -> D386_Device_Params structure (see addSectionInfo() for details)
|
||||
*/
|
||||
if (this.fWinDbgRM) {
|
||||
this.println(this.dumpD386DeviceParms(this.newAddr(DI, ES), !(AL & 0x1)));
|
||||
}
|
||||
this.addSectionInfo(this.newAddr(DI, ES), !(AL & 0x1), this.fWinDbgRM);
|
||||
}
|
||||
if (this.fWinDbgRM) {
|
||||
cpu.regEAX = (cpu.regEAX & ~0xff) | 0x01;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (this.fWinDbgRM) {
|
||||
this.println("INT 0x68: " + str.toHexByte(AH));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
return !this.fWinDbgRM;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2055,6 +2077,120 @@ if (DEBUGGER) {
|
|||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseAddr(sAddr, type, fNoChecks, fPrint)
|
||||
*
|
||||
* 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
|
||||
* (or null).
|
||||
*
|
||||
* To create a segmented address, specify two values separated by ':'; for a linear address, use
|
||||
* a '%' prefix. We check for ':' after '%', so if for some strange reason you specify both, the
|
||||
* address will be treated as segmented, not linear.
|
||||
*
|
||||
* The '%' syntax is similar to that used by the Windows 80386 kernel debugger (wdeb386) for linear
|
||||
* addresses. If/when we add support for processors with page tables, we will likely adopt the same
|
||||
* convention for linear addresses and provide a different syntax (eg, "%%") physical memory references.
|
||||
*
|
||||
* Address evaluation and validation (eg, range checks) are no longer performed at this stage. That's
|
||||
* done later, by getAddr(), which returns X86.ADDR_INVALID for invalid segments, out-of-range offsets,
|
||||
* etc. The Debugger's low-level get/set memory functions verify all getAddr() results, but even if an
|
||||
* invalid address is passed through to the Bus memory interfaces, the address will simply be masked with
|
||||
* Bus.nBusLimit; in the case of X86.ADDR_INVALID, that will generally refer to the top of the physical
|
||||
* address space.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string|undefined} sAddr
|
||||
* @param {number|undefined} [type] is either CODE or DATA, 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} [fPrint]
|
||||
* @return {DbgAddr|null|undefined}
|
||||
*/
|
||||
Debugger.prototype.parseAddr = function(sAddr, type, fNoChecks, fPrint)
|
||||
{
|
||||
var dbgAddr;
|
||||
var dbgAddrNext = (type === Debugger.ADDR.CODE? this.dbgAddrNextCode : this.dbgAddrNextData);
|
||||
|
||||
var off = dbgAddrNext.off, sel = dbgAddrNext.sel, addr = dbgAddrNext.addr;
|
||||
|
||||
type = fNoChecks? Debugger.ADDR.NONE : dbgAddrNext.type;
|
||||
|
||||
if (sAddr !== undefined) {
|
||||
|
||||
sAddr = this.parseReference(sAddr);
|
||||
|
||||
var ch = sAddr.charAt(0);
|
||||
var iColon = sAddr.indexOf(':');
|
||||
|
||||
switch(ch) {
|
||||
case '&':
|
||||
type = Debugger.ADDR.REAL;
|
||||
break;
|
||||
case '#':
|
||||
type = Debugger.ADDR.PROT;
|
||||
break;
|
||||
case '%':
|
||||
type = Debugger.ADDR.LINEAR;
|
||||
off = addr = 0;
|
||||
sel = null; // we still have code that relies on this crutch, instead of the type field
|
||||
break;
|
||||
default:
|
||||
if (iColon >= 0) type = Debugger.ADDR.NONE;
|
||||
ch = '';
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch) {
|
||||
sAddr = sAddr.substr(1);
|
||||
iColon--;
|
||||
}
|
||||
|
||||
dbgAddr = this.findSymbolAddr(sAddr);
|
||||
if (dbgAddr) return dbgAddr;
|
||||
|
||||
if (iColon < 0) {
|
||||
if (sel != null) {
|
||||
off = this.parseExpression(sAddr, fPrint);
|
||||
addr = null;
|
||||
} else {
|
||||
addr = this.parseExpression(sAddr, fPrint);
|
||||
if (addr == null) off = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
sel = this.parseExpression(sAddr.substring(0, iColon), fPrint);
|
||||
off = this.parseExpression(sAddr.substring(iColon + 1), fPrint);
|
||||
addr = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (off != null) {
|
||||
dbgAddr = this.newAddr(off, sel, addr, type);
|
||||
if (!fNoChecks && !this.checkLimit(dbgAddr, true)) {
|
||||
this.println("invalid offset: " + this.hexAddr(dbgAddr));
|
||||
dbgAddr = null;
|
||||
}
|
||||
}
|
||||
return dbgAddr;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseAddrOptions(dbdAddr, sOptions)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {DbgAddr} dbgAddr
|
||||
* @param {string} [sOptions]
|
||||
*/
|
||||
Debugger.prototype.parseAddrOptions = function(dbgAddr, sOptions)
|
||||
{
|
||||
if (sOptions) {
|
||||
var a = sOptions.match(/(['"])(.*?)\1/);
|
||||
if (a) {
|
||||
dbgAddr.aCmds = this.parseCommand(dbgAddr.sCmd = a[2]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* incAddr(dbgAddr, inc)
|
||||
*
|
||||
|
|
@ -2221,7 +2357,7 @@ if (DEBUGGER) {
|
|||
var sInfo = "no information";
|
||||
if (BACKTRACK) {
|
||||
var sAddr = asArgs[0];
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR.CODE, true, true);
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR.CODE, true, false);
|
||||
if (dbgAddr) {
|
||||
var addr = this.getAddr(dbgAddr);
|
||||
sInfo = '%' + str.toHex(addr) + ": " + (this.bus.getSymbol(addr, true) || sInfo);
|
||||
|
|
@ -4075,7 +4211,7 @@ if (DEBUGGER) {
|
|||
|
||||
for (var iOperand = 1; iOperand <= cOperands; iOperand++) {
|
||||
|
||||
var disp, offset, cch;
|
||||
var disp, off, cch;
|
||||
var sOperand = "";
|
||||
var type = aOpDesc[iOperand];
|
||||
if (type === undefined) continue;
|
||||
|
|
@ -4129,12 +4265,12 @@ if (DEBUGGER) {
|
|||
else if (typeMode == Debugger.TYPE_IMMOFF) {
|
||||
if (!dbgAddr.fAddr32) {
|
||||
cch = 4;
|
||||
offset = this.getShort(dbgAddr, 2);
|
||||
off = this.getShort(dbgAddr, 2);
|
||||
} else {
|
||||
cch = 8;
|
||||
offset = this.getLong(dbgAddr, 4);
|
||||
off = this.getLong(dbgAddr, 4);
|
||||
}
|
||||
sOperand = '[' + str.toHex(offset, cch) + ']';
|
||||
sOperand = '[' + str.toHex(off, cch) + ']';
|
||||
}
|
||||
else if (typeMode == Debugger.TYPE_IMMREL) {
|
||||
if (typeSize == Debugger.TYPE_BYTE) {
|
||||
|
|
@ -4143,9 +4279,9 @@ if (DEBUGGER) {
|
|||
else {
|
||||
disp = this.getWord(dbgAddr, true);
|
||||
}
|
||||
offset = (dbgAddr.off + disp) & (dbgAddr.fData32? -1 : 0xffff);
|
||||
sOperand = str.toHex(offset, dbgAddr.fData32? 8: 4);
|
||||
var aSymbol = this.findSymbol(this.newAddr(offset, dbgAddr.sel));
|
||||
off = (dbgAddr.off + disp) & (dbgAddr.fData32? -1 : 0xffff);
|
||||
sOperand = str.toHex(off, dbgAddr.fData32? 8: 4);
|
||||
var aSymbol = this.findSymbol(this.newAddr(off, dbgAddr.sel));
|
||||
if (aSymbol[0]) sOperand += " (" + aSymbol[0] + ")";
|
||||
}
|
||||
else if (typeMode == Debugger.TYPE_IMPREG) {
|
||||
|
|
@ -4618,122 +4754,6 @@ if (DEBUGGER) {
|
|||
return s;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* (or null).
|
||||
*
|
||||
* To create a segmented address, specify two values separated by ':'; for a linear address, use
|
||||
* a '%' prefix. We check for ':' after '%', so if for some strange reason you specify both, the
|
||||
* address will be treated as segmented, not linear.
|
||||
*
|
||||
* The '%' syntax is similar to that used by the Windows 80386 kernel debugger (wdeb386) for linear
|
||||
* addresses. If/when we add support for processors with page tables, we will likely adopt the same
|
||||
* convention for linear addresses and provide a different syntax (eg, "%%") physical memory references.
|
||||
*
|
||||
* Address evaluation and validation (eg, range checks) are no longer performed at this stage. That's
|
||||
* done later, by getAddr(), which returns X86.ADDR_INVALID for invalid segments, out-of-range offsets,
|
||||
* etc. The Debugger's low-level get/set memory functions verify all getAddr() results, but even if an
|
||||
* invalid address is passed through to the Bus memory interfaces, the address will simply be masked with
|
||||
* Bus.nBusLimit; in the case of X86.ADDR_INVALID, that will generally refer to the top of the physical
|
||||
* address space.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string|undefined} sAddr
|
||||
* @param {number|undefined} [type] is either CODE or DATA, 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, fQuiet)
|
||||
{
|
||||
var dbgAddr, fPrint;
|
||||
var dbgAddrNext = (type === Debugger.ADDR.CODE? this.dbgAddrNextCode : this.dbgAddrNextData);
|
||||
|
||||
var off = dbgAddrNext.off, sel = dbgAddrNext.sel, addr = dbgAddrNext.addr;
|
||||
|
||||
type = fNoChecks? Debugger.ADDR.NONE : dbgAddrNext.type;
|
||||
|
||||
if (fQuiet) fPrint = false;
|
||||
|
||||
if (sAddr !== undefined) {
|
||||
|
||||
sAddr = this.parseReference(sAddr);
|
||||
|
||||
var ch = sAddr.charAt(0);
|
||||
var iColon = sAddr.indexOf(':');
|
||||
|
||||
switch(ch) {
|
||||
case '&':
|
||||
type = Debugger.ADDR.REAL;
|
||||
break;
|
||||
case '#':
|
||||
type = Debugger.ADDR.PROT;
|
||||
break;
|
||||
case '%':
|
||||
type = Debugger.ADDR.LINEAR;
|
||||
off = addr = 0;
|
||||
sel = null; // we still have code that relies on this crutch, instead of the type field
|
||||
break;
|
||||
default:
|
||||
if (iColon >= 0) type = Debugger.ADDR.NONE;
|
||||
ch = '';
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch) {
|
||||
sAddr = sAddr.substr(1);
|
||||
iColon--;
|
||||
}
|
||||
|
||||
dbgAddr = this.findSymbolAddr(sAddr);
|
||||
if (dbgAddr) return dbgAddr;
|
||||
|
||||
if (iColon < 0) {
|
||||
if (sel != null) {
|
||||
off = this.parseExpression(sAddr, fPrint);
|
||||
addr = null;
|
||||
} else {
|
||||
addr = this.parseExpression(sAddr, fPrint);
|
||||
if (addr == null) off = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
sel = this.parseExpression(sAddr.substring(0, iColon), fPrint);
|
||||
off = this.parseExpression(sAddr.substring(iColon + 1), fPrint);
|
||||
addr = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (off != null) {
|
||||
dbgAddr = this.newAddr(off, sel, addr, type);
|
||||
if (!fNoChecks && !this.checkLimit(dbgAddr, true)) {
|
||||
this.println("invalid offset: " + this.hexAddr(dbgAddr));
|
||||
dbgAddr = null;
|
||||
}
|
||||
}
|
||||
return dbgAddr;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseAddrOptions(dbdAddr, sOptions)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {DbgAddr} dbgAddr
|
||||
* @param {string} [sOptions]
|
||||
*/
|
||||
Debugger.prototype.parseAddrOptions = function(dbgAddr, sOptions)
|
||||
{
|
||||
if (sOptions) {
|
||||
var a = sOptions.match(/(['"])(.*?)\1/);
|
||||
if (a) {
|
||||
dbgAddr.aCmds = this.parseCommand(dbgAddr.sCmd = a[2]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Debugger.aBinOpPrecedence = {
|
||||
'||': 0, // logical OR
|
||||
'&&': 1, // logical AND
|
||||
|
|
@ -5068,7 +5088,20 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* addSymbols(addr, size, aSymbols)
|
||||
* comparePairs(p1, p2)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {number|string|Array|Object} p1
|
||||
* @param {number|string|Array|Object} p2
|
||||
* @return {number}
|
||||
*/
|
||||
Debugger.prototype.comparePairs = function(p1, p2)
|
||||
{
|
||||
return p1[0] > p2[0]? 1 : p1[0] < p2[0]? -1 : 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* addSymbols(sel, addr, len, aSymbols)
|
||||
*
|
||||
* As filedump.js (formerly convrom.php) explains, aSymbols is a JSON-encoded object whose properties consist
|
||||
* of all the symbols (in upper-case), and the values of those properties are objects containing any or all of
|
||||
|
|
@ -5124,7 +5157,7 @@ if (DEBUGGER) {
|
|||
* We add all these entries to our internal symbol table, which is an array of 4-element arrays, each of which
|
||||
* look like:
|
||||
*
|
||||
* [addr, size, aSymbols, aOffsetPairs]
|
||||
* [sel, addr, len, aSymbols, aOffsetPairs]
|
||||
*
|
||||
* 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
|
||||
|
|
@ -5139,29 +5172,27 @@ if (DEBUGGER) {
|
|||
* properly.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {number} addr is the physical address of the region where the given symbols are located
|
||||
* @param {number} size is the size of the region, in bytes
|
||||
* @param {Object} aSymbols is the collection of symbols (the format of this object is described below)
|
||||
* @param {number} sel (the default segment/selector for all symbols in this group)
|
||||
* @param {number} addr (the physical address of the region where the given symbols are located)
|
||||
* @param {number} len (the size of the region, in bytes)
|
||||
* @param {Object} aSymbols (collection of symbols in this group; the format of this collection is described below)
|
||||
*/
|
||||
Debugger.prototype.addSymbols = function(addr, size, aSymbols)
|
||||
Debugger.prototype.addSymbols = function(sel, addr, len, aSymbols)
|
||||
{
|
||||
var dbgAddr = {};
|
||||
var aOffsetPairs = [];
|
||||
var fnComparePairs = function(p1, p2) {
|
||||
return p1[0] > p2[0]? 1 : p1[0] < p2[0]? -1 : 0;
|
||||
};
|
||||
for (var sSymbol in aSymbols) {
|
||||
var symbol = aSymbols[sSymbol];
|
||||
if (typeof symbol == "number") {
|
||||
aSymbols[sSymbol] = symbol = {'o': symbol};
|
||||
}
|
||||
var off = symbol['o'];
|
||||
var sel = symbol['s'];
|
||||
var selSymbol = symbol['s'];
|
||||
var sAnnotation = symbol['a'];
|
||||
if (off !== undefined) {
|
||||
if (sel !== undefined) {
|
||||
if (selSymbol !== undefined) {
|
||||
dbgAddr.off = off;
|
||||
dbgAddr.sel = sel;
|
||||
dbgAddr.sel = selSymbol;
|
||||
dbgAddr.addr = null;
|
||||
/*
|
||||
* getAddr() computes the corresponding physical address and saves it in dbgAddr.addr.
|
||||
|
|
@ -5177,11 +5208,11 @@ if (DEBUGGER) {
|
|||
}
|
||||
symbol['p'] = dbgAddr.addr;
|
||||
}
|
||||
usr.binaryInsert(aOffsetPairs, [off, sSymbol], fnComparePairs);
|
||||
usr.binaryInsert(aOffsetPairs, [off >>> 0, sSymbol], this.comparePairs);
|
||||
}
|
||||
if (sAnnotation) symbol['a'] = sAnnotation.replace(/''/g, "\"");
|
||||
}
|
||||
this.aSymbolTable.push([addr, size, aSymbols, aOffsetPairs]);
|
||||
this.aSymbolTable.push([sel, addr, len, aSymbols, aOffsetPairs]);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -5195,19 +5226,20 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.dumpSymbols = function()
|
||||
{
|
||||
for (var i = 0; i < this.aSymbolTable.length; i++) {
|
||||
var addr = this.aSymbolTable[i][0];
|
||||
//var size = this.aSymbolTable[i][1];
|
||||
var aSymbols = this.aSymbolTable[i][2];
|
||||
var sel = this.aSymbolTable[i][0];
|
||||
var addr = this.aSymbolTable[i][1];
|
||||
//var len = this.aSymbolTable[i][2];
|
||||
var aSymbols = this.aSymbolTable[i][3];
|
||||
for (var sSymbol in aSymbols) {
|
||||
if (sSymbol.charAt(0) == '.') continue;
|
||||
var symbol = aSymbols[sSymbol];
|
||||
var off = symbol['o'];
|
||||
if (off === undefined) continue;
|
||||
var sel = symbol['s'];
|
||||
if (sel === undefined) sel = (addr >>> 4);
|
||||
var selSym = symbol['s'];
|
||||
if (selSym === undefined) selSym = sel;
|
||||
var sSymbolOrig = aSymbols[sSymbol]['l'];
|
||||
if (sSymbolOrig) sSymbol = sSymbolOrig;
|
||||
this.println(this.hexOffset(off, sel) + ' ' + sSymbol);
|
||||
this.println(this.hexOffset(off, selSym) + ' ' + sSymbol);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -5228,34 +5260,33 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.findSymbol = function(dbgAddr, fNearest)
|
||||
{
|
||||
var aSymbol = [];
|
||||
var addr = this.getAddr(dbgAddr);
|
||||
var addrSymbol = this.getAddr(dbgAddr) >>> 0;
|
||||
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);
|
||||
var sel = this.aSymbolTable[iTable][0];
|
||||
var addr = this.aSymbolTable[iTable][1] >>> 0;
|
||||
var len = this.aSymbolTable[iTable][2];
|
||||
if (sel == dbgAddr.sel || (sel == 0x28 || sel == 0x30) && (dbgAddr.sel == 0x28 || dbgAddr.sel == 0x30)) {
|
||||
if (addrSymbol >= addr && addrSymbol < addr + len) {
|
||||
var off = dbgAddr.off >>> 0;
|
||||
var aOffsetPairs = this.aSymbolTable[iTable][4];
|
||||
var result = usr.binarySearch(aOffsetPairs, [off], this.comparePairs);
|
||||
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;
|
||||
}
|
||||
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);
|
||||
var sSymbol = this.bus.getSymbol(addrSymbol, true);
|
||||
if (sSymbol) {
|
||||
aSymbol.push(sSymbol);
|
||||
aSymbol.push(addr);
|
||||
aSymbol.push(addrSymbol);
|
||||
}
|
||||
}
|
||||
return aSymbol;
|
||||
|
|
@ -5276,21 +5307,23 @@ if (DEBUGGER) {
|
|||
if (sSymbol.match(/^[a-z_][a-z0-9_]*$/i)) {
|
||||
var sUpperCase = sSymbol.toUpperCase();
|
||||
for (var i = 0; i < this.aSymbolTable.length; i++) {
|
||||
var addr = this.aSymbolTable[i][0];
|
||||
//var size = this.aSymbolTable[i][1];
|
||||
var aSymbols = this.aSymbolTable[i][2];
|
||||
var sel = this.aSymbolTable[i][0];
|
||||
var addr = this.aSymbolTable[i][1];
|
||||
//var len = this.aSymbolTable[i][2];
|
||||
var aSymbols = this.aSymbolTable[i][3];
|
||||
var symbol = aSymbols[sUpperCase];
|
||||
if (symbol !== undefined) {
|
||||
var off = symbol['o'];
|
||||
if (off !== undefined) {
|
||||
/*
|
||||
* We assume that every ROM is ORG'ed at 0x0000, and therefore unless the symbol has an
|
||||
* explicitly-defined segment, we return the segment as "addr >>> 4". Down the road, we may
|
||||
* want/need to support a special symbol entry (eg, ".ORG") that defines an alternate origin.
|
||||
* explicitly-defined segment, we return the segment associated with the entire group; for
|
||||
* a ROM, that segment is normally "addrROM >>> 4". Down the road, we may want/need to
|
||||
* support a special symbol entry (eg, ".ORG") that defines an alternate origin.
|
||||
*/
|
||||
var sel = symbol['s'];
|
||||
if (sel === undefined) sel = addr >>> 4;
|
||||
dbgAddr = this.newAddr(off, sel, symbol['p']);
|
||||
var selSym = symbol['s'];
|
||||
if (selSym === undefined) selSym = sel;
|
||||
dbgAddr = this.newAddr(off, selSym, symbol['p']);
|
||||
}
|
||||
/*
|
||||
* The symbol matched, but it wasn't for an address (no 'o' offset), and there's no point
|
||||
|
|
@ -5315,14 +5348,14 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.returnSymbol = function(iTable, iOffset, aSymbol)
|
||||
{
|
||||
var symbol = {};
|
||||
var aOffsetPairs = this.aSymbolTable[iTable][3];
|
||||
var aOffsetPairs = this.aSymbolTable[iTable][4];
|
||||
var offset = 0, sSymbol = null;
|
||||
if (iOffset >= 0 && iOffset < aOffsetPairs.length) {
|
||||
offset = aOffsetPairs[iOffset][0];
|
||||
sSymbol = aOffsetPairs[iOffset][1];
|
||||
}
|
||||
if (sSymbol) {
|
||||
symbol = this.aSymbolTable[iTable][2][sSymbol];
|
||||
symbol = this.aSymbolTable[iTable][3][sSymbol];
|
||||
sSymbol = (sSymbol.charAt(0) == '.'? null : (symbol['l'] || sSymbol));
|
||||
}
|
||||
aSymbol.push(sSymbol);
|
||||
|
|
@ -5974,38 +6007,49 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* doList(sSymbol)
|
||||
* doList(sAddr, fPrint)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} sSymbol
|
||||
* @param {string} sAddr
|
||||
* @param {boolean} [fPrint]
|
||||
* @return {string|null}
|
||||
*/
|
||||
Debugger.prototype.doList = function(sSymbol)
|
||||
Debugger.prototype.doList = function(sAddr, fPrint)
|
||||
{
|
||||
var dbgAddr = this.parseAddr(sSymbol, Debugger.ADDR.CODE);
|
||||
if (!dbgAddr) return;
|
||||
var sSymbol = null;
|
||||
|
||||
var addr = this.getAddr(dbgAddr);
|
||||
sSymbol = sSymbol? (sSymbol + ": ") : "";
|
||||
this.println(sSymbol + this.hexAddr(dbgAddr) + " (%" + str.toHex(addr, this.cchAddr) + ')');
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR.CODE);
|
||||
if (dbgAddr) {
|
||||
|
||||
var aSymbol = this.findSymbol(dbgAddr, true);
|
||||
if (aSymbol.length) {
|
||||
var nDelta, sDelta;
|
||||
if (aSymbol[0]) {
|
||||
sDelta = "";
|
||||
nDelta = dbgAddr.off - aSymbol[1];
|
||||
if (nDelta) sDelta = " + " + str.toHexWord(nDelta);
|
||||
this.println(aSymbol[0] + " (" + this.hexOffset(aSymbol[1], dbgAddr.sel) + ')' + sDelta);
|
||||
var addr = this.getAddr(dbgAddr);
|
||||
if (MAXDEBUG && fPrint) {
|
||||
this.println(this.hexAddr(dbgAddr) + " (%" + str.toHex(addr, this.cchAddr) + ')');
|
||||
}
|
||||
if (aSymbol.length > 4 && aSymbol[4]) {
|
||||
sDelta = "";
|
||||
nDelta = aSymbol[5] - dbgAddr.off;
|
||||
if (nDelta) sDelta = " - " + str.toHexWord(nDelta);
|
||||
this.println(aSymbol[4] + " (" + this.hexOffset(aSymbol[5], dbgAddr.sel) + ')' + sDelta);
|
||||
|
||||
var aSymbol = this.findSymbol(dbgAddr, true);
|
||||
if (aSymbol.length) {
|
||||
var nDelta, sDelta, s;
|
||||
if (aSymbol[0]) {
|
||||
sDelta = "";
|
||||
nDelta = dbgAddr.off - aSymbol[1];
|
||||
if (nDelta) sDelta = " + " + str.toHexWord(nDelta);
|
||||
s = aSymbol[0] + " (" + this.hexOffset(aSymbol[1], dbgAddr.sel) + ')' + sDelta;
|
||||
if (fPrint) this.println(s);
|
||||
sSymbol = s;
|
||||
}
|
||||
if (aSymbol.length > 4 && aSymbol[4]) {
|
||||
sDelta = "";
|
||||
nDelta = aSymbol[5] - dbgAddr.off;
|
||||
if (nDelta) sDelta = " - " + str.toHexWord(nDelta);
|
||||
s = aSymbol[4] + " (" + this.hexOffset(aSymbol[5], dbgAddr.sel) + ')' + sDelta;
|
||||
if (fPrint) this.println(s);
|
||||
if (!sSymbol) sSymbol = s;
|
||||
}
|
||||
} else {
|
||||
if (fPrint) this.println("no symbols");
|
||||
}
|
||||
} else {
|
||||
this.println("no symbols");
|
||||
}
|
||||
return sSymbol;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -6034,12 +6078,6 @@ if (DEBUGGER) {
|
|||
if (!asArgs[1] || asArgs[1] == '?') {
|
||||
this.println("list/load commands:");
|
||||
this.println("\tl [address] [drive #] [sector #] [# sectors]");
|
||||
this.println("\tln [address] lists symbol(s) nearest to address");
|
||||
return;
|
||||
}
|
||||
|
||||
if (asArgs[0] == "ln") {
|
||||
this.doList(asArgs[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -6800,11 +6838,15 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* doStackTrace()
|
||||
* doStackTrace(sCmd, sAddr)
|
||||
*
|
||||
* Use "k" for a normal stack trace and "ks" for a stack trace with symbolic info.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} [sCmd]
|
||||
* @param {string} [sAddr] (not used yet)
|
||||
*/
|
||||
Debugger.prototype.doStackTrace = function()
|
||||
Debugger.prototype.doStackTrace = function(sCmd, sAddr)
|
||||
{
|
||||
var nFrames = 10, cFrames = 0;
|
||||
var selCode = this.cpu.segCS.sel;
|
||||
|
|
@ -6822,9 +6864,7 @@ if (DEBUGGER) {
|
|||
if (dbgAddrStack.addr == null || !cTests--) break;
|
||||
dbgAddrCall.sel = selCode;
|
||||
sCall = this.getCall(dbgAddrCall);
|
||||
if (sCall) {
|
||||
break;
|
||||
}
|
||||
if (sCall) break;
|
||||
dbgAddrCall.sel = this.getWord(dbgAddrStack);
|
||||
sCall = this.getCall(dbgAddrCall, true);
|
||||
if (sCall) {
|
||||
|
|
@ -6838,7 +6878,12 @@ if (DEBUGGER) {
|
|||
}
|
||||
}
|
||||
if (!sCall) break;
|
||||
sCall = str.pad(sCall, 50) + " ;stack=" + this.hexAddr(dbgAddrStack) + " return=" + this.hexAddr(dbgAddrCall);
|
||||
var sSymbol = null;
|
||||
if (sCmd == "ks") {
|
||||
var a = sCall.match(/[0-9A-F]+$/);
|
||||
if (a) sSymbol = this.doList(a[0]);
|
||||
}
|
||||
sCall = str.pad(sCall, 50) + " ;" + (sSymbol || "stack=" + this.hexAddr(dbgAddrStack) + " return=" + this.hexAddr(dbgAddrCall));
|
||||
this.println(sCall);
|
||||
cFrames++;
|
||||
}
|
||||
|
|
@ -7159,9 +7204,13 @@ if (DEBUGGER) {
|
|||
this.doInput(asArgs[1]);
|
||||
break;
|
||||
case 'k':
|
||||
this.doStackTrace();
|
||||
this.doStackTrace(asArgs[0], asArgs[1]);
|
||||
break;
|
||||
case 'l':
|
||||
if (asArgs[0] == "ln") {
|
||||
this.doList(asArgs[1], true);
|
||||
break;
|
||||
}
|
||||
this.shiftArgs(asArgs);
|
||||
this.doLoad(asArgs);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ ROM.prototype.powerUp = function(data, fRepower)
|
|||
{
|
||||
if (this.aSymbols) {
|
||||
if (this.dbg) {
|
||||
this.dbg.addSymbols(this.addrROM, this.sizeROM, this.aSymbols);
|
||||
this.dbg.addSymbols(this.addrROM >>> 4, this.addrROM, this.sizeROM, this.aSymbols);
|
||||
}
|
||||
/*
|
||||
* Our only role in the handling of symbols is to hand them off to the Debugger at our
|
||||
|
|
|
|||
|
|
@ -5575,6 +5575,12 @@ Video.prototype.updateScreenGraphicsEGA = function(addrBuffer, addrScreen, addrS
|
|||
* and in the array that are outside the signed 32-bit range, potentially triggering floating-point.
|
||||
*
|
||||
* if (dwPixel < 0) dwPixel += 0x100000000;
|
||||
*
|
||||
* An even simpler solution would be to use the unsigned right-shift operator:
|
||||
*
|
||||
* dwPixel >>> 0
|
||||
*
|
||||
* but again, all that does is produce a value outside the signed 32-bit range, which is sub-optimal.
|
||||
*/
|
||||
this.assert(Video.aEGADWToByte[dwPixel] !== undefined);
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in a new issue