Added a few PDP-10 byte pointer fixes, along with the ability to pass MACRO-10 text directly into the MACRO-10 assembler (use the Debugger's "as" command)
This commit is contained in:
parent
38f709bb7a
commit
d28d1ee940
10 changed files with 481 additions and 375 deletions
|
|
@ -257,7 +257,16 @@ PDP10.opFSC = function(op, ac)
|
|||
*
|
||||
* From the DEC PDP-10 System Reference Manual (May 1968), p. 2-16:
|
||||
*
|
||||
* Increment the byte pointer in location E as explained [below].
|
||||
* Increment the byte pointer in location E as explained [below]. The pointer has the format:
|
||||
*
|
||||
* 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
* P P P P P P S S S S S S - I X X X X Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
|
||||
*
|
||||
* where S is the size of the byte as a number of bits, and P is its position as the number of bits
|
||||
* remaining at the right of the byte in the word (eg if P is 3 the rightmost bit of the byte is bit 32
|
||||
* of the word). The rest of the pointer is interpreted in the same way as in an instruction: I, X and Y
|
||||
* are used to calculate the address of the location that is the source or destination of the byte.
|
||||
*
|
||||
* To facilitate processing a series of bytes, several of the byte instructions increment the
|
||||
* pointer, ie, modify it so that it points to the next byte position in a set of memory locations.
|
||||
|
|
@ -277,9 +286,6 @@ PDP10.opFSC = function(op, ac)
|
|||
* a byte of size 36 - P is loaded from position P, or the right 36 - P bits of the byte are deposited
|
||||
* in position P.
|
||||
*
|
||||
* NOTE: Until I can conduct real-world testing, I'm assuming when they say "100 - S", DEC really meant "64 - S",
|
||||
* which is 100 [base 8], which makes much more sense, since P is limited to a range of 00-77 [base 8].
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} op
|
||||
* @param {number} ac
|
||||
|
|
@ -294,7 +300,7 @@ PDP10.opIBP = function(op, ac)
|
|||
if (p < 0) {
|
||||
inc++;
|
||||
p = 36 - s;
|
||||
if (p < 0) p = 64 - s; // see SPECIAL CONSIDERATIONS and NOTE above
|
||||
if (p < 0) p = 100 - s; // see SPECIAL CONSIDERATIONS and NOTE above
|
||||
}
|
||||
/*
|
||||
* Since the documentation above makes clear that the effect of the increment (of w) extends past the Y
|
||||
|
|
@ -378,7 +384,7 @@ PDP10.opLDB = function(op, ac)
|
|||
}
|
||||
var p = (this.regBP / PDP10.OPCODE.P_SCALE) & PDP10.OPCODE.P_MASK;
|
||||
var s = (this.regBP >> PDP10.OPCODE.S_SHIFT) & PDP10.OPCODE.S_MASK;
|
||||
if (p + s <= 32) {
|
||||
if (p + s < 32) {
|
||||
/*
|
||||
* WARNING: When using JavaScript's 32-bit operators with values that could set bit 31 and produce a
|
||||
* negative value, it's critical to perform a final right-shift of 0, ensuring that the final result is
|
||||
|
|
@ -389,7 +395,7 @@ PDP10.opLDB = function(op, ac)
|
|||
/*
|
||||
* Regarding the SPECIAL CONSIDERATIONS above, even if the P ("shift") and/or S ("mask") values are
|
||||
* over-large, that should be fine, because nothing but additional zero bits will be included (provided
|
||||
* our memory is working properly and didn't give us more than 36 bits).
|
||||
* our memory is working properly and didn't give us more than 36 bits of unsigned data).
|
||||
*/
|
||||
w = Math.trunc(w / Math.pow(2, p)) % Math.pow(2, s);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -910,7 +910,7 @@ class CPUStatePDP10 extends CPUPDP10 {
|
|||
* non-DEBUGGER builds.
|
||||
*/
|
||||
if (DEBUGGER && (this.opFlags & PDP10.OPFLAG.DEBUGGER)) {
|
||||
if (this.dbg.checkInstruction(this.getPC(), nDebugState)) {
|
||||
if (this.dbg.checkInstruction(this.getXC(), nDebugState)) {
|
||||
this.stopCPU();
|
||||
break;
|
||||
}
|
||||
|
|
@ -927,7 +927,7 @@ class CPUStatePDP10 extends CPUPDP10 {
|
|||
*/
|
||||
if ((this.opFlags & (PDP10.OPFLAG.IRQ_MASK | PDP10.OPFLAG.WAIT)) /* && nDebugState >= 0 */) {
|
||||
if (this.checkInterrupts()) {
|
||||
if ((this.opFlags & PDP10.OPFLAG.DEBUGGER) && this.dbg.checkInstruction(this.getPC(), nDebugState)) {
|
||||
if ((this.opFlags & PDP10.OPFLAG.DEBUGGER) && this.dbg.checkInstruction(this.getXC(), nDebugState)) {
|
||||
this.stopCPU();
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2669,8 +2669,8 @@ class DebuggerPDP10 extends Debugger {
|
|||
return true;
|
||||
}
|
||||
|
||||
var match = sOpcode.match(/^(['"]?)(.*\.klm|.*\.mac|.*\.html|.*\.txt)\1$/i);
|
||||
if (match) {
|
||||
var match = sOpcode.match(/^(['"]?)(.*?)(\.klm|\.mac|\.html|\.txt|)\1$/i);
|
||||
if (match && (match[1] || match[3])) {
|
||||
var dbg = this;
|
||||
var cpu = this.cpu;
|
||||
dbgAddr = this.parseAddr(sAddr);
|
||||
|
|
@ -2678,9 +2678,11 @@ class DebuggerPDP10 extends Debugger {
|
|||
dbg.println("assembly already in progress");
|
||||
}
|
||||
else {
|
||||
var sFile = match[2];
|
||||
var sFile = match[2] + match[3];
|
||||
if (!match[3]) sOptions += 's';
|
||||
var addrLoad = dbgAddr.addr;
|
||||
var macro10 = this.macro10 = new Macro10(sFile, addrLoad, sOptions, dbg, function doneMacro10(nErrorCode, sURL) {
|
||||
var macro10 = this.macro10 = new Macro10(dbg);
|
||||
macro10.assembleFiles(sFile, addrLoad, sOptions, function doneMacro10(nErrorCode, sURL) {
|
||||
if (!nErrorCode) {
|
||||
/*
|
||||
* NOTE: Most Debugger operations run in the context of doCommand(), which catches any exceptions;
|
||||
|
|
@ -3706,21 +3708,48 @@ class DebuggerPDP10 extends Debugger {
|
|||
}
|
||||
|
||||
/**
|
||||
* splitArgs(sCmd)
|
||||
* splitArgs(sCmd, sDelim)
|
||||
*
|
||||
* @this {DebuggerPDP10}
|
||||
* @param {string} sCmd
|
||||
* @param {string} [sDelim]
|
||||
* @return {Array.<string>}
|
||||
*/
|
||||
splitArgs(sCmd)
|
||||
splitArgs(sCmd, sDelim = " ")
|
||||
{
|
||||
var asArgs = sCmd.replace(/ +/g, ' ').split(' ');
|
||||
var asArgs = [];
|
||||
var chQuote = "";
|
||||
var i = 0, iLast = 0;
|
||||
|
||||
while (i < sCmd.length) {
|
||||
var ch = sCmd[i++];
|
||||
if (chQuote) {
|
||||
if (ch == chQuote) {
|
||||
chQuote = "";
|
||||
asArgs.push(sCmd.substr(iLast, i - iLast));
|
||||
iLast = i;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (ch == '"' || ch == "'") {
|
||||
chQuote = ch;
|
||||
continue;
|
||||
}
|
||||
if (sDelim.indexOf(ch) >= 0) {
|
||||
asArgs.push(sCmd.substr(iLast, i - iLast - 1));
|
||||
iLast = i;
|
||||
}
|
||||
}
|
||||
if (iLast < i) {
|
||||
asArgs.push(sCmd.substr(iLast, i - iLast));
|
||||
}
|
||||
|
||||
asArgs[0] = asArgs[0].toLowerCase();
|
||||
if (asArgs && asArgs.length) {
|
||||
var s0 = asArgs[0];
|
||||
var ch0 = s0.charAt(0);
|
||||
for (var i = 1; i < s0.length; i++) {
|
||||
var ch = s0.charAt(i);
|
||||
for (i = 1; i < s0.length; i++) {
|
||||
ch = s0.charAt(i);
|
||||
if (ch0 == '?' || ch0 == 'r' || ch < 'a' || ch > 'z') {
|
||||
asArgs[0] = s0.substr(i);
|
||||
asArgs.unshift(s0.substr(0, i));
|
||||
|
|
|
|||
|
|
@ -91,66 +91,87 @@ var Scope;
|
|||
/**
|
||||
* @class Macro10
|
||||
* @property {string} sURL
|
||||
* @property {number} addrLoad
|
||||
* @property {number|null} addrLoad
|
||||
* @property {string} sOptions
|
||||
* @property {DebuggerPDP10} dbg
|
||||
* @property {function(...)} done
|
||||
* @property {function(...)|undefined} done
|
||||
* @property {number} iURL
|
||||
* @property {Array.<string>} asURLs
|
||||
* @property {Array.<string>} aURLs
|
||||
* @property {Array.<number>} anLines
|
||||
* @property {Array.<string>} asLines
|
||||
* @property {number|null|undefined} addrStart
|
||||
* @unrestricted
|
||||
*/
|
||||
class Macro10 {
|
||||
/**
|
||||
* Macro10(sURL, addrLoad, sOptions, dbg, done)
|
||||
* Macro10(dbg)
|
||||
*
|
||||
* A "mini" version of DEC's MACRO-10 assembler, with just enough features to support the handful
|
||||
* of DEC diagnostic source code files that we choose to throw at it.
|
||||
*
|
||||
* Requests the resource(s) specified by sURL; multiple resources can be requested by separating
|
||||
* them with semicolons.
|
||||
*
|
||||
* The done() callback is called after the resource(s) have been loaded and parsed. The caller
|
||||
* must use other methods to obtain further results (eg, getImage(), getStart()).
|
||||
*
|
||||
* The callback includes a non-zero error code if there was an error (and the URL):
|
||||
*
|
||||
* done(nErrorCode, sURL)
|
||||
*
|
||||
* We rely on the calling component (dbg) to provide a variety of helper services (eg, println(),
|
||||
* parseExpression(), etc). This is NOT a subclass of Component, so Component services are not part
|
||||
* of this class.
|
||||
*
|
||||
* @this {Macro10}
|
||||
* @param {DebuggerPDP10} dbg (used to provide helper services to the Macro10 class)
|
||||
*/
|
||||
constructor(dbg)
|
||||
{
|
||||
this.dbg = dbg;
|
||||
}
|
||||
|
||||
/**
|
||||
* init(addrLoad, sOptions, done)
|
||||
*
|
||||
* Initializes all instance properties. Called by assembleFiles() and assembleString().
|
||||
*
|
||||
* 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
|
||||
* 's': treat the URL as a string and assemble it (assembleFiles() only)
|
||||
*
|
||||
* The done() callback is called after the resource(s) have been loaded (if necessary), parsed, and
|
||||
* assembled. The caller must use other methods to obtain further results (eg, getImage(), getStart()).
|
||||
*
|
||||
* The callback includes a non-zero error code if there was an error (and the URL):
|
||||
*
|
||||
* done(nErrorCode, sURL)
|
||||
*
|
||||
* @this {Macro10}
|
||||
* @param {string} sURL (the URL(s) of the resource to be assembled)
|
||||
* @param {number|null} addrLoad (the absolute address to assemble the code at, if any)
|
||||
* @param {string|null} sOptions (zero or more letter codes to control the assembly process)
|
||||
* @param {DebuggerPDP10} dbg (used to provide helper services to the Macro10 class)
|
||||
* @param {function(...)} done
|
||||
* @param {number|null} [addrLoad] (the absolute address to assemble the code at, if any)
|
||||
* @param {string|undefined} [sOptions] (zero or more letter codes to control the assembly process)
|
||||
* @param {function(...)|undefined} [done]
|
||||
*/
|
||||
constructor(sURL, addrLoad, sOptions, dbg, done)
|
||||
init(addrLoad, sOptions, done)
|
||||
{
|
||||
this.sURL = sURL;
|
||||
this.addrLoad = addrLoad;
|
||||
this.sOptions = sOptions || "";
|
||||
this.dbg = dbg;
|
||||
this.sOptions = (sOptions || "").toLowerCase();
|
||||
this.done = done;
|
||||
|
||||
/*
|
||||
* The next set of properties may be updated by the assembly process and queried by the caller.
|
||||
this.iURL = 0;
|
||||
this.aURLs = [];
|
||||
|
||||
/**
|
||||
* Number of lines per file.
|
||||
*
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.addrStart = null;
|
||||
this.anLines = [];
|
||||
|
||||
/**
|
||||
* Lines from all the resources.
|
||||
*
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
this.asLines = [];
|
||||
|
||||
if (MAXDEBUG) this.println("starting PCjs MACRO-10 Mini-Assembler...");
|
||||
|
||||
/*
|
||||
* Initialize all the tables that MACRO-10 uses.
|
||||
* Initialize all the tables and other data structures that MACRO-10 uses.
|
||||
*
|
||||
* The Macros (tblMacros) and Symbols (tblSymbols) tables are fairly straightforward: they are
|
||||
* indexed by a macro or symbol name, and each element is a Mac or Sym object, respectively.
|
||||
|
|
@ -266,18 +287,65 @@ class Macro10 {
|
|||
*/
|
||||
this.sASCII = "";
|
||||
|
||||
/**
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
this.asLines = [];
|
||||
this.addrStart = null;
|
||||
}
|
||||
|
||||
this.iURL = 0;
|
||||
this.asURLs = sURL.split(';');
|
||||
this.anLines = []; // keeps track of the number of lines per file
|
||||
/**
|
||||
* assembleFiles(sURL, addrLoad, sOptions, done)
|
||||
*
|
||||
* Requests the resource(s) specified by sURL; multiple resources can be requested by separating
|
||||
* them with semicolons. The resources are requested and combined in the same order they are listed,
|
||||
* and after the last resource has been received, they are assembled as a single unit.
|
||||
*
|
||||
* As a courtesy to the Debugger's doAssemble() function, we allow it to select between assembleFiles()
|
||||
* and assembleString() by specifying an 's' option here, rather than having two separate code paths.
|
||||
*
|
||||
* @this {Macro10}
|
||||
* @param {string} sURL (the URL(s) of the resource to be assembled)
|
||||
* @param {number|null} [addrLoad] (the absolute address to assemble the code at, if any)
|
||||
* @param {string|undefined} [sOptions] (zero or more letter codes to control the assembly process)
|
||||
* @param {function(...)|undefined} [done]
|
||||
*/
|
||||
assembleFiles(sURL, addrLoad, sOptions, done)
|
||||
{
|
||||
if (sOptions && sOptions.indexOf('s') >= 0) {
|
||||
this.assembleString(sURL, addrLoad, sOptions, done);
|
||||
return;
|
||||
}
|
||||
|
||||
this.init(addrLoad, sOptions, done);
|
||||
|
||||
this.aURLs = sURL.split(';');
|
||||
|
||||
this.loadNextResource();
|
||||
}
|
||||
|
||||
/**
|
||||
* assembleString(sText, addrLoad, sOptions, done)
|
||||
*
|
||||
* Assembles the given text.
|
||||
*
|
||||
* @this {Macro10}
|
||||
* @param {string} sText
|
||||
* @param {number|null} [addrLoad] (the absolute address to assemble the code at, if any)
|
||||
* @param {string|undefined} [sOptions] (zero or more letter codes to control the assembly process)
|
||||
* @param {function(...)|undefined} [done]
|
||||
* @return {number}
|
||||
*/
|
||||
assembleString(sText, addrLoad, sOptions, done)
|
||||
{
|
||||
this.init(addrLoad, sOptions, done);
|
||||
|
||||
this.asLines = sText.split(/(\r?\n)/);
|
||||
this.anLines.push(this.asLines.length);
|
||||
|
||||
this.parseResources();
|
||||
|
||||
if (this.done) this.done(this.nError);
|
||||
|
||||
return this.nError;
|
||||
}
|
||||
|
||||
/**
|
||||
* loadNextResource()
|
||||
*
|
||||
|
|
@ -285,13 +353,14 @@ class Macro10 {
|
|||
*/
|
||||
loadNextResource()
|
||||
{
|
||||
if (this.iURL == this.asURLs.length) {
|
||||
this.done(this.parseResources());
|
||||
if (this.iURL == this.aURLs.length) {
|
||||
this.parseResources();
|
||||
if (this.done) this.done(this.nError);
|
||||
return;
|
||||
}
|
||||
|
||||
var macro10 = this;
|
||||
var sURL = this.asURLs[this.iURL];
|
||||
var sURL = this.aURLs[this.iURL];
|
||||
|
||||
this.println("loading " + Str.getBaseName(sURL));
|
||||
|
||||
|
|
@ -305,7 +374,7 @@ class Macro10 {
|
|||
|
||||
Web.getResource(sURL, null, true, function processMacro10(sFile, sResource, nErrorCode) {
|
||||
if (nErrorCode) {
|
||||
macro10.done(nErrorCode, sFile);
|
||||
if (macro10.done) macro10.done(nErrorCode, sFile);
|
||||
return;
|
||||
}
|
||||
var sText = sResource;
|
||||
|
|
@ -1100,13 +1169,13 @@ class Macro10 {
|
|||
var iURL = 0;
|
||||
while (nLine > this.anLines[iURL]) {
|
||||
nLine -= this.anLines[iURL];
|
||||
if (iURL < this.asURLs.length - 1) {
|
||||
if (iURL < this.aURLs.length - 1) {
|
||||
iURL++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Str.getBaseName(this.asURLs[iURL] + " line " + nLine);
|
||||
return Str.getBaseName(this.aURLs[iURL] + " line " + nLine);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue