Debugger fixes for memory breakpoints, "if" processing, register parsing, and more
This commit is contained in:
parent
490289c75d
commit
9e055b7be3
3 changed files with 151 additions and 96 deletions
|
|
@ -44,9 +44,9 @@ var DumpAPI = require("../../shared/lib/dumpapi");
|
||||||
* FileDump()
|
* FileDump()
|
||||||
*
|
*
|
||||||
* TODO: Consider adding a "map" option that allows the user to supply a MAP filename (via a "map" API parameter
|
* TODO: Consider adding a "map" option that allows the user to supply a MAP filename (via a "map" API parameter
|
||||||
* or a "--map" command-line option), which in turn triggers a call to loadMap(). Note that loadMap() will need
|
* or a "--map" command-line option), which in turn triggers a call to loadMap(). Note that loadMap() will need to
|
||||||
* to be a bit more general and use a worker function that calls either net.getFile() or fs.readFile(), similar
|
* be a bit more general and use a worker function that calls either net.getFile() or fs.readFile(), similar to
|
||||||
* to what our loadFile() function already does.
|
* what the loadFile() function already does.
|
||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {string|undefined} sFormat should be one of "json"|"data"|"hex"|"bytes"|"rom" (see the FORMAT constants)
|
* @param {string|undefined} sFormat should be one of "json"|"data"|"hex"|"bytes"|"rom" (see the FORMAT constants)
|
||||||
|
|
@ -54,7 +54,7 @@ var DumpAPI = require("../../shared/lib/dumpapi");
|
||||||
* @param {boolean|string|undefined} fDecimal forces decimal output if not undefined
|
* @param {boolean|string|undefined} fDecimal forces decimal output if not undefined
|
||||||
* @param {number|string|undefined} offDump
|
* @param {number|string|undefined} offDump
|
||||||
* @param {number|string|undefined} nWidthDump
|
* @param {number|string|undefined} nWidthDump
|
||||||
* @param {string} [sServerRoot]
|
* @param {string} [sServerRoot] (if omitted, we assume local operation)
|
||||||
*/
|
*/
|
||||||
function FileDump(sFormat, fComments, fDecimal, offDump, nWidthDump, sServerRoot)
|
function FileDump(sFormat, fComments, fDecimal, offDump, nWidthDump, sServerRoot)
|
||||||
{
|
{
|
||||||
|
|
@ -67,6 +67,7 @@ function FileDump(sFormat, fComments, fDecimal, offDump, nWidthDump, sServerRoot
|
||||||
this.fDecimal = fDecimal;
|
this.fDecimal = fDecimal;
|
||||||
this.offDump = +offDump || 0;
|
this.offDump = +offDump || 0;
|
||||||
this.nWidthDump = +nWidthDump || 16;
|
this.nWidthDump = +nWidthDump || 16;
|
||||||
|
this.fLocal = !sServerRoot;
|
||||||
this.sServerRoot = sServerRoot || process.cwd();
|
this.sServerRoot = sServerRoot || process.cwd();
|
||||||
this.buf = null;
|
this.buf = null;
|
||||||
/*
|
/*
|
||||||
|
|
@ -135,7 +136,7 @@ FileDump.CLI = function()
|
||||||
var args = proc.getArgs();
|
var args = proc.getArgs();
|
||||||
|
|
||||||
if (!args.argc) {
|
if (!args.argc) {
|
||||||
console.log("usage: filedump --file=({path}|{URL}) [--merge=({path}|{url})] [--format=(json|data|hex|bytes|rom)] [--comments] [--decimal] [--output={path}] [--overwrite]");
|
console.log("usage: filedump --file=({path}|{URL}) [--merge=({path}|{url})] [--format=(json|data|hex|bytes|rom)] [--comments] [--decimal] [--offset={number}] [--width={number}] [--output={path}] [--overwrite]");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,7 +148,6 @@ FileDump.CLI = function()
|
||||||
}
|
}
|
||||||
|
|
||||||
var sOutputFile = argv['output'];
|
var sOutputFile = argv['output'];
|
||||||
if (sOutputFile && sOutputFile.charAt(0) != '/') sOutputFile = path.join(process.cwd(), sOutputFile);
|
|
||||||
var fOverwrite = argv['overwrite'];
|
var fOverwrite = argv['overwrite'];
|
||||||
|
|
||||||
var sFormat = FileDump.validateFormat(argv['format']);
|
var sFormat = FileDump.validateFormat(argv['format']);
|
||||||
|
|
@ -229,8 +229,8 @@ FileDump.validateFormat = function(sFormat)
|
||||||
* loadFile(sFile, iStart, nSkip, done)
|
* loadFile(sFile, iStart, nSkip, done)
|
||||||
*
|
*
|
||||||
* This used to be part of the FileDump constructor, but I felt it would be safer to separate
|
* This used to be part of the FileDump constructor, but I felt it would be safer to separate
|
||||||
* object creation from any I/O that the object may perform, to ensure that a callback can never
|
* object creation from any I/O, ensuring that the callback will not be invoked until the caller
|
||||||
* be called before the caller has actually received the newly created object.
|
* has received the FileDump object.
|
||||||
*
|
*
|
||||||
* @this {FileDump}
|
* @this {FileDump}
|
||||||
* @param {string} sFile
|
* @param {string} sFile
|
||||||
|
|
@ -245,7 +245,7 @@ FileDump.prototype.loadFile = function(sFile, iStart, nSkip, done)
|
||||||
var sExt = str.getExtension(sFile);
|
var sExt = str.getExtension(sFile);
|
||||||
var options = {encoding: sExt == DumpAPI.FORMAT.JSON || sExt == DumpAPI.FORMAT.HEX? "utf8" : null};
|
var options = {encoding: sExt == DumpAPI.FORMAT.JSON || sExt == DumpAPI.FORMAT.HEX? "utf8" : null};
|
||||||
|
|
||||||
var sFilePath = net.isRemote(sFile)? sFile : path.join(this.sServerRoot, sFile);
|
var sFilePath = (this.fLocal || net.isRemote(sFile))? sFile : path.join(this.sServerRoot, sFile);
|
||||||
|
|
||||||
if (!this.sFilePath) this.sFilePath = sFilePath;
|
if (!this.sFilePath) this.sFilePath = sFilePath;
|
||||||
if (this.fDebug) console.log("loadFile(" + sFilePath + "," + iStart + "," + nSkip + ")");
|
if (this.fDebug) console.log("loadFile(" + sFilePath + "," + iStart + "," + nSkip + ")");
|
||||||
|
|
@ -464,15 +464,15 @@ FileDump.prototype.dumpBuffer = function(sKey, buf, len, cbItem, offDump, nWidth
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* loadMap(sFilePath, done)
|
* loadMap(sMapFile, done)
|
||||||
*
|
*
|
||||||
* NOTE: Since ".map" files are an internal construct, I support only local map files (for now)
|
* NOTE: Since ".map" files are an internal construct, I support only local map files (for now)
|
||||||
*
|
*
|
||||||
* @this {FileDump}
|
* @this {FileDump}
|
||||||
* @param {string} sFilePath
|
* @param {string} sMapFile
|
||||||
* @param {function(Error,string)} done
|
* @param {function(Error,string)} done
|
||||||
*/
|
*/
|
||||||
FileDump.prototype.loadMap = function(sFilePath, done)
|
FileDump.prototype.loadMap = function(sMapFile, done)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The HEX format doesn't support MAP files, because old HEX clients expect an Array of bytes,
|
* The HEX format doesn't support MAP files, because old HEX clients expect an Array of bytes,
|
||||||
|
|
@ -484,10 +484,14 @@ FileDump.prototype.loadMap = function(sFilePath, done)
|
||||||
this.json = '"bytes":' + this.json;
|
this.json = '"bytes":' + this.json;
|
||||||
}
|
}
|
||||||
var obj = this;
|
var obj = this;
|
||||||
var sMapPath = sFilePath.replace(/\.(rom|json)$/, ".map");
|
|
||||||
if (str.endsWith(sMapPath, ".map")) {
|
sMapFile = sMapFile.replace(/\.(rom|json)$/, ".map");
|
||||||
var sMapFile = path.basename(sMapPath);
|
|
||||||
fs.readFile(sMapPath, {encoding: "utf8"}, function(err, str) {
|
if (str.endsWith(sMapFile, ".map")) {
|
||||||
|
|
||||||
|
var sMapName = path.basename(sMapFile);
|
||||||
|
|
||||||
|
fs.readFile(sMapFile, {encoding: "utf8"}, function(err, str) {
|
||||||
var sMapData = null;
|
var sMapData = null;
|
||||||
if (err) {
|
if (err) {
|
||||||
/*
|
/*
|
||||||
|
|
@ -612,7 +616,7 @@ FileDump.prototype.loadMap = function(sFilePath, done)
|
||||||
nBias = parseInt(sValue, 16);
|
nBias = parseInt(sValue, 16);
|
||||||
continue;
|
continue;
|
||||||
default:
|
default:
|
||||||
done(new Error("unrecognized symbol type (" + sType + ") in MAP file: " + sMapFile), null);
|
done(new Error("unrecognized symbol type (" + sType + ") in MAP file: " + sMapName), null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sID != sSymbol) {
|
if (sID != sSymbol) {
|
||||||
|
|
@ -624,7 +628,7 @@ FileDump.prototype.loadMap = function(sFilePath, done)
|
||||||
aSymbols[sID] = aValue;
|
aSymbols[sID] = aValue;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
done(new Error("unrecognized line (" + s + ") in MAP file: " + sMapFile), null);
|
done(new Error("unrecognized line (" + s + ") in MAP file: " + sMapName), null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sMapData = JSON.stringify(aSymbols);
|
sMapData = JSON.stringify(aSymbols);
|
||||||
|
|
@ -692,6 +696,9 @@ FileDump.prototype.convertToJSON = function(done)
|
||||||
*/
|
*/
|
||||||
FileDump.prototype.convertToFile = function(sOutputFile, fOverwrite)
|
FileDump.prototype.convertToFile = function(sOutputFile, fOverwrite)
|
||||||
{
|
{
|
||||||
|
if (sOutputFile && !this.fLocal) {
|
||||||
|
sOutputFile = path.join(this.sServerRoot, sOutputFile);
|
||||||
|
}
|
||||||
if (this.sFormat != DumpAPI.FORMAT.ROM) {
|
if (this.sFormat != DumpAPI.FORMAT.ROM) {
|
||||||
var obj = this;
|
var obj = this;
|
||||||
this.buildJSON();
|
this.buildJSON();
|
||||||
|
|
|
||||||
|
|
@ -755,7 +755,7 @@ HTTPAPI.processDumpAPI = function(req, res)
|
||||||
if (req.query[DumpAPI.QUERY.DECIMAL]) {
|
if (req.query[DumpAPI.QUERY.DECIMAL]) {
|
||||||
fDecimal = (req.query[DumpAPI.QUERY.DECIMAL] == "true");
|
fDecimal = (req.query[DumpAPI.QUERY.DECIMAL] == "true");
|
||||||
}
|
}
|
||||||
var file = new FileDump(sFormat, fComments, fDecimal, sServerRoot);
|
var file = new FileDump(sFormat, fComments, fDecimal, 0, 0, sServerRoot);
|
||||||
file.loadFile(sFile, 0, 0, function(err) {
|
file.loadFile(sFile, 0, 0, function(err) {
|
||||||
HTTPAPI.dumpFile(err, file, res);
|
HTTPAPI.dumpFile(err, file, res);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,7 @@ if (DEBUGGER) {
|
||||||
'a [#]': "assemble",
|
'a [#]': "assemble",
|
||||||
'b [#]': "breakpoint", // multiple variations (use b? to list them)
|
'b [#]': "breakpoint", // multiple variations (use b? to list them)
|
||||||
'c': "clear output",
|
'c': "clear output",
|
||||||
'd [#]': "dump memory",
|
'd [#]': "dump memory", // additional syntax: d [#] [l#], where l# is a number of bytes to dump
|
||||||
'e [#]': "edit memory",
|
'e [#]': "edit memory",
|
||||||
'f': "frequencies",
|
'f': "frequencies",
|
||||||
'g [#]': "go [to #]",
|
'g [#]': "go [to #]",
|
||||||
|
|
@ -2337,55 +2337,55 @@ if (DEBUGGER) {
|
||||||
var cpu = this.cpu;
|
var cpu = this.cpu;
|
||||||
switch(iReg) {
|
switch(iReg) {
|
||||||
case Debugger.REG_AL:
|
case Debugger.REG_AL:
|
||||||
n = cpu.regEAX;
|
n = cpu.regEAX & 0xff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_CL:
|
case Debugger.REG_CL:
|
||||||
n = cpu.regECX;
|
n = cpu.regECX & 0xff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_DL:
|
case Debugger.REG_DL:
|
||||||
n = cpu.regEDX;
|
n = cpu.regEDX & 0xff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_BL:
|
case Debugger.REG_BL:
|
||||||
n = cpu.regEBX;
|
n = cpu.regEBX & 0xff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_AH:
|
case Debugger.REG_AH:
|
||||||
n = cpu.regEAX >> 8;
|
n = (cpu.regEAX >> 8) & 0xff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_CH:
|
case Debugger.REG_CH:
|
||||||
n = cpu.regECX >> 8;
|
n = (cpu.regECX >> 8) & 0xff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_DH:
|
case Debugger.REG_DH:
|
||||||
n = cpu.regEDX >> 8;
|
n = (cpu.regEDX >> 8) & 0xff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_BH:
|
case Debugger.REG_BH:
|
||||||
n = cpu.regEBX >> 8;
|
n = (cpu.regEBX >> 8) & 0xff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_AX:
|
case Debugger.REG_AX:
|
||||||
n = cpu.regEAX;
|
n = cpu.regEAX & 0xffff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_CX:
|
case Debugger.REG_CX:
|
||||||
n = cpu.regECX;
|
n = cpu.regECX & 0xffff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_DX:
|
case Debugger.REG_DX:
|
||||||
n = cpu.regEDX;
|
n = cpu.regEDX & 0xffff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_BX:
|
case Debugger.REG_BX:
|
||||||
n = cpu.regEBX;
|
n = cpu.regEBX & 0xffff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_SP:
|
case Debugger.REG_SP:
|
||||||
n = cpu.getSP();
|
n = cpu.getSP() & 0xffff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_BP:
|
case Debugger.REG_BP:
|
||||||
n = cpu.regEBP;
|
n = cpu.regEBP & 0xffff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_SI:
|
case Debugger.REG_SI:
|
||||||
n = cpu.regESI;
|
n = cpu.regESI & 0xffff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_DI:
|
case Debugger.REG_DI:
|
||||||
n = cpu.regEDI;
|
n = cpu.regEDI & 0xffff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_IP:
|
case Debugger.REG_IP:
|
||||||
n = cpu.getIP();
|
n = cpu.getIP() & 0xffff;
|
||||||
break;
|
break;
|
||||||
case Debugger.REG_PS:
|
case Debugger.REG_PS:
|
||||||
n = cpu.getPS();
|
n = cpu.getPS();
|
||||||
|
|
@ -3271,39 +3271,59 @@ if (DEBUGGER) {
|
||||||
{
|
{
|
||||||
var fSuccess = false;
|
var fSuccess = false;
|
||||||
this.nSuppressBreaks++;
|
this.nSuppressBreaks++;
|
||||||
if (!this.findBreakpoint(aBreak, dbgAddr)) {
|
|
||||||
dbgAddr.fTempBreak = fTempBreak;
|
/*
|
||||||
aBreak.push(dbgAddr);
|
* We need to allow a temporary breakpoint at an address where they may already be a breakpoint.
|
||||||
if (aBreak != this.aBreakExec) {
|
*/
|
||||||
this.bus.addMemBreak(this.getAddr(dbgAddr), aBreak == this.aBreakWrite);
|
if (fTempBreak || !this.findBreakpoint(aBreak, dbgAddr)) {
|
||||||
}
|
|
||||||
if (fTempBreak) {
|
|
||||||
/*
|
|
||||||
* Force temporary breakpoints to be interpreted as linear breakpoints
|
|
||||||
* (hence the assertion that there IS a linear address stored in dbgAddr);
|
|
||||||
* this allows us to step over calls or interrupts that change the processor mode.
|
|
||||||
*/
|
|
||||||
if (dbgAddr.addr) dbgAddr.sel = null;
|
|
||||||
} else {
|
|
||||||
this.printBreakpoint(aBreak, aBreak.length-1);
|
|
||||||
}
|
|
||||||
if (!fTempBreak) this.historyInit();
|
|
||||||
fSuccess = true;
|
fSuccess = true;
|
||||||
|
|
||||||
|
if (aBreak != this.aBreakExec) {
|
||||||
|
var addr = this.getAddr(dbgAddr);
|
||||||
|
if (addr == X86.ADDR_INVALID) {
|
||||||
|
fSuccess = false;
|
||||||
|
} else {
|
||||||
|
this.bus.addMemBreak(addr, aBreak == this.aBreakWrite);
|
||||||
|
/*
|
||||||
|
* Force memory breakpoints to use their linear address, by zapping the selector.
|
||||||
|
*/
|
||||||
|
dbgAddr.sel = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fSuccess) {
|
||||||
|
aBreak.push(dbgAddr);
|
||||||
|
if (fTempBreak) {
|
||||||
|
/*
|
||||||
|
* Force temporary breakpoints to use their linear address, if one is available, by zapping
|
||||||
|
* the selector; this allows us to step over calls or interrupts that change the processor mode.
|
||||||
|
* TODO: Unfortunately, this will fail to trigger a "step" over a call in segment that moves
|
||||||
|
* during the call; consider alternatives.
|
||||||
|
*/
|
||||||
|
if (dbgAddr.addr != null) dbgAddr.sel = null;
|
||||||
|
dbgAddr.fTempBreak = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.printBreakpoint(aBreak, aBreak.length-1);
|
||||||
|
this.historyInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.nSuppressBreaks--;
|
this.nSuppressBreaks--;
|
||||||
return fSuccess;
|
return fSuccess;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* findBreakpoint(aBreak, dbgAddr, fRemove)
|
* findBreakpoint(aBreak, dbgAddr, fRemove, fTempBreak)
|
||||||
*
|
*
|
||||||
* @this {Debugger}
|
|
||||||
* @param {Array} aBreak
|
|
||||||
* @param {DbgAddr} dbgAddr
|
* @param {DbgAddr} dbgAddr
|
||||||
* @param {boolean} [fRemove]
|
* @param {boolean} [fRemove]
|
||||||
|
* @param {boolean} [fTempBreak]
|
||||||
* @return {boolean} true if found, false if not
|
* @return {boolean} true if found, false if not
|
||||||
*/
|
*/
|
||||||
Debugger.prototype.findBreakpoint = function(aBreak, dbgAddr, fRemove)
|
Debugger.prototype.findBreakpoint = function(aBreak, dbgAddr, fRemove, fTempBreak)
|
||||||
{
|
{
|
||||||
var fFound = false;
|
var fFound = false;
|
||||||
var addr = this.mapBreakpoint(this.getAddr(dbgAddr));
|
var addr = this.mapBreakpoint(this.getAddr(dbgAddr));
|
||||||
|
|
@ -3311,18 +3331,22 @@ if (DEBUGGER) {
|
||||||
var dbgAddrBreak = aBreak[i];
|
var dbgAddrBreak = aBreak[i];
|
||||||
if (addr != X86.ADDR_INVALID && addr == this.mapBreakpoint(this.getAddr(dbgAddrBreak)) ||
|
if (addr != X86.ADDR_INVALID && addr == this.mapBreakpoint(this.getAddr(dbgAddrBreak)) ||
|
||||||
addr == X86.ADDR_INVALID && dbgAddr.sel == dbgAddrBreak.sel && dbgAddr.off == dbgAddrBreak.off) {
|
addr == X86.ADDR_INVALID && dbgAddr.sel == dbgAddrBreak.sel && dbgAddr.off == dbgAddrBreak.off) {
|
||||||
fFound = true;
|
if (!fTempBreak || dbgAddrBreak.fTempBreak) {
|
||||||
if (fRemove) {
|
fFound = true;
|
||||||
aBreak.splice(i, 1);
|
if (fRemove) {
|
||||||
if (aBreak != this.aBreakExec) {
|
if (!dbgAddrBreak.fTempBreak) {
|
||||||
this.bus.removeMemBreak(addr, aBreak == this.aBreakWrite);
|
this.printBreakpoint(aBreak, i, "cleared");
|
||||||
|
}
|
||||||
|
aBreak.splice(i, 1);
|
||||||
|
if (aBreak != this.aBreakExec) {
|
||||||
|
this.bus.removeMemBreak(addr, aBreak == this.aBreakWrite);
|
||||||
|
}
|
||||||
|
this.historyInit();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (!dbgAddrBreak.fTempBreak) this.println("breakpoint cleared: " + this.hexAddr(dbgAddrBreak) + " (" + aBreak[0] + ')');
|
this.printBreakpoint(aBreak, i, "exists");
|
||||||
this.historyInit();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.println("breakpoint exists: " + this.hexAddr(dbgAddrBreak) + " (" + aBreak[0] + ')');
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fFound;
|
return fFound;
|
||||||
|
|
@ -3351,11 +3375,12 @@ if (DEBUGGER) {
|
||||||
* @this {Debugger}
|
* @this {Debugger}
|
||||||
* @param {Array} aBreak
|
* @param {Array} aBreak
|
||||||
* @param {number} i
|
* @param {number} i
|
||||||
|
* @param {string} [sAction]
|
||||||
*/
|
*/
|
||||||
Debugger.prototype.printBreakpoint = function(aBreak, i)
|
Debugger.prototype.printBreakpoint = function(aBreak, i, sAction)
|
||||||
{
|
{
|
||||||
var dbgAddr = aBreak[i];
|
var dbgAddr = aBreak[i];
|
||||||
this.println("breakpoint enabled: " + this.hexAddr(dbgAddr) + " (" + aBreak[0] + ')' + (dbgAddr.sCmd? (' "' + dbgAddr.sCmd + '"') : ''));
|
this.println("breakpoint " + (sAction || "enabled") + ": " + this.hexAddr(dbgAddr) + " (" + aBreak[0] + ')' + (dbgAddr.sCmd? (' "' + dbgAddr.sCmd + '"') : ''));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -3384,7 +3409,7 @@ if (DEBUGGER) {
|
||||||
for (var i = 1; i < this.aBreakExec.length; i++) {
|
for (var i = 1; i < this.aBreakExec.length; i++) {
|
||||||
var dbgAddrBreak = this.aBreakExec[i];
|
var dbgAddrBreak = this.aBreakExec[i];
|
||||||
if (dbgAddrBreak.fTempBreak) {
|
if (dbgAddrBreak.fTempBreak) {
|
||||||
if (!this.findBreakpoint(this.aBreakExec, dbgAddrBreak, true)) break;
|
if (!this.findBreakpoint(this.aBreakExec, dbgAddrBreak, true, true)) break;
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3477,7 +3502,7 @@ if (DEBUGGER) {
|
||||||
var a;
|
var a;
|
||||||
fBreak = true;
|
fBreak = true;
|
||||||
if (dbgAddrBreak.fTempBreak) {
|
if (dbgAddrBreak.fTempBreak) {
|
||||||
this.findBreakpoint(aBreak, dbgAddrBreak, true);
|
this.findBreakpoint(aBreak, dbgAddrBreak, true, true);
|
||||||
fTempBreak = true;
|
fTempBreak = true;
|
||||||
}
|
}
|
||||||
else if (a = dbgAddrBreak.aCmds) {
|
else if (a = dbgAddrBreak.aCmds) {
|
||||||
|
|
@ -3488,21 +3513,37 @@ if (DEBUGGER) {
|
||||||
* if you want the breakpoint to stop execution.
|
* if you want the breakpoint to stop execution.
|
||||||
*
|
*
|
||||||
* Another useful command is "if", which will return false if the expression is false,
|
* Another useful command is "if", which will return false if the expression is false,
|
||||||
* forcing us to set fBreak to true and skip the rest of the commands.
|
* at which point we'll jump ahead to the next "else" command, and if there isn't an "else",
|
||||||
|
* we abort.
|
||||||
*/
|
*/
|
||||||
fBreak = false;
|
fBreak = false;
|
||||||
for (var s in a) {
|
for (var j = 0; j < a.length; j++) {
|
||||||
if (!this.doCommand(a[s], true)) {
|
if (!this.doCommand(a[j], true)) {
|
||||||
fBreak = true;
|
if (a[j].indexOf("if")) {
|
||||||
break;
|
fBreak = true; // the failed command wasn't "if", so abort
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var k = j + 1;
|
||||||
|
for (; k < a.length; k++) {
|
||||||
|
if (!a[k].indexOf("else")) break;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
if (k == a.length) { // couldn't find an "else" after the "if", so abort
|
||||||
|
fBreak = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* If we're still here, we'll execute the "else" command (which is just a no-op),
|
||||||
|
* followed by any remaining commands.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this.cpu.isRunning()) fBreak = true;
|
if (!this.cpu.isRunning()) fBreak = true;
|
||||||
}
|
}
|
||||||
if (fBreak && !fTempBreak) {
|
if (fBreak) {
|
||||||
this.println("breakpoint hit: " + this.hexAddr(dbgAddrBreak) + " (" + aBreak[0] + ')');
|
if (!fTempBreak) this.printBreakpoint(aBreak, i, "hit");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
addrBreak++;
|
addrBreak++;
|
||||||
n++;
|
n++;
|
||||||
|
|
@ -4872,6 +4913,7 @@ if (DEBUGGER) {
|
||||||
dbgAddr.fProt = undefined;
|
dbgAddr.fProt = undefined;
|
||||||
|
|
||||||
sAddr = (dbgAddr.off == null? sAddr : str.toHexWord(dbgAddr.off));
|
sAddr = (dbgAddr.off == null? sAddr : str.toHexWord(dbgAddr.off));
|
||||||
|
|
||||||
if (sParm == 'c') {
|
if (sParm == 'c') {
|
||||||
if (dbgAddr.off == null) {
|
if (dbgAddr.off == null) {
|
||||||
this.clearBreakpoints();
|
this.clearBreakpoints();
|
||||||
|
|
@ -4887,15 +4929,19 @@ if (DEBUGGER) {
|
||||||
this.println("breakpoint missing: " + this.hexAddr(dbgAddr));
|
this.println("breakpoint missing: " + this.hexAddr(dbgAddr));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sParm == 'i') {
|
if (sParm == 'i') {
|
||||||
this.println("breakpoint " + (this.bus.addPortInputBreak(dbgAddr.off)? "enabled" : "cleared") + ": port " + sAddr + " (input)");
|
this.println("breakpoint " + (this.bus.addPortInputBreak(dbgAddr.off)? "enabled" : "cleared") + ": port " + sAddr + " (input)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sParm == 'o') {
|
if (sParm == 'o') {
|
||||||
this.println("breakpoint " + (this.bus.addPortOutputBreak(dbgAddr.off)? "enabled" : "cleared") + ": port " + sAddr + " (output)");
|
this.println("breakpoint " + (this.bus.addPortOutputBreak(dbgAddr.off)? "enabled" : "cleared") + ": port " + sAddr + " (output)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dbgAddr.off == null) return;
|
if (dbgAddr.off == null) return;
|
||||||
|
|
||||||
if (sOptions) {
|
if (sOptions) {
|
||||||
var a = sOptions.match(/(['"])(.*?)\1/);
|
var a = sOptions.match(/(['"])(.*?)\1/);
|
||||||
if (a) {
|
if (a) {
|
||||||
|
|
@ -4939,9 +4985,6 @@ if (DEBUGGER) {
|
||||||
* because we always display whole lines. If sLen is omitted/undefined, then we default to 8 lines, regardless
|
* because we always display whole lines. If sLen is omitted/undefined, then we default to 8 lines, regardless
|
||||||
* whether dumping bytes or words.
|
* whether dumping bytes or words.
|
||||||
*
|
*
|
||||||
* Also, unlike sAddr, sLen is interpreted as a decimal number, unless a radix specifier is included (eg, "0x100");
|
|
||||||
* sLen also supports the DEBUG.COM-style syntax of a preceding 'l' (eg, "l16").
|
|
||||||
*
|
|
||||||
* @this {Debugger}
|
* @this {Debugger}
|
||||||
* @param {string} sCmd
|
* @param {string} sCmd
|
||||||
* @param {string|undefined} sAddr
|
* @param {string|undefined} sAddr
|
||||||
|
|
@ -4971,26 +5014,32 @@ if (DEBUGGER) {
|
||||||
if (sDumpers.length) this.println("dump extensions:\n\t" + sDumpers);
|
if (sDumpers.length) this.println("dump extensions:\n\t" + sDumpers);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sAddr == "symbols") {
|
if (sAddr == "symbols") {
|
||||||
this.dumpSymbols();
|
this.dumpSymbols();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var cLines = 0;
|
|
||||||
|
var cb = 0;
|
||||||
if (sLen) {
|
if (sLen) {
|
||||||
if (sLen.charAt(0) == 'l') sLen = sLen.substr(1);
|
if (sLen.charAt(0) == 'l') sLen = sLen.substr(1);
|
||||||
cLines = str.parseInt(sLen, 10);
|
cb = this.parseValue(sLen) >>> 0; // negative lengths not allowed
|
||||||
|
if (cb > 0x10000) cb = 0x10000; // prevent bad user (or register) input from producing excessive output
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sCmd == 'd') {
|
if (sCmd == 'd') {
|
||||||
sCmd = this.sCmdDumpPrev || "db";
|
sCmd = this.sCmdDumpPrev || "db";
|
||||||
} else {
|
} else {
|
||||||
this.sCmdDumpPrev = sCmd;
|
this.sCmdDumpPrev = sCmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sAddr == "state") {
|
if (sAddr == "state") {
|
||||||
this.println(this.cmp.powerOff(true));
|
this.println(this.cmp.powerOff(true));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sCmd == "dh") {
|
if (sCmd == "dh") {
|
||||||
this.dumpHistory(sAddr, cLines);
|
this.dumpHistory(sAddr, cb);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sCmd == "dos") {
|
if (sCmd == "dos") {
|
||||||
|
|
@ -5003,11 +5052,13 @@ if (DEBUGGER) {
|
||||||
this.messageInt(Interrupts.DOS.VECTOR, this.cpu.regLIP, true);
|
this.messageInt(Interrupts.DOS.VECTOR, this.cpu.regLIP, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sCmd == "ds") { // transform a "ds" command into a "d desc" command
|
if (sCmd == "ds") { // transform a "ds" command into a "d desc" command
|
||||||
sCmd = 'd';
|
sCmd = 'd';
|
||||||
sLen = sAddr;
|
sLen = sAddr;
|
||||||
sAddr = "desc";
|
sAddr = "desc";
|
||||||
}
|
}
|
||||||
|
|
||||||
for (m in Debugger.MESSAGES) {
|
for (m in Debugger.MESSAGES) {
|
||||||
if (sAddr == m) {
|
if (sAddr == m) {
|
||||||
var fnDumper = this.afnDumpers[m];
|
var fnDumper = this.afnDumpers[m];
|
||||||
|
|
@ -5019,6 +5070,7 @@ if (DEBUGGER) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_DATA);
|
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_DATA);
|
||||||
if (!dbgAddr) return;
|
if (!dbgAddr) return;
|
||||||
|
|
||||||
|
|
@ -5030,14 +5082,8 @@ if (DEBUGGER) {
|
||||||
sDump += sInfo || "no information";
|
sDump += sInfo || "no information";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var cBytes = (sCmd == "dd"? 4 : (sCmd == "dw"? 2 : 1));
|
var cLines = (((cb || 256) + 15) >> 4) || 1;
|
||||||
var cNumbers = (16 / cBytes)|0;
|
var size = (sCmd == "dd"? 4 : (sCmd == "dw"? 2 : 1));
|
||||||
if (!cLines) {
|
|
||||||
cLines = 8;
|
|
||||||
} else {
|
|
||||||
cLines = ((cLines + cNumbers - 1) / cNumbers)|0;
|
|
||||||
if (!cLines) cLines = 1;
|
|
||||||
}
|
|
||||||
for (var iLine = 0; iLine < cLines; iLine++) {
|
for (var iLine = 0; iLine < cLines; iLine++) {
|
||||||
var data = 0, iByte = 0;
|
var data = 0, iByte = 0;
|
||||||
var sData = "", sChars = "";
|
var sData = "", sChars = "";
|
||||||
|
|
@ -5045,9 +5091,9 @@ if (DEBUGGER) {
|
||||||
for (var i = 0; i < 16; i++) {
|
for (var i = 0; i < 16; i++) {
|
||||||
var b = this.getByte(dbgAddr, 1);
|
var b = this.getByte(dbgAddr, 1);
|
||||||
data |= (b << (iByte++ << 3));
|
data |= (b << (iByte++ << 3));
|
||||||
if (iByte == cBytes) {
|
if (iByte == size) {
|
||||||
sData += str.toHex(data, cBytes * 2);
|
sData += str.toHex(data, size * 2);
|
||||||
sData += (cBytes == 1? (i == 7? '-' : ' ') : " ");
|
sData += (size == 1? (i == 7? '-' : ' ') : " ");
|
||||||
data = iByte = 0;
|
data = iByte = 0;
|
||||||
}
|
}
|
||||||
sChars += (b >= 32 && b < 128? String.fromCharCode(b) : '.');
|
sChars += (b >= 32 && b < 128? String.fromCharCode(b) : '.');
|
||||||
|
|
@ -5056,6 +5102,7 @@ if (DEBUGGER) {
|
||||||
sDump += sAddr + " " + sData + ' ' + sChars;
|
sDump += sAddr + " " + sData + ' ' + sChars;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sDump) this.println(sDump);
|
if (sDump) this.println(sDump);
|
||||||
this.dbgAddrNextData = dbgAddr;
|
this.dbgAddrNextData = dbgAddr;
|
||||||
};
|
};
|
||||||
|
|
@ -5170,7 +5217,7 @@ if (DEBUGGER) {
|
||||||
{
|
{
|
||||||
sCmd = str.trim(sCmd);
|
sCmd = str.trim(sCmd);
|
||||||
if (!this.parseExpression(sCmd)) {
|
if (!this.parseExpression(sCmd)) {
|
||||||
this.println("false: " + sCmd);
|
if (!fQuiet) this.println("false: " + sCmd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!fQuiet) this.println("true: " + sCmd);
|
if (!fQuiet) this.println("true: " + sCmd);
|
||||||
|
|
@ -6296,7 +6343,7 @@ if (DEBUGGER) {
|
||||||
a = [sCmd];
|
a = [sCmd];
|
||||||
} else {
|
} else {
|
||||||
a = sCmd.split(chSep || ';');
|
a = sCmd.split(chSep || ';');
|
||||||
for (var s in a) a[s] = str.trim(a[s]);
|
for (var i = 0; i < a.length; i++) a[i] = str.trim(a[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return a;
|
return a;
|
||||||
|
|
@ -6368,6 +6415,7 @@ if (DEBUGGER) {
|
||||||
this.doDump(asArgs[0], asArgs[1], asArgs[2]);
|
this.doDump(asArgs[0], asArgs[1], asArgs[2]);
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
|
if (asArgs[0] == "else") break;
|
||||||
this.doEdit(asArgs);
|
this.doEdit(asArgs);
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue