Lots of MACRO-10 improvements, DAKAM now assembles (but does not pass)

This commit is contained in:
Jeff 2017-04-03 16:10:08 -07:00 • committed by Jeff Parsons
commit 09dc4dcb9a
13 changed files with 22047 additions and 1758 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,154 @@
;DAKAM
MCNVER==0
DECVER==2
XLIST
DEFINE NAME (MCNVER,DECVER)<
TITLE DAKAM PDP-10 KA10 BASIC INSTRUCTION DIAGNOSTIC (13) MCNVER,DECVER
>
LIST
LALL
NAME \MCNVER,\DECVER
;(BYTE, BLOCK TRANSFER, JFFO AND OTHER BASIC MISCELLANEOUS INSTRUCTIONS)
;COPYRIGHT 1975,1977
;DIGITAL EQUIPMENT CORPORATION
;MARLBORO, MASS. 01752
;JOHN R. KIRCHOFF
LOC 137
MCNVER,,DECVER
NOSYM
SUBTTL DIAGNOSTIC PARAMETERS
;OPERATOR DEFINITIONS
OPDEF ER1 [1B8]
OPDEF ER2 [2B8]
OPDEF ER3 [3B8]
OPDEF ER4 [4B8]
OPDEF ER5 [5B8]
OPDEF ER6 [6B8]
OPDEF ER7 [7B8]
OPDEF ER10 [10B8]
OPDEF ER11 [11B8]
OPDEF ER12 [12B8]
OPDEF ER13 [13B8]
LUUO1=ERRMES
LUUO2=ERRMES
LUUO3=ERRMES
LUUO4=ERRMES
LUUO5=ERRMES
LUUO6=ERRMES
LUUO7=ERRMES
LUUO10=ERRMES
LUUO11=ERRMES
LUUO12=ERRMES
LUUO13=ERRMES
;SUBROUTINE ASSEMBLY DEFINITIONS
DEBUG=40
EXCASB=1
USRASB=1
KA10=1
PGMEND=1
ERDIAG=1
;SPECIAL FEATURE DEFINITIONS
SADR1=BEGIN
SADR2=BEGIN
SADR3=BEGIN
SADR4=BEGIN
SADR5=JRST BEGIN
SADR6=JRST BEGIN
SADR7=JRST BEGIN
SADR8=JRST BEGIN
SADR9=JRST BEGIN
SADR10=JRST BEGIN
SADR11=JRST BEGIN
;SPECIAL FEATURE PARAMETERS
PAREA0=0
PAREA1=0
PAREA2=0
PAREA3=SIXBIT/DAKAM/
PAREA4=SIXBIT/TMP/
PAREA5=0
PAREA6=0
ITERAT==1000
;MACROS
DEFINE SAVEAC (A,B)<
MOVEI AC+2,. ;SAVE TEST PC
MOVEM AC+2,TESTPC
MOVEI AC+2,<AC+2>&17 ;INFORM ERROR ROUTINE WHICH
MOVEM AC+2,ERRLOP# ;AC IS USED FOR ITERATION>
;SPECIAL INSTRUCTIONS TO ASSEMBLER
IF1,<BYTBLK=14773> ;BYTBLK BEYOND AC RANGE FOR PASS 1
IF1,<BLTBLK=15000> ;BLTBLK BEYOND AC RANGE FOR PASS 1
;MACROS
DEFINE GMOV (X1,DATA),<
IFLE <X1-20>,<
MOVE X1,[DATA] ;PRELOAD X1 WITH DATA>
IFG <X1-20>,<
MOVE AC+1,[DATA]
MOVEM AC+1,X1 ;PRELOAD X1 WITH DATA>>
DEFINE GCAM (X1,DATA),<
SN=SN+1
IFG <X1-20>,<
MOVE AC-1,X1
CAME AC-1,[DATA] ;IS C(X1) = DATA
IFIDN <DEST>,<X1>,<
ER2 AC-1,SN ;DEST NOT LOADED CORRECTLY>
IFDIF <DEST>,<X1>,<
ER2 AC-1,SN ;C(X1) WAS CLOBBERED>>
IFLE <X1-20>,<
CAME X1,[DATA] ;IS C(X1) = DATA
IFIDN <DEST>,<X1>,<
ER2 X1,SN ;DEST NOT LOADED CORRECTLY>
IFDIF <DEST>,<X1>,<
ER2 X1,SN ;C(X1) WAS CLOBBERED>>>
DEFINE BLTM1 (A1,C1,S1,D1,SC)<
;VERIFY THAT BLT WILL TRANSFER A 1 WORD BLOCK OF DATA
;WHERE SOURCE AND DESTINATION ARE DIFFERENT
;SOURCE DATA = SC
AC=C1
SAVEAC (1,1)
SN=A1
SRCE=S1 ;1ST LOC OF SOURCE = S1
DEST=D1 ;1ST LOC OF DESTINATION = D1
SIZ=1 ;BLOCK SIZE = 1 WORD
G'A1: MOVE AC,[SRCE,,DEST] ;LOAD BLT AC
GMOV (SRCE-1,<252525252525>)
GMOV (SRCE,<SC>)
GMOV (SRCE+1,<770077007700>)
GMOV (DEST-1,<070707070707>)
GMOV (DEST,<123456765432>)
GMOV (DEST+1,<552255225522>)
BLT AC,DEST+SIZ-1 ;*BLOCK TRANSFER
GCAM (SRCE-1,<252525252525>)
GCAM (SRCE,<SC>)
GCAM (SRCE+1,<770077007700>)
GCAM (DEST-1,<070707070707>)
GCAM (DEST,<SC>)
GCAM (DEST+1,<552255225522>)
JUMPL AC+2,G'A1 ;LOOP ON ERROR>

View file

@ -0,0 +1,430 @@
---
layout: page
title: PDP-10 KA10 Basic Instruction Diagnostic #13
permalink: /apps/pdp10/diags/ka10/dakam/
machines:
- id: testka10
type: pdp10
config: /devices/pdp10/machine/ka10/test/debugger/machine.xml
debugger: true
commands: a dakam.mac
---
PDP-10 KA10 Basic Instruction Diagnostic #13
--------------------------------------------
The *PDP-10 KA10 Basic Instruction Diagnostic #13* (MAINDEC-10-DAKAM) test code has been extracted from
[DAKAMM.MAC](DAKAMM.MAC.txt) [[original](http://pdp-10.trailing-edge.com/klad_sources/01/klad.sources/dakamm.mac.html)] and
[DAKAMT.MAC](DAKAMT.MAC.txt) [[original](http://pdp-10.trailing-edge.com/klad_sources/01/klad.sources/dakamt.mac.html)]
for use with the [PDP-10 Test Machine with Debugger](/devices/pdp10/machine/ka10/test/debugger/) below.
This diagnostic "TESTS THE BYTE, BLOCK TRANSFER AND JFFO INSTRUCTIONS."
Information regarding this diagnostic includes:
- [Instructions](#dakamtxt)
- [History](#dakamhst)
- [Source Code](#dakammac)
- [MACRO-10 Listing](DAKAM.LST.txt)
- [Additional Information](http://archive.pcjs.org/apps/pdp10/diags/ka10/dakam/DAKAM.SEQ.txt)
{% include machine.html id="testka10" %}
The Debugger's assemble ("a") command can be used to test the new built-in
[MACRO-10 Mini-Assembler](/modules/pdp10/lib/macro10.js), which supports a subset
of the [MACRO-10](http://archive.pcjs.org/pubs/dec/pdp10/tops10/02_1973AsmRef_macro.pdf) assembly language.
This command:
a DAKAM.MAC
will automatically read the [DAKAM.MAC](DAKAM.MAC.txt) source file (a slightly modified copy of [DAKAMM.MAC](DAKAMM.MAC.txt)),
assemble it, and then load the binary image at the location specified in the file.
---
DAKAM.TXT
---------
```
MAINDEC-10-DAKAM.TXT
IDENTIFICATION
--------------
PRODUCT CODE: MAINDEC-10-DAKAM-B-D
PRODUCT NAME: DECSYSTEM10 PDP-10 KA10 BASIC
INSTRUCTION DIAGNOSTIC (13)
FUNCTION: BYTE, BLT, JFFO
VERSION: 0.2
DATE RELEASED: JANUARY 1977
MAINTAINED BY: DIAGNOSTIC ENGINEERING GROUP
AUTHOR: JOHN R. KIRCHOFF
COPYRIGHT(C) 1976,1977
DIGITAL EQUIPMENT CORPORATION
MARLBORO, MASS. 01752
THIS SOFTWARE IS FURNISHED UNDER A LICENSE FOR USE ONLY
ON A SINGLE COMPUTER SYSTEM AND MAY BE COPIED ONLY WITH
THE INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE,
OR ANY OTHER COPIES THEREOF, MAY NOT BE PROVIDED OR OTHERWISE
MADE AVAILABLE TO ANY OTHER PERSON EXECPT FOR USE ON SUCH SYSTEM
AND TO ONE WHO AGREES TO THESE LICENSE TERMS. TITLE TO AND
OWNERSHIP OF THE SOFTWARE SHALL AT ALL TIMES REMAIN IN DEC.
THE INFORMATION IN THIS DOCUMENT IS SUBJECT TO CHANGE WITHOUT
NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL
EQUIPMENT CORPORATION.
DEC ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS
SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DEC.
MAINDEC-10-DAKAM.TXT
PAGE 2
TABLE OF CONTENTS
-----------------
1.0 ABSTRACT
2.0 REQUIREMENTS
2.1 EQUIPMENT
2.2 STORAGE
2.3 PRELIMINARY PROGRAMS
3.0 PROGRAM PROCEDURES
3.1 LOADING PROCEDURE
3.2 STARTING PROCEDURE
3.3 OPERATING PROCEDURE
4.0 DATA SWITCH FUNCTIONS
5.0 ERRORS
6.0 ITERATION COUNTER
7.0 CYCLE TIME
8.0 OPERATIONAL VARIATIONS
9.0 MISCELLANEOUS
10.0 LISTING
MAINDEC-10-DAKAM.TXT
PAGE 3
1.0 ABSTRACT
THIS PDP-10 KA10 BASIC INSTRUCTION DIAGNOSTIC IS THE
THIRTEENTH IN A SERIES OF PDP-10 KA10 PROCESSOR DIAGNOSTICS.
THE DIAGNOSTIC TESTS THE BYTE, BLOCK TRANSFER AND
JFFO INSTRUCTIONS.
2.0 REQUIREMENTS
2.1 EQUIPMENT
A PDP-10 KA10 EQUIPPED WITH A MINIMUM OF 32K OF MEMORY
PAPER TAPE READER
CONSOLE TELETYPE
DECTAPE
LINE PRINTER (OPTIONAL)
2.2 STORAGE
THE PROGRAM RUNS WITHIN 32K OF MEMORY.
2.3 PRELIMINARY PROGRAMS
PREVIOUS PROCESSOR DIAGNOSTICS
3.0 PROGRAM PROCEDURES
3.1 LOADING PROCEDURE
THIS DIAGNOSTIC REQUIRES THAT THE DECSYSTEM10 SUBROUTINE
PROGRAM BE RESIDENT IN THE PDP-10.
PAPER TAPE - HARDWARE READ-IN (READER DEVICE CODE 104)
DECTAPE - LOAD WITH DIAMON (DECTAPE DEVICE CODE 320)
TIME SHARING - RUN UNDER DIAMON.
MAINDEC-10-DAKAM.TXT
PAGE 4
3.2 STARTING PROCEDURE
A. SELECT OPERATIONAL CONSOLE DATA SWITCH SETTINGS (REFER TO
4.0 DATA SWITCH FUNCTIONS).
B. EXEC MODE
STAND-ALONE STARTING ADDRESS IS 30000.
C. USER MODE
RUN UNDER "DIAMON".
IN USER MODE THE FOLLOWING QUESTIONS WILL BE ASKED TO
SELECT THE OPERATIONAL SWITCHES:
TELETYPE SWITCH CONTROL ? 0,S,Y OR N (CR) -
IF THE OPERATOR TYPES "N", THE ACTUAL CONSOLE
SWITCHES ARE USED.
IF THE OPERATOR TYPES "Y", THE FOLLOWING QUESTIONS
ARE ASKED AND THE OPERATOR RESPONDS BY TYPING
THE ANSWER AS SIX OCTAL DIGITS REPRESENTING
THE DESIRED SWITCH SETTINGS.
SPECIFY LH SWITCHES IN OCTAL-
SPECIFY RH SWITCHES IN OCTAL-
IF THE OPERATOR TYPES "0", ZERO'S ARE USED FOR
THE SWITCH SETTINGS.
IF THE OPERATOR TYPES "S", PREVIOUSLY SET SWITCHES
ARE USED. THIS IS ONLY VALID UPON RESTARTING
OF AN INTERRUPTED PROGRAM.
MAINDEC-10-DAKAM.TXT
PAGE 5
3.3 OPERATING PROCEDURE
A. TO THROUGHLY TEST ALL HARDWARE, ALL TEST CONTROL DATA
SWITCHES SHOULD BE SET TO 0.
B. WHEN DEBUGGING HARDWARE, SET SWITCHES TO 0. ALLOW THE
TELETYPE TO PRINT THE ERROR MESSAGES. THIS ALLOWS THE
PROGRAM TO RUN A COMPLETE PASS AND THEN THE ERROR MESSAGES
MAY BE CORRELATED TO QUICKLY DIAGNOSE THE FAILURE. IF A
HARDWARE PROBLEM IS SUCH THAT THE ERROR MESSAGES, AFTER THE
FIRST ONE, HAVE NO MEANING (FIRST ERROR CAUSES ALL FOLLOWING
TESTS TO FAIL) SET THE LOOP ON ERROR SWITCH AND RESTART THE
TEST FROM THE BEGINNING. THE FIRST FAILURE WILL THEN CAUSE
THE PROGRAM TO ENTER A LOOP SUITABLE FOR SCOPING.
THE ERROR MESSAGE USED IN CONJUNCTION WITH THE LISTING AND
SCOPING IF NECESSARY SHOULD ALLOW THE FAILING CONPONENT
TO BE ISOLATED AND REPLACED AND/OR REPAIRED.
C. WHEN TAKING MARGINS, SET DATA SWITCHES 'NOPNT' AND 'DING'.
THIS WILL INHIBIT PRINTOUT BUT WILL ALLOW THE TELETYPE
BELL TO BE RUNG WHEN A ERROR OCCURS. IF THE MARGIN OBTAINED
IS UNACCEPTABLE, THE OPERATOR MAY REVERT TO STANDARD SWITCH
SETTINGS FOR DEBUGGING PURPOSES.
D. ERROR INFORMATION MAY BE OBTAINED QUICKLY BY PRINTING
ERRORS ON THE LINE PRINTER.
E. IN THE EVENT OF A PRINT ROUTINE FAILURE THE 'NOPNT' SWITCH
AND THE 'ERSTOP' SWITCH MAY BE SET TO INHIBIT PRINTOUT
BUT HALT THE PROGRAM POINTING TO THE ERROR.
MAINDEC-10-DAKAM.TXT
PAGE 6
4.0 DATA SWITCH FUNCTIONS
SWITCH STATE FUNCTION
------ ----- --------
0 ABORT 0 NORMAL OPERATION
1 ABORT AT END OF PASS
1 RSTART NOT USED
2 TOTALS NOT USED
3 NOPNT 0 NORMAL TYPEOUT
1 INHIBIT ALL PRINT/TYPEOUT
(EXCEPT FORCED)
4 PNTLPT 0 NORMAL OUTPUT TO TTY
1 PRINT ALL DATA ON LPT
(LOGICAL DEVICE, USER MODE)
5 DING 0 NO FUNCTION
1 RING TTY BELL ON ERROR
6 LOOPER 0 PROCEED TO NEXT TEST
1 ENTER SCOPE LOOP ON TEST ERROR
7 ERSTOP 0 NO FUNCTION
1 HALT ON TEST ERROR
8 PALERS 0 PRINT ONLY FIRST ERROR WHEN LOOPING
1 PRINT ALL ERRORS, EVEN IF SAME ERROR
9 RELIAB NOT USED
10 TXTINH 0 PRINT FULL ERROR MESSAGES.
1 INHIBIT COMMENT PORTION OF
ERROR MESSAGES.
11 INHPAG 0 ALLOW PAGING AND TRAP ENABLE
1 INHIBIT PAGING AND TRAPPING
12 MODDVC NOT USED
13 INHCSH NOT USED
MAINDEC-10-DAKAM.TXT
PAGE 7
5.0 ERRORS
ERRORS ARE PRINTED ON THE TTY OR LINE PRINTER. THE ERROR
PRINTOUT CONTAINS THE TEST TITLE, THE PC OF THE FAILURE, ERROR
NUMBER AND THE CONTENTS OF AN APPLICABLE AC.
THE PC VALUE IS USEFUL IN RELATING THE FAILURE TO THE LISTING.
THE ERROR NUMBER IS PROVIDED SUCH THAT AN ERROR DICTIONARY MAY
BE MADE AT SOME FUTURE DATE.
WHEN THE SCOPE LOOP MODE IS USED THE MI REGISTER WILL COUNT
FOR EACH OCCURANCE OF AN ERROR. IF AN AUDIO INDICATION OF
A CONTINUING ERROR IS DESIRED THE 'DING' SWITCH MAY BE SET.
6.0 ITERATION COUNTER
THE ITERATION COUNT OF THE PROGRAM IS DISPLAYED IN THE MEMORY
INDICATORS (MI). THIS COUNT IS A DECREMENTING COUNT AND
INITIALLY STARTS AT -1 IN STAND-ALONE OPERATION.
7.0 CYCLE TIME
THE CYCLE TIME OF THE PROGRAM IS IN THE MILLISECOND RANGE AND
IS THEREFORE SUITABLE FOR TAKING MARGINS, VIBRATION TESTS,
ETC.
MAINDEC-10-DAKAM.TXT
PAGE 8
8.0 OPERATIONAL VARIATIONS
A. DIAGNOSTIC MONITOR
THE PROGRAM IS USABLE WITH THE DIAGNOSTIC MONITOR TO PROVIDE
RELIABILITY TESTS, ACCEPTANCE TESTS, AND/OR TO PROVIDE A
QUICK METHOD OF ISOLATION OF A FAULT TO A PARTICULAR AREA
OF THE PROCESSOR. CERTAIN PROCEDURES ARE USED WHEN THE
PROGRAM IS USED IN THIS MANNER. THEY ARE:
1. THE DIAGNOSTIC MONITOR TRANSFERS CONTROL TO THE PROGRAM
AND STARTS IT AT LOCATION 30002.
2. MONCTL - LOCATION 30043 IS USED AS THE DIAGNOSTIC MONITOR
CONTROL WORD.
LH = 0, STAND-ALONE OPERATION
-1, RUNNING UNDER DIAGNOSTIC MONITOR
RH = RIGHT HALF OF CONSOLE SWITCHES IF UNDER
DIAGNOSTIC MONITOR CONTROL.
B. USER MODE
TO OUTPUT THE PRINTED ERROR MESSAGES TO A USER SPECIFIED
DEVICE IN USER MODE, ASSIGN THE DESIRED OUTPUT DEVICE TO
DEVICE NAME 'DEV' AND SET SWITCH 'PNTLPT'. THE PHYSICAL
DEVICE USED CAN BE ANY DEVICE THAT CAN ACCEPT ASCII OUTPUT
FORMAT SUCH AS LPT, DSK, DTA, ETC. THE CORRESPONDING
OUTPUT FILE IS 'DAKAM.TMP'
EXAMPLE DEVICE ASSIGNMENT:
.ASSIGN DSK DEV
IN USER MODE THE PROGRAM WILL MAKE 1000(8) PASSES AND THEN
RETURN TO DIAMON COMMAND MODE.
MAINDEC-10-DAKAM.TXT
PAGE 9
8.0 OPERATIONAL VARIATIONS (CON'T)
THE OUTPUT FILE (IF USED) MAY THEN BE LISTED BY USING THE
NORMAL MONITOR COMMANDS (PRINT, LIST, TYPE, PIP, ETC.).
IF THE PROGRAM IS ABORTED BEFORE COMPLETION (BY ^C, ETC.) THE
OUTPUT FILE MAY BE CLOSED BY USING THE MONITOR 'REENTER'
COMMAND.
C. SYSTEM EXERCISER
START ADDRESS IS 30003. DATA SWITCHES ARE PRESTORED IN
'SWTEXR' LOC 30023.
9.0 MISCELLANEOUS
THE NON-EX-MEMORY AND PARITY STOP SWITCHES SHOULD BE RESET
(0). THESE ERRORS, ILLEGAL UUO'S AND OTHER ERRORS OF THIS
TYPE ARE HANDLED BY PRINTOUT ON THE TELETYPE.
10.0 LISTING
```
DAKAM.HST
---------
THIS IS A HISTORY OF THE DEVELOPMENT OF MAINDEC-10-DAKAM
************************************************************************
PRODUCT CODE: MAINDEC-10-DAKAM
PRODUCT NAME: BASIC INSTRUCTION DIAGNOSTIC #13
DATE RELEASED: JANUARY 1977
VERSION: 0.2
UPDATE AUTHOR: JOHN R. KIRCHOFF
CHANGES MADE:
1. UPGRADE TO ALLOW COMPATABILITY WITH THE SUBROUTINE PACKAGE.
2. ADD TEST FOR BLT WITH EFFECTIVE ADDRESS LESS THAN CONTENTS OF
AC RIGHT.
************************************************************************
ORIGINAL VERSION: 0.1
ORIGINAL AUTHOR: RICHARD MALISKA
ORIGINAL RELEASE: 16-MAR-72
************************************************************************
DAKAM.MAC
---------
[[Download](DAKAM.MAC.txt)]
{% highlight text %}
{% include_relative DAKAM.MAC.txt %}
{% endhighlight %}

File diff suppressed because one or more lines are too long

View file

@ -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);
}

View file

@ -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,19 +972,15 @@ 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;
/*
* 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 = sExp.match(/^([^,]*),,([^,]*)$/);
if (!match) {
var result = -1;
if (nLocation === undefined) {
nLocation = (this.nLocationScope >= 0? this.nLocationScope : this.nLocation);
}
@ -1027,22 +990,22 @@ class Macro10 {
* (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.
*/
sExp = sExp.replace(/SIXBIT\s*(\S)(.*?)\1/g, "'$2'").replace(/ASCII\s*(\S)(.*?)\1/g, '"$2"');
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.
*/
result = -1;
if (fPass1 !== false) {
if (aUndefined) {
var match;
var sOperator = "";
var sOperands = sExp;
if (match = sExp.match(/^([^\s]+)\s*(.*?)\s*$/)) {
var sOperands = sEval;
if (match = sEval.match(/^([^\s]+)\s*(.*?)\s*$/)) {
sOperator = match[1];
sOperands = match[2];
}
result = this.dbg.parseInstruction(sOperator, sOperands, nLocation, fPass1, true);
result = this.dbg.parseInstruction(sOperator, sOperands, nLocation, aUndefined);
}
if (result < 0) {
@ -1054,25 +1017,13 @@ class Macro10 {
* 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.
* 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);
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 + "'");
}
}
} 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);
}
this.error("unable to parse expression '" + sExp + "'", nLine);
}
}
return result;
@ -1487,6 +1438,11 @@ class Macro10 {
/*
* This must be a REPEAT or CONDITIONAL block.
*/
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 + "'");
@ -1494,9 +1450,16 @@ class Macro10 {
}
sOperands = sOperands.substr(sOperand.length + 1);
sOperand = sOperand.trim();
match = sOperands.match(/\s*(<|)([\s\S]*)/i);
name = '?' + sOperator;
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 {
@ -1506,6 +1469,9 @@ class Macro10 {
*/
nOperand = this.parseExpression(sOperand) || 0;
}
}
match = sOperands.match(/\s*(<|)([\s\S]*)/i);
name = '?' + sOperator;
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"
};

View file

@ -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,14 +1162,18 @@ 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);
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

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