More Front Panel improvements

This commit is contained in:
Jeff 2016-11-02 13:35:50 -07:00 committed by Jeff Parsons
commit d5c7bf0b5d
23 changed files with 1514 additions and 1281 deletions

View file

@ -117,14 +117,16 @@ function BusPDP11(parmsBus, cpu, dbg)
* Memory access handlers must service the entire block; see the setAccess() function in the Memory
* component for details.
*
* Finally, for debugging purposes, if an I/O address has a symbolic name, it will be saved here:
* Finally, for debugging purposes, if an I/O address has a symbolic name and message category,
* they will be saved here:
*
* [4]: symbolic name of I/O address
* [5]: message category
*
* UPDATE: The Debugger wants to piggy-back on these arrays to indicate addresses for which it wants
* notification. In those cases, the following additional element will be set:
*
* [5]: true to break on I/O, false to ignore I/O
* [6]: true to break on I/O, false to ignore I/O
*
* The false case is important if fIOBreakAll is set, because it allows the Debugger to selectively
* ignore specific addresses.
@ -183,7 +185,9 @@ BusPDP11.IOHANDLER = {
WRITE_BYTE: 1,
READ_WORD: 2,
WRITE_WORD: 3,
NAME: 4
NAME: 4,
MSG_CATEGORY: 5,
DBG_BREAK: 6
};
/*
@ -257,14 +261,14 @@ BusPDP11.IOController = {
}
}
if (b >= 0) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".readByte(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(b), true, true);
}
return b;
}
bus.fault(addr, PDP11.CPUERR.TIMEOUT, PDP11.ACCESS.READ_BYTE);
b = 0xff;
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage("warning: unconverted read access to byte @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(b), true, true);
}
return b;
@ -331,13 +335,13 @@ BusPDP11.IOController = {
}
}
if (fWrite) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".writeByte(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(b) + ")", true, true);
}
return;
}
bus.fault(addr, PDP11.CPUERR.TIMEOUT, PDP11.ACCESS.WRITE_BYTE);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage("warning: unconverted write access to byte @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(b), true, true);
}
},
@ -363,14 +367,14 @@ BusPDP11.IOController = {
}
}
if (w >= 0) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".readWord(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(w), true, true);
}
return w;
}
bus.fault(addr, PDP11.CPUERR.TIMEOUT, PDP11.ACCESS.READ_WORD);
w = 0xffff;
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage("warning: unconverted read access to word @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(w), true, true);
}
return w;
@ -400,13 +404,13 @@ BusPDP11.IOController = {
}
}
if (fWrite) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".writeWord(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(w) + ")", true, true);
}
return;
}
bus.fault(addr, PDP11.CPUERR.TIMEOUT, PDP11.ACCESS.WRITE_WORD);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage("warning: unconverted write access to word @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(w), true, true);
}
}
@ -1172,8 +1176,9 @@ BusPDP11.prototype.restoreMemory = function(a)
* @param {function(number)|null|undefined} fnReadWord
* @param {function(number,number)|null|undefined} fnWriteWord
* @param {string} [sName]
* @param {number} [msgCategory]
*/
BusPDP11.prototype.addIOHandlers = function(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sName)
BusPDP11.prototype.addIOHandlers = function(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sName, msgCategory)
{
for (var addr = start; addr <= end; addr += 2) {
var off = addr & BusPDP11.IOPAGE_MASK;
@ -1181,21 +1186,22 @@ BusPDP11.prototype.addIOHandlers = function(start, end, fnReadByte, fnWriteByte,
Component.warning("I/O address already registered: " + str.toHexLong(addr));
continue;
}
this.aIOHandlers[off] = [fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sName || "unknown", false];
this.aIOHandlers[off] = [fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sName || "unknown", msgCategory, false];
if (MAXDEBUG) this.log("addIOHandlers(" + str.toHexLong(addr) + ")");
}
};
/**
* addIOTable(component, table)
* addIOTable(component, table, msgCategory)
*
* Add I/O notification handlers from the specified table (a batch version of addIOHandlers).
*
* @this {BusPDP11}
* @param {Component} component
* @param {Object} table
* @param {number} [msgCategory] (default is BUS)
*/
BusPDP11.prototype.addIOTable = function(component, table)
BusPDP11.prototype.addIOTable = function(component, table, msgCategory)
{
for (var port in table) {
var addr = +port;
@ -1237,7 +1243,7 @@ BusPDP11.prototype.addIOTable = function(component, table)
var sReg = afn[4];
for (var iReg = 0; iReg < nRegs; iReg++, addr += 2) {
if (sReg && nRegs > 1) sReg = afn[4] + iReg;
this.addIOHandlers(addr, addr, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sReg);
this.addIOHandlers(addr, addr, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sReg, msgCategory || MessagesPDP11.BUS);
}
}
};
@ -1267,9 +1273,8 @@ BusPDP11.prototype.fault = function(addr, err, access)
{
this.fFault = true;
if (!this.nDisableFaults) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.FAULT)) {
this.dbg.printMessage("memory fault (" + access + ") on address " + this.dbg.toStrBase(addr), true, true);
this.dbg.stopInstruction();
}
if (err) this.cpu.regErr |= err;
this.cpu.trap(PDP11.TRAP.BUS_ERROR, addr);

View file

@ -186,7 +186,6 @@ CPUPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
this.cmp = cmp;
this.bus = bus;
this.dbg = dbg;
this.panel = cmp.panel;
for (var i = 0; i < CPUPDP11.BUTTONS.length; i++) {
var control = this.bindings[CPUPDP11.BUTTONS[i]];
@ -283,6 +282,9 @@ CPUPDP11.prototype.powerUp = function(data, fRepower)
* is now responsible for managing a component's fPowered flag, not us.
*
* this.flags.powered = true;
*
* And we don't have to worry whether this.cmp is set, because powerUp() handlers are never called
* before initBus() handlers.
*/
this.cmp.updateStatus();
return true;
@ -1077,7 +1079,7 @@ CPUPDP11.prototype.runCPU = function()
nCycles = this.endBurst(true);
/*
* Add nCycles to nCyclesThisRun, as well as nRunCycles (the cycle count since the CPU first started).
* Add nCycles to nCyclesThisRun, as well as nRunCycles (the cycle count since the CPU started).
*/
this.nCyclesThisRun += nCycles;
this.nRunCycles += nCycles;
@ -1207,7 +1209,7 @@ CPUPDP11.prototype.yieldCPU = function()
* odd for those messages to show CPU state changes if the Control Panel, Video display, etc, does not,
* so I've added this call to try to keep things looking synchronized.
*/
this.cmp.updateStatus();
if (this.cmp) this.cmp.updateStatus();
};
if (NODE) module.exports = CPUPDP11;

View file

@ -1760,7 +1760,7 @@ PDP11.opWAIT = function(opCode)
/*
* Since here we're actually transitioning to WAIT, let's update the Panel's LEDs (well, OK, among other things).
*/
this.cmp.updateStatus();
if (this.cmp) this.cmp.updateStatus();
}
this.opFlags |= PDP11.OPFLAG.WAIT;
this.advancePC(-2);

View file

@ -310,7 +310,6 @@ CPUStatePDP11.prototype.setMMR0 = function(newMMR0)
this.mmuEnable = mmuEnable;
this.setMemoryAccess();
}
if (this.panel) this.panel.updateStatus();
}
};
@ -358,7 +357,6 @@ CPUStatePDP11.prototype.setMMR3 = function(newMMR3)
this.regMMR3 = newMMR3;
this.mmuMask = (newMMR3 & PDP11.MMR3.MMU_22BIT)? 0x3fffff : 0x3ffff;
this.setMemoryAccess();
if (this.panel) this.panel.updateStatus();
}
};

View file

@ -43,8 +43,8 @@ if (DEBUGGER) {
var State = require("../../shared/lib/state");
var PDP11 = require("./defines");
var CPUPDP11 = require("./cpu");
var MessagesPDP11 = require("./messages");
var MemoryPDP11 = require("./memory");
var MessagesPDP11 = require("./messages");
}
}
@ -1212,7 +1212,7 @@ if (DEBUGGER) {
if (this.sMessagePrev && sMessage == this.sMessagePrev) return;
this.sMessagePrev = sMessage;
if (this.bitsMessage & MessagesPDP11.HALT) {
if ((this.bitsMessage & MessagesPDP11.HALT) && this.cpu && this.cpu.isRunning()) {
this.stopCPU();
sMessage += " (cpu halted)";
}
@ -1334,7 +1334,7 @@ if (DEBUGGER) {
nCycles = this.cpu.getBurstCycles(nCycles);
var nCyclesStep = this.cpu.stepCPU(nCycles);
if (nCyclesStep > 0) {
this.cpu.updateTimers(nCycles);
this.cpu.updateTimers(nCyclesStep);
this.nCycles += nCyclesStep;
this.cpu.addCycles(nCyclesStep, true);
this.cpu.updateChecksum(nCyclesStep);
@ -1358,7 +1358,13 @@ if (DEBUGGER) {
* and then update our own state. Normally, the only time fUpdateStatus will be false is when doTrace()
* is calling us in a loop, in which case it will perform its own updateStatus() when it's done.
*/
if (fUpdateStatus !== false) this.cmp.updateStatus();
if (fUpdateStatus !== false) {
/*
* Make an effort to keep any Front Panel in sync with us.
*/
if (this.cmp.panel && this.cmp.panel.stop) this.cmp.panel.stop();
this.cmp.updateStatus();
}
this.updateStatus(fRegs || false);
return (this.nCycles > 0);
@ -3660,6 +3666,7 @@ if (DEBUGGER) {
* with displayLiveRegs enabled, so once the repeat count has been exhausted, we must
* perform a final updateStatus().
*/
if (dbg.cmp.panel && dbg.cmp.panel.stop) dbg.cmp.panel.stop();
dbg.cmp.updateStatus();
dbg.setBusy(false);
}

View file

@ -590,7 +590,6 @@ MemoryPDP11.prototype = {
readNone: function readNone(off, addr) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEMORY) /* && !off */) {
this.dbg.printMessage("attempt to read invalid address " + this.dbg.toStrBase(addr), true);
this.dbg.stopInstruction();
}
this.bus.fault(addr, PDP11.CPUERR.NOMEMORY, PDP11.ACCESS.READ);
return 0xff;
@ -606,7 +605,6 @@ MemoryPDP11.prototype = {
writeNone: function writeNone(off, v, addr) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEMORY) /* && !off */) {
this.dbg.printMessage("attempt to write " + this.dbg.toStrBase(v) + " to invalid addresses " + this.dbg.toStrBase(addr), true);
this.dbg.stopInstruction();
}
this.bus.fault(addr, PDP11.CPUERR.NOMEMORY, PDP11.ACCESS.WRITE);
},

View file

@ -35,6 +35,7 @@
var MessagesPDP11 = {
CPU: 0x00000001,
TRAP: 0x00000010,
FAULT: 0x00000020,
BUS: 0x00000040,
MEMORY: 0x00000080,
DEVICE: 0x00000100,
@ -67,6 +68,7 @@ var MessagesPDP11 = {
MessagesPDP11.CATEGORIES = {
"cpu": MessagesPDP11.CPU,
"trap": MessagesPDP11.TRAP,
"fault": MessagesPDP11.FAULT,
"bus": MessagesPDP11.BUS,
"memory": MessagesPDP11.MEMORY,
"device": MessagesPDP11.DEVICE,

View file

@ -71,7 +71,7 @@ function PanelPDP11(parmsPanel)
*
* regAddr is an internal register containing the contents of the Front Panel's ADDRESS display,
* and regData corresponds to the DATA display. They are updated by setAddr() and setData(),
* which in turn take care of calling updateLEDArray().
* which in turn take care of calling setLEDArray().
*/
this.regCNSW = 0;
this.regAddr = this.regData = 0;
@ -82,28 +82,28 @@ function PanelPDP11(parmsPanel)
*
* 0 if off, 1 if on
*
* initBus() will call updateLEDs() to ensure that every LED is set to its initial value.
* initBus() will call displayLEDs() to ensure that every LED is set to its initial value.
*/
this.leds = {};
/*
* Every switch has an array associated with it:
*
* [0]: initial/current value of switch (0 if down, 1 if up)
* [0]: initial/current value of switch (0 if "down", 1 if "up")
* [1]: true if the switch is momentary, false if not
* [2]: true if the switch is currently being pressed, false if not
* [2]: true if the switch is currently pressed, false if released
* [3]: optional handler to call whenever the switch is pressed or released
* [4]: optional switch index (used with CNSW switches 'S0' through 'S21')
*
* initBus() will call updateSwitches() to ensure that every switch is the position represented below.
* initBus() will call displaySwitches() to ensure that every switch is the position represented below.
*
* NOTE: Not all switches have the same "process" criteria. For example, 'TEST' will perform a lamp test
* when it is momentarily pressed "up", whereas 'LOAD [ADRS]' will load the address register from the switch
* when it is momentarily pressed "up", whereas 'LOAD [ADRS]' will load the ADDRESS register from the SWITCH
* register when it is momentarily pressed "down".
*
* This means that processLampTest(value) must act when value == 1, whereas processLoadAddr(value) must
* act when value == 0. You can infer all this from the table below, because the initial value of any
* momentary switch is its "passive" value, so the opposite is its "active" value.
* This means that processLampTest(value) must act when value == 1 ("up"), whereas processLoadAddr(value)
* must act when value == 0 ("down"). You can infer all this from the table below, because the initial value
* of any momentary switch is its "inactive" value, so the opposite is its "active" value.
*/
this.switches = {
'START': [1, true, false, this.processStart],
@ -127,9 +127,9 @@ Component.subclass(PanelPDP11);
*
* this.getSwitch(PanelPDP11.SWITCH.ENABLE)
*
* However, I haven't filled out this table, primarily because the only switches we need to query
* are the non-momentary ones (eg, ENABLE and STEP), and EXAM and DEP (because they have special
* "step" behavior when pressed more than once in a row).
* I haven't filled out this table, primarily it only needs to list switches we actually query
* (eg, non-momentary ones like 'ENABLE' and 'STEP', and 'EXAM' and 'DEP' since they have special
* "step" behavior when pressed more than once in a row). Ditto for the LED table.
*/
PanelPDP11.SWITCH = {
DEP: 'DEP',
@ -138,6 +138,12 @@ PanelPDP11.SWITCH = {
STEP: 'STEP'
};
PanelPDP11.LED = {
B16: 'B16',
B18: 'B18',
B22: 'B22'
};
/**
* getSwitch(name)
*
@ -275,8 +281,8 @@ PanelPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
bus.addIOTable(this, PanelPDP11.UNIBUS_IOTABLE);
bus.addResetHandler(this.reset.bind(this));
this.updateLEDs();
this.updateSwitches();
this.displayLEDs();
this.displaySwitches();
};
/**
@ -301,7 +307,11 @@ PanelPDP11.prototype.powerUp = function(data, fRepower)
* currently unnecessary, since we've added reset() to the addResetHandler() list.
*
* this.reset();
*
* In the meantime, simulate a call to our stop() handler, to update the panel's ADDRESS register
* with the new PC.
*/
this.stop();
}
return true;
};
@ -320,7 +330,66 @@ PanelPDP11.prototype.powerDown = function(fSave, fShutdown)
};
/**
* updateValue(sLabel, nValue, cch)
* displayLED(sBinding, value)
*
* @this {PanelPDP11}
* @param {string} sBinding
* @param {boolean|number} value (true or non-zero if the LED should be on, false or zero if off)
*/
PanelPDP11.prototype.displayLED = function(sBinding, value)
{
var control = this.bindings[sBinding];
if (control) {
/*
* TODO: Add support for user-definable LED colors?
*/
control.style.backgroundColor = (value? "#ff0000" : "#000000");
}
};
/**
* displayLEDs(override)
*
* @this {PanelPDP11}
* @param {boolean|number|null} [override] (true turn on all LEDs, false to turn off all LEDs, null or undefined for normal LED activity)
*/
PanelPDP11.prototype.displayLEDs = function(override)
{
for (var sBinding in this.leds) {
this.displayLED(sBinding, override != null? override : this.leds[sBinding]);
}
};
/**
* displaySwitch(sBinding, value)
*
* @this {PanelPDP11}
* @param {string} sBinding
* @param {boolean|number} value (true if the switch should be "up" (on), false if "down" (off))
*/
PanelPDP11.prototype.displaySwitch = function(sBinding, value)
{
var control = this.bindings[sBinding];
if (control) {
control.style.marginTop = (value? "0px" : "20px");
control.style.backgroundColor = (value? "#00ff00" : "#228B22");
}
};
/**
* displaySwitches()
*
* @this {PanelPDP11}
*/
PanelPDP11.prototype.displaySwitches = function()
{
for (var sBinding in this.switches) {
this.displaySwitch(sBinding, this.switches[sBinding][0]);
}
};
/**
* displayValue(sLabel, nValue, cch)
*
* This is principally for displaying register values, but in reality, it can be used to display any
* numeric value bound to the given label.
@ -330,7 +399,7 @@ PanelPDP11.prototype.powerDown = function(fSave, fShutdown)
* @param {number} nValue
* @param {number} [cch]
*/
PanelPDP11.prototype.updateValue = function(sLabel, nValue, cch)
PanelPDP11.prototype.displayValue = function(sLabel, nValue, cch)
{
if (this.bindings[sLabel]) {
if (nValue === undefined) {
@ -354,69 +423,6 @@ PanelPDP11.prototype.updateValue = function(sLabel, nValue, cch)
}
};
/**
* setLED(sBinding, value)
*
* @this {PanelPDP11}
* @param {string} sBinding
* @param {boolean|number} value (true if the LED should be on, false if off)
*/
PanelPDP11.prototype.setLED = function(sBinding, value)
{
var control = this.bindings[sBinding];
if (control) {
/*
* TODO: Add support for user-definable LED colors?
*/
control.style.backgroundColor = (value? "#ff0000" : "#000000");
}
};
/**
* updateLEDs(override)
*
* @this {PanelPDP11}
* @param {boolean|number|null} [override] (true turn on all LEDs, false to turn off all LEDs, null or undefined for normal LED activity)
*/
PanelPDP11.prototype.updateLEDs = function(override)
{
for (var sBinding in this.leds) {
this.setLED(sBinding, override != null? override : this.leds[sBinding]);
}
};
/**
* updateLEDArray(sPrefix, data, nLEDs)
*
* @this {PanelPDP11}
* @param {string} sPrefix
* @param {number} data
* @param {number} nLEDs
*/
PanelPDP11.prototype.updateLEDArray = function(sPrefix, data, nLEDs)
{
for (var i = 0; i < nLEDs; i++) {
var sBinding = sPrefix + i;
this.setLED(sBinding, data & (1 << i));
}
};
/**
* setSwitch(sBinding, value)
*
* @this {PanelPDP11}
* @param {string} sBinding
* @param {boolean|number} value (true if the switch should be "up" (on), false if "down" (off))
*/
PanelPDP11.prototype.setSwitch = function(sBinding, value)
{
var control = this.bindings[sBinding];
if (control) {
control.style.marginTop = (value? "0px" : "20px");
control.style.backgroundColor = (value? "#00ff00" : "#228B22");
}
};
/**
* pressSwitch(sBinding)
*
@ -426,9 +432,22 @@ PanelPDP11.prototype.setSwitch = function(sBinding, value)
PanelPDP11.prototype.pressSwitch = function(sBinding)
{
var sw = this.switches[sBinding];
this.setSwitch(sBinding, sw[0] = 1 - sw[0]);
/*
* Set the new switch value in sw[0] and then immediately display it
*/
this.displaySwitch(sBinding, (sw[0] = 1 - sw[0]));
/*
* Mark the switch as "pressed"
*/
sw[2] = true;
/*
* Call the appropriate process handler with the current switch value (sw[0])
*/
if (sw[3]) sw[3].call(this, sw[0], sw[4]);
this.fDeposit = (sBinding == PanelPDP11.SWITCH.DEP);
this.fExamine = (sBinding == PanelPDP11.SWITCH.EXAM);
};
@ -448,13 +467,23 @@ PanelPDP11.prototype.releaseSwitch = function(sBinding)
* we receive EITHER of those events AND the switch is marked momentary (sw[1]) AND the switch is pressed (sw[2]),
* then we must flip the switch back to its original value.
*
* Otherwise, the only thing we have to do is mark the switch as no longer "pressed" (ie, set sw[2] to false).
* Otherwise, the only thing we have to do is mark the switch as "released" (ie, set sw[2] to false).
*/
var sw = this.switches[sBinding];
if (sw[1] && sw[2]) {
this.setSwitch(sBinding, sw[0] = 1 - sw[0]);
/*
* Set the new switch value in sw[0] and then immediately display it
*/
this.displaySwitch(sBinding, (sw[0] = 1 - sw[0]));
/*
* Call the appropriate process handler with the current switch value (sw[0])
*/
if (sw[3]) sw[3].call(this, sw[0], sw[4]);
}
/*
* Mark the switch as "released"
*/
sw[2] = false;
};
@ -499,6 +528,10 @@ PanelPDP11.prototype.processStart = function(value, index)
*/
PanelPDP11.prototype.processStep = function(value, index)
{
/*
* There's really nothing for us to do here, because the normal press and release handlers
* already record the state of this switch, so it can be queried as needed, using getSwitch().
*/
};
/**
@ -532,20 +565,60 @@ PanelPDP11.prototype.processEnable = function(value, index)
PanelPDP11.prototype.processContinue = function(value, index)
{
if (!value && !this.cpu.isRunning()) {
/*
* TODO: Technically, we're also supposed to check the 'STEP' switch to determine if we should
* step one instruction or just one cycle, but we don't currently have the ability to do the latter.
*/
if (!this.getSwitch(PanelPDP11.SWITCH.ENABLE)) {
/*
* TODO: Technically, we're also supposed to check the 'STEP' switch to determine if we should
* step one instruction or just one cycle, but we don't currently have the ability to do the latter.
* Using the Debugger's stepCPU() function is more convenient, and has the pleasant side-effect
* of updating the debugger's display; however, not all machines with a Front Panel will necessarily
* also have the Debugger loaded.
*/
var dbg = this.dbg;
if (dbg && !dbg.isBusy(true)) {
dbg.setBusy(true);
dbg.stepCPU(0);
dbg.setBusy(false);
} else {
this.cpu.stepCPU(1);
this.updateStatus();
}
else {
/*
* For this tiny single-instruction burst, mimic what runCPU() does.
*/
try {
var nCyclesStep = this.cpu.stepCPU(1);
if (nCyclesStep > 0) {
this.cpu.updateTimers(nCyclesStep);
this.cpu.addCycles(nCyclesStep, true);
this.cpu.updateChecksum(nCyclesStep);
}
}
catch(exception) {
/*
* We assume that any numeric exception was explicitly thrown by the CPU to interrupt the
* current instruction. For all other exceptions, we attempt a stack dump.
*/
if (typeof exception != "number") {
var e = exception;
this.cpu.setError(e.stack || e.message);
}
}
}
/*
* Simulate a call to our stop() handler, to update the panel's ADDRESS register with the new PC.
*/
this.stop();
/*
* Going through the normal channels (ie, the Computer's updateStatus() interface) ensures that ALL
* updateStatus() handlers will be called, including ours.
*
* NOTE: If we used the Debugger's stepCPU() function, then that includes a call to updateStatus();
* unfortunately, it will have happened BEFORE we called stop() to update the ADDRESS register, so we
* still need to call it again.
*/
if (this.cmp) this.cmp.updateStatus();
}
else {
this.cpu.startCPU();
@ -610,7 +683,7 @@ PanelPDP11.prototype.processLoadAddr = function(value, index)
*/
PanelPDP11.prototype.processLampTest = function(value, index)
{
this.updateLEDs(value || null);
this.displayLEDs(value || null);
};
/**
@ -639,7 +712,7 @@ PanelPDP11.prototype.processSwitchReg = function(value, index)
PanelPDP11.prototype.setAddr = function(value)
{
this.regAddr = value & this.bus.nBusMask;
this.updateLEDArray("A", this.regAddr, 22);
this.setLEDArray("A", this.regAddr, 22);
return this.regAddr;
};
@ -653,13 +726,64 @@ PanelPDP11.prototype.setAddr = function(value)
PanelPDP11.prototype.setData = function(value)
{
this.regData = value & 0xffff;
this.updateLEDArray("D", this.regData, 16);
this.setLEDArray("D", this.regData, 16);
return this.regData;
};
/**
* setLED(sBinding, value)
*
* @this {PanelPDP11}
* @param {string} sBinding
* @param {number} value
* @return {number}
*/
PanelPDP11.prototype.setLED = function(sBinding, value)
{
this.leds[sBinding] = value;
if (!this.fLampTest) this.displayLED(sBinding, value);
return value;
};
/**
* setLEDArray(sPrefix, data, nLEDs)
*
* @this {PanelPDP11}
* @param {string} sPrefix
* @param {number} data
* @param {number} nLEDs
*/
PanelPDP11.prototype.setLEDArray = function(sPrefix, data, nLEDs)
{
for (var i = 0; i < nLEDs; i++) {
var sBinding = sPrefix + i;
this.setLED(sBinding, data & (1 << i));
}
};
/**
* stop(ms, nCycles)
*
* This is a notification handler, called by the Computer, to inform us the CPU has now stopped.
*
* @this {PanelPDP11}
* @param {number} [ms]
* @param {number} [nCycles]
*/
PanelPDP11.prototype.stop = function(ms, nCycles)
{
this.setAddr(this.cpu.regsGen[7]);
/*
* TODO: Consider an option to call setData() with the current opcode as well; presumably that wouldn't be
* normal Front Panel behavior, but it could be useful for debugging.
*/
};
/**
* updateStatus(fForce)
*
* Called by the Computer component at appropriate intervals to update any register displays, LEDs, etc.
*
* @this {PanelPDP11}
* @param {boolean} [fForce] (true will display registers even if the CPU is running and "live" registers are not enabled)
*/
@ -668,39 +792,27 @@ PanelPDP11.prototype.updateStatus = function(fForce)
if (this.cLiveRegs) {
if (fForce || !this.cpu.isRunning() || this.fDisplayLiveRegs) {
for (var i = 0; i < this.cpu.regsGen.length; i++) {
this.updateValue('R'+i, this.cpu.regsGen[i]);
this.displayValue('R'+i, this.cpu.regsGen[i]);
}
var regPSW = this.cpu.getPSW();
this.updateValue("PS", regPSW);
this.updateValue("NF", (regPSW & PDP11.PSW.NF)? 1 : 0, 1);
this.updateValue("ZF", (regPSW & PDP11.PSW.ZF)? 1 : 0, 1);
this.updateValue("VF", (regPSW & PDP11.PSW.VF)? 1 : 0, 1);
this.updateValue("CF", (regPSW & PDP11.PSW.CF)? 1 : 0, 1);
if (!this.fLampTest) {
this.setData(this.cpu.regsGen[0]);
this.setAddr(this.cpu.regsGen[7]);
/*
* Set bit to 1 (22-bit), 2 (18-bit), or 4 (16-bit)
*/
var bit = this.cpu.mmuEnable? ((this.cpu.regMMR3 & PDP11.MMR3.MMU_22BIT)? 1 : 2) : 4;
// this.misc = (this.misc & ~7) | bit;
}
this.displayValue("PS", regPSW);
this.displayValue("NF", (regPSW & PDP11.PSW.NF)? 1 : 0, 1);
this.displayValue("ZF", (regPSW & PDP11.PSW.ZF)? 1 : 0, 1);
this.displayValue("VF", (regPSW & PDP11.PSW.VF)? 1 : 0, 1);
this.displayValue("CF", (regPSW & PDP11.PSW.CF)? 1 : 0, 1);
this.setLEDArray("D", this.regData, 16);
this.setLEDArray("A", this.regAddr, 22);
/*
* Set bit to 1 (22-bit), 2 (18-bit), or 4 (16-bit)
*/
var bit = this.cpu.mmuEnable? ((this.cpu.regMMR3 & PDP11.MMR3.MMU_22BIT)? 1 : 2) : 4;
this.setLED(PanelPDP11.LED.B22, bit & 1);
this.setLED(PanelPDP11.LED.B18, bit & 2);
this.setLED(PanelPDP11.LED.B16, bit & 4);
}
}
};
/**
* updateSwitches()
*
* @this {PanelPDP11}
*/
PanelPDP11.prototype.updateSwitches = function()
{
for (var sBinding in this.switches) {
this.setSwitch(sBinding, this.switches[sBinding][0]);
}
};
/**
* readCNSW(addr)
*

View file

@ -55,16 +55,16 @@ function RL11(parms)
Component.call(this, "RL11", parms, RL11, MessagesPDP11.DISK);
/*
* We record any 'autoMount' object now, but we no longer parse it until initBus(), because the
* Computer's getMachineParm() service may have an override for us.
* We record any 'autoMount' object now, but we no longer parse it until initBus(),
* because the Computer's getMachineParm() service may have an override for us.
*/
this.configMount = parms['autoMount'] || null;
this.cAutoMount = 0;
/*
* TODO: Make this configurable
* The RL11 has two Drive Select bits, representing up to four drives.
*/
this.nDrives = 1;
this.nDrives = 4;
this.aDrives = new Array(this.nDrives);
this.fLocalDisks = (!web.isMobile() && window && 'FileReader' in window);
@ -282,7 +282,7 @@ RL11.prototype.initBus = function(cmp, bus, cpu, dbg)
this.triggerInterrupt = this.cpu.addTrigger(PDP11.RL11.VEC, PDP11.RL11.PRI);
bus.addIOTable(this, RL11.UNIBUS_IOTABLE);
bus.addIOTable(this, RL11.UNIBUS_IOTABLE, MessagesPDP11.DISK);
this.addDisk("None", RL11.SOURCE.NONE, true);
if (this.fLocalDisks) this.addDisk("Local Disk", RL11.SOURCE.LOCAL);

View file

@ -129,9 +129,14 @@
color: #ffffff;
background-color: #404040;
}
.pcjs-tripled {
.pcjs-triplet {
padding: 1px;
}
.pcjs-ledlbl {
text-align: center;
font-size: 40%;
background-color: #000000;
}
.pcjs-ledlbl0 {
text-align: right;
font-size: 50%;
@ -147,6 +152,8 @@
font-size: x-small;
line-height: 32px;
background-color: #000000;
border-bottom-left-radius: 20%;
border-bottom-right-radius: 20%;
}
.pcjs-led {
float: left;
@ -173,8 +180,10 @@
.pcjs-swlbl {
text-align: center;
font-size: 40%;
line-height: 32px;
line-height: 16px;
background-color: #000000;
border-top-left-radius: 20%;
border-top-right-radius: 20%;
}
.pcjs-swpad {
height: 32px;