Fixed CPU start/stop control; runCPU is now strictly a worker function, not a control interface (PDP-11 only; other CPUs still need to be updated to match)

This commit is contained in:
Jeff 2016-09-28 22:21:52 -07:00 committed by Jeff Parsons
commit c7dbfc02be
18 changed files with 945 additions and 918 deletions

View file

@ -65,7 +65,7 @@ if (NODE) {
*/
function BusPDP11(parmsBus, cpu, dbg)
{
Component.call(this, "Bus", parmsBus, BusPDP11);
Component.call(this, "Bus", parmsBus, BusPDP11, MessagesPDP11.BUS);
this.cpu = cpu;
this.dbg = dbg;
@ -124,6 +124,10 @@ function BusPDP11(parmsBus, cpu, dbg)
*
* The false case is important if fIOBreakAll is set, because it allows the Debugger to selectively
* ignore specific addresses.
*
* Finally, for debugging purposes, if an I/O address has a symbolic name, it will be saved here:
*
* [5]: symbolic name of I/O address
*/
this.aIOHandlers = [];
this.fIOBreakAll = false;
@ -186,6 +190,7 @@ BusPDP11.IOController = {
var bus = this.controller;
var afn = bus.aIOHandlers[off];
if (afn) {
if (bus.messageEnabled()) bus.printMessage(afn[5] + ".readByte(" + str.toOct(addr) + ")", 0, true);
if (afn[0]) {
return afn[0](addr);
} else if (afn[2]) {
@ -197,8 +202,11 @@ BusPDP11.IOController = {
}
} else if (addr & 0x1) {
afn = bus.aIOHandlers[off & ~0x1];
if (afn[2]) {
return afn[2](addr & ~0x1) >> 8;
if (afn) {
if (bus.messageEnabled()) bus.printMessage(afn[5] + ".readByte(" + str.toOct(addr) + ")", 0, true);
if (afn[2]) {
return afn[2](addr & ~0x1) >> 8;
}
}
}
bus.println("warning: unconverted read access to byte @" + str.toOct(addr));
@ -219,6 +227,7 @@ BusPDP11.IOController = {
var bus = this.controller;
var afn = bus.aIOHandlers[off];
if (afn) {
if (bus.messageEnabled()) bus.printMessage(afn[5] + ".writeByte(" + str.toOct(addr) + "," + str.toOct(b) + ")", 0, true);
/*
* If a writeByte() handler exists, call it; we're done
*/
@ -246,11 +255,14 @@ BusPDP11.IOController = {
* data pre-inserted into (the high byte of) the original data.
*/
afn = bus.aIOHandlers[off & ~0x1];
if (afn[3]) {
addr &= ~0x1;
w = afn[2]? afn[2](addr) : 0;
afn[3]((w & 0xff) | (b << 8), addr);
return;
if (afn) {
if (bus.messageEnabled()) bus.printMessage(afn[5] + ".writeByte(" + str.toOct(addr) + "," + str.toOct(b) + ")", 0, true);
if (afn[3]) {
addr &= ~0x1;
w = afn[2]? afn[2](addr) : 0;
afn[3]((w & 0xff) | (b << 8), addr);
return;
}
}
}
bus.println("warning: unconverted write access to byte @" + str.toOct(addr));
@ -271,6 +283,7 @@ BusPDP11.IOController = {
Component.assert(!(addr & 1)); // unaligned addresses should be getting trapped at a higher level
var afn = bus.aIOHandlers[off];
if (afn) {
if (bus.messageEnabled()) bus.printMessage(afn[5] + ".readWord(" + str.toOct(addr) + ")", 0, true);
if (afn[2]) {
return afn[2](addr);
} else if (afn[0]) {
@ -295,6 +308,7 @@ BusPDP11.IOController = {
Component.assert(!(addr & 1)); // unaligned addresses should be getting trapped at a higher level
var afn = bus.aIOHandlers[off];
if (afn) {
if (bus.messageEnabled()) bus.printMessage(afn[5] + ".writeWord(" + str.toOct(addr) + "," + str.toOct(w) + ")", 0, true);
if (afn[3]) {
afn[3](w, addr);
return;
@ -972,7 +986,7 @@ BusPDP11.prototype.restoreMemory = function(a)
};
/**
* addIOHandlers(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord)
* addIOHandlers(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sName)
*
* Add I/O notification handlers to the master list (aIOHandlers). The start and end addresses are typically
* relative to the starting IOPAGE address, but they can also be absolute; we simply mask all addresses with
@ -985,8 +999,9 @@ BusPDP11.prototype.restoreMemory = function(a)
* @param {function(number,number)|null|undefined} fnWriteByte
* @param {function(number)|null|undefined} fnReadWord
* @param {function(number,number)|null|undefined} fnWriteWord
* @param {string} [sName]
*/
BusPDP11.prototype.addIOHandlers = function(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord)
BusPDP11.prototype.addIOHandlers = function(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sName)
{
for (var addr = start; addr <= end; addr += 2) {
var off = addr & BusPDP11.IOPAGE_MASK;
@ -994,7 +1009,7 @@ 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, false];
this.aIOHandlers[off] = [fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, false, sName || "unknown"];
if (MAXDEBUG) this.log("addIOHandlers(" + str.toHexLong(addr) + ")");
}
};
@ -1002,7 +1017,7 @@ BusPDP11.prototype.addIOHandlers = function(start, end, fnReadByte, fnWriteByte,
/**
* addIOTable(component, table)
*
* Add I/O notification handlers from the specified table (a batch version of addIOHandlers)
* Add I/O notification handlers from the specified table (a batch version of addIOHandlers).
*
* @this {BusPDP11}
* @param {Component} component
@ -1016,7 +1031,7 @@ BusPDP11.prototype.addIOTable = function(component, table)
var fnWriteByte = afn[1]? afn[1].bind(component) : null;
var fnReadWord = afn[2]? afn[2].bind(component) : null;
var fnWriteWord = afn[3]? afn[3].bind(component) : null;
this.addIOHandlers(+port, +port, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord);
this.addIOHandlers(+port, +port, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, afn[4]);
}
};

View file

@ -983,7 +983,7 @@ ComputerPDP11.prototype.reset = function()
*
* Notify all (other) components with a start() method that the CPU has started.
*
* Note that we're called by runCPU(), which is why we exclude the CPU component,
* Note that we're called by startCPU(), which is why we exclude the CPU component,
* as well as ourselves.
*
* @this {ComputerPDP11}
@ -1007,7 +1007,7 @@ ComputerPDP11.prototype.start = function(ms, nCycles)
*
* Notify all (other) components with a stop() method that the CPU has stopped.
*
* Note that we're called by runCPU(), which is why we exclude the CPU component,
* Note that we're called by stopCPU(), which is why we exclude the CPU component,
* as well as ourselves.
*
* @this {ComputerPDP11}

View file

@ -303,10 +303,10 @@ CPUPDP11.prototype.autoStart = function()
*/
if (this.flags.autoStart || (!DEBUGGER || !this.dbg) && this.bindings["run"] === undefined) {
/*
* Now we ALSO set fUpdateFocus when calling runCPU(), on the assumption that in the "auto-starting" context,
* a machine without focus is like a day without sunshine.
* Now we ALSO set fUpdateFocus when calling startCPU(), on the assumption that in the "auto-starting"
* context, a machine without focus is like a day without sunshine.
*/
this.runCPU(true);
this.startCPU(true);
return true;
}
return false;
@ -496,12 +496,12 @@ CPUPDP11.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
control.onclick = function onClickRun() {
if (!cpu.cmp || !cpu.cmp.checkPower()) return;
/*
* We no longer pass true to these runCPU()/stopCPU() calls, on the theory that if the "run"
* We no longer pass true to these startCPU()/stopCPU() calls, on the theory that if the "run"
* control is visible, then the computer is probably sufficiently visible as well; the problem
* with setting fUpdateFocus to true is that it can jerk the web page around in annoying ways.
*/
if (!cpu.flags.running)
cpu.runCPU();
cpu.startCPU();
else
cpu.stopCPU();
};
@ -1053,20 +1053,13 @@ CPUPDP11.prototype.endBurst = function()
};
/**
* runCPU(fUpdateFocus)
* runCPU()
*
* @this {CPUPDP11}
* @param {boolean} [fUpdateFocus] is true to update Computer focus
*/
CPUPDP11.prototype.runCPU = function(fUpdateFocus)
CPUPDP11.prototype.runCPU = function()
{
if (!this.setBusy(true)) {
this.updateCPU();
if (this.cmp) this.cmp.stop(usr.getTime(), this.getCycles());
return;
}
this.startCPU(fUpdateFocus);
if (!this.flags.running) return;
/*
* calcStartTime() initializes the cycle counter and timestamp for this runCPU() invocation, and optionally
@ -1131,42 +1124,49 @@ CPUPDP11.prototype.runCPU = function(fUpdateFocus)
this.stopCPU();
this.updateCPU();
if (this.cmp) this.cmp.stop(usr.getTime(), this.getCycles());
this.setBusy(false);
this.setError(e.stack || e.message);
return;
}
setTimeout(this.onRunTimeout, this.calcRemainingTime());
if (this.flags.running) setTimeout(this.onRunTimeout, this.calcRemainingTime());
};
/**
* startCPU(fUpdateFocus)
*
* WARNING: Other components must use runCPU() to get the CPU running; this is a runCPU() helper function only.
* For use by any component that wants to start the CPU.
*
* @param {boolean} [fUpdateFocus]
* @return {boolean}
*/
CPUPDP11.prototype.startCPU = function(fUpdateFocus)
{
if (!this.flags.running) {
/*
* setSpeed() without a speed parameter leaves the selected speed in place, but also resets the
* cycle counter and timestamp for the current series of runCPU() calls, calculates the maximum number
* of cycles for each burst based on the last known effective CPU speed, and resets the nCyclesRecalc
* threshold counter.
*/
this.setSpeed();
if (this.cmp) this.cmp.start(this.msStartRun, this.getCycles());
this.flags.running = true;
this.flags.starting = true;
if (this.chipset) this.chipset.start();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Halt";
if (this.cmp) {
this.cmp.updateStatus(true);
if (fUpdateFocus) this.cmp.updateFocus(true);
}
if (this.isError()) {
return false;
}
if (this.flags.running) {
this.println(this.toString() + " busy");
return false;
}
/*
* setSpeed() without a speed parameter leaves the selected speed in place, but also resets the
* cycle counter and timestamp for the current series of runCPU() calls, calculates the maximum number
* of cycles for each burst based on the last known effective CPU speed, and resets the nCyclesRecalc
* threshold counter.
*/
this.setSpeed();
this.flags.running = true;
this.flags.starting = true;
if (this.chipset) this.chipset.start();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Halt";
if (this.cmp) {
this.cmp.start(this.msStartRun, this.getCycles());
if (fUpdateFocus) this.cmp.updateFocus(true);
}
this.updateCPU(true);
setTimeout(this.onRunTimeout, 0);
return true;
};
/**
@ -1196,15 +1196,18 @@ CPUPDP11.prototype.stepCPU = function(nMinCycles)
*/
CPUPDP11.prototype.stopCPU = function(fComplete)
{
this.isBusy(true);
this.endBurst();
this.addCycles(this.nRunCycles);
this.nRunCycles = 0;
if (this.flags.running) {
this.endBurst();
this.addCycles(this.nRunCycles);
this.nRunCycles = 0;
this.flags.running = false;
if (this.chipset) this.chipset.stop();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Run";
if (this.cmp) {
this.cmp.stop(usr.getTime(), this.getCycles());
}
this.updateCPU();
}
this.flags.complete = fComplete;
};

View file

@ -912,8 +912,8 @@ CPUStatePDP11.prototype.trap = function(vector, reason)
this.trapPSW = -1; // reset flag that we have a trap within a trap
if (DEBUG && this.dbg) {
if (reason != PDP11.REASON.INTERRUPT) {
this.dbg.println("trap to vector " + this.dbg.toBase(vector, 0, true) + (reason? " (reason " + reason + ")" : ""));
if (this.messageEnabled(MessagesPDP11.TRAP)) {
this.printMessage("trap to vector " + this.dbg.toBase(vector, 0, true) + (reason? " (reason " + reason + ")" : ""), MessagesPDP11.TRAP, true);
}
}
@ -1719,7 +1719,11 @@ CPUStatePDP11.prototype.stepCPU = function(nMinCycles)
this.opFlags &= ~PDP11.OPFLAG.TRAP_MASK;
}
if (this.priorityReview) {
/*
* By requiring nMinCycles to be non-zero before checking the interrupt queue, we avoid
* interrupting the natural flow of instructions when the Debugger is stepping through code.
*/
if (nMinCycles && this.priorityReview) {
this.checkInterruptQueue();
}

View file

@ -1053,7 +1053,7 @@ if (DEBUGGER) {
*
* @this {DebuggerPDP11}
* @param {string} sMessage is any caller-defined message string
* @param {boolean} [fAddress] is true to display the current CS:IP
* @param {boolean} [fAddress] is true to display the current PC
*/
DebuggerPDP11.prototype.message = function(sMessage, fAddress)
{
@ -1151,16 +1151,16 @@ if (DEBUGGER) {
};
/**
* runCPU(fUpdateFocus)
* startCPU(fUpdateFocus)
*
* @this {DebuggerPDP11}
* @param {boolean} [fUpdateFocus] is true to update focus
* @return {boolean} true if run request successful, false if not
*/
DebuggerPDP11.prototype.runCPU = function(fUpdateFocus)
DebuggerPDP11.prototype.startCPU = function(fUpdateFocus)
{
if (!this.isCPUAvail()) return false;
this.cpu.runCPU(fUpdateFocus);
if (!this.checkCPU()) return false;
this.cpu.startCPU(fUpdateFocus);
return true;
};
@ -1175,7 +1175,7 @@ if (DEBUGGER) {
*/
DebuggerPDP11.prototype.stepCPU = function(nCycles, fRegs, fUpdateCPU)
{
if (!this.isCPUAvail()) return false;
if (!this.checkCPU()) return false;
this.nCycles = 0;
@ -1212,7 +1212,7 @@ if (DEBUGGER) {
}
/*
* Because we called cpu.stepCPU() and not cpu.runCPU(), we must nudge the cpu's update code,
* Because we called cpu.stepCPU() and not cpu.startCPU(), we must nudge the cpu's update code,
* and then update our own state. Normally, the only time fUpdateCPU will be false is when doTrace()
* is calling us in a loop, in which case it will perform its own updateCPU() when it's done.
*/
@ -1257,14 +1257,14 @@ if (DEBUGGER) {
};
/**
* isCPUAvail()
* checkCPU()
*
* Make sure the CPU is ready (finished initializing), not busy (already running), and not in an error state.
* Make sure the CPU is ready (finished initializing), powered, not already running, and not in an error state.
*
* @this {DebuggerPDP11}
* @return {boolean}
*/
DebuggerPDP11.prototype.isCPUAvail = function()
DebuggerPDP11.prototype.checkCPU = function()
{
if (!this.cpu)
return false;
@ -1272,7 +1272,7 @@ if (DEBUGGER) {
return false;
if (!this.cpu.isPowered())
return false;
if (this.cpu.isBusy())
if (this.cpu.isRunning())
return false;
return !this.cpu.isError();
};
@ -3209,7 +3209,7 @@ if (DEBUGGER) {
this.parseAddrOptions(dbgAddr, sOptions);
this.setTempBreakpoint(dbgAddr);
}
if (!this.runCPU(true)) {
if (!this.startCPU(true)) {
if (!fQuiet) this.println("cpu busy or unavailable, run command ignored");
}
};
@ -3268,7 +3268,7 @@ if (DEBUGGER) {
if (this.nStep) {
this.setTempBreakpoint(dbgAddr);
if (!this.runCPU()) {
if (!this.startCPU()) {
if (this.cmp) this.cmp.updateFocus();
this.nStep = 0;
}

View file

@ -218,6 +218,9 @@ var PDP11 = {
PIRQ: 0xA0, // 240 PIRQ, program interrupt request
MMU_FAULT: 0xA8 // 250 MMU aborts and traps
},
/*
* PDP-11 trap reasons (for diagnostic purposes only)
*/
REASON: {
BPT: 1,
EMT: 2,
@ -225,18 +228,18 @@ var PDP11 = {
IOT: 4,
TRAP: 5,
RESERVED: 6,
ODDMEMADDR: 22,
NOMEMORY: 24,
ODDMMUADDR: 26,
MAPERROR: 28,
PUSHERROR: 32,
NOREGADDR: 34,
STACKMODE1: 36,
STACKERROR: 38,
INTERRUPT: 44,
TRAPMMU: 52,
TRAPSP: 54,
TRAPTF: 56
ODDMEMADDR: 10,
NOMEMORY: 12,
ODDMMUADDR: 14,
MAPERROR: 16,
PUSHERROR: 18,
NOREGADDR: 20,
STACKMODE1: 22,
STACKERROR: 24,
INTERRUPT: 26,
TRAPMMU: 28,
TRAPSP: 30,
TRAPTF: 32
},
/*
* Internal memory access flags

View file

@ -199,7 +199,7 @@ DevicePDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
switch(this.sDeviceName) {
case DevicePDP11.UNIBUS_NAME:
default:
bus.addIOTable(this, DevicePDP11.UNIBUS_TABLE);
bus.addIOTable(this, DevicePDP11.UNIBUS_IOTABLE);
bus.addIODefaultHandlers(this.reset.bind(this), this.access.bind(this));
break;
}
@ -951,8 +951,8 @@ DevicePDP11.prototype.access = function(physicalAddress, data, byteFlag)
case 0x3FFFC0: /*017777700*/ // 017777700 - 017777777
switch (physicalAddress & ~1) {
//
// Superseded by UNIBUS_TABLE (more of this code will be commented out
// as it is replaced by read/write handlers in UNIBUS_TABLE; stay tuned).
// Superseded by UNIBUS_IOTABLE (more of this code will be commented out
// as it is replaced by read/write handlers in UNIBUS_IOTABLE; stay tuned).
//
// case 0x3FFFFE: // 017777776 // PSW
// result = cpu.getPSW();
@ -1537,12 +1537,12 @@ DevicePDP11.prototype.access = function(physicalAddress, data, byteFlag)
/*
* ES6 ALERT: As you can see below, I've finally started using computed property names.
*/
DevicePDP11.UNIBUS_TABLE = {
[PDP11.UNIBUS.LKS]: /* 177546 */ [null, null, DevicePDP11.prototype.readLKS, DevicePDP11.prototype.writeLKS],
[PDP11.UNIBUS.RCSR]: /* 177560 */ [null, null, DevicePDP11.prototype.readRCSR, DevicePDP11.prototype.writeRCSR],
[PDP11.UNIBUS.XCSR]: /* 177564 */ [null, null, DevicePDP11.prototype.readXCSR, DevicePDP11.prototype.writeXCSR],
[PDP11.UNIBUS.MMR0]: /* 177572 */ [null, null, DevicePDP11.prototype.readMMR0, DevicePDP11.prototype.writeMMR0],
[PDP11.UNIBUS.PSW]: /* 177776 */ [null, null, DevicePDP11.prototype.readPSW, DevicePDP11.prototype.writePSW]
DevicePDP11.UNIBUS_IOTABLE = {
[PDP11.UNIBUS.LKS]: /* 177546 */ [null, null, DevicePDP11.prototype.readLKS, DevicePDP11.prototype.writeLKS, "LKS"],
[PDP11.UNIBUS.RCSR]: /* 177560 */ [null, null, DevicePDP11.prototype.readRCSR, DevicePDP11.prototype.writeRCSR, "RCSR"],
[PDP11.UNIBUS.XCSR]: /* 177564 */ [null, null, DevicePDP11.prototype.readXCSR, DevicePDP11.prototype.writeXCSR, "XCSR"],
[PDP11.UNIBUS.MMR0]: /* 177572 */ [null, null, DevicePDP11.prototype.readMMR0, DevicePDP11.prototype.writeMMR0, "MMR0"],
[PDP11.UNIBUS.PSW]: /* 177776 */ [null, null, DevicePDP11.prototype.readPSW, DevicePDP11.prototype.writePSW, "PSW"]
};
/**

View file

@ -35,6 +35,7 @@
var MessagesPDP11 = {
CPU: 0x00000001,
TRAP: 0x00000010,
BUS: 0x00000040,
MEM: 0x00000080,
KEYBOARD: 0x00010000,
@ -65,6 +66,7 @@ var MessagesPDP11 = {
*/
MessagesPDP11.CATEGORIES = {
"cpu": MessagesPDP11.CPU,
"trap": MessagesPDP11.TRAP,
"bus": MessagesPDP11.BUS,
"mem": MessagesPDP11.MEM,
"keyboard": MessagesPDP11.KEYBOARD, // "kbd" is also allowed as shorthand for "keyboard"; see doMessages()

View file

@ -949,7 +949,7 @@ Component.prototype = {
/**
* setBusy(fBusy)
*
* Update the current busy state; if an fCancel request is pending, it will be honored now.
* Update the current busy state; if a busyCancel request is pending, it will be honored now.
*
* @this {Component}
* @param {boolean} fBusy
@ -957,9 +957,7 @@ Component.prototype = {
*/
setBusy: function(fBusy) {
if (this.flags.busyCancel) {
if (this.flags.busy) {
this.flags.busy = false;
}
this.flags.busy = false;
this.flags.busyCancel = false;
return false;
}