Started integrating Windows Debugger segment load info with PCjs Debugger symbol tables

This commit is contained in:
Jeff Parsons 2015-09-08 11:05:09 -07:00
commit 1d0b7974fc
19 changed files with 342 additions and 347 deletions

View file

@ -23,28 +23,31 @@ To verify, type the following into any JavaScript REPL (eg, Node):
> n == -0x80000000
true
So the notion that bitwise operators yield 32-bit results isn't exactly right; the sign (bit 31) of every 32-bit
result is always extended into the entire 52 "significand" bits of the underlying 64-bit float. And it's
impossible to simply "mask away" those additional sign bits, thanks to the fundamental restriction of JavaScript bitwise
operators: they operate *only* on the low 32 bits.
The sign (bit 31) of every 32-bit result is always extended into the entire 52 "significand" bits of the underlying
64-bit float. And it's impossible to simply "mask away" those additional sign bits, thanks to a fundamental
restriction of JavaScript bitwise operators: they operate *only* on the low 32 bits.
The easiest way to remove the high-order sign bits from a negative 32-bit value is to add the 33-bit value 0x100000000:
With one exception: the unsigned right-shift operator. It does more than simply shift zero bits in from
the left; it also zeros all the bits above the sign bit. This means that `n >>> 0`, while leaving the low 32 bits
unchanged, also clears the upper bits, resulting in a value that is positive, albeit outside the signed 32-bit range.
It is equivalent to adding the 33-bit value 0x100000000 to a negative 32-bit number:
> n = (n < 0? n + 0x100000000 : n)
2147483648
> n.toString(16)
'80000000'
This works because JavaScript is perfectly capable of representing 0x80000000, or any other 32-bit value, as a positive
number, albeit in floating point. And be careful, because as soon as you perform *any* bitwise operation on a value
with bit 31 set, even an operation as innocuous-looking as:
These operations work because JavaScript is perfectly capable of representing 0x80000000, or any other 32-bit value,
as a positive number, but it must use a floating point value to do so. And be careful, because as soon as you perform
*any* bitwise operation on a value with bit 31 set, even an operation as innocuous-looking as:
> n |= 0
-2147483648
> n.toString(16)
'-80000000'
Viola: instant negative number!
the result will be negative again. This is simply how all bitwise operators (except for unsigned right-shift) operate:
they truncate the result to a signed 32-bit value.
This might tempt you to think that the right way to write negative 32-bit constants in hex is to simply precede
them with a minus sign. But that would be wrong. For example, if you wrote the constant 0x80000080 as "-0x80000080",
@ -52,8 +55,8 @@ JavaScript would treat that as negation of 2147483776, resulting in a value whos
0x80000080.
The safest way to write a 32-bit constant like 0x80000080 is "0x80000080|0", which will produce -2147483520. If you
write all your negative 32-bit constants that way, then you won't have to resort to 33-bit addition and potential
floating point operations.
write all your negative 32-bit constants that way, then you won't have to resort to using either unsigned right-shifts
or 33-bit addition, which in turn avoids the use of floating point values.
To continue the fun, try setting bit 0 of 0x80000000, which should give you 0x80000001:
@ -71,4 +74,4 @@ explain, for a negative number, toString() returns the positive representation o
*not* the "two's complement" of the number.
*[@jeffpar](http://twitter.com/jeffpar)*
*October 26, 2014*
*October 26, 2014 (Updated September 8, 2015)*

View file

@ -36,7 +36,6 @@
<manifest ref="/disks/pc/windows/1.04/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/windows/2.03/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/windows/3.00/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/windows/win95/4.00.499/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/windows/win95/4.00.950/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/minix/1.1/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/unix/ibm/pcix/manifest.xml" disk="*"/>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/versions/pcjs/1.19.4/manifest.xsl"?>
<manifest type="software">
<title>Windows 95 (Build 499)</title>
<version>4.00.499</version>
<type>Windows</type>
<category>Operating System</category>
<author>Microsoft</author>
<releaseDate>June 16, 1995</releaseDate>
<disk id="disk01" size="1474560" chs="80:2:18" img="../static/4.00.499/WIN95-DISK01.img" href="/disks/pc/windows/win95/4.00.499/WIN95-DISK01.json" md5="732d1cb011be22d1beda31f4dcd8ea56" md5json="6fa3bc43a08502be56bcf765c2470c35">
<name>Win95 Build 499 (Disk 1)</name>
</disk>
<disk id="disk02" size="1720320" chs="80:2:21" img="../static/4.00.499/WIN95-DISK02.img" href="/disks/pc/windows/win95/4.00.499/WIN95-DISK02.json" md5="3c4ff0741307c736fd956447b82db041" md5json="5688fde3e3f978e4d31127b118383f79">
<name>Win95 Build 499 (Disk 2)</name>
</disk>
<disk id="disk03" size="1720320" chs="80:2:21" img="../static/4.00.499/WIN95-DISK03.img" href="/disks/pc/windows/win95/4.00.499/WIN95-DISK03.json" md5="7c635a4d309ecc182db99dd3e6642bac" md5json="939be24d14aff97578200c8429ca4a05">
<name>Win95 Build 499 (Disk 3)</name>
</disk>
<disk id="disk04" size="1720320" chs="80:2:21" img="../static/4.00.499/WIN95-DISK04.img" href="/disks/pc/windows/win95/4.00.499/WIN95-DISK04.json" md5="59e781f887857883108d3e0a4ea338bc" md5json="4f62253c0731ac0d87d6cdbdc218a921">
<name>Win95 Build 499 (Disk 4)</name>
</disk>
<disk id="disk05" size="1720320" chs="80:2:21" img="../static/4.00.499/WIN95-DISK05.img" href="/disks/pc/windows/win95/4.00.499/WIN95-DISK05.json" md5="4866930efb5198e6e9c9ea6958989547" md5json="e9b2b53575ee7ca1546be36f9c36d143">
<name>Win95 Build 499 (Disk 5)</name>
</disk>
<disk id="disk06" size="1720320" chs="80:2:21" img="../static/4.00.499/WIN95-DISK06.img" href="/disks/pc/windows/win95/4.00.499/WIN95-DISK06.json" md5="99d60c50db21206908d569db0d859f5d" md5json="48aa2717c9cbcc46639cef839eed5378">
<name>Win95 Build 499 (Disk 6)</name>
</disk>
<disk id="disk07" size="1720320" chs="80:2:21" img="../static/4.00.499/WIN95-DISK07.img" href="/disks/pc/windows/win95/4.00.499/WIN95-DISK07.json" md5="c4eb6a910b5310427e3099c079c62261" md5json="75f2d3b963e81c1b37c725f350971562">
<name>Win95 Build 499 (Disk 7)</name>
</disk>
<disk id="disk08" size="1720320" chs="80:2:21" img="../static/4.00.499/WIN95-DISK08.img" href="/disks/pc/windows/win95/4.00.499/WIN95-DISK08.json" md5="d6ba0299f3f02851ac2cc44ed54dbcc3" md5json="2b89cfd0f05934bdc4cca15064f0d7d4">
<name>Win95 Build 499 (Disk 8)</name>
</disk>
<disk id="disk09" size="1720320" chs="80:2:21" img="../static/4.00.499/WIN95-DISK09.img" href="/disks/pc/windows/win95/4.00.499/WIN95-DISK09.json" md5="308387d3c51e837fd7869a7b0b44a3cb" md5json="4a86ce9737eb664450c351fb274f940c">
<name>Win95 Build 499 (Disk 9)</name>
</disk>
<disk id="disk10" size="1720320" chs="80:2:21" img="../static/4.00.499/WIN95-DISK10.img" href="/disks/pc/windows/win95/4.00.499/WIN95-DISK10.json" md5="1cc5e299dfb83dd555227ad9546393fe" md5json="dbee2d4261d25c235f01278fded36a0b">
<name>Win95 Build 499 (Disk 10)</name>
</disk>
<disk id="disk11" size="1720320" chs="80:2:21" img="../static/4.00.499/WIN95-DISK11.img" href="/disks/pc/windows/win95/4.00.499/WIN95-DISK11.json" md5="abe21872967030c77c4b1c5acc330944" md5json="40d23db1b9fd26cb728854aaf3a1d4f8">
<name>Win95 Build 499 (Disk 11)</name>
</disk>
<disk id="disk12" size="1720320" chs="80:2:21" img="../static/4.00.499/WIN95-DISK12.img" href="/disks/pc/windows/win95/4.00.499/WIN95-DISK12.json" md5="55a8eabe146455de6d724912608c4dae" md5json="f609a685c6a4e98447193a969d342a10">
<name>Win95 Build 499 (Disk 12)</name>
</disk>
<disk id="disk13" size="1720320" chs="80:2:21" img="../static/4.00.499/WIN95-DISK13.img" href="/disks/pc/windows/win95/4.00.499/WIN95-DISK13.json" md5="9842dbeabf3b7dc98ee727467c81cef6" md5json="4de833b8106fa20db8b760c2c2633123">
<name>Win95 Build 499 (Disk 13)</name>
</disk>
</manifest>

View file

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

View file

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

View file

@ -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);
/*