Fixed DL11 for RT-11, and added more message capabilities

This commit is contained in:
Jeff Parsons 2016-11-22 16:54:45 -08:00 committed by Jeff Parsons
commit 708869815d
12 changed files with 135 additions and 87 deletions

View file

@ -277,7 +277,7 @@ BusPDP11.IOController = {
}
}
if (b >= 0) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS | afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".readByte(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(b), true, !bus.nDisableFaults);
}
return b;
@ -358,7 +358,7 @@ BusPDP11.IOController = {
}
}
if (fWrite) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS | afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".writeByte(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(b) + ")", true, !bus.nDisableFaults);
}
return;
@ -397,7 +397,7 @@ BusPDP11.IOController = {
}
}
if (w >= 0) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS | afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".readWord(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(w), true, !bus.nDisableFaults);
}
return w;
@ -441,7 +441,7 @@ BusPDP11.IOController = {
}
}
if (fWrite) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS | afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".writeWord(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(w) + ")", true, !bus.nDisableFaults);
}
return;
@ -1265,7 +1265,7 @@ BusPDP11.prototype.getMemorySize = function(type)
};
/**
* addIOHandlers(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, msgCategory, sName)
* addIOHandlers(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, message, 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
@ -1285,11 +1285,11 @@ BusPDP11.prototype.getMemorySize = function(type)
* @param {function(number,number)|null|undefined} fnWriteByte
* @param {function(number)|null|undefined} fnReadWord
* @param {function(number,number)|null|undefined} fnWriteWord
* @param {number} [msgCategory]
* @param {number} [message]
* @param {string} [sName]
* @return {boolean} (true if entire range successfully registered, false if any conflicts)
*/
BusPDP11.prototype.addIOHandlers = function(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, msgCategory, sName)
BusPDP11.prototype.addIOHandlers = function(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, message, sName)
{
for (var addr = start; addr <= end; addr += 2) {
var off = addr & BusPDP11.IOPAGE_MASK;
@ -1297,7 +1297,7 @@ BusPDP11.prototype.addIOHandlers = function(start, end, fnReadByte, fnWriteByte,
Component.warning("I/O address already registered: " + str.toHexLong(addr));
return false;
}
this.aIOHandlers[off] = [fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sName || "unknown", msgCategory, false];
this.aIOHandlers[off] = [fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sName || "unknown", message || MessagesPDP11.BUS, false];
if (MAXDEBUG) this.log("addIOHandlers(" + str.toHexLong(addr) + ")");
}
return true;
@ -1356,7 +1356,7 @@ BusPDP11.prototype.addIOTable = function(component, table)
for (var iReg = 0; iReg < nRegs; iReg++, addr += 2) {
if (sReg && nRegs > 1) sReg = afn[4] + iReg;
if (!this.addIOHandlers(addr, addr, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, afn[7] || component.bitsMessage || MessagesPDP11.BUS, sReg || component.idComponent)) {
if (!this.addIOHandlers(addr, addr, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, afn[7] || component.bitsMessage, sReg || component.idComponent)) {
return false;
}
}

View file

@ -111,6 +111,7 @@ Component.subclass(CPUStatePDP11, CPUPDP11);
* @typedef {{
* vector: number,
* priority: number,
* message: number,
* next: (Trigger|null)
* }} Trigger
*/
@ -767,16 +768,17 @@ CPUStatePDP11.prototype.setSP = function(addr)
};
/**
* addTrigger(vector, priority)
* addTrigger(vector, priority, message)
*
* @this {CPUStatePDP11}
* @param {number} vector
* @param {number} priority
* @param {number} [message]
* @return {Trigger}
*/
CPUStatePDP11.prototype.addTrigger = function(vector, priority)
CPUStatePDP11.prototype.addTrigger = function(vector, priority, message)
{
var trigger = {vector: vector, priority: priority, next: null};
var trigger = {vector: vector, priority: priority, message: message || 0, next: null};
if (DEBUG) this.aTriggers.push(trigger);
return trigger;
};
@ -842,14 +844,34 @@ CPUStatePDP11.prototype.removeTrigger = function(trigger)
CPUStatePDP11.prototype.setTrigger = function(trigger)
{
this.insertTrigger(trigger);
/*
* See the writeXCSR() function for an explanation of why signalling an INTQ hardware interrupt condition
* should be done using INTQ_DELAY rather than setting INTQ directly.
*/
this.opFlags |= PDP11.OPFLAG.INTQ_DELAY;
if (trigger.message && this.messageEnabled(trigger.message | MessagesPDP11.INT)) {
this.printMessage("setInterrupt(vector=" + str.toOct(trigger.vector) + ",priority=" + trigger.priority + ")", true, true);
}
return false;
};
/**
* clearTrigger(trigger)
*
* @this {CPUStatePDP11}
* @param {Trigger} trigger
*/
CPUStatePDP11.prototype.clearTrigger = function(trigger)
{
this.removeTrigger(trigger);
if (trigger.message && this.messageEnabled(trigger.message | MessagesPDP11.INT)) {
this.printMessage("clearInterrupt(vector=" + str.toOct(trigger.vector) + ",priority=" + trigger.priority + ")", true, true);
}
};
/**
* checkTriggers(priority)
*

View file

@ -492,10 +492,10 @@ var PDP11 = {
PPS: 0o177554, // PC11 Punch Status Register
PPB: 0o177556, // PC11 Punch Buffer Register
RCSR: 0o177560, // Display Terminal: Receiver Status Register
RBUF: 0o177562, // Display Terminal: Receiver Data Buffer Register
XCSR: 0o177564, // Display Terminal: Transmitter Status Register
XBUF: 0o177566, // Display Terminal: Transmitter Data Buffer Register
RCSR: 0o177560, // DL11 Display Terminal: Receiver Status Register
RBUF: 0o177562, // DL11 Display Terminal: Receiver Data Buffer Register
XCSR: 0o177564, // DL11 Display Terminal: Transmitter Status Register
XBUF: 0o177566, // DL11 Display Terminal: Transmitter Data Buffer Register
CNSW: 0o177570, // Console (Front Panel) Switch/Display Register

View file

@ -90,7 +90,7 @@ DevicePDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
device.interruptKW11();
});
this.kw11.trigger = cpu.addTrigger(PDP11.KW11.VEC, PDP11.KW11.PRI);
this.kw11.trigger = cpu.addTrigger(PDP11.KW11.VEC, PDP11.KW11.PRI, MessagesPDP11.KW11);
bus.addIOTable(this, DevicePDP11.UNIBUS_IOTABLE);
bus.addResetHandler(this.reset.bind(this));

View file

@ -34,23 +34,30 @@
var MessagesPDP11 = {
CPU: 0x00000001,
TRAP: 0x00000010,
FAULT: 0x00000020,
BUS: 0x00000040,
MEMORY: 0x00000080,
MMU: 0x00000100,
ROM: 0x00000200,
DEVICE: 0x00000400,
PANEL: 0x00000800,
KEYBOARD: 0x00010000,
KEYS: 0x00020000,
PAPER: 0x00100000,
DISK: 0x00200000,
RK11: 0x00400000,
RL11: 0x00800000,
SERIAL: 0x01000000,
SPEAKER: 0x02000000,
COMPUTER: 0x04000000,
TRAP: 0x00000002,
FAULT: 0x00000004,
INT: 0x00000008,
BUS: 0x00000010,
MEMORY: 0x00000020,
MMU: 0x00000040,
ROM: 0x00000080,
DEVICE: 0x00000100,
PANEL: 0x00000200,
KEYBOARD: 0x00000400,
KEYS: 0x00000800,
PC11: 0x00001000,
PAPER: 0x00001000,
DISK: 0x00002000,
READ: 0x00004000,
WRITE: 0x00008000,
RK11: 0x00010000,
RL11: 0x00020000,
DL11: 0x00040000,
SERIAL: 0x00040000,
KW11: 0x00080000,
TIMER: 0x00080000,
SPEAKER: 0x01000000,
COMPUTER: 0x02000000,
LOG: 0x10000000,
WARN: 0x20000000,
BUFFER: 0x40000000,
@ -75,6 +82,7 @@ MessagesPDP11.CATEGORIES = {
"cpu": MessagesPDP11.CPU,
"trap": MessagesPDP11.TRAP,
"fault": MessagesPDP11.FAULT,
"int": MessagesPDP11.INT,
"bus": MessagesPDP11.BUS,
"memory": MessagesPDP11.MEMORY,
"mmu": MessagesPDP11.MMU,
@ -83,11 +91,17 @@ MessagesPDP11.CATEGORIES = {
"panel": MessagesPDP11.PANEL,
"keyboard": MessagesPDP11.KEYBOARD, // "kbd" is also allowed as shorthand for "keyboard"; see doMessages()
"key": MessagesPDP11.KEYS, // using "key" instead of "keys", since the latter is a method on JavasScript objects
"pc11": MessagesPDP11.PC11,
"paper": MessagesPDP11.PAPER,
"disk": MessagesPDP11.DISK,
"read": MessagesPDP11.READ,
"write": MessagesPDP11.WRITE,
"rk11": MessagesPDP11.RK11,
"rl11": MessagesPDP11.RL11,
"dl11": MessagesPDP11.DL11,
"serial": MessagesPDP11.SERIAL,
"kw11": MessagesPDP11.KW11,
"timer": MessagesPDP11.TIMER,
"speaker": MessagesPDP11.SPEAKER,
"computer": MessagesPDP11.COMPUTER,
"log": MessagesPDP11.LOG,

View file

@ -280,7 +280,7 @@ PC11.prototype.initBus = function(cmp, bus, cpu, dbg)
}
}
this.triggerReaderInterrupt = this.cpu.addTrigger(PDP11.PC11.RVEC, PDP11.PC11.PRI);
this.triggerReaderInterrupt = this.cpu.addTrigger(PDP11.PC11.RVEC, PDP11.PC11.PRI, MessagesPDP11.PC11);
this.timerReaderAdvance = this.cpu.addTimer(function readyReader() {
pc11.advanceReader();

View file

@ -290,7 +290,7 @@ RK11.prototype.initBus = function(cmp, bus, cpu, dbg)
*/
this.initController();
this.triggerInterrupt = this.cpu.addTrigger(PDP11.RK11.VEC, PDP11.RK11.PRI);
this.triggerInterrupt = this.cpu.addTrigger(PDP11.RK11.VEC, PDP11.RK11.PRI, MessagesPDP11.RK11);
bus.addIOTable(this, RK11.UNIBUS_IOTABLE);
bus.addResetHandler(this.reset.bind(this));
@ -931,6 +931,10 @@ RK11.prototype.processCommand = function()
}
addr = (((this.csr & PDP11.RK11.RKCS.MEX)) << (16 - PDP11.RK11.RKCS.SHIFT.MEX)) | this.bar;
nWords = (0x10000 - this.wcr) & 0xffff;
if (DEBUG && this.messageEnabled(MessagesPDP11.READ)) {
var pos = ((((iCylinder << 1) + iHead) * drive.nSectors) + iSector) * 256;
this.printMessage((fnReadWrite == this.readData? "readData" : "writeData") + "(pos=" + pos + ",addr=" + str.toOct(addr) + ",bytes=" + (nWords * 2) + ")", true, true);
}
fInterrupt = fnReadWrite.call(this, drive, iCylinder, iHead, iSector, nWords, addr, this.endReadWrite.bind(this));
break;
@ -952,8 +956,6 @@ RK11.prototype.processCommand = function()
}
};
/**
* endReadWrite(err, iCylinder, iHead, iSector, nWords, addr)
*
@ -1002,6 +1004,7 @@ RK11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, add
nWords = 0;
}
var sWords = "";
while (nWords--) {
if (!sector) {
sector = drive.disk.seek(iCylinder, iHead, iSector + 1);
@ -1011,12 +1014,16 @@ RK11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, add
}
ibSector = 0;
}
var b0, b1;
var b0, b1, data;
if ((b0 = drive.disk.read(sector, ibSector++)) < 0 || (b1 = drive.disk.read(sector, ibSector++)) < 0) {
err = PDP11.RK11.RKER.NXS;
break;
}
var data = this.bus.setWordDirect(addr, b0 | (b1 << 8));
this.bus.setWordDirect(addr, data = b0 | (b1 << 8));
if (DEBUG && this.messageEnabled(MessagesPDP11.READ) && sWords.length < 56) {
sWords += str.toOct(data) + ' ';
if (sWords.length >= 56) this.printMessage(sWords + '\n', true);
}
if (this.bus.checkFault()) {
err = PDP11.RK11.RKER.NXM;
break;

View file

@ -290,7 +290,7 @@ RL11.prototype.initBus = function(cmp, bus, cpu, dbg)
*/
this.initController();
this.triggerInterrupt = this.cpu.addTrigger(PDP11.RL11.VEC, PDP11.RL11.PRI);
this.triggerInterrupt = this.cpu.addTrigger(PDP11.RL11.VEC, PDP11.RL11.PRI, MessagesPDP11.RL11);
bus.addIOTable(this, RL11.UNIBUS_IOTABLE);
bus.addResetHandler(this.reset.bind(this));

View file

@ -36,6 +36,7 @@ if (NODE) {
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var Keys = require("../../shared/lib/keys");
var State = require("../../shared/lib/state");
var PDP11 = require("./defines");
var MessagesPDP11 = require("./messages");
@ -195,26 +196,24 @@ SerialPortPDP11.prototype.setBinding = function(sType, sBinding, control, sValue
this.bindings[sBinding] = this.controlIOBuffer = control;
/*
* By establishing an onkeypress handler here, we make it possible for DOS commands like
* "CTTY COM1" to more or less work (use "CTTY CON" to restore control to the DOS console).
* An onkeydown handler is required for certain keys that browsers tend to consume themselves;
* for example, BACKSPACE is often defined as going back to the previous web page, and certain
* CTRL keys are often used for browser shortcuts (usually on Windows-based browsers).
*/
control.onkeydown = function onKeyDown(event) {
/*
* This is required in addition to onkeypress, because it's the only way to prevent
* BACKSPACE (keyCode 8) from being interpreted by the browser as a "Back" operation;
* moreover, not all browsers generate an onkeypress notification for BACKSPACE.
*
* A related problem exists for Ctrl-key combinations in most Windows-based browsers
* (eg, IE, Edge, Chrome for Windows, etc), because keys like Ctrl-C and Ctrl-S have
* special meanings (eg, Copy, Save). To the extent the browser will allow it, we
* attempt to disable that default behavior when this control receives an onkeydown
* event for one of those keys (probably the only event the browser generates for them).
*/
event = event || window.event;
var fProcess = false;
var keyCode = event.keyCode;
if (keyCode === 0x08 || event.ctrlKey && keyCode >= 0x41 && keyCode <= 0x5A) {
if (keyCode == Keys.KEYCODE.BS) {
fProcess = true;
keyCode = Keys.ASCII.DEL;
}
else if (event.ctrlKey && keyCode >= Keys.ASCII.A && keyCode <= Keys.ASCII.Z) {
fProcess = true;
keyCode -= (Keys.ASCII.A - Keys.ASCII.CTRL_A);
}
if (fProcess) {
if (event.preventDefault) event.preventDefault();
if (keyCode > 0x40) keyCode -= 0x40;
serial.receiveData(keyCode);
}
return true;
@ -285,7 +284,7 @@ SerialPortPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
var serial = this;
this.triggerReceiveInterrupt = this.cpu.addTrigger(PDP11.DL11.RVEC, PDP11.DL11.PRI);
this.triggerReceiveInterrupt = this.cpu.addTrigger(PDP11.DL11.RVEC, PDP11.DL11.PRI, MessagesPDP11.DL11);
this.timerReceiveInterrupt = this.cpu.addTimer(function readyReceiver() {
if (!(serial.rcsr & PDP11.DL11.RCSR.RD)) {
@ -315,7 +314,7 @@ SerialPortPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
}
});
this.triggerTransmitInterrupt = this.cpu.addTrigger(PDP11.DL11.XVEC, PDP11.DL11.PRI);
this.triggerTransmitInterrupt = this.cpu.addTrigger(PDP11.DL11.XVEC, PDP11.DL11.PRI, MessagesPDP11.DL11);
this.timerTransmitInterrupt = this.cpu.addTimer(function readyTransmitter() {
serial.xcsr |= PDP11.DL11.XCSR.READY;
@ -584,7 +583,15 @@ SerialPortPDP11.prototype.transmitByte = function(b)
*/
if (this.iLogicalCol > 0) this.iLogicalCol--;
}
else {
else if (b) {
/*
* RT-11 outputs lots of NULL characters, at least after a "D 56=5015" (0x0A0D) command has
* been issued, hence the "if (b)" check above.
*
* TODO: Also consider a check for Keys.ASCII.CTRL_C, because by default, RT-11 outputs "raw"
* CTRL_C characters, which we capture below and render as <ETX>. RT-11 does this for other keys
* as well, such as CTRL_K (<VT>) and CTRL_L (<FF>).
*/
var s = str.toASCIICode(b); // formerly: String.fromCharCode(b);
var nChars = s.length; // formerly: (b >= 0x20? 1 : 0);
if (b < 0x20 && nChars == 1) nChars = 0;
@ -698,7 +705,7 @@ SerialPortPDP11.prototype.writeXCSR = function(data, addr)
if (data & PDP11.DL11.XCSR.TIE) {
this.cpu.setTrigger(this.triggerTransmitInterrupt);
} else {
this.cpu.removeTrigger(this.triggerTransmitInterrupt);
this.cpu.clearTrigger(this.triggerTransmitInterrupt);
}
}
this.xcsr = (this.xcsr & ~PDP11.DL11.XCSR.WMASK) | (data & PDP11.DL11.XCSR.WMASK);
@ -726,16 +733,14 @@ SerialPortPDP11.prototype.readXBUF = function(addr)
SerialPortPDP11.prototype.writeXBUF = function(data, addr)
{
data &= PDP11.DL11.XBUF.DATA;
if (data) {
this.transmitByte(data);
this.xcsr &= ~PDP11.DL11.XCSR.READY;
/*
* NOTE: When debugging issues involving the SerialPort, such as debugging code between a pair of
* transmitted bytes, you can pass 0 instead of getBaudTimeout() to setTimer() to minimize the amount
* of time spent waiting for XCSR.READY to be set again.
*/
this.cpu.setTimer(this.timerTransmitInterrupt, this.getBaudTimeout(this.nBaudTransmit));
}
this.transmitByte(data);
this.xcsr &= ~PDP11.DL11.XCSR.READY;
/*
* NOTE: When debugging issues involving the SerialPort, such as debugging code between a pair of
* transmitted bytes, you can pass 0 instead of getBaudTimeout() to setTimer() to minimize the amount
* of time spent waiting for XCSR.READY to be set again.
*/
this.cpu.setTimer(this.timerTransmitInterrupt, this.getBaudTimeout(this.nBaudTransmit));
};
/*

View file

@ -49,7 +49,7 @@ var Keys = {
'`': 96, a: 97, b: 98, c: 99, d: 100, e: 101, f: 102, g: 103,
h: 104, i: 105, j: 106, k: 107, l: 108, m: 109, n: 110, o: 111,
p: 112, q: 113, r: 114, s: 115, t: 116, u: 117, v: 118, w: 119,
x: 120, y: 121, z: 122, '{':123, '|':124, '}':125, '~':126
x: 120, y: 121, z: 122, '{':123, '|':124, '}':125, '~':126, DEL: 127
},
/*
* Browser keyCodes we must pay particular attention to. For the most part, these are non-alphanumeric
@ -62,10 +62,10 @@ var Keys = {
* them using ASCII codes (eg, the LEFT arrow key uses the ASCII code for '%' or 37).
*/
KEYCODE: {
/* 0x08 */ BS: 8,
/* 0x08 */ BS: 8, // BACKSPACE
/* 0x09 */ TAB: 9,
/* 0x0A */ LF: 10, // TODO: Determine if any key actually generates this
/* 0x0D */ CR: 13,
/* 0x0A */ LF: 10, // LINE FEED (TODO: Determine if any key actually generates this)
/* 0x0D */ CR: 13, // CARRIAGE RETURN
/* 0x10 */ SHIFT: 16,
/* 0x11 */ CTRL: 17,
/* 0x12 */ ALT: 18,