Improved fixup handling for better MACRO-10 support
This commit is contained in:
parent
c4a3d72674
commit
0e8e2ed4a2
12 changed files with 975 additions and 916 deletions
|
|
@ -959,7 +959,7 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* parseAddr(sAddr, fCode, fNoChecks, fPrint)
|
||||
* parseAddr(sAddr, fCode, fNoChecks)
|
||||
*
|
||||
* Address evaluation and validation (eg, range checks) are no longer performed at this stage. That's
|
||||
* done later, by getAddr(), which returns CPUDef.ADDR_INVALID for invalid segments, out-of-range offsets,
|
||||
|
|
@ -972,10 +972,9 @@ if (DEBUGGER) {
|
|||
* @param {string|undefined} sAddr
|
||||
* @param {boolean} [fCode] (true if target is code, false if target is data)
|
||||
* @param {boolean} [fNoChecks] (true when setting breakpoints that may not be valid now, but will be later)
|
||||
* @param {boolean} [fPrint]
|
||||
* @return {DbgAddr6502|null|undefined}
|
||||
*/
|
||||
Debugger6502.prototype.parseAddr = function(sAddr, fCode, fNoChecks, fPrint)
|
||||
Debugger6502.prototype.parseAddr = function(sAddr, fCode, fNoChecks)
|
||||
{
|
||||
var dbgAddr;
|
||||
var dbgAddrNext = (fCode? this.dbgAddrNextCode : this.dbgAddrNextData);
|
||||
|
|
@ -984,7 +983,7 @@ if (DEBUGGER) {
|
|||
sAddr = this.parseReference(sAddr);
|
||||
dbgAddr = this.findSymbolAddr(sAddr);
|
||||
if (dbgAddr) return dbgAddr;
|
||||
addr = this.parseExpression(sAddr, fPrint);
|
||||
addr = this.parseExpression(sAddr);
|
||||
}
|
||||
if (addr != null) {
|
||||
dbgAddr = this.newAddr(addr);
|
||||
|
|
@ -3801,7 +3800,7 @@ if (DEBUGGER) {
|
|||
sCmd = str.trim(sCmd);
|
||||
var a = sCmd.match(/^(['"])(.*?)\1$/);
|
||||
if (!a) {
|
||||
this.parseExpression(sCmd, true);
|
||||
this.parseExpression(sCmd, false);
|
||||
} else {
|
||||
this.println(this.replaceRegs(a[2]));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -473,7 +473,7 @@ class Debugger8080 extends Debugger {
|
|||
}
|
||||
|
||||
/**
|
||||
* parseAddr(sAddr, fCode, fNoChecks, fPrint)
|
||||
* parseAddr(sAddr, fCode, fNoChecks)
|
||||
*
|
||||
* Address evaluation and validation (eg, range checks) are no longer performed at this stage. That's
|
||||
* done later, by getAddr(), which returns CPUDef8080.ADDR_INVALID for invalid segments, out-of-range offsets,
|
||||
|
|
@ -486,10 +486,9 @@ class Debugger8080 extends Debugger {
|
|||
* @param {string|undefined} sAddr
|
||||
* @param {boolean} [fCode] (true if target is code, false if target is data)
|
||||
* @param {boolean} [fNoChecks] (true when setting breakpoints that may not be valid now, but will be later)
|
||||
* @param {boolean} [fPrint]
|
||||
* @return {DbgAddr8080|null|undefined}
|
||||
*/
|
||||
parseAddr(sAddr, fCode, fNoChecks, fPrint)
|
||||
parseAddr(sAddr, fCode, fNoChecks)
|
||||
{
|
||||
var dbgAddr;
|
||||
var dbgAddrNext = (fCode? this.dbgAddrNextCode : this.dbgAddrNextData);
|
||||
|
|
@ -498,7 +497,7 @@ class Debugger8080 extends Debugger {
|
|||
sAddr = this.parseReference(sAddr) || sAddr;
|
||||
dbgAddr = this.findSymbolAddr(sAddr);
|
||||
if (dbgAddr) return dbgAddr;
|
||||
addr = this.parseExpression(sAddr, fPrint);
|
||||
addr = this.parseExpression(sAddr);
|
||||
}
|
||||
if (addr != null) {
|
||||
dbgAddr = this.newAddr(addr);
|
||||
|
|
@ -3337,7 +3336,7 @@ class Debugger8080 extends Debugger {
|
|||
sCmd = Str.trim(sCmd);
|
||||
var a = sCmd.match(/^(['"])(.*?)\1$/);
|
||||
if (!a) {
|
||||
this.parseExpression(sCmd, true);
|
||||
this.parseExpression(sCmd, false);
|
||||
} else {
|
||||
this.println(this.replaceRegs(a[2]));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1375,7 +1375,7 @@ class DebuggerX86 extends Debugger {
|
|||
}
|
||||
|
||||
/**
|
||||
* parseAddr(sAddr, fCode, fNoChecks, fPrint)
|
||||
* parseAddr(sAddr, fCode, 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
|
||||
|
|
@ -1400,10 +1400,10 @@ class DebuggerX86 extends Debugger {
|
|||
* @param {string|undefined} sAddr
|
||||
* @param {boolean} [fCode] (true if target is code, false if target is data)
|
||||
* @param {boolean} [fNoChecks] (true when setting breakpoints that may not be valid now, but will be later)
|
||||
* @param {boolean} [fPrint]
|
||||
* @param {boolean} [fQuiet]
|
||||
* @return {DbgAddrX86|null|undefined}
|
||||
*/
|
||||
parseAddr(sAddr, fCode, fNoChecks, fPrint)
|
||||
parseAddr(sAddr, fCode, fNoChecks, fQuiet)
|
||||
{
|
||||
var dbgAddr;
|
||||
var dbgAddrNext = (fCode? this.dbgAddrNextCode : this.dbgAddrNextData);
|
||||
|
|
@ -1451,16 +1451,16 @@ class DebuggerX86 extends Debugger {
|
|||
|
||||
if (iColon < 0) {
|
||||
if (sel != null) {
|
||||
off = this.parseExpression(sAddr, fPrint);
|
||||
off = this.parseExpression(sAddr, fQuiet);
|
||||
addr = null;
|
||||
} else {
|
||||
addr = this.parseExpression(sAddr, fPrint);
|
||||
addr = this.parseExpression(sAddr, fQuiet);
|
||||
if (addr == null) off = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
sel = this.parseExpression(sAddr.substring(0, iColon), fPrint);
|
||||
off = this.parseExpression(sAddr.substring(iColon + 1), fPrint);
|
||||
sel = this.parseExpression(sAddr.substring(0, iColon), fQuiet);
|
||||
off = this.parseExpression(sAddr.substring(iColon + 1), fQuiet);
|
||||
addr = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1598,7 +1598,7 @@ class DebuggerX86 extends Debugger {
|
|||
var sInfo = "no information";
|
||||
if (BACKTRACK) {
|
||||
var sAddr = asArgs[0];
|
||||
var dbgAddr = this.parseAddr(sAddr, true, true, false);
|
||||
var dbgAddr = this.parseAddr(sAddr, true, true, true);
|
||||
if (dbgAddr) {
|
||||
var addr = this.getAddr(dbgAddr);
|
||||
if (dbgAddr.type != DebuggerX86.ADDRTYPE.PHYSICAL) {
|
||||
|
|
@ -5907,7 +5907,7 @@ class DebuggerX86 extends Debugger {
|
|||
sCmd = Str.trim(sCmd);
|
||||
var a = sCmd.match(/^(['"])(.*?)\1$/);
|
||||
if (!a) {
|
||||
this.parseExpression(sCmd, true);
|
||||
this.parseExpression(sCmd, false);
|
||||
} else {
|
||||
this.println(this.replaceRegs(a[2]));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -502,7 +502,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
}
|
||||
|
||||
/**
|
||||
* parseAddr(sAddr, dbgAddr, fPrint)
|
||||
* parseAddr(sAddr, dbgAddr)
|
||||
*
|
||||
* Address evaluation and validation (eg, range checks) are no longer performed at this stage. That's
|
||||
* done later, by getAddr(), which returns PDP10.ADDR_INVALID for invalid segments, out-of-range offsets,
|
||||
|
|
@ -514,10 +514,9 @@ class DebuggerPDP10 extends Debugger {
|
|||
* @this {DebuggerPDP10}
|
||||
* @param {string|undefined} sAddr
|
||||
* @param {DbgAddrPDP10} [dbgAddr]
|
||||
* @param {boolean} [fPrint]
|
||||
* @return {DbgAddrPDP10|null|undefined}
|
||||
*/
|
||||
parseAddr(sAddr, dbgAddr, fPrint)
|
||||
parseAddr(sAddr, dbgAddr)
|
||||
{
|
||||
var fPhysical, nBase;
|
||||
if (!dbgAddr) dbgAddr = this.newAddr();
|
||||
|
|
@ -538,7 +537,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
} else if (sAddr.indexOf('.') >= 0) {
|
||||
nBase = 10;
|
||||
}
|
||||
addr = this.parseExpression(sAddr, fPrint);
|
||||
addr = this.parseExpression(sAddr);
|
||||
}
|
||||
if (addr != null) {
|
||||
addr = this.validateWord(addr, this.nBusWidth);
|
||||
|
|
@ -1611,19 +1610,27 @@ class DebuggerPDP10 extends Debugger {
|
|||
}
|
||||
|
||||
/**
|
||||
* parseInstruction(sOpcode, sOperands, addr)
|
||||
* parseInstruction(sOpcode, sOperands, addr, fQuiet)
|
||||
*
|
||||
* @this {DebuggerPDP10}
|
||||
* @param {string} sOpcode
|
||||
* @param {string} [sOperands]
|
||||
* @param {number} [addr] of memory where this instruction is being assembled
|
||||
* @param {boolean} [fQuiet]
|
||||
* @return {number} (opcode, or -1 if unrecognized instruction)
|
||||
*/
|
||||
parseInstruction(sOpcode, sOperands, addr)
|
||||
parseInstruction(sOpcode, sOperands, addr, fQuiet)
|
||||
{
|
||||
var opCode = -1;
|
||||
var opMask, opNum;
|
||||
|
||||
/*
|
||||
* It's best to always clear sUndefined up front, because the caller won't
|
||||
* necessarily know whether or not we had to call parseExpression() for this
|
||||
* instruction.
|
||||
*/
|
||||
this.sUndefined = null;
|
||||
|
||||
if (!sOpcode) {
|
||||
/*
|
||||
* MACRO-10 permits instructions to be assembled without an explicit opcode;
|
||||
|
|
@ -1700,7 +1707,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
var sOperand = aOperands[i].trim();
|
||||
if (!sOperand) continue;
|
||||
if (i > 1) {
|
||||
this.println("too many operands: " + sOperand);
|
||||
this.println("too many operands: " + sOperands);
|
||||
opCode = -1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1722,7 +1729,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
sOperand = sOperand.replace(/(^|[^0-9])\./g, "$1" + this.toStrOffset(addr));
|
||||
}
|
||||
}
|
||||
var operand = this.parseExpression(sOperand);
|
||||
var operand = this.parseExpression(sOperand, fQuiet);
|
||||
if (operand == undefined) {
|
||||
opCode = -1;
|
||||
break;
|
||||
|
|
@ -1751,7 +1758,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
opCode += operand;
|
||||
sOperand = match[3];
|
||||
if (sOperand) {
|
||||
operand = this.parseExpression(sOperand);
|
||||
operand = this.parseExpression(sOperand, fQuiet);
|
||||
if (operand == undefined) {
|
||||
opCode = -1;
|
||||
break;
|
||||
|
|
@ -1774,7 +1781,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
// }
|
||||
}
|
||||
|
||||
if (opCode < 0 && sOperands != null) {
|
||||
if (opCode < 0 && !fQuiet) {
|
||||
this.println("unknown instruction: " + sOpcode + ' ' + sOperands);
|
||||
}
|
||||
|
||||
|
|
@ -2591,12 +2598,22 @@ class DebuggerPDP10 extends Debugger {
|
|||
dbg.println("assembly already in progress");
|
||||
}
|
||||
else {
|
||||
var sFile = match[2];
|
||||
var addrLoad = dbgAddr.addr;
|
||||
this.macro10 = new Macro10(match[2], addrLoad, sOptions, dbg, function doneMacro10(nErrorCode, sURL) {
|
||||
this.macro10 = new Macro10(sFile, addrLoad, sOptions, dbg, function doneMacro10(nErrorCode, sURL) {
|
||||
if (!nErrorCode) {
|
||||
dbg.loadBin(dbg.macro10.getBin(), addrLoad);
|
||||
/*
|
||||
* NOTE: Most Debugger operations run in the context of doCommand(), which catches any exceptions;
|
||||
* however, this callback may be running in a different context (eg, a network request callback), so
|
||||
* better safe than sorry.
|
||||
*/
|
||||
try {
|
||||
dbg.loadBin(dbg.macro10.getBin(), addrLoad);
|
||||
} catch(e) {
|
||||
dbg.println(e.message);
|
||||
}
|
||||
} else {
|
||||
dbg.println("error (" + nErrorCode + ") processing " + sURL);
|
||||
dbg.println("error (" + nErrorCode + ") processing " + (sURL || sFile));
|
||||
}
|
||||
dbg.macro10 = null;
|
||||
});
|
||||
|
|
@ -3318,7 +3335,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
sCmd = Str.trim(sCmd);
|
||||
var a = sCmd.match(/^(['"])(.*?)\1$/);
|
||||
if (!a) {
|
||||
this.parseExpression(sCmd, true);
|
||||
this.parseExpression(sCmd, false);
|
||||
} else {
|
||||
if (a[2].length > 1) {
|
||||
this.println(this.replaceRegs(a[2]));
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ var Sym;
|
|||
/**
|
||||
* @typedef {{
|
||||
* nLocation:(number),
|
||||
* aValues:(Array.<string>),
|
||||
* sValue:(string)
|
||||
* }}
|
||||
*/
|
||||
var Fixup;
|
||||
|
|
@ -301,50 +301,14 @@ class Macro10 {
|
|||
}
|
||||
|
||||
this.aFixups.forEach(function processFixup(fixup){
|
||||
var value = 0;
|
||||
var sValue = fixup.aValues[0];
|
||||
var sOperand = fixup.aValues[1];
|
||||
var nLocation = fixup.nLocation;
|
||||
if (fixup.aValues.length == 1) {
|
||||
if (sValue.indexOf('@') >= 0 || sValue.indexOf('(') >= 0) {
|
||||
sOperand = sValue;
|
||||
sValue = "";
|
||||
}
|
||||
var value = macro10.parseExpression(fixup.sValue, undefined, nLocation);
|
||||
if (value === undefined) {
|
||||
macro10.error("unable to parse expression: " + fixup.sValue);
|
||||
return;
|
||||
}
|
||||
if (sOperand != null) {
|
||||
value = macro10.dbg.parseInstruction(sValue, sOperand, nLocation);
|
||||
if (value < 0) {
|
||||
macro10.error("unable to parse instruction: " + fixup.aValues[0] + ' ' + fixup.aValues[1]);
|
||||
}
|
||||
} else {
|
||||
value = macro10.parseExpression(sValue, nLocation);
|
||||
if (value === undefined) {
|
||||
macro10.error("unable to parse expression: " + sValue);
|
||||
}
|
||||
else if (macro10.aWords[nLocation] === undefined) {
|
||||
macro10.error("undefined fixup location: " + Str.toOct(nLocation, 0, true));
|
||||
}
|
||||
else {
|
||||
value += macro10.aWords[nLocation];
|
||||
}
|
||||
}
|
||||
/*
|
||||
* At the end of the day, we only want unsigned 36-bit values, and some of our internal calculations
|
||||
* already adhere to that (eg, the parseExpression() concatenation of two unsigned 18-bit values).
|
||||
* But in general, the expression parser is allowed to return negative values, so that signed arithmetic
|
||||
* works naturally. Perhaps that decision should be revisited, but for now, this particular mix of
|
||||
* behaviors means that when checking for out-of-range values, the upper bound must be the unsigned
|
||||
* WORD_LIMIT, while the lower bound must be the signed INT_LIMIT.
|
||||
*
|
||||
* In a perfect world, the bounds would either be -INT_LIMIT,INT_LIMIT-1 or 0,WORD_LIMIT-1. In any
|
||||
* event, the truncate() call will ensure everything is an unsigned 36-bit value. The warning should
|
||||
* trigger only if the original value contained more than 36 significant bits.
|
||||
*/
|
||||
var w = macro10.dbg.truncate(value || 0, 36, true);
|
||||
if (value < -PDP10.INT_LIMIT || value >= PDP10.WORD_LIMIT) {
|
||||
macro10.warning("truncated value " + Str.toOct(value) + " at location " + Str.toOct(nLocation) + " to " + Str.toOct(w));
|
||||
}
|
||||
macro10.aWords[nLocation] = w;
|
||||
value += macro10.aWords[nLocation];
|
||||
macro10.aWords[nLocation] = macro10.truncate(value, nLocation);
|
||||
});
|
||||
|
||||
} catch(err) {
|
||||
|
|
@ -406,9 +370,10 @@ class Macro10 {
|
|||
}
|
||||
}
|
||||
|
||||
this.sOperator = sOperator;
|
||||
sOperands = sOperands.trim();
|
||||
|
||||
if (!sOperator && !sOperands) return true;
|
||||
|
||||
/*
|
||||
* My initial read of the MACRO-10 specification suggested that lines may begin with EITHER
|
||||
* "symbol:" (for a label) or "symbol=" (for an assignment), but apparently they can have BOTH.
|
||||
|
|
@ -418,6 +383,7 @@ class Macro10 {
|
|||
sOperands = sOperands.substr(1);
|
||||
sOperator = '=';
|
||||
}
|
||||
this.sOperator = sOperator;
|
||||
|
||||
/*
|
||||
* Check the operands for a literal. If the line contains and/or ends with a literal
|
||||
|
|
@ -440,12 +406,6 @@ class Macro10 {
|
|||
if (!this.parseMacro(sOperator, sOperands)) {
|
||||
|
||||
switch (sOperator) {
|
||||
case "":
|
||||
if (sOperands) {
|
||||
this.genWord(0, [sOperands]);
|
||||
}
|
||||
break;
|
||||
|
||||
case "=":
|
||||
this.addAssign(sLabel, sOperands);
|
||||
break;
|
||||
|
|
@ -471,10 +431,7 @@ class Macro10 {
|
|||
break;
|
||||
|
||||
default:
|
||||
if (!this.parseOpcode(sOperator, sOperands)) {
|
||||
this.genWord(0, [sOperator + sOperands]);
|
||||
// if (DEBUG) this.println(Str.toDec(this.nLine, 5) + ": label(" + sLabel + ") operator(" + sOperator + ") operands(" + sOperands + ") comment(" + sComment + ")");
|
||||
}
|
||||
this.addWord(sOperator, sOperands);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -529,24 +486,6 @@ class Macro10 {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* parseOpcode(sOpcode, sOperands)
|
||||
*
|
||||
* @this {Macro10}
|
||||
* @param {string} sOpcode
|
||||
* @param {string} sOperands
|
||||
* @return {boolean}
|
||||
*/
|
||||
parseOpcode(sOpcode, sOperands)
|
||||
{
|
||||
var opCode = this.dbg.parseInstruction(sOpcode);
|
||||
if (opCode >= 0) {
|
||||
this.genWord(opCode, [sOpcode, sOperands]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* isSymbolChar(ch)
|
||||
*
|
||||
|
|
@ -653,7 +592,7 @@ class Macro10 {
|
|||
}
|
||||
|
||||
/**
|
||||
* parseExpression(sOperand, nLocation)
|
||||
* parseExpression(sOperand, fPass1, nLocation)
|
||||
*
|
||||
* This is a wrapper around the Debugger's parseExpression() function to take care of some
|
||||
* additional requirements we have, such as interpreting a period as the current location and
|
||||
|
|
@ -662,38 +601,44 @@ class Macro10 {
|
|||
*
|
||||
* @this {Macro10}
|
||||
* @param {string} sOperand
|
||||
* @param {number} [nLocation]
|
||||
* @param {boolean|undefined} [fPass1]
|
||||
* @param {number|undefined} [nLocation]
|
||||
* @return {number|undefined}
|
||||
*/
|
||||
parseExpression(sOperand, nLocation)
|
||||
parseExpression(sOperand, fPass1, nLocation)
|
||||
{
|
||||
var result;
|
||||
|
||||
if (nLocation === undefined) nLocation = this.nLocation;
|
||||
|
||||
/*
|
||||
* Check for the "period" syntax that MACRO-10 uses to represent the value of the current
|
||||
* location. The Debugger's parseInstruction() method understands that syntax, but its
|
||||
* parseExpression() method does not.
|
||||
*
|
||||
* Note that the Debugger's parseInstruction() replaces any period not PRECEDED by a decimal
|
||||
* digit with the current address, because our Debuggers' only other interpretation of a period
|
||||
* is as the suffix of a decimal integer, whereas MACRO-10's only other interpretation of a period
|
||||
* is (I think) as the decimal point within a floating-point number, so here we only replace
|
||||
* periods that are not FOLLOWED by a decimal digit.
|
||||
*/
|
||||
sOperand = sOperand.replace(/\.([^0-9]|$)/g, this.dbg.toStrBase(nLocation, -1) + "$1");
|
||||
|
||||
/*
|
||||
* Check for the "double comma" syntax that MACRO-10 uses to express a 36-bit value as two 18-bit halves.
|
||||
* Check for the "double comma" syntax that MACRO-10 uses to express a 36-bit value as two 18-bit halves,
|
||||
* and invoke ourselves recursively for each half.
|
||||
*/
|
||||
var match = sOperand.match(/^([^,]*),,([^,]*)$/);
|
||||
if (!match) {
|
||||
result = this.dbg.parseExpression(sOperand);
|
||||
if (nLocation === undefined) nLocation = this.nLocation;
|
||||
/*
|
||||
* Check for the "period" syntax that MACRO-10 uses to represent the value of the current
|
||||
* location. The Debugger's parseInstruction() method understands that syntax, but its
|
||||
* parseExpression() method does not.
|
||||
*
|
||||
* Note that the Debugger's parseInstruction() replaces any period not PRECEDED by a decimal
|
||||
* digit with the current address, because our Debuggers' only other interpretation of a period
|
||||
* is as the suffix of a decimal integer, whereas MACRO-10's only other interpretation of a period
|
||||
* is (I think) as the decimal point within a floating-point number, so here we only replace
|
||||
* periods that are not FOLLOWED by a decimal digit.
|
||||
*/
|
||||
result = this.dbg.parseExpression(sOperand.replace(/\.([^0-9]|$)/g, this.dbg.toStrBase(nLocation, -1) + "$1"), fPass1);
|
||||
if (result === undefined) {
|
||||
this.error("error parsing expression: " + sOperand);
|
||||
}
|
||||
// else if (this.dbg.sUndefined != null) {
|
||||
// /*
|
||||
// * If a valid result was returned but sUndefined is also set, then this must be a pass1 evaluation.
|
||||
// */
|
||||
// }
|
||||
} else {
|
||||
var wLeft = match[1]? this.dbg.parseExpression(match[1]) : 0;
|
||||
var wLeft = match[1]? this.parseExpression(match[1]) : 0;
|
||||
if (wLeft !== undefined) {
|
||||
var wRight = match[2]? this.dbg.parseExpression(match[2]) : 0;
|
||||
var wRight = match[2]? this.parseExpression(match[2]) : 0;
|
||||
if (wRight !== undefined) {
|
||||
/*
|
||||
* NOTE: These must be combined as UNSIGNED values, so that's what we tell truncate().
|
||||
|
|
@ -880,6 +825,9 @@ class Macro10 {
|
|||
aParms = aDefaults = [];
|
||||
|
||||
if (sOperator == Macro10.PSEUDO_OP.DEFINE) {
|
||||
/*
|
||||
* This is a DEFINE (macro) block.
|
||||
*/
|
||||
match = sOperands.match(/([A-Z$%.][0-9A-Z$%.]*)\s*(\([^)]*\)|)\s*(<|)(.*)/i);
|
||||
if (!match) {
|
||||
this.error("unrecognized " + sOperator + " definition: " + sOperands);
|
||||
|
|
@ -902,6 +850,9 @@ class Macro10 {
|
|||
iMatch = 3;
|
||||
}
|
||||
else if (sOperator == Macro10.PSEUDO_OP.LITERAL) {
|
||||
/*
|
||||
* This is a LITERAL block.
|
||||
*/
|
||||
this.chMacroOpen = '[';
|
||||
this.chMacroClose = ']';
|
||||
name = '_' + Str.toDec(this.nLocation, 5);
|
||||
|
|
@ -914,6 +865,9 @@ class Macro10 {
|
|||
iMatch = 0;
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* This must be a REPEAT or CONDITIONAL block.
|
||||
*/
|
||||
var sOperand = this.getExpression(sOperands);
|
||||
if (!sOperand) {
|
||||
this.error("missing " + sOperator + " expression: " + sOperands);
|
||||
|
|
@ -923,6 +877,10 @@ class Macro10 {
|
|||
sOperand = sOperand.trim();
|
||||
match = sOperands.match(/\s*(<|)(.*)/i);
|
||||
name = '_' + sOperator;
|
||||
/*
|
||||
* The expression is either a repeat count or a condition. Either way, we must be able to
|
||||
* resolve it now, so we don't set fPass1 (but that doesn't mean it's the second pass, either).
|
||||
*/
|
||||
nOperand = this.parseExpression(sOperand) || 0;
|
||||
iMatch = 1;
|
||||
}
|
||||
|
|
@ -1008,6 +966,36 @@ class Macro10 {
|
|||
this.dbg.setVariable(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* addWord(sOperator, sOperands)
|
||||
*
|
||||
* @this {Macro10}
|
||||
* @param {string} sOperator
|
||||
* @param {string} sOperands
|
||||
*/
|
||||
addWord(sOperator, sOperands)
|
||||
{
|
||||
var w;
|
||||
var sExp = sOperator + sOperands;
|
||||
if (sExp.indexOf(",,") >= 0 || !sOperator && sExp.indexOf('@') < 0 && sExp.indexOf('(') < 0) {
|
||||
w = this.parseExpression(sExp, true);
|
||||
} else {
|
||||
w = this.dbg.parseInstruction(sOperator, sOperands, this.nLocation, true);
|
||||
if (w < 0) {
|
||||
/*
|
||||
* MACRO-10 also allows instructions to be assembled without an opcode (ie, just an address reference).
|
||||
*/
|
||||
w = this.dbg.parseInstruction("", sExp, this.nLocation, true);
|
||||
if (w < 0) w = undefined;
|
||||
}
|
||||
}
|
||||
if (w !== undefined) {
|
||||
this.genWord(w, this.dbg.sUndefined);
|
||||
} else {
|
||||
this.error("unrecognized expression: " + sExp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* addXWD()
|
||||
*
|
||||
|
|
@ -1019,7 +1007,7 @@ class Macro10 {
|
|||
*/
|
||||
addXWD(sOperands)
|
||||
{
|
||||
this.genWord(0, [sOperands.replace(",", ",,")]);
|
||||
this.genWord(0, sOperands.replace(",", ",,"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1068,18 +1056,35 @@ class Macro10 {
|
|||
}
|
||||
|
||||
/**
|
||||
* genWord(w, aValues)
|
||||
* genWord(value, sFixup)
|
||||
*
|
||||
* @this {Macro10}
|
||||
* @param {number} w (default value for the current location)
|
||||
* @param {Array.<string>} [aValues] (optional fixup value(s) to evaluate later)
|
||||
* @param {number} value (default value for the current location)
|
||||
* @param {string|null} [sFixup] (optional fixup value to evaluate later)
|
||||
*/
|
||||
genWord(w, aValues)
|
||||
genWord(value, sFixup)
|
||||
{
|
||||
this.aWords[this.nLocation] = w;
|
||||
if (aValues) this.aFixups.push({nLocation: this.nLocation, aValues});
|
||||
this.aWords[this.nLocation] = this.truncate(value);
|
||||
if (sFixup != null) this.aFixups.push({nLocation: this.nLocation, sValue: sFixup});
|
||||
this.nLocation++;
|
||||
}
|
||||
|
||||
/**
|
||||
* truncate(value, nLocation)
|
||||
*
|
||||
* @this {Macro10}
|
||||
* @param {number} value
|
||||
* @param {number} [nLocation]
|
||||
* @return {number}
|
||||
*/
|
||||
truncate(value, nLocation = this.nLocation)
|
||||
{
|
||||
var w = this.dbg.truncate(value || 0, 36, true);
|
||||
if (value < -PDP10.INT_LIMIT || value >= PDP10.WORD_LIMIT) {
|
||||
this.warning("truncated value " + Str.toOct(value) + " at location " + Str.toOct(nLocation) + " to " + Str.toOct(w));
|
||||
}
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
Macro10.PSEUDO_OP = {
|
||||
|
|
|
|||
|
|
@ -532,7 +532,7 @@ class DebuggerPDP11 extends Debugger {
|
|||
}
|
||||
|
||||
/**
|
||||
* parseAddr(sAddr, fCode, fNoChecks, fPrint)
|
||||
* parseAddr(sAddr, fCode, fNoChecks)
|
||||
*
|
||||
* Address evaluation and validation (eg, range checks) are no longer performed at this stage. That's
|
||||
* done later, by getAddr(), which returns PDP11.ADDR_INVALID for invalid segments, out-of-range offsets,
|
||||
|
|
@ -545,10 +545,9 @@ class DebuggerPDP11 extends Debugger {
|
|||
* @param {string|undefined} sAddr
|
||||
* @param {boolean} [fCode] (true if target is code, false if target is data)
|
||||
* @param {boolean} [fNoChecks] (true when setting breakpoints that may not be valid now, but will be later)
|
||||
* @param {boolean} [fPrint]
|
||||
* @return {DbgAddrPDP11|null|undefined}
|
||||
*/
|
||||
parseAddr(sAddr, fCode, fNoChecks, fPrint)
|
||||
parseAddr(sAddr, fCode, fNoChecks)
|
||||
{
|
||||
var dbgAddr;
|
||||
var dbgAddrNext = (fCode? this.dbgAddrNextCode : this.dbgAddrNextData);
|
||||
|
|
@ -570,7 +569,7 @@ class DebuggerPDP11 extends Debugger {
|
|||
} else if (sAddr.indexOf('.') >= 0) {
|
||||
nBase = 10;
|
||||
}
|
||||
addr = this.parseExpression(sAddr, fPrint);
|
||||
addr = this.parseExpression(sAddr);
|
||||
}
|
||||
if (addr != null) {
|
||||
dbgAddr = this.newAddr(addr, fPhysical, nBase);
|
||||
|
|
@ -3483,7 +3482,7 @@ class DebuggerPDP11 extends Debugger {
|
|||
sCmd = Str.trim(sCmd);
|
||||
var a = sCmd.match(/^(['"])(.*?)\1$/);
|
||||
if (!a) {
|
||||
this.parseExpression(sCmd, true);
|
||||
this.parseExpression(sCmd, false);
|
||||
} else {
|
||||
if (a[2].length > 1) {
|
||||
this.println(this.replaceRegs(a[2]));
|
||||
|
|
|
|||
|
|
@ -77,7 +77,8 @@ var DbgAddr;
|
|||
*
|
||||
* @unrestricted
|
||||
*/
|
||||
class Debugger extends Component {
|
||||
class Debugger extends Component
|
||||
{
|
||||
/**
|
||||
* Debugger(parmsDbg)
|
||||
*
|
||||
|
|
@ -91,7 +92,8 @@ class Debugger extends Component {
|
|||
*
|
||||
* @param {Object} parmsDbg
|
||||
*/
|
||||
constructor(parmsDbg) {
|
||||
constructor(parmsDbg)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
|
||||
super("Debugger", parmsDbg);
|
||||
|
|
@ -107,6 +109,19 @@ class Debugger extends Component {
|
|||
*/
|
||||
this.nBits = 32;
|
||||
|
||||
/*
|
||||
* sUndefined is set to the last unknown variable that parseExpression() encounters, if if
|
||||
* is called in "quiet mode"; the value used for the variable is zero. This mode was added
|
||||
* for components that need to support expressions containing "fixups" (ie, values that
|
||||
* must be determined later).
|
||||
*
|
||||
* TODO: Only one unknown (fixup) per expression is supported, and we don't indicate what
|
||||
* operation was performed with the value (callers generally assume addition). If a call
|
||||
* encounters more than one undefined value, it will revert to normal error reporting and
|
||||
* return an undefined value for the entire expression.
|
||||
*/
|
||||
this.sUndefined = null;
|
||||
|
||||
this.achGroup = ['{','}'];
|
||||
this.achAddress = ['[',']'];
|
||||
|
||||
|
|
@ -163,7 +178,8 @@ class Debugger extends Component {
|
|||
* @param {number} [off] optional offset into sReg
|
||||
* @return {number} register index, or -1 if not found
|
||||
*/
|
||||
getRegIndex(sReg, off) {
|
||||
getRegIndex(sReg, off)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -176,7 +192,8 @@ class Debugger extends Component {
|
|||
* @param {number} iReg
|
||||
* @return {number|undefined}
|
||||
*/
|
||||
getRegValue(iReg) {
|
||||
getRegValue(iReg)
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
|
@ -192,7 +209,8 @@ class Debugger extends Component {
|
|||
* @param {string} sAddr
|
||||
* @return {string}
|
||||
*/
|
||||
parseAddrReference(s, sAddr) {
|
||||
parseAddrReference(s, sAddr)
|
||||
{
|
||||
return s.replace('[' + sAddr + ']', "unimplemented");
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +220,8 @@ class Debugger extends Component {
|
|||
* @this {Debugger}
|
||||
* @return {string}
|
||||
*/
|
||||
getNextCommand() {
|
||||
getNextCommand()
|
||||
{
|
||||
var sCmd;
|
||||
if (this.iPrevCmd > 0) {
|
||||
sCmd = this.aPrevCmds[--this.iPrevCmd];
|
||||
|
|
@ -219,7 +238,8 @@ class Debugger extends Component {
|
|||
* @this {Debugger}
|
||||
* @return {string|null}
|
||||
*/
|
||||
getPrevCommand() {
|
||||
getPrevCommand()
|
||||
{
|
||||
var sCmd = null;
|
||||
if (this.iPrevCmd < this.aPrevCmds.length - 1) {
|
||||
sCmd = this.aPrevCmds[++this.iPrevCmd];
|
||||
|
|
@ -236,7 +256,8 @@ class Debugger extends Component {
|
|||
* @param {string} [chSep] is the command separator character (default is ';')
|
||||
* @return {Array.<string>}
|
||||
*/
|
||||
parseCommand(sCmd, fSave, chSep) {
|
||||
parseCommand(sCmd, fSave, chSep)
|
||||
{
|
||||
if (fSave) {
|
||||
if (!sCmd) {
|
||||
if (this.fAssemble) {
|
||||
|
|
@ -434,7 +455,8 @@ class Debugger extends Component {
|
|||
* @param {number} [cOps] (default is -1 for all)
|
||||
* @return {boolean} true if successful, false if error
|
||||
*/
|
||||
evalOps(aVals, aOps, cOps = -1) {
|
||||
evalOps(aVals, aOps, cOps = -1)
|
||||
{
|
||||
while (cOps-- && aOps.length) {
|
||||
var chOp = aOps.pop();
|
||||
if (aVals.length < 2) return false;
|
||||
|
|
@ -511,7 +533,7 @@ class Debugger extends Component {
|
|||
}
|
||||
|
||||
/**
|
||||
* parseExpression(sExp, fPrint)
|
||||
* parseExpression(sExp, fQuiet)
|
||||
*
|
||||
* A quick-and-dirty expression parser. It takes an expression like:
|
||||
*
|
||||
|
|
@ -543,15 +565,17 @@ class Debugger extends Component {
|
|||
*
|
||||
* @this {Debugger}
|
||||
* @param {string|undefined} sExp
|
||||
* @param {boolean} [fPrint] is true to print all resolved values, false for quiet parsing
|
||||
* @param {boolean} [fQuiet] (true for quiet parsing)
|
||||
* @return {number|undefined} numeric value, or undefined if sExp contains any undefined or invalid values
|
||||
*/
|
||||
parseExpression(sExp, fPrint) {
|
||||
parseExpression(sExp, fQuiet)
|
||||
{
|
||||
var value;
|
||||
|
||||
/*
|
||||
* First process (and eliminate) any references, aka sub-expressions.
|
||||
*/
|
||||
this.sUndefined = null;
|
||||
if (sExp) sExp = this.parseReference(sExp);
|
||||
|
||||
if (sExp) {
|
||||
|
|
@ -584,16 +608,21 @@ class Debugger extends Component {
|
|||
while (i < asValues.length) {
|
||||
var sValue = asValues[i++];
|
||||
var cchValue = sValue.length;
|
||||
var s = Str.trim(sValue);
|
||||
if (!s) {
|
||||
sValue = Str.trim(sValue);
|
||||
if (!sValue) {
|
||||
fError = true;
|
||||
break;
|
||||
}
|
||||
var v = this.parseValue(s, null, fPrint === false);
|
||||
var v = this.parseValue(sValue, null, fQuiet);
|
||||
if (v === undefined) {
|
||||
fError = true;
|
||||
fPrint = false;
|
||||
break;
|
||||
if (this.sUndefined == null && fQuiet) {
|
||||
this.sUndefined = sValue;
|
||||
v = 0;
|
||||
} else {
|
||||
fError = true;
|
||||
fQuiet = !fQuiet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
aVals.push(this.truncate(v, this.nBits));
|
||||
if (i == asValues.length) break;
|
||||
|
|
@ -610,9 +639,9 @@ class Debugger extends Component {
|
|||
}
|
||||
if (!fError) {
|
||||
value = aVals.pop();
|
||||
if (fPrint) this.printValue(null, value);
|
||||
if (fQuiet === false) this.printValue(null, value);
|
||||
} else {
|
||||
if (fPrint) this.println("error parsing '" + sExpOrig + "' at character " + (sExpOrig.length - sExp.length));
|
||||
if (fQuiet === false) this.println("error parsing '" + sExpOrig + "' at character " + (sExpOrig.length - sExp.length));
|
||||
}
|
||||
}
|
||||
return value;
|
||||
|
|
@ -629,7 +658,8 @@ class Debugger extends Component {
|
|||
* @param {string} s
|
||||
* @return {string|undefined}
|
||||
*/
|
||||
parseReference(s) {
|
||||
parseReference(s)
|
||||
{
|
||||
var a;
|
||||
var chOpen = this.achGroup[0];
|
||||
var chClose = this.achGroup[1];
|
||||
|
|
@ -677,7 +707,8 @@ class Debugger extends Component {
|
|||
* @param {string} s
|
||||
* @return {string}
|
||||
*/
|
||||
parseSysVars(s) {
|
||||
parseSysVars(s)
|
||||
{
|
||||
var a;
|
||||
while (a = s.match(/\$([a-z]+)/i)) {
|
||||
var v = null;
|
||||
|
|
@ -701,7 +732,8 @@ class Debugger extends Component {
|
|||
* @param {boolean} [fQuiet]
|
||||
* @return {number|undefined} numeric value, or undefined if sValue is either undefined or invalid
|
||||
*/
|
||||
parseValue(sValue, sName, fQuiet) {
|
||||
parseValue(sValue, sName, fQuiet)
|
||||
{
|
||||
var value;
|
||||
if (sValue != null) {
|
||||
var iReg = this.getRegIndex(sValue);
|
||||
|
|
@ -732,7 +764,8 @@ class Debugger extends Component {
|
|||
* @param {number|undefined} value
|
||||
* @return {boolean} true if value defined, false if not
|
||||
*/
|
||||
printValue(sVar, value) {
|
||||
printValue(sVar, value)
|
||||
{
|
||||
var sValue;
|
||||
var fDefined = false;
|
||||
if (value !== undefined) {
|
||||
|
|
@ -753,7 +786,8 @@ class Debugger extends Component {
|
|||
* @this {Debugger}
|
||||
* @return {Object}
|
||||
*/
|
||||
resetVariables() {
|
||||
resetVariables()
|
||||
{
|
||||
var a = this.aVariables;
|
||||
this.aVariables = {};
|
||||
return a;
|
||||
|
|
@ -765,7 +799,8 @@ class Debugger extends Component {
|
|||
* @this {Debugger}
|
||||
* @param {Object} a (from previous resetVariables() call)
|
||||
*/
|
||||
restoreVariables(a) {
|
||||
restoreVariables(a)
|
||||
{
|
||||
this.aVariables = a;
|
||||
}
|
||||
|
||||
|
|
@ -776,7 +811,8 @@ class Debugger extends Component {
|
|||
* @param {string} [sVar]
|
||||
* @return {boolean} true if all value(s) defined, false if not
|
||||
*/
|
||||
printVariable(sVar) {
|
||||
printVariable(sVar)
|
||||
{
|
||||
if (sVar) {
|
||||
return this.printValue(sVar, this.aVariables[sVar]);
|
||||
}
|
||||
|
|
@ -794,7 +830,8 @@ class Debugger extends Component {
|
|||
* @this {Debugger}
|
||||
* @param {string} sVar
|
||||
*/
|
||||
delVariable(sVar) {
|
||||
delVariable(sVar)
|
||||
{
|
||||
delete this.aVariables[sVar];
|
||||
}
|
||||
|
||||
|
|
@ -805,7 +842,8 @@ class Debugger extends Component {
|
|||
* @param {string} sVar
|
||||
* @return {number|undefined}
|
||||
*/
|
||||
getVariable(sVar) {
|
||||
getVariable(sVar)
|
||||
{
|
||||
return this.aVariables[sVar];
|
||||
}
|
||||
|
||||
|
|
@ -816,7 +854,8 @@ class Debugger extends Component {
|
|||
* @param {string} sVar
|
||||
* @param {number} value
|
||||
*/
|
||||
setVariable(sVar, value) {
|
||||
setVariable(sVar, value)
|
||||
{
|
||||
this.aVariables[sVar] = value;
|
||||
}
|
||||
|
||||
|
|
@ -830,7 +869,8 @@ class Debugger extends Component {
|
|||
* @param {number} [nBits] (-1 to strip leading zeros, 0 to allow a variable number of digits)
|
||||
* @return {string}
|
||||
*/
|
||||
toStrBase(n, nBits = 0) {
|
||||
toStrBase(n, nBits = 0)
|
||||
{
|
||||
var s;
|
||||
switch(this.nBase) {
|
||||
case 8:
|
||||
|
|
|
|||
Loading…
Reference in a new issue