Merge branch 'master' into gh-pages

This commit is contained in:
Jeff Parsons 2017-01-28 23:03:45 -08:00
commit 0c08b72d99
57 changed files with 2704 additions and 2829 deletions

View file

@ -534,7 +534,7 @@ MarkOut.prototype.convertMD = function(sIndent)
* command lines, followed by the second command name, followed by one or more command lines, and so on.
*/
for (var iCommand = 1; iCommand < aCommands.length; iCommand += 2) {
this.aCommandDefs[aCommands[iCommand]] = aCommands[iCommand+1].trim().replace(/\n[ \t]*/g, "").replace(/"/g, "&quot;");
this.aCommandDefs[aCommands[iCommand]] = aCommands[iCommand+1].replace(/\n[ \t]*/g, " ").trim().replace(/"/g, "&quot;");
}
}

View file

@ -72,7 +72,7 @@ class Bus8080 extends Component {
*/
constructor(parmsBus, cpu, dbg)
{
super("Bus", parmsBus, Bus8080);
super("Bus", parmsBus);
this.cpu = cpu;
this.dbg = dbg;

View file

@ -60,7 +60,7 @@ class ChipSet8080 extends Component {
*/
constructor(parmsChipSet)
{
super("ChipSet", parmsChipSet, ChipSet8080, Messages8080.CHIPSET);
super("ChipSet", parmsChipSet, Messages8080.CHIPSET);
var model = parmsChipSet['model'];

View file

@ -112,7 +112,7 @@ class Computer8080 extends Component {
*/
constructor(parmsComputer, parmsMachine, fSuspended)
{
super("Computer", parmsComputer, Computer8080, Messages8080.COMPUTER);
super("Computer", parmsComputer, Messages8080.COMPUTER);
this.flags.powered = false;

View file

@ -76,7 +76,7 @@ class CPU8080 extends Component {
*/
constructor(parmsCPU, nCyclesDefault)
{
super("CPU", parmsCPU, CPU8080, Messages8080.CPU);
super("CPU", parmsCPU, Messages8080.CPU);
var nCycles = parmsCPU['cycles'] || nCyclesDefault;

View file

@ -59,7 +59,7 @@ class Keyboard8080 extends Component {
*/
constructor(parmsKbd)
{
super("Keyboard", parmsKbd, Keyboard8080, Messages8080.KEYBOARD);
super("Keyboard", parmsKbd, Messages8080.KEYBOARD);
var model = parmsKbd['model'];

View file

@ -55,7 +55,7 @@ class Panel8080 extends Component {
*/
constructor(parmsPanel)
{
super("Panel", parmsPanel, Panel8080);
super("Panel", parmsPanel);
}
/**

View file

@ -66,7 +66,7 @@ class RAM8080 extends Component {
*/
constructor(parmsRAM)
{
super("RAM", parmsRAM, RAM8080);
super("RAM", parmsRAM);
this.abInit = null;
this.aSymbols = null;

View file

@ -66,7 +66,7 @@ class ROM8080 extends Component {
*/
constructor(parmsROM)
{
super("ROM", parmsROM, ROM8080);
super("ROM", parmsROM);
this.abROM = null;
this.addrROM = parmsROM['addr'];

View file

@ -69,7 +69,7 @@ class SerialPort8080 extends Component {
*/
constructor(parmsSerial)
{
super("SerialPort", parmsSerial, SerialPort8080, Messages8080.SERIAL);
super("SerialPort", parmsSerial, Messages8080.SERIAL);
this.iAdapter = +parmsSerial['adapter'];
@ -397,7 +397,7 @@ class SerialPort8080 extends Component {
if (this.sendData) {
this.fNullModem = fNullModem;
this.updateStatus = exports['receiveStatus'];
this.status(this.idMachine + '.' + sSourceID + " connected to " + sTargetID);
this.status("Connected " + this.idMachine + '.' + sSourceID + " to " + sTargetID);
return;
}
}

View file

@ -101,7 +101,7 @@ class Video8080 extends Component {
*/
constructor(parmsVideo, canvas, context, textarea, container)
{
super("Video", parmsVideo, Video8080, Messages8080.VIDEO);
super("Video", parmsVideo, Messages8080.VIDEO);
var video = this;
this.fGecko = Web.isUserAgent("Gecko/");
@ -355,6 +355,48 @@ class Video8080 extends Component {
return true;
}
/**
* setBinding(sHTMLType, sBinding, control, sValue)
*
* @this {Video8080}
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "refresh")
* @param {Object} control is the HTML control DOM object (eg, HTMLButtonElement)
* @param {string} [sValue] optional data value
* @return {boolean} true if binding was successful, false if unrecognized binding request
*/
setBinding(sHTMLType, sBinding, control, sValue)
{
var video = this;
/*
* TODO: A more general-purpose binding mechanism would be nice someday....
*/
if (sHTMLType == "led" || sHTMLType == "rled") {
this.ledBindings[sBinding] = control;
return true;
}
switch (sBinding) {
case "fullScreen":
this.bindings[sBinding] = control;
if (this.container && this.container.doFullScreen) {
control.onclick = function onClickFullScreen() {
if (DEBUG) video.printMessage("fullScreen()");
video.doFullScreen();
};
} else {
if (DEBUG) this.log("FullScreen API not available");
control.parentNode.removeChild(/** @type {Node} */ (control));
}
return true;
default:
break;
}
return false;
}
/**
* initBus(cmp, bus, cpu, dbg)
*
@ -581,6 +623,11 @@ class Video8080 extends Component {
*/
powerUp(data, fRepower)
{
if (!fRepower) {
if (data) {
if (!this.restore(data)) return false;
}
}
/*
* Because the VT100 frame buffer can be located anywhere in RAM (above 0x2000), we must defer this
* test code until the powerUp() notification handler is called, when all RAM has (hopefully) been allocated.
@ -653,45 +700,45 @@ class Video8080 extends Component {
}
/**
* setBinding(sHTMLType, sBinding, control, sValue)
* powerDown(fSave, fShutdown)
*
* @this {Video8080}
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "refresh")
* @param {Object} control is the HTML control DOM object (eg, HTMLButtonElement)
* @param {string} [sValue] optional data value
* @return {boolean} true if binding was successful, false if unrecognized binding request
* @param {boolean} [fSave]
* @param {boolean} [fShutdown]
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
setBinding(sHTMLType, sBinding, control, sValue)
powerDown(fSave, fShutdown)
{
var video = this;
return !fSave || this.save();
}
/*
* TODO: A more general-purpose binding mechanism would be nice someday....
*/
if (sHTMLType == "led" || sHTMLType == "rled") {
this.ledBindings[sBinding] = control;
return true;
}
/**
* save()
*
* This implements save support for the Video8080 component.
*
* @this {Video8080}
* @return {Object|null}
*/
save()
{
var state = new State(this);
state.set(0, []);
return state.data();
}
switch (sBinding) {
case "fullScreen":
this.bindings[sBinding] = control;
if (this.container && this.container.doFullScreen) {
control.onclick = function onClickFullScreen() {
if (DEBUG) video.printMessage("fullScreen()");
video.doFullScreen();
};
} else {
if (DEBUG) this.log("FullScreen API not available");
control.parentNode.removeChild(/** @type {Node} */ (control));
}
return true;
default:
break;
}
return false;
/**
* restore(data)
*
* This implements restore support for the Video8080 component.
*
* @this {Video8080}
* @param {Object} data
* @return {boolean} true if restore successful, false if not
*/
restore(data)
{
return true;
}
/**

View file

@ -684,7 +684,7 @@ FDC.prototype.initBus = function(cmp, bus, cpu, dbg)
FDC.prototype.powerUp = function(data, fRepower)
{
if (!fRepower) {
if (!data || !this.restore) {
if (!data) {
this.reset();
if (this.cmp.fReload) {
/*
@ -798,7 +798,7 @@ FDC.prototype.initController = function(data)
var i = 0, iDrive;
var fSuccess = true;
if (data === undefined) {
if (!data) {
data = [0, 0, FDC.REG_STATUS.RQM, new Array(9), 0, 0, 0, []];
}
@ -1140,6 +1140,7 @@ FDC.prototype.saveDrives = function()
* saveDrive(drive)
*
* @this {FDC}
* @param {Object} drive
* @return {Array}
*/
FDC.prototype.saveDrive = function(drive)

View file

@ -668,7 +668,7 @@ HDC.prototype.initBus = function(cmp, bus, cpu, dbg)
HDC.prototype.powerUp = function(data, fRepower)
{
if (!fRepower) {
if (!data || !this.restore) {
if (!data) {
this.initController();
if (this.cmp.fReload) {
/*

View file

@ -507,7 +507,7 @@ SerialPort.prototype.initConnection = function(fNullModem)
if (this.sendData) {
this.fNullModem = fNullModem;
this.updateStatus = exports['receiveStatus'];
this.status(this.idMachine + '.' + sSourceID + " connected to " + sTargetID);
this.status("Connected " + this.idMachine + '.' + sSourceID + " to " + sTargetID);
return;
}
}

View file

@ -83,7 +83,7 @@ var aComponents = [];
var asComponentsIgnore = ["embed", "save"];
/*
* A few of the components are subclasses of other classes (eg, "cpustate" is a subclass of "cpu").
* A few of the components are subclasses of other classes (eg, "cpustate" subclasses "cpu").
* In those situations, we "hoist" the subclass constructor into the corresponding superclass,
* because it is the name of the superclass that we rely on during machine initialization.
*/
@ -91,7 +91,7 @@ var aSubClasses = {
"pdp11/lib/cpustate": "pdp11/lib/cpu",
"pdp11/lib/debugger": "shared/es6/debugger"
};
/**
* loadComponents(asFiles)
*

View file

@ -5,9 +5,9 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
* It has been adapted from the JavaScript PDP 11/70 Emulator written by Paul Nankervis
* (paulnank@hotmail.com) at <http://skn.noip.me/pdp11/pdp11.html>. This code may be used
* freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
@ -54,7 +54,7 @@ if (NODE) {
* count: BitField,
* btmod: BitField,
* type: BitField
* }} BlockInfoPDP11
* }}
*/
var BlockInfoPDP11 = Usr.defineBitFields({num:20, count:8, btmod:1, type:3});
@ -69,7 +69,7 @@ var BlockInfoPDP11 = Usr.defineBitFields({num:20, count:8, btmod:1, type:3});
* cbTotal: number,
* cBlocks: number,
* aBlocks: Array.<BlockInfoPDP11>
* }} BusInfoPDP11
* }}
*/
var BusInfoPDP11;
@ -95,7 +95,7 @@ class BusPDP11 extends Component {
*/
constructor(parmsBus, cpu, dbg)
{
super("Bus", parmsBus, BusPDP11, MessagesPDP11.BUS);
super("Bus", parmsBus, MessagesPDP11.BUS);
this.cpu = cpu;
this.dbg = dbg;
@ -456,7 +456,7 @@ class BusPDP11 extends Component {
}
if (sizeLeft <= 0) {
this.status((size >> 10) + "Kb " + MemoryPDP11.TYPE_NAMES[type] + " at " + Str.toOct(addr));
this.status("Added " + (size >> 10) + "Kb " + MemoryPDP11.TYPE_NAMES[type] + " at " + Str.toOct(addr));
return true;
}

View file

@ -5,10 +5,6 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
@ -108,7 +104,7 @@ class ComputerPDP11 extends Component {
*/
constructor(parmsComputer, parmsMachine, fSuspended)
{
super("Computer", parmsComputer, ComputerPDP11, MessagesPDP11.COMPUTER);
super("Computer", parmsComputer, MessagesPDP11.COMPUTER);
this.flags.powered = false;
@ -186,7 +182,7 @@ class ComputerPDP11 extends Component {
this.println(PDP11.APPNAME + " v" + PDP11.APPVERSION + "\n" + COPYRIGHT + "\n" + LICENSE);
this.println("Portions adapted from the PDP-11/70 Emulator v1.4 by Paul Nankervis <paulnank@hotmail.com>");
this.println("Portions adapted from the PDP-11/70 Emulator by Paul Nankervis <http://skn.noip.me/pdp11/pdp11.html>");
if (DEBUG && this.messageEnabled()) this.printMessage("TYPEDARRAYS: " + TYPEDARRAYS);
@ -731,7 +727,7 @@ class ComputerPDP11 extends Component {
* TODO: Do we not care about the return value here? (ie, is checking fRestoreError sufficient)?
*/
this.powerRestore(this.cpu, stateComputer, fRepower, fRestore);
this.updateDisplays();
this.updateDisplays(-2);
this.cpu.autoStart();
}
@ -853,7 +849,10 @@ class ComputerPDP11 extends Component {
* after they're no longer ready.
*/
if (this.cpu && this.cpu.powerDown) {
if (fShutdown) this.cpu.stopCPU();
if (fShutdown) {
if (fSave) this.cpu.flags.autoStart = this.cpu.flags.running;
this.cpu.stopCPU();
}
data = this.cpu.powerDown(fSave, fShutdown);
if (typeof data === "object") stateComputer.set(this.cpu.id, data);
if (fShutdown) {

View file

@ -5,10 +5,6 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
@ -97,7 +93,7 @@ class CPUPDP11 extends Component {
*/
constructor(parmsCPU, nCyclesDefault)
{
super("CPU", parmsCPU, CPUPDP11, MessagesPDP11.CPU);
super("CPU", parmsCPU, MessagesPDP11.CPU);
var nCycles = +parmsCPU['cycles'] || nCyclesDefault;
@ -120,8 +116,7 @@ class CPUPDP11 extends Component {
/*
* We add a number of flags to the set initialized by Component
*/
this.flags.running = false;
this.flags.starting = false;
this.flags.running = this.flags.starting = false;
this.flags.autoStart = parmsCPU['autoStart'];
if (typeof this.flags.autoStart == "string") this.flags.autoStart = (this.flags.autoStart == "true");
@ -267,7 +262,7 @@ class CPUPDP11 extends Component {
* However, we don't actually need to check all that; it's always safe use println(), regardless whether
* a Control Panel with a "print" control is present or not.
*/
this.println("No debugger detected");
this.status("No debugger detected");
}
if (!this.flags.autoStart) {
this.println("CPU will not be auto-started " + (this.panel? "(click Run to start)" : "(type 'go' to start)"));
@ -996,6 +991,37 @@ class CPUPDP11 extends Component {
return nCycles;
}
/**
* saveTimers()
*
* @this {CPUPDP11}
* @return {Array.<number>}
*/
saveTimers()
{
var aTimerCycles = [];
for (var i = 0; i < this.aTimers.length; i++) {
var timer = this.aTimers[i];
aTimerCycles.push(timer[0]);
}
return aTimerCycles;
}
/**
* restoreTimers(aTimerCycles)
*
* @this {CPUPDP11}
* @param {Array.<number>} aTimerCycles
*/
restoreTimers(aTimerCycles)
{
this.assert(aTimerCycles.length === this.aTimers.length);
for (var i = 0; i < this.aTimers.length && i < aTimerCycles.length; i++) {
var timer = this.aTimers[i];
timer[0] = aTimerCycles[i];
}
}
/**
* updateTimers(nCycles)
*

View file

@ -5,9 +5,9 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
* It has been adapted from the JavaScript PDP 11/70 Emulator written by Paul Nankervis
* (paulnank@hotmail.com) at <http://skn.noip.me/pdp11/pdp11.html>. This code may be used
* freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
@ -490,6 +490,33 @@ PDP11.opADCB = function(opCode)
/**
* opADD(opCode)
*
* From the PDP-11/20 Processor HandBook (1971), p. 61:
*
* Add src,dst (06SSDD)
*
* Operation:
* (dst) = (src) + (dst)
*
* Condition Codes:
* N: set if result < 0; cleared otherwise
* Z: set if result = 0; cleared otherwise
* V: set if there was arithmetic overflow as a result of the operation, that is both operands
* were of the same sign and the result was of the opposite sign; cleared otherwise
* C: set if there was a carry from the most significant bit of the result; cleared otherwise
*
* Description:
* Adds the source operand to the destination operand and stores the result at the destination address.
* The original contents of the destination are lost. The contents of the source are not affected.
* Two's complement addition is performed.
*
* Examples:
* Add to register: ADD 20,R0
* Add to memory: ADD R1,XXX
* Add register to register: ADD R1,R2
* Add memory to memory: ADD @#17750,XXX
*
* XXX is a programmer-defined mnemonic for a memory location.
*
* @this {CPUStatePDP11}
* @param {number} opCode
*/
@ -1101,7 +1128,7 @@ PDP11.opDIV = function(opCode)
this.regsGen[reg] = this.regsGen[reg | 1] = 1; // etc
}
}
this.nStepCycles -= (52 + 1); // 52 is the mean of the shortest and longest times
this.nStepCycles -= (52 + 1); // 52 is the average of the shortest and longest times
}
};
@ -1325,6 +1352,7 @@ PDP11.opMFPI = function(opCode)
*/
PDP11.opMFPS = function(opCode)
{
//noinspection JSUnresolvedFunction
PDP11.opUndefined.call(this, opCode);
};
@ -1347,6 +1375,7 @@ PDP11.opMFPS = function(opCode)
*/
PDP11.opMFPT = function(opCode)
{
//noinspection JSUnresolvedFunction
PDP11.opUndefined.call(this, opCode);
};
@ -1442,6 +1471,7 @@ PDP11.opMTPI = function(opCode)
*/
PDP11.opMTPS = function(opCode)
{
//noinspection JSUnresolvedFunction
PDP11.opUndefined.call(this, opCode);
};
@ -1539,8 +1569,20 @@ PDP11.opRESET = function(opCode)
* that the last CNSW.writeWord(177570) was for 000101, not 166667. So I'm guessing that the RESET
* instruction is supposed to propagate R0 to the console's DISPLAY register.
*
* This is similar to what we do for the HALT instruction on the PDP-11/20. None of these Console features
* seem to be very well documented (assuming they exist).
* This is similar to what we do for the HALT instruction (but only if this.model == PDP11.MODEL_1120).
* These Console features do not seem to be very well documented, assuming they exist.
*
* UPDATE: This behavior appears to be confirmed by remarks in the PDP-11/20 Processor Handbook (1971),
* p. 141:
*
* HALT - displays processor register R0 when bus control is transferred to console during a HALT
* instruction.
*
* RESET - displays register R0 for during [duration?] of RESET (70 msec).
*
* I haven't found similar remarks in the PDP-11/70 Processor Handbooks, so I'm not sure if that's an
* oversight or if 11/70 panels are slightly different in this regard. It's also not clear what they meant
* by "for duration of RESET". Is something supposed to happen to the DATA lights after the RESET is done?
*/
this.panel.setData(this.regsGen[0], true);
}
@ -1786,6 +1828,35 @@ PDP11.opSPL = function(opCode)
/**
* opSUB(opCode)
*
* From the PDP-11/20 Processor HandBook (1971), p. 62:
*
* Subtract src,dst (16SSDD)
*
* Operation:
* (dst) = (dst) - (src) [in detail, (dst) + ~(src) + 1 (dst)]
*
* Condition Codes:
* N: set if result < 0; cleared otherwise
* Z: set if result = 0; cleared otherwise
* V: set if there was arithmetic overflow as a result of the operation, that is if operands were of
* opposite signs and the sign of the source was the same as the sign of the result; cleared otherwise
* C: cleared if there was a carry from the most significant bit of the result; set otherwise
*
* Description:
* Subtracts the source operand from the destination operand and leaves the result at the destination address.
* The orignial [sic] contents of the destination are lost. The contents of the source are not affected.
* In double-precision arithmetic the C-bit, when set, indicates a "borrow".
*
* Example:
* SUB R1,R2
*
* BEFORE AFTER
* (R1) = 011111 (R2) = 012345
* (R1) = 011111 (R2) = 001234
*
* NZVC NZVC
* 1111 0001
*
* @this {CPUStatePDP11}
* @param {number} opCode
*/

View file

@ -5,9 +5,9 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
* It has been adapted from the JavaScript PDP 11/70 Emulator written by Paul Nankervis
* (paulnank@hotmail.com) at <http://skn.noip.me/pdp11/pdp11.html>. This code may be used
* freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
@ -74,7 +74,7 @@ if (NODE) {
* priority: number,
* message: number,
* next: (IRQ|null)
* }} IRQ
* }}
*/
var IRQ;
@ -117,25 +117,22 @@ class CPUStatePDP11 extends CPUPDP11 {
* These properties will be initialized by initCPU()
*/
this.flagC = this.flagV = this.flagZ = this.flagN = 0;
this.regPSW = 0;
this.regsGen = this.regsAlt = this.regsAltStack = [];
this.pswMode = 0;
this.mapMMR3 = [];
this.mmuPDR = this.mmuPAR = this.regsUniMap = this.regsControl = [];
this.regMBR = 0;
this.opFlags = 0;
this.srcMode = this.srcReg = 0;
this.dstMode = this.dstReg = this.dstAddr = 0;
this.regPSW = this.pswMode = 0;
this.pswTrap = 0;
this.regsGen = this.regsAlt = this.regsAltStack = [];
this.regsPAR = this.regsPDR = this.regsUniMap = this.regsControl = [];
this.opFlags = 0;
/*
* These properties will be initialized by initMMU()
*/
this.regMMR0 = this.regMMR1 = this.regMMR2 = this.regMMR3 = 0;
this.regErr = this.regPIR = this.regSLR = 0;
this.regErr = this.regMBR = this.regPIR = this.regSLR = 0;
this.mmuEnable = this.mmuLastMode = this.mmuLastPage = this.mmuMask = 0;
this.addrLast = this.opLast = this.addrInvalid = 0;
this.mapMMR3 = [4,2,0,1]; // map from mode to MMR3 I/D bit
/*
* Initialize processor operation to match the requested model.
*
@ -167,6 +164,7 @@ class CPUStatePDP11 extends CPUPDP11 {
}
this.nDisableTraps = 0;
this.trapVector = this.trapReason = 0;
/** @type {IRQ|null} */
this.irqNext = null; // the head of the active IRQ list, in priority order
@ -174,8 +172,6 @@ class CPUStatePDP11 extends CPUPDP11 {
/** @type {Array.<IRQ>} */
this.aIRQs = []; // list of all IRQs, active or not (to be used for auto-configuration)
this.flags.complete = false;
this.getByte = this.getByteDirect = this.getByteChecked;
this.getWord = this.getWordDirect = this.getWordChecked;
this.setByte = this.setByteDirect = this.setByteChecked;
@ -187,7 +183,10 @@ class CPUStatePDP11 extends CPUPDP11 {
this.readWord = this.readWordFromVirtual;
this.writeWord = this.writeWordToVirtual;
this.trapVector = this.trapReason = 0;
this.srcMode = this.srcReg = 0;
this.dstMode = this.dstReg = this.dstAddr = 0;
this.flags.complete = false;
}
/**
@ -271,36 +270,37 @@ class CPUStatePDP11 extends CPUPDP11 {
this.flagZ = f; // PSW Z bit (TODO: Why do we clear instead of set Z, like other flags?)
this.flagN = 0x8000; // PSW N bit
this.regPSW = 0x000f; // PSW other bits (TODO: What's the point of setting the flag bits here, too?)
this.regsGen = [ // General R0-R7
0, 0, 0, 0, 0, 0, 0, this.addrReset, -1, -2, -3, -4, -5, -6, -7, -8
];
this.regsAlt = [ // Alternate R0-R5
0, 0, 0, 0, 0, 0
];
this.regsAltStack = [ // Alternate R6 stack pointers (KERNEL, SUPER, UNUSED, USER)
0, 0, 0, 0
];
this.pswMode = 0; // current memory management mode (see PDP11.MODE.KERNEL | SUPER | UNUSED | USER)
this.mapMMR3 = [4,2,0,1]; // map from mode to MMR3 I/D bit
this.mmuPDR = [ // memory management PDR registers by mode
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // KERNEL (8 KIPDR regs followed by 8 KDPDR regs)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // SUPER (8 SIPDR regs followed by 8 SDPDR regs)
[f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f], // mode 2 (not used)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // USER (8 UIPDR regs followed by 8 UDPDR regs)
];
this.mmuPAR = [ // memory management PAR registers by mode
this.regsPAR = [ // memory management PAR registers by mode
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // KERNEL (8 KIPAR regs followed by 8 KDPAR regs)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // SUPER (8 SIPDR regs followed by 8 SDPDR regs)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // mode 2 (not used)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // USER (8 UIPDR regs followed by 8 UDPDR regs)
];
this.regsPDR = [ // memory management PDR registers by mode
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // KERNEL (8 KIPDR regs followed by 8 KDPDR regs)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // SUPER (8 SIPDR regs followed by 8 SDPDR regs)
[f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f], // mode 2 (not used)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // USER (8 UIPDR regs followed by 8 UDPDR regs)
];
this.regsUniMap = [ // 32 UNIBUS map registers
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
this.regsControl = [ // various control registers (177740-177756) we don't really care about
0, 0, 0, 0, 0, 0, 0, 0
];
this.pswMode = 0; // current memory management mode (see PDP11.MODE.KERNEL | SUPER | UNUSED | USER)
this.pswTrap = -1;
this.regMBR = 0;
/*
@ -317,8 +317,6 @@ class CPUStatePDP11 extends CPUPDP11 {
this.srcMode = this.srcReg = 0;
this.dstMode = this.dstReg = this.dstAddr = 0;
this.pswTrap = -1;
this.initMMU();
}
@ -358,7 +356,7 @@ class CPUStatePDP11 extends CPUPDP11 {
/*
* This stores the PC in the lower 16 bits, and any auto-incs or auto-decs from the last opcode in the
* upper 16 bits; the lower 16 bits are used to update MMR2, and the upper 16 bits are used to update MMR1.
* The upper bits are automatically zeroed at the start of every operation when PC is copied to opLast.
* The upper bits are automatically zeroed at the start of every operation when the PC is copied to opLast.
*/
this.opLast = 0;
@ -579,10 +577,11 @@ class CPUStatePDP11 extends CPUPDP11 {
{
this.addrReset = addr;
this.resetCPU();
this.setPC(addr);
this.setPSW(0);
this.resetCPU();
if (fStart) {
/*
* TODO: Review. I'm zeroing R2-R5 in the fStart case (ie, for boot code) simply because that's what I
@ -591,20 +590,28 @@ class CPUStatePDP11 extends CPUPDP11 {
for (var i = 2; i <= 5; i++) {
this.regsGen[i] = 0;
}
if (!this.isRunning()) this.startCPU();
if (!this.flags.powered) {
this.flags.autoStart = true;
}
else if (!this.flags.running) {
this.startCPU();
}
}
else {
if (this.dbg) {
if (this.dbg && this.flags.powered) {
/*
* TODO: Review the decision to always stop the CPU if the Debugger is loaded. Note that
* when stopCPU() stops a running CPU, the Debugger gets notified, so again, no need to notify it here.
* when stopCPU() stops a running CPU, the Debugger gets notified, so no need to notify it again.
*
* TODO: There are more serious problems to deal with if another component is slamming a new PC down
* the CPU's throat (presumably while also dropping some new code into RAM) while the CPU was still running;
* we should probably force a complete reset if the CPU is running, but for now, it's up to the user
* to hit the reset button themselves.
* the CPU's throat (presumably while also dropping some new code into RAM) while the CPU is running;
* we should probably force a complete reset, but for now, it's up to the user to hit the reset button
* themselves.
*/
if (!this.stopCPU()) this.dbg.updateStatus();
if (!this.stopCPU()) {
this.dbg.updateStatus();
this.cmp.updateDisplays(-1);
}
}
else if (fStart === false) {
this.stopCPU();
@ -639,30 +646,27 @@ class CPUStatePDP11 extends CPUPDP11 {
this.regsGen,
this.regsAlt,
this.regsAltStack,
this.regsPAR,
this.regsPDR,
this.regsUniMap,
this.regsControl,
this.regErr,
this.regMBR,
this.regPIR,
this.regSLR,
this.pswTrap,
this.pswMode,
this.opFlags,
this.regMMR0,
this.regMMR1,
this.regMMR2,
this.regMMR3,
this.mmuLastMode,
this.mmuLastPage,
this.mmuPDR,
this.mmuPAR,
this.mmuEnable,
this.mmuMask,
this.addrLast,
this.opLast
this.opFlags,
this.opLast,
this.pswTrap,
this.trapReason,
this.trapVector
]);
state.set(1, [this.getPSW()]);
state.set(2, [this.nTotalCycles, this.getSpeed()]);
state.set(1, [this.getPSW(),this.getMMR0(),this.getMMR1(),this.getMMR2(),this.getMMR3()]);
state.set(2, [this.nTotalCycles, this.getSpeed(), this.flags.autoStart]);
state.set(3, this.saveIRQs());
state.set(4, this.saveTimers());
return state.data();
}
@ -675,45 +679,46 @@ class CPUStatePDP11 extends CPUPDP11 {
*/
restore(data)
{
var a;
/*
* ES6 ALERT: Gotta love these destructuring assignments, which make it easy to perform the inverse
* ES6 ALERT: A handy destructuring assignment, which makes it easy to perform the inverse
* of what save() does when it collects a bunch of object properties into an array.
*/
[
this.regsGen,
this.regsAlt,
this.regsAltStack,
this.regsPAR,
this.regsPDR,
this.regsUniMap,
this.regsControl,
this.regErr,
this.regMBR,
this.regPIR,
this.regSLR,
this.pswTrap,
this.pswMode,
this.opFlags,
this.regMMR0,
this.regMMR1,
this.regMMR2,
this.regMMR3,
this.mmuLastMode,
this.mmuLastPage,
this.mmuPDR,
this.mmuPAR,
this.mmuEnable,
this.mmuMask,
this.addrLast,
this.opLast
this.opFlags,
this.opLast,
this.pswTrap,
this.trapReason,
this.trapVector
] = data[0];
a = data[1];
var a = data[1];
this.setPSW(a[0]);
this.setMMR0(a[1]);
this.regMMR1 = a[2];
this.regMMR2 = a[3];
this.setMMR3(a[4]);
a = data[2];
this.nTotalCycles = a[0];
this.setSpeed(a[1]);
this.flags.autoStart = a[2];
this.restoreIRQs(data[3]);
this.restoreTimers(data[4]);
return true;
}
@ -985,8 +990,7 @@ class CPUStatePDP11 extends CPUPDP11 {
*/
addIRQ(vector, priority, message)
{
var irq = {vector: vector, priority: priority, message: message || 0, next: null};
irq.name = PDP11.VECTORS[vector];
var irq = {vector: vector, priority: priority, message: message || 0, name: PDP11.VECTORS[vector], next: null};
this.aIRQs.push(irq);
return irq;
}
@ -1087,6 +1091,22 @@ class CPUStatePDP11 extends CPUPDP11 {
}
}
/**
* findIRQ(vector)
*
* @this {CPUStatePDP11}
* @param {number} vector
* @return {IRQ|null}
*/
findIRQ(vector)
{
for (var i = 0; i < this.aIRQs.length; i++) {
var irq = this.aIRQs[i];
if (irq.vector === vector) return irq;
}
return null;
}
/**
* checkIRQs(priority)
*
@ -1109,6 +1129,41 @@ class CPUStatePDP11 extends CPUPDP11 {
this.irqNext = null;
}
/**
* saveIRQs()
*
* @this {CPUStatePDP11}
* @return {Array.<number>}
*/
saveIRQs()
{
var aIRQVectors = [];
var irq = this.irqNext;
while (irq) {
aIRQVectors.push(irq.vector);
irq = irq.next;
}
return aIRQVectors;
}
/**
* restoreIRQs(aIRQVectors)
*
* @this {CPUStatePDP11}
* @param {Array.<number>} aIRQVectors
*/
restoreIRQs(aIRQVectors)
{
for (var i = aIRQVectors.length - 1; i >= 0; i--) {
var irq = this.findIRQ(aIRQVectors[i]);
this.assert(irq != null);
if (irq) {
irq.next = this.irqNext;
this.irqNext = irq;
}
}
}
/**
* checkInterrupts()
*
@ -1615,8 +1670,8 @@ class CPUStatePDP11 extends CPUPDP11 {
* These next properties (in conjunction with setting PDP11.OPFLAG.TRAP_LAST) are purely an aid for the Debugger;
* see getTrapStatus().
*/
this.trapVector = vector;
this.trapReason = reason;
this.trapVector = vector;
if (reason == PDP11.REASON.PANIC) {
this.stopCPU();
@ -1757,9 +1812,9 @@ class CPUStatePDP11 extends CPUPDP11 {
var page = addr >> 13;
if (page > 7) mode |= 1;
if (!(this.regMMR3 & this.mapMMR3[this.pswMode])) page &= 7;
var pdr = this.mmuPDR[this.pswMode][page];
var pdr = this.regsPDR[this.pswMode][page];
var off = addr & 0x1fff;
var paf = (this.mmuPAR[this.pswMode][page] << 6);
var paf = (this.regsPAR[this.pswMode][page] << 6);
addrPhysical = (paf + off) & this.mmuMask;
if (addrPhysical >= BusPDP11.UNIBUS_22BIT) addrPhysical = this.mapUnibus(addrPhysical);
a.push(addrPhysical); // a[0]
@ -1838,8 +1893,8 @@ class CPUStatePDP11 extends CPUPDP11 {
page = addrVirtual >> 13;
if (!(this.regMMR3 & this.mapMMR3[this.pswMode])) page &= 7;
pdr = this.mmuPDR[this.pswMode][page];
addr = ((this.mmuPAR[this.pswMode][page] << 6) + (addrVirtual & 0x1fff)) & this.mmuMask;
pdr = this.regsPDR[this.pswMode][page];
addr = ((this.regsPAR[this.pswMode][page] << 6) + (addrVirtual & 0x1fff)) & this.mmuMask;
if (addr >= BusPDP11.UNIBUS_22BIT) addr = this.mapUnibus(addr);
@ -1928,7 +1983,7 @@ class CPUStatePDP11 extends CPUPDP11 {
/*
* Aborts and traps: log FIRST trap and MOST RECENT abort
*/
this.mmuPDR[this.pswMode][page] = pdr;
this.regsPDR[this.pswMode][page] = pdr;
if (addr != ((BusPDP11.IOPAGE_22BIT | PDP11.UNIBUS.MMR0) & this.mmuMask) || this.pswMode) {
this.mmuLastMode = this.pswMode;
this.mmuLastPage = page;
@ -2818,6 +2873,7 @@ class CPUStatePDP11 extends CPUPDP11 {
fnFlags.call(this, data << 8);
} else {
var addr = this.getAddr(mode, reg, PDP11.ACCESS.WRITE_BYTE);
//noinspection JSUnresolvedFunction
fnFlags.call(this, (data = data < 0? (this.regsGen[-data-1] & 0xff) : data) << 8);
this.setByte(addr, data);
if (addr & 1) this.nStepCycles--;
@ -2843,9 +2899,11 @@ class CPUStatePDP11 extends CPUPDP11 {
if (!mode) {
this.regsGen[reg] = (data = data < 0? this.regsGen[-data-1] : data);
//noinspection JSUnresolvedFunction
fnFlags.call(this, data);
} else {
var addr = this.getAddr(mode, reg, PDP11.ACCESS.WRITE_WORD);
//noinspection JSUnresolvedFunction
fnFlags.call(this, (data = data < 0? this.regsGen[-data-1] : data));
this.setWord(addr, data);
}
@ -2974,7 +3032,7 @@ class CPUStatePDP11 extends CPUPDP11 {
} while (this.nStepCycles > 0);
return (this.flags.complete? this.nBurstCycles - this.nStepCycles : (this.flags.complete === undefined? 0 : -1));
return (this.flags.complete? this.nBurstCycles - this.nStepCycles : (this.flags.complete === false? -1 : 0));
}
/**

View file

@ -5,10 +5,6 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
@ -63,7 +59,7 @@ if (NODE) {
* nBase:(number|undefined),
* sCmd:(string|undefined),
* aCmds:(Array.<string>|undefined)
* }} DbgAddrPDP11
* }}
*/
var DbgAddrPDP11;
@ -1270,8 +1266,8 @@ class DebuggerPDP11 extends Debugger {
// this.println(data? "resuming" : "powering up");
if (data && this.restore) {
if (!this.restore(data)) return false;
if (data) {
return this.restore(data);
}
}
return true;
@ -1593,7 +1589,7 @@ class DebuggerPDP11 extends Debugger {
checkMemoryRead(addr, nb)
{
if (this.checkBreakpoint(addr, nb || 1, this.aBreakRead)) {
this.stopCPU(true);
this.stopCPU(false);
return true;
}
return false;
@ -1618,7 +1614,7 @@ class DebuggerPDP11 extends Debugger {
checkMemoryWrite(addr, nb)
{
if (this.checkBreakpoint(addr, nb || 1, this.aBreakWrite)) {
this.stopCPU(true);
this.stopCPU(false);
return true;
}
return false;
@ -1662,6 +1658,7 @@ class DebuggerPDP11 extends Debugger {
* requires reading memory that triggers more memory reads, which triggers more breakpoint checks.
*/
this.nSuppressBreaks = 0;
this.nBreakInstructions = 0;
}
/**
@ -2706,25 +2703,16 @@ class DebuggerPDP11 extends Debugger {
*
* As the "help" output below indicates, the following breakpoint commands are supported:
*
* bp [a] set exec breakpoint on linear addr [a]
* br [a] set read breakpoint on linear addr [a]
* bw [a] set write breakpoint on linear addr [a]
* bc [a] clear breakpoint on linear addr [a] (use "*" for all breakpoints)
* bl list breakpoints
* bp # set exec breakpoint
* br # set read breakpoint
* bw # set write breakpoint
* bc # clear breakpoint (* to clear all)
* bl list all breakpoints
* bn [#] break after # instruction(s)
*
* to which we have recently added the following I/O breakpoint commands:
*
* bi [p] toggle input breakpoint on port [p] (use "*" for all input ports)
* bo [p] toggle output breakpoint on port [p] (use "*" for all output ports)
*
* These two new commands operate as toggles so that if "*" is used to trap all input (or output),
* you can also use these commands to NOT trap specific ports.
*
* bn [n] break after [n] instructions
*
* TODO: Update the "bl" command to include any/all I/O breakpoints, and the "bc" command to
* clear them. Because "bi" and "bo" commands are piggy-backing on Bus functions, those breakpoints
* are currently outside the realm of what the "bl" and "bc" commands are aware of.
* The "bn" command, like the "dh" command and all other commands that use an instruction count,
* assumes a decimal value, regardless of the current base. Use "bn" without an argument to display
* the break count, and use "bn 0" to clear the break count.
*
* @this {DebuggerPDP11}
* @param {string} sCmd
@ -2735,10 +2723,10 @@ class DebuggerPDP11 extends Debugger {
{
if (sAddr == '?') {
this.println("breakpoint commands:");
this.println("\tbp [#]\tset exec breakpoint at addr #");
this.println("\tbr [#]\tset read breakpoint at addr #");
this.println("\tbw [#]\tset write breakpoint at addr #");
this.println("\tbc [#]\tclear breakpoint at addr #");
this.println("\tbp #\tset exec breakpoint");
this.println("\tbr #\tset read breakpoint");
this.println("\tbw #\tset write breakpoint");
this.println("\tbc #\tclear breakpoint (* to clear all)");
this.println("\tbl\tlist all breakpoints");
this.println("\tbn [#]\tbreak after # instruction(s)");
return;
@ -2755,8 +2743,9 @@ class DebuggerPDP11 extends Debugger {
}
if (sParm == 'n') {
this.nBreakInstructions = this.parseValue(sAddr);
this.println("break after " + this.nBreakInstructions + " instruction(s)");
var n = +sAddr || 0;
if (sAddr) this.nBreakInstructions = n;
this.println("break after " + n + " instruction(s)");
return;
}

View file

@ -5,9 +5,9 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
* It has been adapted from the JavaScript PDP 11/70 Emulator written by Paul Nankervis
* (paulnank@hotmail.com) at <http://skn.noip.me/pdp11/pdp11.html>. This code may be used
* freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
@ -690,6 +690,7 @@ var PDP11 = {
RK11: { // RK11 Disk Controller
PRI: 5,
VEC: 0o220,
DRIVES: 8, // maximum of 8 drives
RKDS: { // 177400: Drive Status Register
SC: 0x000F, // (000017) Sector Counter
SCESA: 0x0010, // (000020) Sector Counter Equals Sector Address
@ -770,6 +771,7 @@ var PDP11 = {
RL11: { // RL11 Disk Controller
PRI: 5,
VEC: 0o160,
DRIVES: 4, // maximum of 4 drives
RLCS: { // 174400: Control Status Register
DRDY: 0x0001, // Drive Ready (R/O)
FUNC: 0x000E, // Function Code (F2,F1,F0) (R/W)
@ -874,6 +876,9 @@ var PDP11 = {
}
};
PDP11.RK11.RK05 = [203, 2, 12, 512, PDP11.RK11.RKDS.RK05 | PDP11.RK11.RKDS.SOK | PDP11.RK11.RKDS.RRDY];
PDP11.RL11.RL02K = [512, 2, 40, 256, PDP11.RL11.RLMP.GS_ST.LOCKON | PDP11.RL11.RLMP.GS_BH | PDP11.RL11.RLMP.GS_HO];
PDP11.ACCESS.READ_WORD = PDP11.ACCESS.WORD | PDP11.ACCESS.READ; // formerly READ_MODE (2)
PDP11.ACCESS.READ_BYTE = PDP11.ACCESS.BYTE | PDP11.ACCESS.READ; // formerly READ_MODE (2) | BYTE_MODE (1)
PDP11.ACCESS.WRITE_WORD = PDP11.ACCESS.WORD | PDP11.ACCESS.WRITE; // formerly WRITE_MODE (4)

View file

@ -5,9 +5,9 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
* It has been adapted from the JavaScript PDP 11/70 Emulator written by Paul Nankervis
* (paulnank@hotmail.com) at <http://skn.noip.me/pdp11/pdp11.html>. This code may be used
* freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
@ -36,6 +36,7 @@ if (NODE) {
var Str = require("../../shared/es6/strlib");
var Web = require("../../shared/es6/weblib");
var Component = require("../../shared/es6/component");
var State = require("../../shared/es6/state");
var PDP11 = require("./defines");
var BusPDP11 = require("./bus");
var MemoryPDP11 = require("./memory");
@ -59,10 +60,10 @@ class DevicePDP11 extends Component {
*/
constructor(parmsDevice)
{
super("Device", parmsDevice, DevicePDP11, MessagesPDP11.DEVICE);
super("Device", parmsDevice, MessagesPDP11.DEVICE);
this.kw11 = { // KW11 registers
csr: 0,
lks: PDP11.KW11.LKS.MON,
timer: -1 // initBus() will initialize this timer ID
};
}
@ -111,18 +112,18 @@ class DevicePDP11 extends Component {
{
if (DEBUGGER) {
var cpu = this.cpu;
this.dumpRegs("KIPDR", cpu.mmuPDR[0], 0, asArgs[0]);
this.dumpRegs("KDPDR", cpu.mmuPDR[0], 8, asArgs[0]);
this.dumpRegs("KIPAR", cpu.mmuPAR[0], 0, asArgs[0]);
this.dumpRegs("KDPAR", cpu.mmuPAR[0], 8, asArgs[0], true);
this.dumpRegs("SIPDR", cpu.mmuPDR[1], 0, asArgs[0]);
this.dumpRegs("SDPDR", cpu.mmuPDR[1], 8, asArgs[0]);
this.dumpRegs("SIPAR", cpu.mmuPAR[1], 0, asArgs[0]);
this.dumpRegs("SDPAR", cpu.mmuPAR[1], 8, asArgs[0], true);
this.dumpRegs("UIPDR", cpu.mmuPDR[3], 0, asArgs[0]);
this.dumpRegs("UDPDR", cpu.mmuPDR[3], 8, asArgs[0]);
this.dumpRegs("UIPAR", cpu.mmuPAR[3], 0, asArgs[0]);
this.dumpRegs("UDPAR", cpu.mmuPAR[3], 8, asArgs[0], true);
this.dumpRegs("KIPDR", cpu.regsPDR[0], 0, asArgs[0]);
this.dumpRegs("KDPDR", cpu.regsPDR[0], 8, asArgs[0]);
this.dumpRegs("KIPAR", cpu.regsPAR[0], 0, asArgs[0]);
this.dumpRegs("KDPAR", cpu.regsPAR[0], 8, asArgs[0], true);
this.dumpRegs("SIPDR", cpu.regsPDR[1], 0, asArgs[0]);
this.dumpRegs("SDPDR", cpu.regsPDR[1], 8, asArgs[0]);
this.dumpRegs("SIPAR", cpu.regsPAR[1], 0, asArgs[0]);
this.dumpRegs("SDPAR", cpu.regsPAR[1], 8, asArgs[0], true);
this.dumpRegs("UIPDR", cpu.regsPDR[3], 0, asArgs[0]);
this.dumpRegs("UDPDR", cpu.regsPDR[3], 8, asArgs[0]);
this.dumpRegs("UIPAR", cpu.regsPAR[3], 0, asArgs[0]);
this.dumpRegs("UDPAR", cpu.regsPAR[3], 8, asArgs[0], true);
if (cpu.regMMR3 & PDP11.MMR3.UNIBUS_MAP) {
this.dumpRegs("UNIMAP", cpu.regsUniMap, -1, asArgs[0]);
}
@ -167,6 +168,39 @@ class DevicePDP11 extends Component {
}
}
/**
* powerUp(data, fRepower)
*
* @this {DevicePDP11}
* @param {Object|null} data
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
powerUp(data, fRepower)
{
if (!fRepower) {
if (!data) {
this.reset();
} else {
if (!this.restore(data)) return false;
}
}
return true;
}
/**
* powerDown(fSave, fShutdown)
*
* @this {DevicePDP11}
* @param {boolean} [fSave]
* @param {boolean} [fShutdown]
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
powerDown(fSave, fShutdown)
{
return fSave? this.save() : true;
}
/**
* reset()
*
@ -178,6 +212,45 @@ class DevicePDP11 extends Component {
this.cpu.setTimer(this.kw11.timer, 1000/60, true);
}
/**
* save()
*
* This implements save support for the DevicePDP11 component.
*
* @this {DevicePDP11}
* @return {Object}
*/
save()
{
var state = new State(this);
state.set(0, [
this.kw11.lks
]);
return state.data();
}
/**
* restore(data)
*
* This implements restore support for the DevicePDP11 component.
*
* @this {DevicePDP11}
* @param {Object} data
* @return {boolean} true if successful, false if failure
*/
restore(data)
{
/*
* ES6 ALERT: A handy destructuring assignment, which makes it easy to perform the inverse
* of what save() does when it collects a bunch of object properties into an array.
*/
[
this.kw11.lks
] = data[0];
return true;
}
/**
* interruptKW11()
*
@ -350,7 +423,7 @@ class DevicePDP11 extends Component {
readSIPDR(addr)
{
var reg = (addr >> 1) & 7;
return this.cpu.mmuPDR[1][reg];
return this.cpu.regsPDR[1][reg];
}
/**
@ -363,7 +436,7 @@ class DevicePDP11 extends Component {
writeSIPDR(data, addr)
{
var reg = (addr >> 1) & 7;
this.cpu.mmuPDR[1][reg] = data & 0xff0f;
this.cpu.regsPDR[1][reg] = data & 0xff0f;
}
/**
@ -376,7 +449,7 @@ class DevicePDP11 extends Component {
readSDPDR(addr)
{
var reg = ((addr >> 1) & 7) + 8;
return this.cpu.mmuPDR[1][reg];
return this.cpu.regsPDR[1][reg];
}
/**
@ -389,7 +462,7 @@ class DevicePDP11 extends Component {
writeSDPDR(data, addr)
{
var reg = ((addr >> 1) & 7) + 8;
this.cpu.mmuPDR[1][reg] = data & 0xff0f;
this.cpu.regsPDR[1][reg] = data & 0xff0f;
}
/**
@ -402,7 +475,7 @@ class DevicePDP11 extends Component {
readSIPAR(addr)
{
var reg = (addr >> 1) & 7;
return this.cpu.mmuPAR[1][reg];
return this.cpu.regsPAR[1][reg];
}
/**
@ -415,8 +488,8 @@ class DevicePDP11 extends Component {
writeSIPAR(data, addr)
{
var reg = (addr >> 1) & 7;
this.cpu.mmuPAR[1][reg] = data;
this.cpu.mmuPDR[1][reg] &= 0xff0f;
this.cpu.regsPAR[1][reg] = data;
this.cpu.regsPDR[1][reg] &= 0xff0f;
}
@ -430,7 +503,7 @@ class DevicePDP11 extends Component {
readSDPAR(addr)
{
var reg = ((addr >> 1) & 7) + 8;
return this.cpu.mmuPAR[1][reg];
return this.cpu.regsPAR[1][reg];
}
/**
@ -443,8 +516,8 @@ class DevicePDP11 extends Component {
writeSDPAR(data, addr)
{
var reg = ((addr >> 1) & 7) + 8;
this.cpu.mmuPAR[1][reg] = data;
this.cpu.mmuPDR[1][reg] &= 0xff0f;
this.cpu.regsPAR[1][reg] = data;
this.cpu.regsPDR[1][reg] &= 0xff0f;
}
/**
@ -457,7 +530,7 @@ class DevicePDP11 extends Component {
readKIPDR(addr)
{
var reg = (addr >> 1) & 7;
return this.cpu.mmuPDR[0][reg];
return this.cpu.regsPDR[0][reg];
}
/**
@ -470,7 +543,7 @@ class DevicePDP11 extends Component {
writeKIPDR(data, addr)
{
var reg = (addr >> 1) & 7;
this.cpu.mmuPDR[0][reg] = data & 0xff0f;
this.cpu.regsPDR[0][reg] = data & 0xff0f;
}
/**
@ -483,7 +556,7 @@ class DevicePDP11 extends Component {
readKDPDR(addr)
{
var reg = ((addr >> 1) & 7) + 8;
return this.cpu.mmuPDR[0][reg];
return this.cpu.regsPDR[0][reg];
}
/**
@ -496,7 +569,7 @@ class DevicePDP11 extends Component {
writeKDPDR(data, addr)
{
var reg = ((addr >> 1) & 7) + 8;
this.cpu.mmuPDR[0][reg] = data & 0xff0f;
this.cpu.regsPDR[0][reg] = data & 0xff0f;
}
/**
@ -509,7 +582,7 @@ class DevicePDP11 extends Component {
readKIPAR(addr)
{
var reg = (addr >> 1) & 7;
return this.cpu.mmuPAR[0][reg];
return this.cpu.regsPAR[0][reg];
}
/**
@ -522,8 +595,8 @@ class DevicePDP11 extends Component {
writeKIPAR(data, addr)
{
var reg = (addr >> 1) & 7;
this.cpu.mmuPAR[0][reg] = data;
this.cpu.mmuPDR[0][reg] &= 0xff0f;
this.cpu.regsPAR[0][reg] = data;
this.cpu.regsPDR[0][reg] &= 0xff0f;
}
@ -537,7 +610,7 @@ class DevicePDP11 extends Component {
readKDPAR(addr)
{
var reg = ((addr >> 1) & 7) + 8;
return this.cpu.mmuPAR[0][reg];
return this.cpu.regsPAR[0][reg];
}
/**
@ -550,8 +623,8 @@ class DevicePDP11 extends Component {
writeKDPAR(data, addr)
{
var reg = ((addr >> 1) & 7) + 8;
this.cpu.mmuPAR[0][reg] = data;
this.cpu.mmuPDR[0][reg] &= 0xff0f;
this.cpu.regsPAR[0][reg] = data;
this.cpu.regsPDR[0][reg] &= 0xff0f;
}
/**
@ -564,7 +637,7 @@ class DevicePDP11 extends Component {
readUIPDR(addr)
{
var reg = (addr >> 1) & 7;
return this.cpu.mmuPDR[3][reg];
return this.cpu.regsPDR[3][reg];
}
/**
@ -577,7 +650,7 @@ class DevicePDP11 extends Component {
writeUIPDR(data, addr)
{
var reg = (addr >> 1) & 7;
this.cpu.mmuPDR[3][reg] = data & 0xff0f;
this.cpu.regsPDR[3][reg] = data & 0xff0f;
}
/**
@ -590,7 +663,7 @@ class DevicePDP11 extends Component {
readUDPDR(addr)
{
var reg = ((addr >> 1) & 7) + 8;
return this.cpu.mmuPDR[3][reg];
return this.cpu.regsPDR[3][reg];
}
/**
@ -603,7 +676,7 @@ class DevicePDP11 extends Component {
writeUDPDR(data, addr)
{
var reg = ((addr >> 1) & 7) + 8;
this.cpu.mmuPDR[3][reg] = data & 0xff0f;
this.cpu.regsPDR[3][reg] = data & 0xff0f;
}
/**
@ -616,7 +689,7 @@ class DevicePDP11 extends Component {
readUIPAR(addr)
{
var reg = (addr >> 1) & 7;
return this.cpu.mmuPAR[3][reg];
return this.cpu.regsPAR[3][reg];
}
/**
@ -629,8 +702,8 @@ class DevicePDP11 extends Component {
writeUIPAR(data, addr)
{
var reg = (addr >> 1) & 7;
this.cpu.mmuPAR[3][reg] = data;
this.cpu.mmuPDR[3][reg] &= 0xff0f;
this.cpu.regsPAR[3][reg] = data;
this.cpu.regsPDR[3][reg] &= 0xff0f;
}
@ -644,7 +717,7 @@ class DevicePDP11 extends Component {
readUDPAR(addr)
{
var reg = ((addr >> 1) & 7) + 8;
return this.cpu.mmuPAR[3][reg];
return this.cpu.regsPAR[3][reg];
}
/**
@ -657,8 +730,8 @@ class DevicePDP11 extends Component {
writeUDPAR(data, addr)
{
var reg = ((addr >> 1) & 7) + 8;
this.cpu.mmuPAR[3][reg] = data;
this.cpu.mmuPDR[3][reg] &= 0xff0f;
this.cpu.regsPAR[3][reg] = data;
this.cpu.regsPDR[3][reg] &= 0xff0f;
}
/**

View file

@ -103,13 +103,13 @@ class DiskPDP11 extends Component {
* This means, for example, that all references to "track[iSector].data" must actually appear as
* "track[iSector]['data']".
*
* @param {RK11|RL11} controller
* @param {DriveController|RK11|RL11} controller
* @param {Object} drive
* @param {string} mode
*/
constructor(controller, drive, mode)
{
super("Disk", {'id': controller.idMachine + ".disk" + Str.toHex(++DiskPDP11.nDisks, 4)}, DiskPDP11, MessagesPDP11.DISK);
super("Disk", {'id': controller.idMachine + ".disk" + Str.toHex(++DiskPDP11.nDisks, 4)}, MessagesPDP11.DISK);
/*
* Route all non-Debugger messages (eg, notice() and println() calls) through
@ -371,6 +371,7 @@ class DiskPDP11 extends Component {
}
if (this.fnNotify) {
//noinspection JSUnresolvedFunction
this.fnNotify.call(this.controller, this.drive, disk, this.sDiskName, this.sDiskPath);
this.fnNotify = null;
}
@ -590,6 +591,7 @@ class DiskPDP11 extends Component {
}
if (this.fnNotify) {
//noinspection JSUnresolvedFunction
this.fnNotify.call(this.controllerNotify, this.drive, disk, this.sDiskName, this.sDiskPath);
this.fnNotify = null;
}

1274
modules/pdp11/lib/drive.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -5,10 +5,6 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
@ -47,7 +43,7 @@ class KeyboardPDP11 extends Component {
*/
constructor(parmsKbd)
{
super("Keyboard", parmsKbd, KeyboardPDP11, MessagesPDP11.KEYBOARD);
super("Keyboard", parmsKbd, MessagesPDP11.KEYBOARD);
this.setReady();
}

View file

@ -5,10 +5,6 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.

View file

@ -5,10 +5,6 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.

View file

@ -5,10 +5,6 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.

View file

@ -5,9 +5,9 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
* It has been adapted from the JavaScript PDP 11/70 Emulator written by Paul Nankervis
* (paulnank@hotmail.com) at <http://skn.noip.me/pdp11/pdp11.html>. This code may be used
* freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
@ -36,6 +36,7 @@ if (NODE) {
var Str = require("../../shared/es6/strlib");
var Web = require("../../shared/es6/weblib");
var Component = require("../../shared/es6/component");
var State = require("../../shared/es6/state");
var PDP11 = require("./defines");
var BusPDP11 = require("./bus");
var MessagesPDP11 = require("./messages");
@ -52,7 +53,7 @@ class PanelPDP11 extends Component {
*/
constructor(parmsPanel, fBindings)
{
super("Panel", parmsPanel, PanelPDP11, MessagesPDP11.PANEL);
super("Panel", parmsPanel, MessagesPDP11.PANEL);
/*
* If there are any live registers, LEDs, etc, to display, this will provide a count.
@ -80,6 +81,7 @@ class PanelPDP11 extends Component {
this.regDisplay = 0;
this.regSwitches = 0;
this.regAddr = this.regData = 0;
this.ledAddr = this.ledData = -1;
/*
* The panel hardware has the following additional (supported) state; note that there are several
@ -232,20 +234,22 @@ class PanelPDP11 extends Component {
}
/**
* reset()
* reset(fPowerUp)
*
* NOTE: Since we've registered our handler with the Bus component, we will be called twice whenever
* the entire machine is reset: once when the Computer's reset() handler calls the Bus's reset() handler,
* and again when the Computer's reset() handler calls us directly. Multiple resets should be harmless.
*
* @this {PanelPDP11}
* @param {boolean} [fPowerUp]
*/
reset()
reset(fPowerUp)
{
/*
* Simulate a call to our stop() handler, to update the panel's ADDRESS register with the current PC.
*/
this.stop();
if (fPowerUp) this.setDR(0);
}
/**
@ -396,10 +400,11 @@ class PanelPDP11 extends Component {
*/
if (this.fBindings) PanelPDP11.init();
/*
* TODO: Until we implement a restore() function, all we can do is reset()
*/
this.reset();
if (!data) {
this.reset(true);
} else {
if (!this.restore(data)) return false;
}
}
return true;
}
@ -414,6 +419,45 @@ class PanelPDP11 extends Component {
*/
powerDown(fSave, fShutdown)
{
return fSave? this.save() : true;
}
/**
* save()
*
* This implements save support for the PanelPDP11 component.
*
* @this {PanelPDP11}
* @return {Object}
*/
save()
{
var state = new State(this);
state.set(0, [
this.getAR(),
this.getDR(),
this.getSR()
]);
return state.data();
}
/**
* restore(data)
*
* This implements restore support for the PanelPDP11 component.
*
* @this {PanelPDP11}
* @param {Object} data
* @return {boolean} true if successful, false if failure
*/
restore(data)
{
var a = data[0];
if (a) {
this.setAR(a[0]);
this.setDR(a[1]);
this.setSR(a[2]);
}
return true;
}
@ -490,12 +534,9 @@ class PanelPDP11 extends Component {
displayValue(sLabel, nValue, cch)
{
if (this.bindings[sLabel]) {
if (nValue === undefined) {
this.setError("Value for " + sLabel + " is invalid");
this.cpu.stopCPU();
}
var sVal;
var nBase = this.dbg && this.dbg.nBase || 8;
nValue = nValue || 0;
if (!this.cpu.isRunning() || this.fDisplayLiveRegs) {
sVal = nBase == 8? Str.toOct(nValue, cch) : Str.toHex(nValue, cch);
} else {
@ -865,7 +906,10 @@ class PanelPDP11 extends Component {
updateAddr(value)
{
this.regAddr = value & this.bus.nBusMask;
this.updateLEDArray("A", this.regAddr, 22);
if (this.ledAddr !== this.regAddr) {
this.ledAddr = this.regAddr;
this.updateLEDArray("A", this.ledAddr, 22);
}
return this.regAddr;
}
@ -879,7 +923,10 @@ class PanelPDP11 extends Component {
updateData(value)
{
this.regData = value & 0xffff;
this.updateLEDArray("D", this.regData, 16);
if (this.ledData !== this.regData) {
this.ledData = this.regData;
this.updateLEDArray("D", this.ledData, 16);
}
return this.regData;
}
@ -996,7 +1043,7 @@ class PanelPDP11 extends Component {
* Called by the Computer component at intervals to update registers, LEDs, etc.
*
* @this {PanelPDP11}
* @param {number} [nUpdate] (< 0 for forced, > 0 for periodic, 0 otherwise)
* @param {number} [nUpdate] (-2 for power on, -1 for forced, > 0 for periodic, 0 or undefined otherwise)
*/
updateDisplay(nUpdate)
{
@ -1032,7 +1079,13 @@ class PanelPDP11 extends Component {
* TODO: There is currently no mechanism for selecting regData over regDisplay;
* we are acting as if the DATASEL switch setting is locked to "DISPLAY REGISTER".
*/
this.updateAddr(nUpdate > 0 && fRunning && !fWaiting? this.cpu.getLastAddr() : this.regAddr);
if (nUpdate < -1) {
this.regAddr = this.cpu.regsGen[7];
} else if (nUpdate > 0 && fRunning && !fWaiting) {
this.regAddr = this.cpu.getLastAddr();
}
this.updateAddr(this.regAddr);
this.updateData(this.regDisplay);
var bits = this.cpu.getMMUState();

View file

@ -64,7 +64,7 @@ class PC11 extends Component {
*/
constructor(parms)
{
super("PC11", parms, PC11);
super("PC11", parms);
this.sDevice = "PTR"; // TODO: Make the device name configurable
@ -306,7 +306,7 @@ class PC11 extends Component {
powerUp(data, fRepower)
{
if (!fRepower) {
if (!data || !this.restore) {
if (!data) {
this.reset();
} else {
if (!this.restore(data)) return false;

View file

@ -5,10 +5,6 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
@ -66,7 +62,7 @@ class RAMPDP11 extends Component {
*/
constructor(parmsRAM)
{
super("RAM", parmsRAM, RAMPDP11);
super("RAM", parmsRAM);
this.abInit = null;
this.aSymbols = null;
@ -141,12 +137,14 @@ class RAMPDP11 extends Component {
*/
delete this.aSymbols;
}
/*
* The Computer powers up the CPU last, at which point CPUState state is restored,
* which includes the Bus state, and since we use the Bus to allocate all our memory,
* memory contents are already restored for us, so we don't need the usual restore
* logic.
*/
if (!fRepower) {
/*
* Since we use the Bus to allocate all our memory, memory contents are already restored for us,
* so we don't save any state, and therefore no state should be restored. Just do a reset().
*/
this.assert(!data);
this.reset();
}
return true;
}

View file

@ -5,6 +5,10 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator written by Paul Nankervis
* (paulnank@hotmail.com) at <http://skn.noip.me/pdp11/pdp11.html>. This code may be used
* freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
@ -30,16 +34,12 @@
if (NODE) {
var Str = require("../../shared/es6/strlib");
var Web = require("../../shared/es6/weblib");
var DiskAPI = require("../../shared/es6/diskapi");
var Component = require("../../shared/es6/component");
var State = require("../../shared/es6/state");
var PDP11 = require("./defines");
var MessagesPDP11 = require("./messages");
var DiskPDP11 = require("./disk");
var DriveController = require("./drive");
}
class RK11 extends Component {
class RK11 extends DriveController {
/**
* RK11(parms)
*
@ -58,884 +58,65 @@ class RK11 extends Component {
*/
constructor(parms)
{
super("RK11", parms, RK11, MessagesPDP11.RK11);
super("RK11", parms, MessagesPDP11.RK11, PDP11.RK11, PDP11.RK11.RK05, RK11.UNIBUS_IOTABLE);
/*
* We preliminarily parse and record any 'autoMount' object now, but we no longer process it
* until initBus(), because the Computer's getMachineParm() service may have an override for us.
*/
this.configMount = this.parseConfig(parms['autoMount']);
this.cAutoMount = 0;
/*
* The RK11 has three Drive Select bits, representing up to eight drives.
*/
this.nDrives = 8;
this.aDrives = new Array(this.nDrives);
this.fLocalDisks = (!Web.isMobile() && window && 'FileReader' in window);
this.sDiskSource = RK11.SOURCE.NONE;
this.irq = null;
/*
* Define all the properties to be initialized by initController()
* Define all the registers required for this controller.
*
* TODO: Determine what we should really be doing with the RKDB register.
*/
this.regRKDS = this.regRKER = this.regRKCS = this.regRKWC = this.regRKBA = this.regRKDA = this.regRKDB = 0;
}
/**
* parseConfig(config)
* initController(aRegs)
*
* @this {RK11}
* @param {*} config
* @return {*}
*/
parseConfig(config)
{
if (config && typeof config == "string") {
try {
/*
* The most likely source of any exception will be right here, where we're parsing
* this JSON-encoded data.
*/
config = eval("(" + config + ")");
} catch (e) {
Component.error(this.type + " auto-mount error: " + e.message + " (" + config + ")");
config = null;
}
}
return config || {};
}
/**
* setBinding(sType, sBinding, control, sValue)
*
* @this {RK11}
* @param {string|null} sType is the type of the HTML control (eg, "button", "list", "text", etc)
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "listDisks")
* @param {Object} control is the HTML control DOM object (eg, HTMLButtonElement)
* @param {string} [sValue] optional data value
* @return {boolean} true if binding was successful, false if unrecognized binding request
*/
setBinding(sType, sBinding, control, sValue)
{
var rk11 = this;
switch (sBinding) {
case "listDisks":
this.bindings[sBinding] = control;
control.onchange = function onChangeListDisks(event) {
var controlDesc = rk11.bindings["descDisk"];
var controlOption = control.options && control.options[control.selectedIndex];
if (controlDesc && controlOption) {
var dataValue = {};
var sValue = controlOption.getAttribute("data-value");
if (sValue) {
try {
dataValue = eval("(" + sValue + ")");
} catch (e) {
Component.error("RK11 option error: " + e.message);
}
}
var sHTML = dataValue['desc'];
if (sHTML === undefined) sHTML = "";
var sHRef = dataValue['href'];
if (sHRef !== undefined) sHTML = "<a href=\"" + sHRef + "\" target=\"_blank\">" + sHTML + "</a>";
controlDesc.innerHTML = sHTML;
}
};
return true;
case "descDisk":
case "listDrives":
this.bindings[sBinding] = control;
/*
* I tried going with onclick instead of onchange, so that if you wanted to confirm what's
* loaded in a particular drive, you could click the drive control without having to change it.
* However, that doesn't seem to work for all browsers, so I've reverted to onchange.
*/
control.onchange = function onChangeListDrives(event) {
var iDrive = Str.parseInt(control.value, 10);
if (iDrive != null) rk11.displayDisk(iDrive);
};
return true;
case "loadDisk":
this.bindings[sBinding] = control;
control.onclick = function onClickLoadDrive(event) {
var controlDisks = rk11.bindings["listDisks"];
if (controlDisks && controlDisks.options) {
var sDiskName = controlDisks.options[controlDisks.selectedIndex].text;
var sDiskPath = controlDisks.value;
rk11.loadSelectedDrive(sDiskName, sDiskPath);
}
};
return true;
case "bootDisk":
this.bindings[sBinding] = control;
control.onclick = function onClickBootDisk(event) {
rk11.bootSelectedDisk();
};
return true;
case "saveDisk":
/*
* Yes, technically, this feature does not require "Local disk support" (which is really a reference
* to FileReader support), but since fLocalDisks is also false for all mobile devices, and since there
* is an "orthogonality" to disabling both features in tandem, let's just let it slide, OK?
*/
if (!this.fLocalDisks) {
if (DEBUG) this.log("Local disk support not available");
/*
* We could also simply hide the control; eg:
*
* control.style.display = "none";
*
* but removing the control altogether seems better.
*/
control.parentNode.removeChild(/** @type {Node} */ (control));
return false;
}
this.bindings[sBinding] = control;
control.onclick = function onClickSaveDrive(event) {
var controlDrives = rk11.bindings["listDrives"];
if (controlDrives && controlDrives.options && rk11.aDrives) {
var iDriveSelected = Str.parseInt(controlDrives.value, 10) || 0;
var drive = rk11.aDrives[iDriveSelected];
if (drive) {
/*
* Note the similarity (and hence factoring opportunity) between this code and the HDC's "saveHD*" binding.
*/
var disk = drive.disk;
if (disk) {
if (DEBUG) rk11.println("saving disk " + disk.sDiskPath + "...");
var sAlert = Web.downloadFile(disk.encodeAsBase64(), "octet-stream", true, disk.sDiskFile.replace(".json", ".img"));
Component.alertUser(sAlert);
} else {
rk11.notice("No disk loaded in drive.");
}
} else {
rk11.notice("No disk drive selected.");
}
}
};
return true;
case "mountDisk":
if (!this.fLocalDisks) {
if (DEBUG) this.log("Local disk support not available");
/*
* We could also simply hide the control; eg:
*
* control.style.display = "none";
*
* but removing the control altogether seems better.
*/
control.parentNode.removeChild(/** @type {Node} */ (control));
return false;
}
this.bindings[sBinding] = control;
/*
* Enable "Mount" button only if a file is actually selected
*/
control.addEventListener('change', function() {
var fieldset = control.children[0];
var files = fieldset.children[0].files;
var submit = fieldset.children[1];
submit.disabled = !files.length;
});
control.onsubmit = function(event) {
var file = event.currentTarget[1].files[0];
if (file) {
var sDiskPath = file.name;
var sDiskName = Str.getBaseName(sDiskPath, true);
rk11.loadSelectedDrive(sDiskName, sDiskPath, file);
}
/*
* Prevent reloading of web page after form submission
*/
return false;
};
return true;
default:
break;
}
return false;
}
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {RK11}
* @param {ComputerPDP11} cmp
* @param {BusPDP11} bus
* @param {CPUStatePDP11} cpu
* @param {DebuggerPDP11} dbg
*/
initBus(cmp, bus, cpu, dbg)
{
this.cmp = cmp;
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
var configMount = this.parseConfig(this.cmp.getMachineParm('autoMount'));
/*
* Add only drives from the machine-wide autoMount configuration that match drives managed by this component.
*/
if (configMount) {
for (var sDrive in configMount) {
if (sDrive.substr(0, 2) != this.type.substr(0, 2)) continue;
this.configMount[sDrive] = configMount[sDrive];
}
}
/*
* If we didn't need auto-mount support, we could defer controller initialization until we received
* a powerUp() notification, at which point reset() would call initController(), or restore() would restore
* the controller; in that case, all we'd need to do here is call setReady().
*/
this.initController();
this.irq = this.cpu.addIRQ(RK11.VEC, RK11.PRI, MessagesPDP11.RK11);
bus.addIOTable(this, RK11.UNIBUS_IOTABLE);
bus.addResetHandler(this.reset.bind(this));
this.addDisk("None", RK11.SOURCE.NONE, true);
if (this.fLocalDisks) this.addDisk("Local Disk", RK11.SOURCE.LOCAL);
this.addDisk("Remote Disk", RK11.SOURCE.REMOTE);
if (!this.autoMount()) this.setReady();
}
/**
* getDriveName(iDrive)
*
* @this {RK11}
* @param {number} iDrive
* @return {string}
*/
getDriveName(iDrive)
{
return "RK" + iDrive;
}
/**
* getDriveNumber(sDrive)
*
* @this {RK11}
* @param {string} sDrive
* @return {number} (0-3, or -1 if error)
*/
getDriveNumber(sDrive)
{
var iDrive = -1;
if (sDrive) {
iDrive = sDrive.charCodeAt(sDrive.length - 1) - 0x30;
if (iDrive < 0 || iDrive > 9) iDrive = -1;
}
return iDrive;
}
/**
* powerUp(data, fRepower)
*
* @this {RK11}
* @param {Object|null} data
* @param {boolean} [fRepower]
* @param {Array} [aRegs]
* @return {boolean} true if successful, false if failure
*/
powerUp(data, fRepower)
initController(aRegs)
{
if (!fRepower) {
if (!data || !this.restore) {
this.reset();
if (this.cmp.fReload) {
/*
* If the computer's fReload flag is set, we're required to toss all currently
* loaded disks and remount all disks specified in the auto-mount configuration.
*/
this.unloadAllDrives(true);
this.autoMount(true);
}
} else {
if (!this.restore(data)) return false;
}
/*
* Populate the HTML controls to match the actual (well, um, specified) number of floppy drives.
*/
var controlDrives;
if ((controlDrives = this.bindings['listDrives'])) {
while (controlDrives.firstChild) {
controlDrives.removeChild(controlDrives.firstChild);
}
controlDrives.value = "";
for (var iDrive = 0; iDrive < this.nDrives; iDrive++) {
var controlOption = document.createElement("option");
controlOption.value = iDrive;
controlOption.text = this.getDriveName(iDrive);
controlDrives.appendChild(controlOption);
}
if (this.nDrives > 0) {
controlDrives.value = "0";
this.displayDisk(0);
}
}
if (!aRegs) {
aRegs = [(RK11.RKDS.RK05 | RK11.RKDS.SOK | RK11.RKDS.RRDY), 0, (RK11.RKCS.CRDY), 0, 0, 0, 0];
}
/*
* ES6 ALERT: A handy destructuring assignment, which makes it easy to perform the inverse
* of what saveController() does when it collects a bunch of object properties into an array.
*/
[
this.regRKDS,
this.regRKER,
this.regRKCS,
this.regRKWC,
this.regRKBA,
this.regRKDA,
this.regRKDB
] = aRegs;
return true;
}
/**
* powerDown(fSave, fShutdown)
*
* @this {RK11}
* @param {boolean} [fSave]
* @param {boolean} [fShutdown]
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
powerDown(fSave, fShutdown)
{
return fSave? this.save() : true;
}
/**
* reset()
*
* @this {RK11}
*/
reset()
{
this.initController();
}
/**
* save()
*
* This implements save support for the RK11 component.
*
* @this {RK11}
* @return {Object}
*/
save()
{
var state = new State(this);
// state.set(0, this.saveController());
return state.data();
}
/**
* restore(data)
*
* This implements restore support for the RK11 component.
*
* @this {RK11}
* @param {Object} data
* @return {boolean} true if successful, false if failure
*/
restore(data)
{
return this.initController(data[0]);
}
/**
* saveController()
*
* Basically, the inverse of initController().
*
* @this {RK11}
* @return {Array}
*/
saveController()
{
return [];
}
/**
* autoMount(fRemount)
*
* @this {RK11}
* @param {boolean} [fRemount] is true if we're remounting all auto-mounted disks
* @return {boolean} true if one or more disk images are being auto-mounted, false if none
*/
autoMount(fRemount)
{
if (!fRemount) this.cAutoMount = 0;
for (var sDrive in this.configMount) {
var configDrive = this.configMount[sDrive];
var sDiskPath = configDrive['path'] || "";
var sDiskName = configDrive['name'] || this.findDisk(sDiskPath);
if (sDiskPath && sDiskName) {
var iDrive = this.getDriveNumber(sDrive);
if (iDrive >= 0 && iDrive < this.aDrives.length) {
if (!this.loadDrive(iDrive, sDiskName, sDiskPath, true) && fRemount) {
this.setReady(false);
}
continue;
}
}
this.notice("Incorrect auto-mount settings for drive " + sDrive + " (" + JSON.stringify(configDrive) + ")");
}
return !!this.cAutoMount;
}
/**
* loadSelectedDrive(sDiskName, sDiskPath, file)
*
* @this {RK11}
* @param {string} sDiskName
* @param {string} sDiskPath
* @param {File} [file] is set if there's an associated File object
*/
loadSelectedDrive(sDiskName, sDiskPath, file)
{
var controlDrives = this.bindings["listDrives"];
var iDrive = controlDrives && Str.parseInt(controlDrives.value, 10);
if (iDrive === undefined || iDrive < 0 || iDrive >= this.aDrives.length) {
this.notice("Unable to load the selected drive");
return;
}
if (!sDiskPath) {
this.unloadDrive(iDrive);
return;
}
if (sDiskPath == RK11.SOURCE.LOCAL) {
this.notice('Use "Choose File" and "Mount" to select and load a local disk.');
return;
}
/*
* If the special RK11.SOURCE.REMOTE path is selected, then we want to prompt the user for a URL.
* Oh, and make sure we pass an empty string as the 2nd parameter to prompt(), so that IE won't display
* "undefined" -- because after all, undefined and "undefined" are EXACTLY the same thing, right?
*
* TODO: This is literally all I've done to support remote disk images. There's probably more
* I should do, like dynamically updating "listDisks" to include new entries, and adding new entries
* to the save/restore data.
*/
if (sDiskPath == RK11.SOURCE.REMOTE) {
sDiskPath = window.prompt("Enter the URL of a remote disk image.", "") || "";
if (!sDiskPath) return;
sDiskName = Str.getBaseName(sDiskPath);
this.status("Attempting to load " + sDiskPath + " as \"" + sDiskName + "\"");
this.sDiskSource = RK11.SOURCE.REMOTE;
}
else {
this.sDiskSource = sDiskPath;
}
this.loadDrive(iDrive, sDiskName, sDiskPath, false, file);
}
/**
* bootSelectedDisk()
*
* @this {RK11}
*/
bootSelectedDisk()
{
var drive;
var controlDrives = this.bindings["listDrives"];
var iDrive = controlDrives && Str.parseInt(controlDrives.value, 10);
if (iDrive == null || iDrive < 0 || iDrive >= this.aDrives.length || !(drive = this.aDrives[iDrive])) {
this.notice("Unable to boot the selected drive");
return;
}
if (!drive.disk) {
this.notice("Load a disk into the drive first");
return;
}
/*
* NOTE: We're calling setReset() BEFORE reading the boot code in order to eliminate any side-effects
* of the previous state of either the controller OR the CPU; for example, we don't want any previous MMU
* or UNIBUS Map registers affecting the simulated readData() call. Also, some boot code (eg, RSTS/E)
* expects the controller to be in a READY state; since setReset() triggers a call to our reset() handler,
* a READY state is assured, and the readData() call shouldn't do anything to change that.
*/
this.cpu.setReset(0, true);
var err = this.readData(drive, 0, 0, 0, 512, 0x0000, 2);
if (err) {
this.notice("Unable to read the boot sector (" + err + ")");
}
}
/**
* loadDrive(iDrive, sDiskName, sDiskPath, fAutoMount, file)
*
* NOTE: If sDiskPath is already loaded, nothing needs to be done.
*
* @this {RK11}
* @param {number} iDrive
* @param {string} sDiskName
* @param {string} sDiskPath
* @param {boolean} [fAutoMount]
* @param {File} [file] is set if there's an associated File object
* @return {number} 1 if disk loaded, 0 if queued up (or busy), -1 if already loaded
*/
loadDrive(iDrive, sDiskName, sDiskPath, fAutoMount, file)
{
var nResult = -1;
var drive = this.aDrives[iDrive];
if (drive.sDiskPath.toLowerCase() != sDiskPath.toLowerCase()) {
nResult++;
this.unloadDrive(iDrive, true);
if (drive.fBusy) {
this.notice("RK11 busy");
}
else {
// this.status("disk queued: " + sDiskName);
drive.fBusy = true;
if (fAutoMount) {
drive.fAutoMount = true;
this.cAutoMount++;
if (this.messageEnabled()) this.printMessage("auto-loading disk: " + sDiskName);
}
drive.fLocal = !!file;
var disk = new DiskPDP11(this, drive, DiskAPI.MODE.PRELOAD);
if (disk.load(sDiskName, sDiskPath, file, this.finishLoadDrive)) {
nResult++;
}
}
}
return nResult;
}
/**
* finishLoadDrive(drive, disk, sDiskName, sDiskPath, fAutoMount)
*
* The disk parameter is set if the disk was successfully loaded, null if not.
*
* @this {RK11}
* @param {Object} drive
* @param {DiskPDP11} disk
* @param {string} sDiskName
* @param {string} sDiskPath
* @param {boolean} [fAutoMount]
*/
finishLoadDrive(drive, disk, sDiskName, sDiskPath, fAutoMount)
{
var aDiskInfo;
drive.fBusy = false;
if (disk) {
/*
* TODO: While this is a perfectly reasonable thing to do, one wonders if the Disk object shouldn't
* have done this itself, since we passed our Drive object to it (it already knows the drive's limits).
*/
if (disk.nCylinders > drive.nCylinders || disk.nHeads > drive.nHeads /* || disk.nSectors > drive.nSectors */) {
this.notice("Disk \"" + sDiskName + "\" too large for drive " + this.getDriveName(drive.iDrive));
disk = null;
}
}
if (disk) {
drive.disk = disk;
drive.sDiskName = sDiskName;
drive.sDiskPath = sDiskPath;
/*
* Adding local disk image names to the disk list seems like a nice idea, but it's too confusing,
* because then it looks like the "Mount" button should be able to (re)load them, and that can NEVER
* happen, for security reasons; local disk images can ONLY be loaded via the "Mount" button after
* the user has selected them via the "Choose File" button.
*
* this.addDisk(sDiskName, sDiskPath);
*
* So we're going to take a different approach: when displayDisk() is asked to display the name
* of a local disk image, it will map all such disks to "Local Disk", and any attempt to "Mount" such
* a disk, will essentially result in a "Disk not found" error.
*/
// this.addDiskHistory(sDiskName, sDiskPath, disk);
/*
* For a local disk (ie, one loaded via mountDrive()), the disk.restore() performed by addDiskHistory()
* may have altered the disk geometry, so refresh the disk info.
*/
// aDiskInfo = disk.info();
/*
* Clearly, a successful mount implies a disk change, and I suppose that, technically, an *unsuccessful*
* mount should imply the same, but what would the real-world analog be? Inserting a piece of cardboard
* instead of an actual disk? In any case, if we can do the user a favor by pretending (as far as the
* disk change line is concerned) that an unsuccessful mount never happened, let's do it.
*
* Successful unmounts are a different story, however; those *do* trigger a change. See unloadDrive().
*/
// this.regInput |= FDC.REG_INPUT.DISK_CHANGE;
/*
* With the addition of notify(), users are now "alerted" whenever a disk has finished loading;
* notify() is selective about its output, using print() if a print window is open, alert() otherwise.
*/
this.notice("Loaded disk \"" + sDiskName + "\" in drive " + this.getDriveName(drive.iDrive), drive.fAutoMount || fAutoMount);
/*
* Since you usually want the Computer to have focus again after loading a new disk, let's try automatically
* updating the focus after a successful load.
*/
if (this.cmp) this.cmp.setFocus();
}
else {
drive.fLocal = false;
}
if (drive.fAutoMount) {
drive.fAutoMount = false;
if (!--this.cAutoMount) this.setReady();
}
this.displayDisk(drive.iDrive);
}
/**
* addDisk(sName, sPath, fTop)
*
* @this {RK11}
* @param {string} sName
* @param {string} sPath
* @param {boolean} [fTop] (default is bottom)
*/
addDisk(sName, sPath, fTop)
{
var controlDisks = this.bindings["listDisks"];
if (controlDisks && controlDisks.options) {
for (var i = 0; i < controlDisks.options.length; i++) {
if (controlDisks.options[i].value == sPath) return;
}
var controlOption = document.createElement("option");
controlOption.text = sName;
controlOption.value = sPath;
if (fTop && controlDisks.childNodes[0]) {
controlDisks.insertBefore(controlOption, controlDisks.childNodes[0]);
} else {
controlDisks.appendChild(controlOption);
}
}
}
/**
* findDisk(sPath)
*
* This is used to deal with mount requests (eg, autoMount) that supply a path without a name;
* if we can find the path in the "listDisks" control, then we return the associated disk name.
*
* @this {RK11}
* @param {string} sPath
* @return {string|null}
*/
findDisk(sPath)
{
var controlDisks = this.bindings["listDisks"];
if (controlDisks && controlDisks.options) {
for (var i = 0; i < controlDisks.options.length; i++) {
var control = controlDisks.options[i];
if (control.value == sPath) return control.text;
}
}
return Str.getBaseName(sPath, true);
}
/**
* displayDisk(iDrive, fUpdateDrive)
*
* @this {RK11}
* @param {number} iDrive (unvalidated)
* @param {boolean} [fUpdateDrive] is true to update the drive list to match the specified drive (eg, the auto-mount case)
*/
displayDisk(iDrive, fUpdateDrive)
{
/*
* First things first: validate iDrive.
*/
if (iDrive >= 0 && iDrive < this.aDrives.length) {
var drive = this.aDrives[iDrive];
var controlDisks = this.bindings["listDisks"];
var controlDrives = this.bindings["listDrives"];
/*
* Next, make sure controls for both drives and disks exist.
*/
if (controlDisks && controlDrives && controlDisks.options && controlDrives.options) {
/*
* Next, make sure the drive whose disk we're updating is the currently selected drive.
*/
var i;
var iDriveSelected = Str.parseInt(controlDrives.value, 10);
var sTargetPath = (drive.fLocal? RK11.SOURCE.LOCAL : drive.sDiskPath);
if (!isNaN(iDriveSelected) && iDriveSelected == iDrive) {
for (i = 0; i < controlDisks.options.length; i++) {
if (controlDisks.options[i].value == sTargetPath) {
if (controlDisks.selectedIndex != i) {
controlDisks.selectedIndex = i;
}
break;
}
}
if (i == controlDisks.options.length) controlDisks.selectedIndex = 0;
}
if (fUpdateDrive) {
for (i = 0; i < controlDrives.options.length; i++) {
if (Str.parseInt(controlDrives.options[i].value, 10) == drive.iDrive) {
if (controlDrives.selectedIndex != i) {
controlDrives.selectedIndex = i;
}
break;
}
}
}
}
}
}
/**
* unloadDrive(iDrive, fLoading)
*
* @this {RK11}
* @param {number} iDrive
* @param {boolean} [fLoading]
*/
unloadDrive(iDrive, fLoading)
{
var drive = this.aDrives[iDrive];
if (drive.disk || fLoading === false) {
/*
* Before we toss the disk's information, capture any deltas that may have occurred.
*/
// this.updateDiskHistory(drive.sDiskName, drive.sDiskPath, drive.disk);
drive.sDiskName = "";
drive.sDiskPath = "";
drive.disk = null;
drive.fLocal = false;
// this.regInput |= FDC.REG_INPUT.DISK_CHANGE;
if (!fLoading) {
this.notice("Drive " + this.getDriveName(iDrive) + " unloaded", fLoading);
this.sDiskSource = RK11.SOURCE.NONE;
this.displayDisk(iDrive);
}
}
}
/**
* unloadAllDrives(fDiscard)
*
* @this {RK11}
* @param {boolean} fDiscard to discard all disk history before unloading
*/
unloadAllDrives(fDiscard)
{
// if (fDiscard) this.aDiskHistory = [];
for (var iDrive = 0; iDrive < this.aDrives.length; iDrive++) {
this.unloadDrive(iDrive, true);
}
}
/**
* initController(data)
*
* @this {RK11}
* @param {Array} [data]
* @return {boolean} true if successful, false if failure
*/
initController(data)
{
var i = 0;
if (!data) data = [];
this.regRKDS = data[i++] || (RK11.RKDS.RK05 | RK11.RKDS.SOK | RK11.RKDS.RRDY);
this.regRKER = data[i++] || 0;
this.regRKCS = data[i++] || (RK11.RKCS.CRDY);
this.regRKWC = data[i++] || 0;
this.regRKBA = data[i++] || 0;
this.regRKDA = data[i++] || 0;
this.regRKDB = data[i] || 0; // TODO: Determine if there's anything we should be doing to the RKDB register
var fSuccess = true;
for (var iDrive = 0; iDrive < this.aDrives.length; iDrive++) {
var drive = this.aDrives[iDrive];
if (drive === undefined) {
drive = this.aDrives[iDrive] = {};
}
if (!this.initDrive(drive, iDrive)) {
fSuccess = false;
}
}
return fSuccess;
}
/**
* initDrive(drive, iDrive, data)
*
* TODO: Consider a separate Drive class that any drive controller can use, since there's a lot of commonality
* between the drive objects created by disk controllers. This will clean up overall drive management and allow
* us to factor out some common Drive methods.
*
* @this {RK11}
* @param {Object} drive
* @param {number} iDrive
* @param {Array|undefined} [data]
* @return {boolean} true if successful, false if failure
*/
initDrive(drive, iDrive, data)
{
var i = 0;
var fSuccess = true;
drive.iDrive = iDrive;
drive.name = this.idComponent;
drive.fBusy = drive.fLocal = false;
/*
* NOTE: We initialize the following drive properties to their MAXIMUMs; disks may have
* these or SMALLER values (subject to the limits of what the controller supports, of course).
*/
drive.nCylinders = 203;
drive.nHeads = 2;
drive.nSectors = 12;
drive.cbSector = 512;
drive.fRemovable = true;
/*
* The next group of properties are set by various controller command sequences.
*/
drive.bHead = 0;
drive.bCylinder = 0;
drive.bSector = 1;
drive.bSectorEnd = drive.nSectors; // aka EOT
drive.nBytes = drive.cbSector;
/*
* The next group of properties are managed by worker functions (eg, doRead()) to maintain state across DMA requests.
*/
drive.ibSector = 0;
drive.sector = null;
if (!drive.disk) drive.sDiskPath = ""; // ensure this is initialized to a default that displayDisk() can deal with
drive.status = RK11.RKDS.RK05 | RK11.RKDS.SOK | RK11.RKDS.RRDY;
return fSuccess;
return [
this.regRKDS,
this.regRKER,
this.regRKCS,
this.regRKWC,
this.regRKBA,
this.regRKDA,
this.regRKDB
];
}
/**
@ -1417,20 +598,9 @@ class RK11 extends Component {
}
}
/*
* There's nothing super special about these values, except that NONE should be falsey and the others should not.
*/
RK11.SOURCE = {
NONE: "",
LOCAL: "?",
REMOTE: "??"
};
/*
* Alias RK11 definitions as class constants
*/
RK11.PRI = PDP11.RK11.PRI;
RK11.VEC = PDP11.RK11.VEC;
RK11.RKDS = PDP11.RK11.RKDS; // 177400: Drive Status Register
RK11.RKER = PDP11.RK11.RKER; // 177402: Error Register
RK11.RKCS = PDP11.RK11.RKCS; // 177404: Control Status Register

File diff suppressed because it is too large Load diff

View file

@ -5,10 +5,6 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
@ -64,7 +60,7 @@ class ROMPDP11 extends Component {
*/
constructor(parmsROM)
{
super("ROM", parmsROM, ROMPDP11, MessagesPDP11.ROM);
super("ROM", parmsROM, MessagesPDP11.ROM);
this.abInit = null;
this.aSymbols = null;
@ -268,14 +264,12 @@ class ROMPDP11 extends Component {
*/
addROM(addr)
{
this.status(this.sizeROM + "-byte ROM at " + Str.toOct(addr));
if (addr >= BusPDP11.IOPAGE_16BIT && addr < BusPDP11.IOPAGE_16BIT + BusPDP11.IOPAGE_LENGTH) {
/*
* This code has been added as a work-around to effectively allow us to install small ROMs into portions
* of the IOPAGE address space, by installing I/O handlers for the entire range that return the corresponding
* bytes of the current ROM image on reads, and ignore any writes (which I'm only assuming is how a typical
* ROM "device" deals with writes; if we remove the write handler, then writes will fault).
* ROM "device" deals with writes; we could remove the write handler, but then writes would fault).
*
* TODO: It would be more efficient if we parsed ROM data as words rather than bytes, and then installed
* only word handlers instead of only byte handlers. It was done this way purely for historical reasons (ie,
@ -286,6 +280,7 @@ class ROMPDP11 extends Component {
[addr]: [ROMPDP11.prototype.readROMByte, ROMPDP11.prototype.writeROMByte, null, null, null, this.sizeROM >> 1]
};
if (this.bus.addIOTable(this, IOTable)) {
this.status("Added " + this.sizeROM + "-byte ROM at " + Str.toOct(addr));
this.fRetainROM = true;
return true;
}

View file

@ -5,9 +5,9 @@
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
* It has been adapted from the JavaScript PDP 11/70 Emulator written by Paul Nankervis
* (paulnank@hotmail.com) at <http://skn.noip.me/pdp11/pdp11.html>. This code may be used
* freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
@ -100,7 +100,7 @@ class SerialPortPDP11 extends Component {
*/
constructor(parmsSerial)
{
super("SerialPort", parmsSerial, SerialPortPDP11, MessagesPDP11.SERIAL);
super("SerialPort", parmsSerial, MessagesPDP11.SERIAL);
this.iAdapter = +parmsSerial['adapter'];
this.nBaudReceive = +parmsSerial['baudReceive'] || PDP11.DL11.RCSR.BAUD;
@ -389,7 +389,7 @@ class SerialPortPDP11 extends Component {
if (this.sendData) {
this.fNullModem = fNullModem;
this.updateStatus = exports['receiveStatus'];
this.status(this.idMachine + '.' + sSourceID + " connected to " + sTargetID);
this.status("Connected " + this.idMachine + '.' + sSourceID + " to " + sTargetID);
return;
}
}
@ -423,7 +423,7 @@ class SerialPortPDP11 extends Component {
*/
this.initConnection(this.fNullModem);
if (!data || !this.restore) {
if (!data) {
this.reset();
} else {
if (!this.restore(data)) return false;
@ -485,30 +485,48 @@ class SerialPortPDP11 extends Component {
}
/**
* initState(data)
* initState(a)
*
* @this {SerialPortPDP11}
* @param {Array} [data]
* @param {Array} [a]
* @return {boolean} true if successful, false if failure
*/
initState(data)
initState(a)
{
this.regRBUF = 0;
this.regRCSR = PDP11.DL11.RCSR.CTS; // TODO: I didn't use to set this initially; is this wise?
this.regXCSR = PDP11.DL11.XCSR.READY;
this.abReceive = [];
if (!a) {
a = [0, PDP11.DL11.RCSR.CTS, PDP11.DL11.XCSR.READY, this.abReceive];
}
/*
* ES6 ALERT: A handy destructuring assignment, which makes it easy to perform the inverse
* of what saveRegisters() does when it collects a bunch of object properties into an array.
*/
[
this.regRBUF,
this.regRCSR,
this.regXCSR,
this.abReceive
] = a;
return true;
}
/**
* saveRegisters()
*
* Basically, the inverse of initState().
*
* @this {SerialPortPDP11}
* @return {Array}
*/
saveRegisters()
{
return [];
return [
this.regRBUF,
this.regRCSR,
this.regXCSR,
this.abReceive
];
}
/**

View file

@ -76,7 +76,7 @@
*/
class Component {
/**
* Component(type, parms, constructor, bitsMessage)
* Component(type, parms, bitsMessage)
*
* A Component object requires:
*
@ -88,14 +88,13 @@ class Component {
* name: component name (default is ""; if blank, toString() will use the type name only)
* comment: component comment string (default is undefined)
*
* Subclasses that use Component.subclass() to extend Component will likely have additional (parms) properties.
* Component subclasses will usually have additional (parms) properties.
*
* @param {string} type
* @param {Object} [parms]
* @param {Object} [constructor]
* @param {number} [bitsMessage] selects message(s) that the component wants to enable (default is 0)
*/
constructor(type, parms, constructor, bitsMessage)
constructor(type, parms, bitsMessage)
{
this.type = type;
@ -153,8 +152,8 @@ class Component {
/*
* TODO: Consider adding another parameter to the Component() constructor that allows components to tell
* us if they support single or multiple instances per machine. For example, there can be multiple SerialPort
* components per machine, but only one CPU component (well, OK, an FPU is also supported, but that's considered
* a different component).
* components per machine, but only one CPU component (some machines also support an FPU, but that component
* is considered separate from the CPU).
*
* It's not critical, but it would help catch machine configuration errors; for example, a machine that mistakenly
* includes two CPU components may, aside from wasting memory, end up with odd side-effects, like unresponsive

View file

@ -48,7 +48,7 @@ if (NODE) {
* fTemporary:(boolean|undefined),
* sCmd:(string|undefined),
* aCmds:(Array.<string>|undefined)
* }} DbgAddr
* }}
*/
var DbgAddr;
@ -94,7 +94,7 @@ class Debugger extends Component {
constructor(parmsDbg) {
if (DEBUGGER) {
super("Debugger", parmsDbg, Debugger);
super("Debugger", parmsDbg);
/*
* Default base used to display all values; modified with the "s base" command.