The recursive MACRO-10 test case (MACTEST1.MAC) found in the June 1972 MACRO-10 manual passes now

This commit is contained in:
Jeff 2017-03-19 14:03:49 -07:00 • committed by Jeff Parsons
commit 4cce2177f4
9 changed files with 264 additions and 177 deletions

View file

@ -9,13 +9,20 @@
; last word if it is not already left-justified. The first part of the example shows the
; normal listing, then SALL is set to show what code the macros are generating.
;
; The original listing encodes the following two lines as follows:
; The original listing encoded two of the lines as follows:
;
; IFN ZZ,<DEFINE SHIFT ;;REMAINDER LEFT
; IFE ZZ&777B8,> ;;IF NOT LEFT JUSTIFIED
;
; The April 1978 MACRO-10 manual says that a macro body "must be enclosed in angle brackets",
; so I'm not sure how the above lines make sense.
; so the DEFINE looks wrong, and the IFE contains only a closing angle bracket, which also seems
; wrong. Perhaps what's shown above is a side-effect of some MACRO-10 listing generation bug
; and not an accurate reflection of the original code. In any event, after making two small
; modifications below, macro10.js is able to assemble the code fine.
;
; This example also begs the question: did MACRO-10 optimize tail recursion? Because without
; it, if the SHIFT macro required many iterations (for example, if "ZZ==ZZ_9" was replaced with
; "ZZ==ZZ+9"), wouldn't MACRO-10 blow its stack? I've noted this in macro10.js as well.
;
TITLE STORE TEXT CHARACTER BY CHARACTER
SUBTTL %1 5-APR-72

View file

@ -7,7 +7,7 @@ machines:
type: pdp10
config: /devices/pdp10/machine/ka10/test/debugger/machine.xml
debugger: true
commands: a 100 /apps/pdp10/tests/MACTEST.MAC
commands: a 100 /apps/pdp10/tests/MACTEST1.MAC
---
DEC PDP-10 Opcode Tests

File diff suppressed because one or more lines are too long

View file

@ -91,6 +91,7 @@ var Fixup;
* @property {number} iURL
* @property {Array.<string>} asURLs
* @property {Array.<string>} asLines
* @property {number|null|undefined} nAddrStart
*/
class Macro10 {
/**
@ -128,6 +129,11 @@ class Macro10 {
this.dbg = dbg;
this.done = done;
/*
* The next set of properties may be updated by the assembly process and queried by the caller.
*/
this.nAddrStart = null;
this.println("starting PCjs MACRO-10 Mini-Assembler...");
/*
@ -149,7 +155,7 @@ class Macro10 {
* Macros have the name that was assigned to them, REPEAT and conditional blocks have generated names
* that match the pseudo-op (eg, "?REPEAT", "?IFE"), and LITERAL blocks have generated location-based
* names. All generated names use a leading question mark ('?') so that they don't conflict with
* normal MACRO-10 labels.
* normal MACRO-10 symbols.
*/
/**
@ -313,6 +319,11 @@ class Macro10 {
* are in the array and insert my own uniform CR/LF sequences.
*/
if (!this.parseLine(this.asLines[i] + '\r\n')) break;
/*
* When an END statement is encountered, nAddrStart will change from null to either undefined
* or a starting address.
*/
if (this.nAddrStart !== null) break;
}
if (this.nMacroDef) {
@ -482,6 +493,14 @@ class Macro10 {
this.addASCII(sRemainder);
break;
case Macro10.PSEUDO_OP.END:
this.addEND(sOperands);
break;
case Macro10.PSEUDO_OP.EXP:
this.addEXP(sOperands);
break;
case Macro10.PSEUDO_OP.XWD:
this.addXWD(sOperands);
break;
@ -527,6 +546,13 @@ class Macro10 {
this.macroCall = macro;
macro.aValues = this.getValues(sOperands, true);
this.parseText(macro.sText, macro.aParms, macro.aValues, macro.aDefaults);
/*
* WARNING: Our simplistic approach to macro expansion and processing means that recursive macros
* (such as the SHIFT macro contained in /apps/pdp10/tests/MACTEST1.MAC) could blow the stack. Nothing
* bad should happen (other than a JavaScript stack limit exception aborting the assembly), but it begs
* the question: did MACRO-10 perform any tail recursion optimizations or other tricks to prevent macros
* from running amok?
*/
this.macroCall = macroPrev;
return true;
}
@ -700,9 +726,9 @@ class Macro10 {
this.error("error parsing expression: " + sOperand);
}
} else {
var wLeft = match[1]? this.parseExpression(match[1]) : 0;
var wLeft = match[1]? this.parseExpression(match[1], fPass1, nLocation) : 0;
if (wLeft !== undefined) {
var wRight = match[2]? this.parseExpression(match[2]) : 0;
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.
@ -1034,6 +1060,44 @@ class Macro10 {
return sRemain;
}
/**
* addEND()
*
* Processes the END pseudo-op.
*
* @this {Macro10}
* @param {string} sOperands
*/
addEND(sOperands)
{
if (!sOperands) {
this.nAddrStart = this.nAddr;
} else {
this.nAddrStart = this.parseExpression(sOperands, true);
if (this.nAddrStart === undefined) {
this.error("unrecognized expression: " + sOperands);
}
}
}
/**
* addEXP()
*
* Processes the EXP pseudo-op.
*
* @this {Macro10}
* @param {string} sOperands
*/
addEXP(sOperands)
{
var w = this.parseExpression(sOperands, true);
if (w !== undefined) {
this.genWord(w, this.dbg.sUndefined);
} else {
this.error("unrecognized expression: " + sOperands);
}
}
/**
* addSymbol(name, value, nType)
*
@ -1101,15 +1165,17 @@ class Macro10 {
/**
* 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.
* Processes the XWD pseudo-op.
*
* Since the XWD pseudo-op appears to be equivalent to two values separated by two commas, which addEXP() must also
* support, we can piggy-back on addExp().
*
* @this {Macro10}
* @param {string} sOperands
*/
addXWD(sOperands)
{
this.genWord(0, sOperands.replace(",", ",,"));
this.addEXP(sOperands.replace(",", ",,"));
}
/**
@ -1214,6 +1280,8 @@ Macro10.PSEUDO_OP = {
ASCII: "ASCII",
ASCIZ: "ASCIZ",
DEFINE: "DEFINE",
END: "END",
EXP: "EXP",
IFE: "IFE",
IFN: "IFN",
IRP: "IRP",

View file

@ -721,35 +721,45 @@ class Debugger extends Component
}
/**
* parseASCII(sExp, chDelim, nBits, nMax)
* parseASCII(sExp, chDelim, nBits, cchMax, fQuiet)
*
* @this {Debugger}
* @param {string} sExp
* @param {string} chDelim
* @param {number} nBits
* @param {number} nMax
* @param {number} cchMax
* @param {boolean} [fQuiet] (true for quiet parsing)
* @return {string}
*/
parseASCII(sExp, chDelim, nBits, nMax)
parseASCII(sExp, chDelim, nBits, cchMax, fQuiet)
{
var i;
while ((i = sExp.indexOf(chDelim)) >= 0) {
var v = 0;
var n = nMax;
var j = i + 1;
while (n--) {
var cch = cchMax;
while (cch) {
var ch = sExp[j++];
if (ch == chDelim) break;
if (ch == chDelim) {
cch = -1;
break;
}
var c = ch.charCodeAt(0);
if (nBits == 7) {
c &= 0x7F;
} else {
c = (c - 0x20) & 0x3F;
}
v = this.truncate(v * Math.pow(2, nBits) + c, nBits * nMax, true);
v = this.truncate(v * Math.pow(2, nBits) + c, nBits * cchMax, true);
cch--;
}
if (cch >= 0) {
if (fQuiet === false) this.println("parse error (" + sExp + ")");
break;
} else {
sExp = sExp.substr(0, i) + this.toStrBase(v, -1) + sExp.substr(j);
}
}
return sExp;
}
@ -780,8 +790,7 @@ class Debugger extends Component
*/
parseExpression(sExp, fQuiet)
{
var value;
var value = undefined;
this.sUndefined = null;
if (sExp) {
@ -804,8 +813,10 @@ 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); // MACRO-10 packs up to 5 7-bit ASCII codes in the value
sExp = this.parseASCII(sExp, "'", 6, 6); // MACRO-10 packs up to 6 6-bit ASCII (SIXBIT) codes in the value
sExp = this.parseASCII(sExp, '"', 7, 5, fQuiet); // 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
if (!sExp) return value;
/*
* All browsers (including, I believe, IE9 and up) support the following idiosyncrasy of a RegExp split():

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