}
*/
this.aWords = []; // filled in by the various genXXX() functions
this.nLine = 0;
this.nError = 0;
this.nLocation = this.nAddr; // advanced by the various genXXX() functions
this.sOperator = null; // the active operator, if any
this.nMacroDef = 0; // the active MACRO definition state
this.sMacroDef = null; // the active MACRO definition name
this.chMacroOpen = this.chMacroClose = '';
/**
* If an ASCII/ASCIZ/SIXBIT pseudo-op is active, chASCII is set to the separator
* and sASCII collects the intervening character(s).
*
* @type {null|string}
*/
this.chASCII = null;
/**
* @type {string}
*/
this.sASCII = "";
this.sText = "";
this.iURL = 0;
this.asURLs = sURL.split(';');
this.loadNextResource();
}
/**
* loadNextResource()
*
* @this {Macro10}
*/
loadNextResource()
{
if (this.iURL == this.asURLs.length) {
this.done(this.parseFile());
return;
}
var macro10 = this;
Web.getResource(this.asURLs[this.iURL++], null, true, function processMacro10(sURL, sResource, nErrorCode) {
if (nErrorCode) {
macro10.done(nErrorCode, sURL);
return;
}
var sText = sResource;
if (Str.endsWith(sURL, ".html")) {
/*
* We want to parse ONLY the text between ...
tags, and eliminate any HTML entities.
*/
sText = "";
var match, re = /([\s\S]*?)<\/pre>/gi;
while (match = re.exec(sResource)) {
var s = match[1];
if (s.indexOf('&') >= 0) s = s.replace(/</gi, '<').replace(/>/gi, '>').replace(/&/gi, '&');
sText += s;
}
match = sText.match(/&[a-z]+;/i);
if (match) macro10.warning("unrecognized HTML entity: " + match[0]);
}
if (macro10.sText) macro10.sText += '\n';
macro10.sText += sText;
setTimeout(function() {
macro10.loadNextResource();
}, 0);
});
}
/**
* getBin()
*
* @this {Macro10}
* @return {Array.}
*/
getBin()
{
return this.aWords;
}
/**
* parseFile()
*
* Begin the assembly process.
*
* @this {Macro10}
* @return {number}
*/
parseFile()
{
var macro10 = this;
/*
* If the "preprocess" option is set, then just return the plain text we retrieved.
*/
if (this.sOptions.indexOf('p') >= 0) {
this.dbg.println(this.sText);
return 0;
}
var a = this.dbg.resetVariables();
try {
var i;
var asLines = this.sText.split(/\r?\n/);
for (i = 0; i < asLines.length; i++) {
this.nLine++;
if (!this.parseLine(asLines[i] + '\r\n')) break;
}
if (this.nMacroDef) {
this.error("open block from line " + this.tblMacros[this.sMacroDef].nLine);
}
for (i = 0; i < this.aLiterals.length; i++) {
var name = this.aLiterals[i];
var 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 literal: " + name);
continue;
}
this.parseText(macro.sText);
}
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);
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;
});
} catch(err) {
this.println(err.message);
this.nError = -1;
}
this.dbg.restoreVariables(a);
return this.nError;
}
/**
* parseLine(sLine)
*
* @this {Macro10}
* @param {string} sLine (line contents)
* @return {boolean}
*/
parseLine(sLine)
{
if (this.nMacroDef) {
if (this.nMacroDef == 1) {
var i = sLine.indexOf(this.chMacroOpen);
if (i >= 0) {
this.nMacroDef++;
sLine = sLine.substr(i+1);
} else {
this.error("expected " + this.sOperator + " definition: " + sLine);
}
}
if (this.nMacroDef > 1) {
sLine = this.appendMacro(sLine);
}
if (this.nMacroDef) return true;
}
if (this.chASCII != null) {
sLine = this.addASCII(sLine);
}
var reLine = /\s*([A-Z$%._][0-9A-Z$%.]*[:=]|)\s*([A-Z$%.][0-9A-Z$%.]*|)\s*([^;]+|)\s*(;?.*)/i;
var match = sLine.match(reLine);
if (!match || match[4] && match[4].slice(0, 1) != ';') {
this.error("failed to parse line: " + sLine);
return false;
}
var sLabel = match[1];
var sOperator = match[2].toUpperCase();
var sOperands = match[3].trim();
var sComment = match[4].slice(1);
if (sLabel) {
var chSep = sLabel.slice(-1);
sLabel = sLabel.slice(0, -1);
if (chSep == ':') {
this.addLabel(sLabel);
} else {
sOperands = sOperator + sOperands;
sOperator = chSep;
}
}
this.sOperator = sOperator;
sOperands = sOperands.trim();
/*
* 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.
*/
if (sOperands[0] == '=') {
sLabel = sOperator;
sOperands = sOperands.substr(1);
sOperator = '=';
}
/*
* Check the operands for a literal. If the line contains and/or ends with a literal
* we record it and replace it with an internal symbol. We assume only one literal per line,
* especially since they can be open-ended (ie, continue for multiple lines).
*/
var sLiteral = this.getLiteral(sOperands);
if (sLiteral) {
sOperands = sOperands.replace(sLiteral, this.addMacro(Macro10.PSEUDO_OP.LITERAL, sLiteral));
}
/*
* Check the operands for any reserved symbols (ie, symbols with a trailing '#', such as "USER#").
*/
var sSymbol;
while (sSymbol = this.getReserved(sOperands)) {
sOperands = sOperands.replace(sSymbol, sSymbol.slice(0, -1));
}
if (!this.parseMacro(sOperator, sOperands)) {
switch (sOperator) {
case "":
if (sOperands) {
this.genWord(0, [sOperands]);
}
break;
case "=":
this.addAssign(sLabel, sOperands);
break;
case Macro10.PSEUDO_OP.ASCII:
case Macro10.PSEUDO_OP.ASCIZ:
case Macro10.PSEUDO_OP.SIXBIT:
this.addASCII(sOperands);
break;
case Macro10.PSEUDO_OP.XWD:
this.addXWD(sOperands);
break;
case Macro10.PSEUDO_OP.DEFINE:
case Macro10.PSEUDO_OP.IFE:
case Macro10.PSEUDO_OP.REPEAT:
this.addMacro(sOperator, sOperands);
break;
case Macro10.PSEUDO_OP.PAGE: // TODO
case Macro10.PSEUDO_OP.SUBTTL: // TODO
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 + ")");
}
break;
}
}
return true;
}
/**
* parseMacro(name, sOperands)
*
* @this {Macro10}
* @param {string} name
* @param {string} [sOperands]
* @return {boolean}
*/
parseMacro(name, sOperands)
{
var macro = this.tblMacros[name];
if (!macro) return false;
if (sOperands != null) {
/*
* Process this macro call; start by extracting macro argument values from sOperands.
*/
var sDelim = ',';
if (sOperands[0] == '(') {
sDelim += ')';
sOperands = sOperands.substr(1);
}
var aValues = this.getValues(sOperands, sDelim);
this.parseText(macro.sText, macro.aParms, aValues);
return true;
}
if (name[0] != '_') return false;
switch(name.substr(1)) {
case Macro10.PSEUDO_OP.IFE:
if (!macro.nOperand) {
this.parseText(macro.sText);
}
break;
case Macro10.PSEUDO_OP.REPEAT:
while (macro.nOperand-- > 0) {
this.parseText(macro.sText);
}
break;
default:
return false;
}
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)
*
* @this {Macro10}
* @param {string} ch
* @return {boolean}
*/
isSymbolChar(ch)
{
return !!ch.match(/[0-9A-Z$%.]/i);
}
/**
* parseText(sText, aParms, aValues)
*
* @this {Macro10}
* @param {string} sText
* @param {Array.} [aParms]
* @param {Array.} [aValues]
*/
parseText(sText, aParms, aValues)
{
var asLines = sText.split(/\r?\n/);
for (var iLine = 0; iLine < asLines.length; iLine++) {
var sLine = asLines[iLine] + '\r\n';
if (aParms) {
for (var iParm = 0; iParm < aParms.length; iParm++) {
var sParm = aParms[iParm];
var sReplace = aValues[iParm] || "";
var iSearch = 0;
while (true) {
var iMatch = sLine.indexOf(sParm, iSearch);
if (iMatch < 0) break;
iSearch = iMatch + 1;
var iMatchEnd = iMatch + sParm.length;
if ((!iMatch || !this.isSymbolChar(sLine[iMatch - 1])) && (iMatchEnd >= sLine.length || !this.isSymbolChar(sLine[iMatchEnd]))) {
sLine = sLine.substr(0, iMatch) + sReplace + sLine.substr(iMatchEnd);
iSearch = iMatch + sReplace.length;
}
}
}
}
if (!this.parseLine(sLine)) break;
}
}
/**
* error(sError)
*
* @this {Macro10}
* @param {string} sError
*/
error(sError)
{
throw new Error("error" + (this.nLine? " at line " + Str.toDec(this.nLine) : "") + ": " + sError);
}
/**
* warning(sWarning)
*
* @this {Macro10}
* @param {string} sWarning
*/
warning(sWarning)
{
this.println("warning" + (this.nLine? " at line " + Str.toDec(this.nLine) : "") + ": " + sWarning);
}
/**
* getExpression(sOperands, sDelim)
*
* @this {Macro10}
* @param {string} sOperands
* @param {string} [sDelim] (eg, comma, closing parenthesis)
* @return {string|null} (if the operands begin with an expression, return it)
*/
getExpression(sOperands, sDelim = ",")
{
var i = 0;
var sOperand = null;
var cNesting = 0;
while (i < sOperands.length) {
var ch = sOperands[i];
if (sDelim.indexOf(ch) >= 0) {
break;
}
if (ch == '<') {
cNesting++;
} else if (ch == '>') {
if (--cNesting < 0) {
this.error("missing bracket(s): " + sOperands);
break;
}
}
i++;
}
if (!cNesting) {
sOperand = sOperands.substr(0, i);
}
else if (cNesting > 0) {
this.error("extra bracket(s): " + sOperands);
}
return sOperand;
}
/**
* parseExpression(sOperand, 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
* interpreting two expressions separated by two commas as the left and right 18-bit halves
* of a 36-bit value.
*
* @this {Macro10}
* @param {string} sOperand
* @param {number} [nLocation]
* @return {number|undefined}
*/
parseExpression(sOperand, 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.
*/
var match = sOperand.match(/^([^,]*),,([^,]*)$/);
if (!match) {
result = this.dbg.parseExpression(sOperand);
} else {
var wLeft = match[1]? this.dbg.parseExpression(match[1]) : 0;
if (wLeft !== undefined) {
var wRight = match[2]? this.dbg.parseExpression(match[2]) : 0;
if (wRight !== undefined) {
/*
* NOTE: These must be combined as UNSIGNED values, so that's what we tell truncate().
*/
result = this.dbg.truncate(wLeft, 18, true) * Math.pow(2, 18) + this.dbg.truncate(wRight, 18, true);
}
}
}
return result;
}
/**
* getLiteral(sOperands)
*
* Check the operands for a literal (ie, an expression starting with a square bracket).
*
* @this {Macro10}
* @param {string} sOperands
* @return {string|null} (if the operands contain a literal, return it)
*/
getLiteral(sOperands)
{
var cNesting = 0;
var sLiteral = null;
var i = 0, iLiteral = -1;
while (i < sOperands.length) {
var ch = sOperands[i];
if (ch == '[') {
if (!cNesting++) iLiteral = i;
}
i++;
if (ch == ']' && --cNesting <= 0) break;
}
if (cNesting < 0) {
this.error("missing bracket(s): " + sOperands);
}
if (iLiteral >= 0) {
sLiteral = sOperands.substr(iLiteral, i - iLiteral);
}
return sLiteral;
}
/**
* getReserved(sOperands)
*
* Check the operands for any reserved symbols (ie, symbols with a trailing '#', such as "USER#").
*
* @this {Macro10}
* @param {string} sOperands
* @return {string|null} (if the operands contain a reserved symbol, return it)
*/
getReserved(sOperands)
{
var match, sReserved = null;
if (match = sOperands.match(/([A-Z$%.][0-9A-Z$%.]*)#/i)) {
sReserved = match[0];
var sLabel = match[1];
var name = '_' + sLabel;
if (this.tblMacros[name] !== undefined) {
this.error("reserved symbol redefined: " + sReserved);
}
this.tblMacros[name] = {
name: name,
nOperand: Macro10.MACRO_OP.RESERVED,
aParms: [],
aDefaults: [],
sText: sLabel + ": 0",
nLine: this.nLine
};
this.aLiterals.push(name);
}
return sReserved;
}
/**
* getValues(sOperands, sDelim)
*
* @this {Macro10}
* @param {string} sOperands
* @param {string} [sDelim]
* @return {Array.}
*/
getValues(sOperands, sDelim)
{
var aValues = [];
while (sOperands) {
sOperands = sOperands.trim();
var sOperand = this.getExpression(sOperands, sDelim);
if (!sOperand) break;
aValues.push(sOperand);
sOperands = sOperands.substr(sOperand.length + 1);
}
return aValues;
}
/**
* addASCII(sOperands)
*
* @this {Macro10}
* @param {string} sOperands
* @return {string} (returns whatever portion of the string was not part of an ASCII pseudo-op)
*/
addASCII(sOperands)
{
var sRemain = sOperands;
if (this.chASCII == null) {
this.chASCII = this.sASCII = "";
if (sOperands) {
this.chASCII = sOperands[0];
sRemain = sOperands = sOperands.substr(1);
}
}
if (this.chASCII) {
var i = sOperands.indexOf(this.chASCII);
if (i < 0) {
sRemain = "";
} else {
sRemain = sOperands.substr(i + 1);
sOperands = sOperands.substr(0, i);
this.chASCII = null;
}
this.sASCII += sOperands;
}
if (this.chASCII == null) {
this.genASCII();
}
return sRemain;
}
/**
* addAssign(sName, sOperand)
*
* @this {Macro10}
* @param {string} sName
* @param {string} sOperand
*/
addAssign(sName, sOperand)
{
var value = this.parseExpression(sOperand);
if (value === undefined) {
this.error("parseExpression(" + sOperand + ")");
return;
}
this.addSymbol(sName, value);
}
/**
* addLabel(sLabel)
*
* @this {Macro10}
* @param {string} sLabel
*/
addLabel(sLabel)
{
this.addSymbol(sLabel, this.nLocation, true);
}
/**
* addMacro(sOperator, sOperands)
*
* If sOperator is DEFINE, then a macro definition is expected. If it's REPEAT, then we're starting a
* REPEAT block instead.
*
* REPEAT blocks piggy-back on this code because they're essentially anonymous immediately-invoked macros;
* we use an illegal MACRO-10 symbol ('_REPEAT') to name the anonymous macro while it's being defined, and the
* macro's nOperand field will contain the repeat count.
*
* The piggy-backing continues with other pseudo-ops like IFE, which again contain an anonymous block of text
* that is immediately invoked if the criteria associated with the expression stored in the nOperand field is
* satisfied. That satisfaction occurs (or doesn't occur) when parseMacro() is called, once the macro has
* been fully defined.
*
* @this {Macro10}
* @param {string} sOperator
* @param {string} sOperands
* @return {string}
*/
addMacro(sOperator, sOperands)
{
var match, name, nOperand, aParms, aDefaults, iMatch;
this.chMacroOpen = '<';
this.chMacroClose = '>';
aParms = aDefaults = [];
if (sOperator == Macro10.PSEUDO_OP.DEFINE) {
match = sOperands.match(/([A-Z$%.][0-9A-Z$%.]*)\s*(\([^)]*\)|)\s*(<|)(.*)/i);
if (!match) {
this.error("unrecognized " + sOperator + " definition: " + sOperands);
return "";
}
name = match[1];
/*
* TODO: This may not actually be an error, but I'd like to take a look if/when it happens.
*/
if (this.tblMacros[name] !== undefined) {
this.error("macro redefined: " + name);
}
/*
* TODO: Tighten up this parsing at some point. All this is doing is extracting entire symbols
* from within the parentheses, if any; it's NOT ensuring that those symbols are comma-separated
* with no other intervening characters.
*/
aParms = match[2].match(/[A-Z$%.][0-9A-Z$%.]*/g);
nOperand = Macro10.MACRO_OP.DEFINE;
iMatch = 3;
}
else if (sOperator == Macro10.PSEUDO_OP.LITERAL) {
this.chMacroOpen = '[';
this.chMacroClose = ']';
name = '_' + Str.toDec(this.nLocation, 5);
if (this.tblMacros[name] !== undefined) {
this.error("literal symbol redefined: " + name);
}
match = [sOperands[0], name + ": " + sOperands.substr(1)];
nOperand = Macro10.MACRO_OP.LITERAL;
this.aLiterals.push(name);
iMatch = 0;
}
else {
var sOperand = this.getExpression(sOperands);
if (!sOperand) {
this.error("missing " + sOperator + " expression: " + sOperands);
return "";
}
sOperands = sOperands.substr(sOperand.length + 1);
sOperand = sOperand.trim();
match = sOperands.match(/\s*(<|)(.*)/i);
name = '_' + sOperator;
nOperand = this.parseExpression(sOperand) || 0;
iMatch = 1;
}
/*
* Now we need to set a global parsing state: we are either about to receive a macro definition on
* subsequent lines (1), the definition has already started on the current line (2), or the definition
* started and ended on the current line (0).
*/
name = name.toUpperCase();
this.nMacroDef = 1;
var sText = "";
if (match[iMatch]) { // if there IS also an opening bracket...
this.nMacroDef = 2; // then the macro definition has begun
sText = match[iMatch + 1];
if (sText.slice(-1) == this.chMacroClose) { // and if there is ALSO a closing bracket...
this.nMacroDef = 0; // the macro definition has ended as well
sText = sText.slice(0, -1);
}
}
this.tblMacros[name] = {name, nOperand, aParms, aDefaults, sText, nLine: this.nLine};
if (!this.nMacroDef) {
this.parseMacro(name);
} else {
this.sMacroDef = name;
}
return name;
}
/**
* appendMacro(sLine)
*
* @this {Macro10}
* @param {string} sLine
* @return {string}
*/
appendMacro(sLine)
{
var sRemain = "";
for (var i = 0; i < sLine.length; i++) {
if (sLine[i] == this.chMacroOpen) {
this.nMacroDef++;
} else if (sLine[i] == this.chMacroClose) {
this.nMacroDef--;
if (this.nMacroDef == 1) {
this.nMacroDef = 0;
sRemain = sLine.substr(i + 1);
sLine = sLine.substr(0, i);
break;
}
}
}
var name = this.sMacroDef || "";
this.tblMacros[name].sText += sLine;
if (!this.nMacroDef) {
this.sMacroDef = null;
this.parseMacro(name);
}
return sRemain;
}
/**
* addSymbol(name, value, fLabel, fGlobal, fPrivate)
*
* @this {Macro10}
* @param {string} name
* @param {number} value
* @param {boolean} [fLabel] (default is false, meaning the symbol is an assignment)
* @param {boolean} [fGlobal] (default is false, meaning the symbol is local to the current file)
* @param {boolean} [fPrivate] (default is false, meaning the symbol has visibility to the caller)
*/
addSymbol(name, value, fLabel = false, fGlobal = false, fPrivate = false)
{
name = name.toUpperCase().substr(0, 6);
if (fLabel && this.tblSymbols[name] !== undefined) {
this.error("label " + name + " redefined");
return;
}
this.tblSymbols[name] = {name, value, nLine: this.nLine, fLabel, fGlobal, fPrivate};
this.dbg.setVariable(name, value);
}
/**
* addXWD()
*
* The XWD pseudo-op appears to be equivalent to two values separated by two commas, which our fixup code
* must also support, so we simply treat the XWD operands as a fixup expression.
*
* @this {Macro10}
* @param {string} sOperands
*/
addXWD(sOperands)
{
this.genWord(0, [sOperands.replace(",", ",,")]);
}
/**
* genASCII()
*
* Based on the last operator, generate the appropriate ASCII/ASCIZ/SIXBIT data.
*
* @this {Macro10}
*/
genASCII()
{
var n = 0, w = 0; // number of characters in current word, and current word
var bits, shift; // bits per character, and bits to left-shift next character
var cch = this.sASCII.length;
if (this.sOperator == Macro10.PSEUDO_OP.ASCIZ) cch++;
for (var i = 0; i < cch; i++) {
if (!n) {
w = 0; shift = 29; bits = 7;
if (this.sOperator == Macro10.PSEUDO_OP.SIXBIT) {
bits--; shift++;
}
}
/*
* If we're processing an ASCIZ pseudo-op, then yes, we will fetch one character beyond
* the end of sASCII, which will return NaN, but when we mask a falsey value like NaN, we
* get zero, so it's all good.
*/
var c = this.sASCII.charCodeAt(i) & 0o177;
/*
* If we're doing 6-bit encoding, then perform the conversion of lower-case to upper-case,
* and then adjust/mask.
*/
if (bits == 6) {
if (c >= 0x61 && c <= 0x7A) c -= 0x20;
c = (c + 0o40) & 0o77;
}
w += c * Math.pow(2, shift);
shift -= bits;
n++;
if (shift < 0) {
this.genWord(w);
n = 0;
}
}
if (n) this.genWord(w);
}
/**
* genWord(w, aValues)
*
* @this {Macro10}
* @param {number} w (default value for the current location)
* @param {Array.} [aValues] (optional fixup value(s) to evaluate later)
*/
genWord(w, aValues)
{
this.aWords[this.nLocation] = w;
if (aValues) this.aFixups.push({nLocation: this.nLocation, aValues});
this.nLocation++;
}
}
Macro10.PSEUDO_OP = {
ASCII: "ASCII",
ASCIZ: "ASCIZ",
DEFINE: "DEFINE",
IFE: "IFE",
LITERAL:"LITERAL",
PAGE: "PAGE",
REPEAT: "REPEAT",
SIXBIT: "SIXBIT",
SUBTTL: "SUBTTL",
XWD: "XWD"
};
/*
* This enumerates the kinds of macros stored in tblMacros. The nOperand field should contain
* one of these values, unless it's a REPEAT or CONDITIONAL block, in which case it will contain
* either a repeat count or conditional value.
*/
Macro10.MACRO_OP = {
DEFINE: -1,
LITERAL: -2,
RESERVED: -3,
};