Merge branch 'master' into gh-pages
This commit is contained in:
commit
0c08b72d99
57 changed files with 2704 additions and 2829 deletions
|
|
@ -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, """);
|
||||
this.aCommandDefs[aCommands[iCommand]] = aCommands[iCommand+1].replace(/\n[ \t]*/g, " ").trim().replace(/"/g, """);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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'];
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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'];
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class Panel8080 extends Component {
|
|||
*/
|
||||
constructor(parmsPanel)
|
||||
{
|
||||
super("Panel", parmsPanel, Panel8080);
|
||||
super("Panel", parmsPanel);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ class RAM8080 extends Component {
|
|||
*/
|
||||
constructor(parmsRAM)
|
||||
{
|
||||
super("RAM", parmsRAM, RAM8080);
|
||||
super("RAM", parmsRAM);
|
||||
|
||||
this.abInit = null;
|
||||
this.aSymbols = null;
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ class ROM8080 extends Component {
|
|||
*/
|
||||
constructor(parmsROM)
|
||||
{
|
||||
super("ROM", parmsROM, ROM8080);
|
||||
super("ROM", parmsROM);
|
||||
|
||||
this.abROM = null;
|
||||
this.addrROM = parmsROM['addr'];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
1274
modules/pdp11/lib/drive.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue