Lots of MACRO-10 improvements, DAKAM now assembles (but does not pass)
This commit is contained in:
parent
3fb3f44384
commit
09dc4dcb9a
13 changed files with 22047 additions and 1758 deletions
|
|
@ -1652,28 +1652,20 @@ class DebuggerPDP10 extends Debugger {
|
|||
}
|
||||
|
||||
/**
|
||||
* parseInstruction(sOpcode, sOperands, addr, fQuiet, fPrelim)
|
||||
* parseInstruction(sOpcode, sOperands, addr, aUndefined)
|
||||
*
|
||||
* @this {DebuggerPDP10}
|
||||
* @param {string} sOpcode
|
||||
* @param {string} [sOperands]
|
||||
* @param {number} [addr] of memory where this instruction is being assembled
|
||||
* @param {boolean} [fQuiet]
|
||||
* @param {boolean} [fPrelim] (true if this is a preliminary call and error messages should be suppressed)
|
||||
* @param {Array} [aUndefined]
|
||||
* @return {number} (opcode, or -1 if unrecognized instruction)
|
||||
*/
|
||||
parseInstruction(sOpcode, sOperands, addr, fQuiet, fPrelim)
|
||||
parseInstruction(sOpcode, sOperands, addr, aUndefined)
|
||||
{
|
||||
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 also allows instructions to be assembled without an opcode (ie, just an address expression),
|
||||
|
|
@ -1753,21 +1745,22 @@ class DebuggerPDP10 extends Debugger {
|
|||
|
||||
if (opCode >= 0) {
|
||||
if (sOperands) {
|
||||
|
||||
var aOperands = sOperands.split(',');
|
||||
if (aOperands.length > 2) {
|
||||
if (!aUndefined) this.println("too many operands: " + sOperands);
|
||||
aOperands.length = 0;
|
||||
opCode = -1;
|
||||
}
|
||||
|
||||
for (var i = 0; i < aOperands.length; i++) {
|
||||
|
||||
var sOperand = aOperands[i].trim();
|
||||
if (!sOperand) continue;
|
||||
|
||||
if (i > 1) {
|
||||
this.println("too many operands: " + sOperands);
|
||||
opCode = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
var match = sOperand.match(/(@?)([^(]*)\(?([^)]*)\)?/);
|
||||
if (!match) {
|
||||
this.println("unknown operand: " + sOperand);
|
||||
if (!aUndefined) this.println("unknown operand: " + sOperand);
|
||||
opCode = -1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1782,7 +1775,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
|
||||
sOperand = match[3];
|
||||
if (sOperand) {
|
||||
operand = this.parseExpression(sOperand, fQuiet);
|
||||
operand = this.parseExpression(sOperand, aUndefined);
|
||||
if (operand == undefined) {
|
||||
opCode = -1;
|
||||
break;
|
||||
|
|
@ -1806,7 +1799,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
}
|
||||
}
|
||||
|
||||
var operand = this.parseExpression(sOperand, fQuiet);
|
||||
var operand = this.parseExpression(sOperand, aUndefined);
|
||||
if (operand == undefined) {
|
||||
opCode = -1;
|
||||
break;
|
||||
|
|
@ -1860,7 +1853,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
// }
|
||||
}
|
||||
|
||||
if (opCode < 0 && !fPrelim) {
|
||||
if (opCode < 0 && !aUndefined) {
|
||||
this.println("unknown instruction: " + sOpcode + ' ' + sOperands);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@ if (NODE) {
|
|||
/**
|
||||
* Elements of tblMacros.
|
||||
*
|
||||
* NOTE: The nLine property is not required; it's added for improved error reporting.
|
||||
*
|
||||
* @typedef {{
|
||||
* name:(string),
|
||||
* nOperand:(number),
|
||||
|
|
@ -56,8 +54,6 @@ var Mac;
|
|||
/**
|
||||
* Elements of tblSymbols.
|
||||
*
|
||||
* NOTE: The nLine property is not required; it's added for improved error reporting.
|
||||
*
|
||||
* @typedef {{
|
||||
* name:(string),
|
||||
* value:(number),
|
||||
|
|
@ -127,6 +123,7 @@ class Macro10 {
|
|||
*
|
||||
* Supported options include:
|
||||
*
|
||||
* 'd': leave all symbols in the debugger's variable table
|
||||
* 'p': print the preprocessed resource(s) without assembling them
|
||||
* 'l': print lines as they are parsed
|
||||
*
|
||||
|
|
@ -203,7 +200,7 @@ class Macro10 {
|
|||
this.aWords = []; // filled in by the various genXXX() functions
|
||||
|
||||
/**
|
||||
* This sparse array is indexed by location, and each used entry contains an undefined symbol that
|
||||
* This sparse array is indexed by location, and each used entry contains any undefined symbols that
|
||||
* must be evaluated to fully resolve the word at the corresponding location. NOTE: There's no requirement
|
||||
* that the array be sparse; we could certainly fill each unused entry with null (ie, for locations that
|
||||
* don't require a fixup).
|
||||
|
|
@ -212,6 +209,13 @@ class Macro10 {
|
|||
*/
|
||||
this.aFixups = [];
|
||||
|
||||
/**
|
||||
* This array parallels aFixups, providing context (ie, line numbers) for any fixups that we want to analyze.
|
||||
*
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.aLineRefs = [];
|
||||
|
||||
this.nLine = 0;
|
||||
this.nError = 0;
|
||||
this.nLiteral = 0; // used to uniquely number literals
|
||||
|
|
@ -396,6 +400,13 @@ class Macro10 {
|
|||
}
|
||||
|
||||
var a = this.dbg.resetVariables();
|
||||
|
||||
/*
|
||||
* Add predefined device codes
|
||||
*/
|
||||
this.addSymbol("APR", 0); // Priority Interrupt
|
||||
this.addSymbol("PI", 4); // Arithmetic Processor
|
||||
|
||||
try {
|
||||
for (let i = 0; i < this.asLines.length; i += 2) {
|
||||
|
||||
|
|
@ -434,104 +445,19 @@ class Macro10 {
|
|||
/*
|
||||
* Process all literals next.
|
||||
*/
|
||||
let nLocationLiterals = this.nLocation;
|
||||
|
||||
for (let i = 0; i < this.aLiterals.length; i++) {
|
||||
/*
|
||||
* Apparently, the time has come to implement "literal collapsing"; I was treating it as just
|
||||
* a nice optimization, but it turns out that DEC has written tests that actually DEPEND on it:
|
||||
*
|
||||
* C26300: HRRZI [135531,,246642] ;PRELOAD AC0 WITH 0,, LITERAL ADDRESS
|
||||
* JRA .+1 ;*JRA SHOULD PLACE C(AC0) INTO AC0
|
||||
* CAIE [135531,,246642] ;PASS IF JRA PLACED C(AC0) INTO AC0
|
||||
* STOP
|
||||
*
|
||||
* If the HRRZI and CAIE instructions don't refer to the same exact literal, the test will fail.
|
||||
* For purposes of this particular test, the values they stuffed into the literals are essentially
|
||||
* gibberish, but the same literal may be used in another test where the values are significant.
|
||||
*
|
||||
* However, I'm still going to keep it simple. In this example from p. 2-8 of the April 1978
|
||||
* MACRO-10 manual, I will NOT be attempting to collapse null words at the end of ASCIZ sequences
|
||||
* with other null words, especially if they were defined before the ASCIZ:
|
||||
*
|
||||
* Literals having the same value are collapsed in MACRO's literal pool.
|
||||
* Thus for the statements:
|
||||
*
|
||||
* PUSH P,[0]
|
||||
* PUSH P,[0]
|
||||
* MOVEI 1,[ASCIZ /TEST1/]
|
||||
*
|
||||
* the same address is shared by the two literals [0], and by the null word
|
||||
* generated at the end of [ASCIZ /TEST1/].
|
||||
*/
|
||||
let lit = this.aLiterals[i];
|
||||
/*
|
||||
* First things first: verify that the literal is one contiguous zero-based set of words (I'm not sure
|
||||
* how it couldn't be, but better safe than sorry).
|
||||
*/
|
||||
let aWords = [];
|
||||
let nWords = 0;
|
||||
lit.aWords.forEach(function(w, nLocation) {
|
||||
if (nLocation === aWords.length) aWords.push(w);
|
||||
nWords++;
|
||||
});
|
||||
if (nWords == aWords.length) {
|
||||
/*
|
||||
* So far, so good. Now we'll simply brute-force-search our way through the existing set of
|
||||
* literals, looking for a complete match.
|
||||
*/
|
||||
for (let nLocation = nLocationLiterals; nLocation + nWords <= this.nLocation; nLocation++) {
|
||||
let n;
|
||||
for (n = 0; n < nWords; n++) {
|
||||
/*
|
||||
* This check requires that the initial values of the words in the literals match AND that
|
||||
* their fixup expressions, if any, match as well. Here again, MACRO-10 may be more aggressive
|
||||
* in either verifying fixup equality or evaluating any fixups that it can immediately, but
|
||||
* but I prefer to leave all fixup evaluation where it is (below), after all literals and then
|
||||
* all variables have been added to the output.
|
||||
*/
|
||||
if (aWords[n] !== this.aWords[nLocation + n] || lit.aFixups[n] != this.aFixups[nLocation]) break;
|
||||
}
|
||||
if (n == nWords) {
|
||||
this.addSymbol(lit.name, nLocation, Macro10.SYMTYPE.LABEL);
|
||||
lit = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lit) {
|
||||
this.addSymbol(lit.name, this.nLocation, Macro10.SYMTYPE.LABEL);
|
||||
lit.aWords.forEach(function(w, nLocation) {
|
||||
macro10.genWord(w, lit.aFixups[nLocation]);
|
||||
});
|
||||
}
|
||||
}
|
||||
this.doLiterals();
|
||||
|
||||
/*
|
||||
* Now add all variable definitions.
|
||||
* Process all variables next.
|
||||
*/
|
||||
for (let i = 0; i < this.aVariables.length; i++) {
|
||||
let name = this.aVariables[i];
|
||||
let macro = this.tblMacros[name];
|
||||
if (!macro) {
|
||||
/*
|
||||
* This is more of an assert(), because it should never happen, regardless of input.
|
||||
*/
|
||||
this.error("missing definition for variable '" + name + "'");
|
||||
continue;
|
||||
}
|
||||
this.parseText(macro.sText);
|
||||
}
|
||||
this.doVariables();
|
||||
|
||||
/*
|
||||
* And last but not least, perform all fixups.
|
||||
*/
|
||||
this.aFixups.forEach(function processFixup(sValue, nLocation) {
|
||||
let value = macro10.parseExpression(sValue, false, nLocation);
|
||||
if (value === undefined) {
|
||||
macro10.error("unable to parse expression '" + sValue + "'");
|
||||
return;
|
||||
}
|
||||
let value = macro10.parseExpression(sValue, undefined, nLocation, macro10.aLineRefs[nLocation]);
|
||||
if (value === undefined) return;
|
||||
value += macro10.aWords[nLocation];
|
||||
macro10.aWords[nLocation] = macro10.truncate(value, nLocation);
|
||||
});
|
||||
|
|
@ -540,7 +466,9 @@ class Macro10 {
|
|||
this.println(err.message);
|
||||
this.nError = -1;
|
||||
}
|
||||
this.dbg.restoreVariables(a);
|
||||
|
||||
if (this.sOptions.indexOf('d') < 0) this.dbg.restoreVariables(a);
|
||||
|
||||
return this.nError;
|
||||
}
|
||||
|
||||
|
|
@ -556,22 +484,7 @@ class Macro10 {
|
|||
*/
|
||||
parseLine(sLine, aParms, aValues, aDefaults)
|
||||
{
|
||||
var i;
|
||||
if (this.nMacroDef) {
|
||||
if (this.nMacroDef == 1) {
|
||||
i = sLine.indexOf(this.chMacroOpen);
|
||||
if (i >= 0) {
|
||||
this.nMacroDef++;
|
||||
sLine = sLine.substr(i+1);
|
||||
} else {
|
||||
this.error("expected " + this.sOperator + " definition in '" + sLine + "'");
|
||||
}
|
||||
}
|
||||
if (this.nMacroDef > 1) {
|
||||
sLine = this.appendMacro(sLine);
|
||||
}
|
||||
if (this.nMacroDef) return true;
|
||||
}
|
||||
var i, matchLine;
|
||||
|
||||
if (this.chASCII != null) {
|
||||
sLine = this.defASCII(sLine);
|
||||
|
|
@ -581,7 +494,7 @@ class Macro10 {
|
|||
var sLabel, sOperator = "", sSeparator, sOperands, sRemainder;
|
||||
|
||||
while (fParse) {
|
||||
var matchLine = sLine.match(this.reLine);
|
||||
matchLine = sLine.match(this.reLine);
|
||||
if (!matchLine || matchLine[5] && matchLine[5].slice(0, 1) != ';') {
|
||||
this.error("failed to parse line '" + sLine + "'");
|
||||
return false;
|
||||
|
|
@ -617,6 +530,22 @@ class Macro10 {
|
|||
}
|
||||
aParms = null;
|
||||
}
|
||||
if (this.nMacroDef) {
|
||||
if (this.nMacroDef == 1) {
|
||||
i = sLine.indexOf(this.chMacroOpen);
|
||||
if (i >= 0) {
|
||||
this.nMacroDef++;
|
||||
sLine = sLine.substr(i+1);
|
||||
} else {
|
||||
this.error("expected " + this.sOperator + " definition in '" + sLine + "'");
|
||||
}
|
||||
}
|
||||
if (this.nMacroDef > 1) {
|
||||
sLine = this.appendMacro(sLine);
|
||||
fParse = true;
|
||||
}
|
||||
if (this.nMacroDef) return true;
|
||||
}
|
||||
}
|
||||
|
||||
sLabel = matchLine[1];
|
||||
|
|
@ -681,6 +610,10 @@ class Macro10 {
|
|||
this.defBLOCK(sOperands);
|
||||
break;
|
||||
|
||||
case Macro10.PSEUDO_OP.BYTE:
|
||||
this.defBYTE(sOperands);
|
||||
break;
|
||||
|
||||
case Macro10.PSEUDO_OP.END:
|
||||
this.defEND(sOperands);
|
||||
break;
|
||||
|
|
@ -689,19 +622,32 @@ class Macro10 {
|
|||
this.defEXP(sOperands);
|
||||
break;
|
||||
|
||||
case Macro10.PSEUDO_OP.LIT:
|
||||
this.doLiterals();
|
||||
break;
|
||||
|
||||
case Macro10.PSEUDO_OP.LOC:
|
||||
this.defLocation(sOperands);
|
||||
break;
|
||||
|
||||
case Macro10.PSEUDO_OP.VAR:
|
||||
this.doVariables();
|
||||
break;
|
||||
|
||||
case Macro10.PSEUDO_OP.XWD:
|
||||
this.defXWD(sOperands);
|
||||
break;
|
||||
|
||||
case Macro10.PSEUDO_OP.DEFINE:
|
||||
case Macro10.PSEUDO_OP.IF1:
|
||||
case Macro10.PSEUDO_OP.IFDEF:
|
||||
case Macro10.PSEUDO_OP.IFDIF:
|
||||
case Macro10.PSEUDO_OP.IFE:
|
||||
case Macro10.PSEUDO_OP.IFG:
|
||||
case Macro10.PSEUDO_OP.IFGE:
|
||||
case Macro10.PSEUDO_OP.IFIDN:
|
||||
case Macro10.PSEUDO_OP.IFL:
|
||||
case Macro10.PSEUDO_OP.IFLE:
|
||||
case Macro10.PSEUDO_OP.IFN:
|
||||
case Macro10.PSEUDO_OP.IFNDEF:
|
||||
case Macro10.PSEUDO_OP.IRP:
|
||||
|
|
@ -711,6 +657,10 @@ class Macro10 {
|
|||
this.defMacro(sOperator, sRemainder);
|
||||
break;
|
||||
|
||||
case Macro10.PSEUDO_OP.PURGE:
|
||||
this.delSymbols(sOperands);
|
||||
break;
|
||||
|
||||
case Macro10.PSEUDO_OP.LALL: // TODO
|
||||
case Macro10.PSEUDO_OP.LIST: // TODO
|
||||
case Macro10.PSEUDO_OP.NOSYM: // TODO
|
||||
|
|
@ -731,8 +681,7 @@ class Macro10 {
|
|||
/**
|
||||
* parseLiteral(name, sText)
|
||||
*
|
||||
* This is like parseText() except that we first set up a new scope, which includes new aWords and aFixups
|
||||
* arrays.
|
||||
* This is like parseText() except that we set up a new scope, including new aWords and aFixups arrays.
|
||||
*
|
||||
* @this {Macro10}
|
||||
* @param {string} name
|
||||
|
|
@ -836,6 +785,7 @@ class Macro10 {
|
|||
|
||||
switch(sOperator) {
|
||||
case Macro10.PSEUDO_OP.IFE:
|
||||
case Macro10.PSEUDO_OP.IFDIF:
|
||||
case Macro10.PSEUDO_OP.IFNDEF:
|
||||
if (!macro.nOperand) {
|
||||
this.parseText(macro.sText);
|
||||
|
|
@ -848,14 +798,28 @@ class Macro10 {
|
|||
}
|
||||
break;
|
||||
|
||||
case Macro10.PSEUDO_OP.IFGE:
|
||||
if (macro.nOperand >= 0) {
|
||||
this.parseText(macro.sText);
|
||||
}
|
||||
break;
|
||||
|
||||
case Macro10.PSEUDO_OP.IFL:
|
||||
if (macro.nOperand < 0) {
|
||||
this.parseText(macro.sText);
|
||||
}
|
||||
break;
|
||||
|
||||
case Macro10.PSEUDO_OP.IFLE:
|
||||
if (macro.nOperand <= 0) {
|
||||
this.parseText(macro.sText);
|
||||
}
|
||||
break;
|
||||
|
||||
case Macro10.PSEUDO_OP.IF1:
|
||||
case Macro10.PSEUDO_OP.IFN:
|
||||
case Macro10.PSEUDO_OP.IFDEF:
|
||||
case Macro10.PSEUDO_OP.IFIDN:
|
||||
if (macro.nOperand) {
|
||||
this.parseText(macro.sText);
|
||||
}
|
||||
|
|
@ -955,6 +919,9 @@ class Macro10 {
|
|||
/**
|
||||
* getExpression(sOperands, sDelim)
|
||||
*
|
||||
* TODO: Add support for IFIDN, IFDIF, IFB and IFNB: if the first non-blank, non-tab character is
|
||||
* a character other than '<', then that becomes the delimiter, allowing angle brackets in the string.
|
||||
*
|
||||
* @this {Macro10}
|
||||
* @param {string} sOperands
|
||||
* @param {string} [sDelim] (eg, comma, closing parenthesis)
|
||||
|
|
@ -996,7 +963,7 @@ class Macro10 {
|
|||
}
|
||||
|
||||
/**
|
||||
* parseExpression(sExp, fPass1, nLocation)
|
||||
* parseExpression(sExp, aUndefined, nLocation, nLine)
|
||||
*
|
||||
* 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
|
||||
|
|
@ -1005,74 +972,58 @@ class Macro10 {
|
|||
*
|
||||
* @this {Macro10}
|
||||
* @param {string} sExp
|
||||
* @param {boolean|undefined} [fPass1]
|
||||
* @param {Array} [aUndefined]
|
||||
* @param {number|undefined} [nLocation]
|
||||
* @param {number|undefined} [nLine]
|
||||
* @return {number|undefined}
|
||||
*/
|
||||
parseExpression(sExp, fPass1, nLocation)
|
||||
parseExpression(sExp, aUndefined, nLocation, nLine)
|
||||
{
|
||||
var result;
|
||||
var result = -1;
|
||||
|
||||
if (nLocation === undefined) {
|
||||
nLocation = (this.nLocationScope >= 0? this.nLocationScope : this.nLocation);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* The SIXBIT (and presumably ASCII; not sure about ASCIZ) pseudo-ops can also be used in expressions
|
||||
* (or at least assignments), so we check for those in the given expression and convert them to quoted
|
||||
* sequences that the Debugger's parseExpression() understands.
|
||||
*/
|
||||
var match = sExp.match(/^([^,]*),,([^,]*)$/);
|
||||
if (!match) {
|
||||
if (nLocation === undefined) {
|
||||
nLocation = (this.nLocationScope >= 0? this.nLocationScope : this.nLocation);
|
||||
}
|
||||
var sEval = sExp.replace(/SIXBIT\s*(\S)(.*?)\1/g, "'$2'").replace(/ASCII\s*(\S)(.*?)\1/g, '"$2"');
|
||||
|
||||
/*
|
||||
* If this is NOT a first-pass call, then let's not waste time calling parseInstruction(); not only
|
||||
* may it generate redundant error messages, but it shouldn't be necessary, because we should be down
|
||||
* to fixup expressions.
|
||||
*/
|
||||
if (aUndefined) {
|
||||
var match;
|
||||
var sOperator = "";
|
||||
var sOperands = sEval;
|
||||
if (match = sEval.match(/^([^\s]+)\s*(.*?)\s*$/)) {
|
||||
sOperator = match[1];
|
||||
sOperands = match[2];
|
||||
}
|
||||
result = this.dbg.parseInstruction(sOperator, sOperands, nLocation, aUndefined);
|
||||
}
|
||||
|
||||
if (result < 0) {
|
||||
/*
|
||||
* The SIXBIT (and presumably ASCII; not sure about ASCIZ) pseudo-ops can also be used in expressions
|
||||
* (or at least assignments), so we check for those in the given expression and convert them to quoted
|
||||
* sequences that the Debugger's parseExpression() understands.
|
||||
* 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.
|
||||
*/
|
||||
sExp = sExp.replace(/SIXBIT\s*(\S)(.*?)\1/g, "'$2'").replace(/ASCII\s*(\S)(.*?)\1/g, '"$2"');
|
||||
|
||||
/*
|
||||
* If this is NOT a first-pass call, then let's not waste time calling parseInstruction(); not only
|
||||
* may it generate redundant error messages, but it shouldn't be necessary, because we should be down
|
||||
* to fixup expressions.
|
||||
*/
|
||||
result = -1;
|
||||
if (fPass1 !== false) {
|
||||
var sOperator = "";
|
||||
var sOperands = sExp;
|
||||
if (match = sExp.match(/^([^\s]+)\s*(.*?)\s*$/)) {
|
||||
sOperator = match[1];
|
||||
sOperands = match[2];
|
||||
}
|
||||
result = this.dbg.parseInstruction(sOperator, sOperands, nLocation, fPass1, true);
|
||||
}
|
||||
|
||||
if (result < 0) {
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
if (fPass1 === false) fPass1 = undefined;
|
||||
result = this.dbg.parseExpression(sExp.replace(/\.([^0-9]|$)/g, this.dbg.toStrBase(nLocation, -1) + "$1"), fPass1);
|
||||
if (result === undefined) {
|
||||
this.error("unable to parse expression '" + sExp + "'");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var wLeft = match[1]? this.parseExpression(match[1], fPass1, nLocation) : 0;
|
||||
if (wLeft !== undefined) {
|
||||
var wRight = match[2]? this.parseExpression(match[2], fPass1, nLocation) : 0;
|
||||
if (wRight !== undefined) {
|
||||
/*
|
||||
* NOTE: These must be combined as UNSIGNED values, so that's what we tell truncate() to produce.
|
||||
*/
|
||||
result = this.dbg.truncate(wLeft, 18, true) * Math.pow(2, 18) + this.dbg.truncate(wRight, 18, true);
|
||||
}
|
||||
sEval = sEval.replace(/\.([^0-9]|$)/g, this.dbg.toStrBase(nLocation, -1) + "$1");
|
||||
result = this.dbg.parseExpression(sEval, aUndefined);
|
||||
if (result === undefined) {
|
||||
this.error("unable to parse expression '" + sExp + "'", nLine);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
@ -1487,25 +1438,40 @@ class Macro10 {
|
|||
/*
|
||||
* This must be a REPEAT or CONDITIONAL block.
|
||||
*/
|
||||
var sOperand = this.getExpression(sOperands);
|
||||
if (!sOperand) {
|
||||
this.error("missing " + sOperator + " expression '" + sOperands + "'");
|
||||
return sOperands;
|
||||
if (sOperator == Macro10.PSEUDO_OP.IF1) {
|
||||
nOperand = 1;
|
||||
if (sOperands[0] == ',') sOperands = sOperands.substr(1).trim();
|
||||
}
|
||||
else {
|
||||
var sOperand = this.getExpression(sOperands);
|
||||
if (!sOperand) {
|
||||
this.error("missing " + sOperator + " expression '" + sOperands + "'");
|
||||
return sOperands;
|
||||
}
|
||||
sOperands = sOperands.substr(sOperand.length + 1);
|
||||
sOperand = sOperand.trim();
|
||||
if (sOperator == Macro10.PSEUDO_OP.IFDIF || sOperator == Macro10.PSEUDO_OP.IFIDN) {
|
||||
var sOperand2 = this.getExpression(sOperands);
|
||||
if (!sOperand2) {
|
||||
this.error("missing second " + sOperator + " expression '" + sOperands + "'");
|
||||
return sOperands;
|
||||
}
|
||||
sOperands = sOperands.substr(sOperand2.length + 1);
|
||||
sOperand2 = sOperand2.trim();
|
||||
nOperand = (sOperand == sOperand2)? 1 : 0;
|
||||
}
|
||||
if (sOperator == Macro10.PSEUDO_OP.IFDEF || sOperator == Macro10.PSEUDO_OP.IFNDEF) {
|
||||
nOperand = this.isDefined(sOperand)? 1 : 0;
|
||||
} else {
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
sOperands = sOperands.substr(sOperand.length + 1);
|
||||
sOperand = sOperand.trim();
|
||||
match = sOperands.match(/\s*(<|)([\s\S]*)/i);
|
||||
name = '?' + sOperator;
|
||||
|
||||
if (sOperator == Macro10.PSEUDO_OP.IFDEF || sOperator == Macro10.PSEUDO_OP.IFNDEF) {
|
||||
nOperand = this.isDefined(sOperand)? 1 : 0;
|
||||
} else {
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
|
@ -1574,19 +1540,16 @@ class Macro10 {
|
|||
this.error("label '" + name + "' redefined");
|
||||
return;
|
||||
}
|
||||
var sUndefined = null;
|
||||
var sUndefined = undefined;
|
||||
if (typeof value == 'string') {
|
||||
/*
|
||||
* We now pass true to parseExpression() to allow at least one undefined symbol to exist in the expression,
|
||||
* which we will record alongside the variable.
|
||||
*/
|
||||
var v = this.parseExpression(value, true);
|
||||
var aUndefined = [];
|
||||
var v = this.parseExpression(value, aUndefined);
|
||||
if (v === undefined) {
|
||||
this.error("parseExpression(" + value + ") failed");
|
||||
this.error("symbol error (" + value + ")");
|
||||
return;
|
||||
}
|
||||
value = v;
|
||||
sUndefined = this.dbg.sUndefined;
|
||||
sUndefined = aUndefined[0];
|
||||
}
|
||||
var sym = this.tblSymbols[name];
|
||||
if (sym) {
|
||||
|
|
@ -1610,13 +1573,61 @@ class Macro10 {
|
|||
defBLOCK(sOperands)
|
||||
{
|
||||
var w = this.parseExpression(sOperands);
|
||||
if (w !== undefined && this.dbg.sUndefined == null && w >= 0 && w <= 0o777777) {
|
||||
if (w !== undefined && w >= 0 && w <= 0o777777) {
|
||||
this.nLocation += w; // while (w--) this.genWord(0);
|
||||
} else {
|
||||
this.error("unrecognized BLOCK expression '" + sOperands + "'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* defBYTE()
|
||||
*
|
||||
* Processes the BYTE pseudo-op.
|
||||
*
|
||||
* @this {Macro10}
|
||||
* @param {string} sOperands
|
||||
*/
|
||||
defBYTE(sOperands)
|
||||
{
|
||||
var sOperand;
|
||||
var nBits = 0, nValue = 0, nBitsRemaining = 36;
|
||||
|
||||
while (sOperand = this.getExpression(sOperands)) {
|
||||
sOperands = sOperands.substr(sOperand.length).trim();
|
||||
var sValue = sOperand;
|
||||
var match = sOperand.match(/^\((.*)\)\s*(.*)$/);
|
||||
if (match) {
|
||||
if (match[1]) nBits = this.parseExpression("^D" + match[1]);
|
||||
sValue = match[2];
|
||||
}
|
||||
if (!nBits) {
|
||||
/*
|
||||
* According the the 1978 MACRO-10 spec, "If the byte size is 0 or is missing (empty parentheses), a zero word
|
||||
* is generated." I'm not clear exactly on how to interpret this, so I'll make a simplistic assumption for now.
|
||||
*/
|
||||
this.genWord(0);
|
||||
continue;
|
||||
}
|
||||
var v = sValue? this.parseExpression(sValue) : 0;
|
||||
if (v === undefined || nBits < 0 || nBits > 36) {
|
||||
this.error("unexpected BYTE operand: " + sOperand);
|
||||
break;
|
||||
}
|
||||
v = this.dbg.truncate(v, nBits, true);
|
||||
if (nBitsRemaining < nBits) {
|
||||
this.genWord(nValue);
|
||||
nValue = 0;
|
||||
nBitsRemaining = 36;
|
||||
}
|
||||
nValue += v * Math.pow(2, nBitsRemaining - nBits);
|
||||
nBitsRemaining -= nBits;
|
||||
}
|
||||
if (nBitsRemaining < 36) {
|
||||
this.genWord(nValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* defEND()
|
||||
*
|
||||
|
|
@ -1630,7 +1641,7 @@ class Macro10 {
|
|||
if (!sOperands) {
|
||||
this.addrStart = this.addrLoad;
|
||||
} else {
|
||||
this.addrStart = this.parseExpression(sOperands, true);
|
||||
this.addrStart = this.parseExpression(sOperands);
|
||||
if (this.addrStart === undefined) {
|
||||
this.error("unrecognized END expression '" + sOperands + "'");
|
||||
}
|
||||
|
|
@ -1647,9 +1658,10 @@ class Macro10 {
|
|||
*/
|
||||
defEXP(sOperands)
|
||||
{
|
||||
var w = this.parseExpression(sOperands, true);
|
||||
var aUndefined = [];
|
||||
var w = this.parseExpression(sOperands, aUndefined);
|
||||
if (w !== undefined) {
|
||||
this.genWord(w, this.dbg.sUndefined);
|
||||
this.genWord(w, aUndefined[0]);
|
||||
} else {
|
||||
this.error("unrecognized EXP expression '" + sOperands + "'");
|
||||
}
|
||||
|
|
@ -1694,17 +1706,136 @@ class Macro10 {
|
|||
*/
|
||||
defWord(sOperator, sSeparator, sOperands)
|
||||
{
|
||||
var aUndefined = [];
|
||||
var sExp = (sOperator + sSeparator + sOperands).trim();
|
||||
|
||||
var w = this.parseExpression(sExp, true);
|
||||
|
||||
var w = this.parseExpression(sExp, aUndefined);
|
||||
if (w !== undefined) {
|
||||
this.genWord(w, this.dbg.sUndefined);
|
||||
this.genWord(w, aUndefined[0]);
|
||||
} else {
|
||||
this.error("unrecognized word expression '" + sExp + "'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* delSymbols(sOperands)
|
||||
*
|
||||
* @this {Macro10}
|
||||
* @param {string} sOperands
|
||||
*/
|
||||
delSymbols(sOperands)
|
||||
{
|
||||
var aValues = this.getValues(sOperands);
|
||||
for (var i = 0; i < aValues.length; i++) {
|
||||
this.dbg.delVariable(aValues[i]);
|
||||
delete this.tblSymbols[aValues[i]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* doLiterals()
|
||||
*
|
||||
* @this {Macro10}
|
||||
*/
|
||||
doLiterals()
|
||||
{
|
||||
let nLocationLiterals = this.nLocation;
|
||||
|
||||
for (let i = 0; i < this.aLiterals.length; i++) {
|
||||
/*
|
||||
* Apparently, the time has come to implement "literal collapsing"; I was treating it as just
|
||||
* a nice optimization, but it turns out that DEC has written tests that actually DEPEND on it:
|
||||
*
|
||||
* C26300: HRRZI [135531,,246642] ;PRELOAD AC0 WITH 0,, LITERAL ADDRESS
|
||||
* JRA .+1 ;*JRA SHOULD PLACE C(AC0) INTO AC0
|
||||
* CAIE [135531,,246642] ;PASS IF JRA PLACED C(AC0) INTO AC0
|
||||
* STOP
|
||||
*
|
||||
* If the HRRZI and CAIE instructions don't refer to the same exact literal, the test will fail.
|
||||
* For purposes of this particular test, the values they stuffed into the literals are essentially
|
||||
* gibberish, but the same literal may be used in another test where the values are significant.
|
||||
*
|
||||
* However, I'm still going to keep it simple. In this example from p. 2-8 of the April 1978
|
||||
* MACRO-10 manual, I will NOT be attempting to collapse null words at the end of ASCIZ sequences
|
||||
* with other null words, especially if they were defined before the ASCIZ:
|
||||
*
|
||||
* Literals having the same value are collapsed in MACRO's literal pool.
|
||||
* Thus for the statements:
|
||||
*
|
||||
* PUSH P,[0]
|
||||
* PUSH P,[0]
|
||||
* MOVEI 1,[ASCIZ /TEST1/]
|
||||
*
|
||||
* the same address is shared by the two literals [0], and by the null word
|
||||
* generated at the end of [ASCIZ /TEST1/].
|
||||
*/
|
||||
let lit = this.aLiterals[i];
|
||||
/*
|
||||
* First things first: verify that the literal is one contiguous zero-based set of words (I'm not sure
|
||||
* how it couldn't be, but better safe than sorry).
|
||||
*/
|
||||
let aWords = [];
|
||||
let nWords = 0;
|
||||
lit.aWords.forEach(function(w, nLocation) {
|
||||
if (nLocation === aWords.length) aWords.push(w);
|
||||
nWords++;
|
||||
});
|
||||
if (nWords == aWords.length) {
|
||||
/*
|
||||
* So far, so good. Now we'll simply brute-force-search our way through the existing set of
|
||||
* literals, looking for a complete match.
|
||||
*/
|
||||
for (let nLocation = nLocationLiterals; nLocation + nWords <= this.nLocation; nLocation++) {
|
||||
let n;
|
||||
for (n = 0; n < nWords; n++) {
|
||||
/*
|
||||
* This check requires that the initial values of the words in the literals match AND that
|
||||
* their fixup expressions, if any, match as well. Here again, MACRO-10 may be more aggressive
|
||||
* in either verifying fixup equality or evaluating any fixups that it can immediately, but
|
||||
* but I prefer to leave all fixup evaluation where it is (below), after all literals and then
|
||||
* all variables have been added to the output.
|
||||
*/
|
||||
if (aWords[n] !== this.aWords[nLocation + n] || lit.aFixups[n] != this.aFixups[nLocation]) break;
|
||||
}
|
||||
if (n == nWords) {
|
||||
this.addSymbol(lit.name, nLocation, Macro10.SYMTYPE.LABEL);
|
||||
lit = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lit) {
|
||||
var macro10 = this;
|
||||
this.addSymbol(lit.name, this.nLocation, Macro10.SYMTYPE.LABEL);
|
||||
lit.aWords.forEach(function(w, nLocation) {
|
||||
macro10.genWord(w, lit.aFixups[nLocation]);
|
||||
});
|
||||
}
|
||||
}
|
||||
this.aLiterals = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* doVariables()
|
||||
*
|
||||
* @this {Macro10}
|
||||
*/
|
||||
doVariables()
|
||||
{
|
||||
for (let i = 0; i < this.aVariables.length; i++) {
|
||||
let name = this.aVariables[i];
|
||||
let macro = this.tblMacros[name];
|
||||
if (!macro) {
|
||||
/*
|
||||
* This is more of an assert(), because it should never happen, regardless of input.
|
||||
*/
|
||||
this.error("missing definition for variable '" + name + "'");
|
||||
continue;
|
||||
}
|
||||
this.parseText(macro.sText);
|
||||
}
|
||||
this.aVariables = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* genASCII()
|
||||
*
|
||||
|
|
@ -1751,16 +1882,17 @@ class Macro10 {
|
|||
}
|
||||
|
||||
/**
|
||||
* genWord(value, sFixup)
|
||||
* genWord(value, sUndefined)
|
||||
*
|
||||
* @this {Macro10}
|
||||
* @param {number} value (default value for the current location)
|
||||
* @param {string|null|undefined} [sFixup] (optional fixup value to evaluate later)
|
||||
* @param {string} [sUndefined] (optional fixup to evaluate later)
|
||||
*/
|
||||
genWord(value, sFixup)
|
||||
genWord(value, sUndefined)
|
||||
{
|
||||
this.aWords[this.nLocation] = this.truncate(value);
|
||||
if (sFixup != null) this.aFixups[this.nLocation] = sFixup;
|
||||
if (sUndefined !== undefined) this.aFixups[this.nLocation] = sUndefined;
|
||||
this.aLineRefs[this.nLocation] = this.nLine;
|
||||
this.nLocation++;
|
||||
}
|
||||
|
||||
|
|
@ -1832,28 +1964,37 @@ Macro10.PSEUDO_OP = {
|
|||
ASCII: "ASCII",
|
||||
ASCIZ: "ASCIZ",
|
||||
BLOCK: "BLOCK",
|
||||
BYTE: "BYTE",
|
||||
DEFINE: "DEFINE",
|
||||
END: "END",
|
||||
EXP: "EXP",
|
||||
IF1: "IF1",
|
||||
IFDEF: "IFDEF",
|
||||
IFDIF: "IFDIF",
|
||||
IFE: "IFE",
|
||||
IFG: "IFG",
|
||||
IFGE: "IFGE",
|
||||
IFIDN: "IFIDN",
|
||||
IFL: "IFL",
|
||||
IFLE: "IFLE",
|
||||
IFN: "IFN",
|
||||
IFNDEF: "IFNDEF",
|
||||
IRP: "IRP",
|
||||
IRPC: "IRPC",
|
||||
LALL: "LALL",
|
||||
LIT: "LIT",
|
||||
LITERAL: "LITERAL", // this is a pseudo-pseudo-op, for internal use only
|
||||
LIST: "LIST",
|
||||
LOC: "LOC",
|
||||
NOSYM: "NOSYM",
|
||||
OPDEF: "OPDEF",
|
||||
PAGE: "PAGE",
|
||||
PURGE: "PURGE",
|
||||
REPEAT: "REPEAT",
|
||||
SIXBIT: "SIXBIT",
|
||||
SUBTTL: "SUBTTL",
|
||||
TITLE: "TITLE",
|
||||
VAR: "VAR",
|
||||
XWD: "XWD",
|
||||
XLIST: "XLIST"
|
||||
};
|
||||
|
|
|
|||
|
|
@ -109,19 +109,6 @@ 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 = ['[',']'];
|
||||
|
||||
|
|
@ -594,6 +581,9 @@ class Debugger extends Component
|
|||
case '||':
|
||||
valNew = (val1 || val2? 1 : 0);
|
||||
break;
|
||||
case ',,':
|
||||
valNew = this.truncate(val1, 18, true) * Math.pow(2, 18) + this.truncate(val2, 18, true);
|
||||
break;
|
||||
case '_':
|
||||
case '^_':
|
||||
valNew = val1;
|
||||
|
|
@ -625,7 +615,7 @@ class Debugger extends Component
|
|||
}
|
||||
|
||||
/**
|
||||
* parseArray(asValues, iValue, iLimit, nBase, fQuiet)
|
||||
* parseArray(asValues, iValue, iLimit, nBase, aUndefined)
|
||||
*
|
||||
* parseExpression() takes a complete expression and divides it into array elements, where even elements
|
||||
* are values (which may be empty if two or more operators appear consecutively) and odd elements are operators.
|
||||
|
|
@ -643,10 +633,10 @@ class Debugger extends Component
|
|||
* @param {number} iValue
|
||||
* @param {number} iLimit
|
||||
* @param {number} nBase
|
||||
* @param {boolean} [fQuiet]
|
||||
* @param {Array|undefined} [aUndefined]
|
||||
* @return {number|undefined}
|
||||
*/
|
||||
parseArray(asValues, iValue, iLimit, nBase, fQuiet)
|
||||
parseArray(asValues, iValue, iLimit, nBase, aUndefined)
|
||||
{
|
||||
var value;
|
||||
var sValue, sOp;
|
||||
|
|
@ -663,7 +653,7 @@ class Debugger extends Component
|
|||
sOp = (iValue < iLimit? asValues[iValue++] : "");
|
||||
|
||||
if (sValue) {
|
||||
v = this.parseValue(sValue, null, fQuiet, nUnary);
|
||||
v = this.parseValue(sValue, null, aUndefined, nUnary);
|
||||
} else {
|
||||
if (sOp == '{') {
|
||||
var cOpen = 1;
|
||||
|
|
@ -677,7 +667,7 @@ class Debugger extends Component
|
|||
if (!--cOpen) break;
|
||||
}
|
||||
}
|
||||
v = this.parseArray(asValues, iStart, iValue-1, this.nBase, fQuiet);
|
||||
v = this.parseArray(asValues, iStart, iValue-1, this.nBase, aUndefined);
|
||||
if (v != null && nUnary) {
|
||||
v = this.parseUnary(v, nUnary);
|
||||
}
|
||||
|
|
@ -730,12 +720,12 @@ class Debugger extends Component
|
|||
}
|
||||
|
||||
if (v === undefined) {
|
||||
if (this.sUndefined == null && fQuiet) {
|
||||
this.sUndefined = sValue;
|
||||
if (aUndefined) {
|
||||
aUndefined.push(sValue);
|
||||
v = 0;
|
||||
} else {
|
||||
fError = true;
|
||||
fQuiet = !fQuiet;
|
||||
aUndefined = [];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -786,7 +776,7 @@ class Debugger extends Component
|
|||
if (!fError) {
|
||||
value = aVals.pop();
|
||||
this.assert(!aVals.length);
|
||||
} else if (fQuiet === false) {
|
||||
} else if (!aUndefined) {
|
||||
this.println("parse error (" + (sValue || sOp) + ")");
|
||||
}
|
||||
|
||||
|
|
@ -795,17 +785,16 @@ class Debugger extends Component
|
|||
}
|
||||
|
||||
/**
|
||||
* parseASCII(sExp, chDelim, nBits, cchMax, fQuiet)
|
||||
* parseASCII(sExp, chDelim, nBits, cchMax)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} sExp
|
||||
* @param {string} chDelim
|
||||
* @param {number} nBits
|
||||
* @param {number} cchMax
|
||||
* @param {boolean} [fQuiet] (true for quiet parsing)
|
||||
* @return {string}
|
||||
*/
|
||||
parseASCII(sExp, chDelim, nBits, cchMax, fQuiet)
|
||||
parseASCII(sExp, chDelim, nBits, cchMax)
|
||||
{
|
||||
var i;
|
||||
while ((i = sExp.indexOf(chDelim)) >= 0) {
|
||||
|
|
@ -829,7 +818,7 @@ class Debugger extends Component
|
|||
v = this.truncate(v * Math.pow(2, nBits) + c, nBits * cchMax, true);
|
||||
}
|
||||
if (cch >= 0) {
|
||||
if (fQuiet === false) this.println("parse error (" + sExp + ")");
|
||||
this.println("parse error (" + chDelim + sExp + chDelim + ")");
|
||||
break;
|
||||
} else {
|
||||
sExp = sExp.substr(0, i) + this.toStrBase(v, -1) + sExp.substr(j);
|
||||
|
|
@ -858,15 +847,20 @@ class Debugger extends Component
|
|||
* predecessor is encountered, evaluate, and push the result back onto aVals. Only selected unary
|
||||
* operators are supported (eg, negate and complement); no ternary operators like '?:' are supported.
|
||||
*
|
||||
* fQuiet can be used to pass an array that collects any undefined variables that parseExpression()
|
||||
* encounters; the value of an undefined variable is zero. This mode was added for components that need
|
||||
* to support expressions containing "fixups" (ie, values that must be determined later).
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string|undefined} sExp
|
||||
* @param {boolean} [fQuiet] (true for quiet parsing)
|
||||
* @param {Array|undefined|boolean} [fQuiet]
|
||||
* @return {number|undefined} numeric value, or undefined if sExp contains any undefined or invalid values
|
||||
*/
|
||||
parseExpression(sExp, fQuiet)
|
||||
{
|
||||
var value = undefined;
|
||||
this.sUndefined = null;
|
||||
var fPrint = (fQuiet === false);
|
||||
var aUndefined = Array.isArray(fQuiet)? fQuiet : undefined;
|
||||
|
||||
if (sExp) {
|
||||
|
||||
|
|
@ -888,9 +882,9 @@ class Debugger extends Component
|
|||
* Quoted ASCII characters can have a numeric value, too, which must be converted now, to avoid any
|
||||
* conflicts with the operators below.
|
||||
*/
|
||||
sExp = this.parseASCII(sExp, '"', 7, 5, fQuiet); // MACRO-10 packs up to 5 7-bit ASCII codes into a value
|
||||
sExp = this.parseASCII(sExp, '"', 7, 5); // MACRO-10 packs up to 5 7-bit ASCII codes into a value
|
||||
if (!sExp) return value;
|
||||
sExp = this.parseASCII(sExp, "'", 6, 6, fQuiet); // MACRO-10 packs up to 6 6-bit ASCII (SIXBIT) codes into a value
|
||||
sExp = this.parseASCII(sExp, "'", 6, 6); // MACRO-10 packs up to 6 6-bit ASCII (SIXBIT) codes into a value
|
||||
if (!sExp) return value;
|
||||
|
||||
/*
|
||||
|
|
@ -928,11 +922,11 @@ class Debugger extends Component
|
|||
* to remove spaces entirely, because if an operator-less expression like "A B" was passed in, we would want
|
||||
* that to generate an error; if we converted it to "AB", evaluation might inadvertently succeed.
|
||||
*/
|
||||
var regExp = /({|}|\|\||&&|\||\^!|\^B|\^O|\^D|\^L|\^-|~|\^_|_|&|!=|!|==|>=|>>>|>>|>|<=|<<|<|-|\+|\^\/|\/|\*| )/;
|
||||
var regExp = /({|}|\|\||&&|\||\^!|\^B|\^O|\^D|\^L|\^-|~|\^_|_|&|!=|!|==|>=|>>>|>>|>|<=|<<|<|-|\+|\^\/|\/|\*|,,| )/;
|
||||
sExp = sExp.replace(/(^|[^A-Z0-9$%.])([0-9]+)B/, "$1$2^_").replace(/\s+/g, ' ');
|
||||
var asValues = sExp.split(regExp);
|
||||
value = this.parseArray(asValues, 0, asValues.length, this.nBase, fQuiet);
|
||||
if (value !== undefined && fQuiet === false) {
|
||||
value = this.parseArray(asValues, 0, asValues.length, this.nBase, aUndefined);
|
||||
if (value !== undefined && fPrint) {
|
||||
this.printValue(null, value);
|
||||
}
|
||||
}
|
||||
|
|
@ -1060,13 +1054,15 @@ class Debugger extends Component
|
|||
* @this {Debugger}
|
||||
* @param {string|undefined} sValue
|
||||
* @param {string|null} [sName] is the name of the value, if any
|
||||
* @param {boolean} [fQuiet]
|
||||
* @param {Array|undefined|boolean} [fQuiet]
|
||||
* @param {number} [nUnary] (0 for none, 1 for negate, 2 for complement, 3 for leading zeros)
|
||||
* @return {number|undefined} numeric value, or undefined if sValue is either undefined or invalid
|
||||
*/
|
||||
parseValue(sValue, sName, fQuiet, nUnary = 0)
|
||||
{
|
||||
var value;
|
||||
var aUndefined = Array.isArray(fQuiet)? fQuiet : undefined;
|
||||
|
||||
if (sValue != null) {
|
||||
var iReg = this.getRegIndex(sValue);
|
||||
if (iReg >= 0) {
|
||||
|
|
@ -1076,13 +1072,10 @@ class Debugger extends Component
|
|||
if (value != null) {
|
||||
var sUndefined = this.getVariableFixup(sValue);
|
||||
if (sUndefined) {
|
||||
if (!this.sUndefined) {
|
||||
this.sUndefined = sUndefined;
|
||||
if (aUndefined) {
|
||||
aUndefined.push(sUndefined);
|
||||
} else {
|
||||
if (!fQuiet) {
|
||||
this.println(sValue + " has too many undefined values (" + sUndefined + ',' + this.sUndefined + ")");
|
||||
fQuiet = true;
|
||||
}
|
||||
this.println(sValue + " is undefined (" + sUndefined + ")");
|
||||
value = undefined;
|
||||
}
|
||||
}
|
||||
|
|
@ -1169,13 +1162,17 @@ class Debugger extends Component
|
|||
*/
|
||||
printVariable(sVar)
|
||||
{
|
||||
if (sVar) {
|
||||
return this.printValue(sVar, this.aVariables[sVar].value);
|
||||
}
|
||||
var cVariables = 0;
|
||||
for (sVar in this.aVariables) {
|
||||
this.printValue(sVar, this.aVariables[sVar].value);
|
||||
cVariables++;
|
||||
if (this.aVariables) {
|
||||
if (sVar) {
|
||||
return this.printValue(sVar, this.aVariables[sVar] && this.aVariables[sVar].value);
|
||||
}
|
||||
var aVars = Object.keys(this.aVariables);
|
||||
aVars.sort();
|
||||
for (var i = 0; i < aVars.length; i++) {
|
||||
this.printValue(aVars[i], this.aVariables[aVars[i]].value);
|
||||
cVariables++;
|
||||
}
|
||||
}
|
||||
return cVariables > 0;
|
||||
}
|
||||
|
|
@ -1200,6 +1197,10 @@ class Debugger extends Component
|
|||
*/
|
||||
getVariable(sVar)
|
||||
{
|
||||
if (this.aVariables[sVar]) {
|
||||
return this.aVariables[sVar].value;
|
||||
}
|
||||
sVar = sVar.substr(0, 6);
|
||||
return this.aVariables[sVar] && this.aVariables[sVar].value;
|
||||
}
|
||||
|
||||
|
|
@ -1233,7 +1234,7 @@ class Debugger extends Component
|
|||
* @this {Debugger}
|
||||
* @param {string} sVar
|
||||
* @param {number} value
|
||||
* @param {string|null} [sUndefined]
|
||||
* @param {string|undefined} [sUndefined]
|
||||
*/
|
||||
setVariable(sVar, value, sUndefined)
|
||||
{
|
||||
|
|
@ -1317,6 +1318,7 @@ if (DEBUGGER) {
|
|||
'}': 20 // close grouped expression (converted from achGroup[1])
|
||||
};
|
||||
Debugger.aDECOpPrecedence = {
|
||||
',,': 1, // high-word,,low-word
|
||||
'||': 5, // logical OR
|
||||
'&&': 6, // logical AND
|
||||
'!=': 10, // inequality
|
||||
|
|
|
|||
Loading…
Reference in a new issue