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
File diff suppressed because one or more lines are too long
|
|
@ -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) {
|
||||
/*
|
||||
* 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 = "";
|
||||
}
|
||||
}
|
||||
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);
|
||||
var value = macro10.parseExpression(fixup.sValue, undefined, nLocation);
|
||||
if (value === undefined) {
|
||||
macro10.error("unable to parse expression: " + sValue);
|
||||
macro10.error("unable to parse expression: " + fixup.sValue);
|
||||
return;
|
||||
}
|
||||
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;
|
||||
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,15 +601,20 @@ 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;
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
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
|
||||
|
|
@ -682,18 +626,19 @@ class Macro10 {
|
|||
* 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.
|
||||
*/
|
||||
var match = sOperand.match(/^([^,]*),,([^,]*)$/);
|
||||
if (!match) {
|
||||
result = this.dbg.parseExpression(sOperand);
|
||||
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,17 +608,22 @@ 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) {
|
||||
if (this.sUndefined == null && fQuiet) {
|
||||
this.sUndefined = sValue;
|
||||
v = 0;
|
||||
} else {
|
||||
fError = true;
|
||||
fPrint = false;
|
||||
fQuiet = !fQuiet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
aVals.push(this.truncate(v, this.nBits));
|
||||
if (i == asValues.length) break;
|
||||
var sOp = asValues[i++], cchOp = sOp.length;
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
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
Loading…
Reference in a new issue