Merge branch 'next-release'

This commit is contained in:
Jeff Parsons 2016-11-22 16:55:53 -08:00
commit 08efc44370
264 changed files with 2318 additions and 421 deletions

View file

@ -716,7 +716,7 @@ DiskDump.API = function(aParms)
{
var sDisk = aParms[DumpAPI.QUERY.DISK];
var sFormat = aParms[DumpAPI.QUERY.FORMAT] || DumpAPI.FORMAT.JSON;
var fComments = (aParms[DumpAPI.QUERY.COMMENTS]? true : false);
var fComments = (!!aParms[DumpAPI.QUERY.COMMENTS]);
if (sDisk) {
var disk = new DiskDump(sDisk, null, sFormat, fComments);
@ -823,7 +823,7 @@ DiskDump.outputDisk = function(err, disk, sDiskPath, sOutputFile, fOverwrite, sM
* We'll dump JSON to the console, but not a raw disk buffer; we could add an option to
* "stringify" buffers, but if that's what the caller wants, they should use "--format=json".
*/
if (typeof data == "string") {
if (typeof data === "string") {
DiskDump.logConsole(data);
} else {
DiskDump.logConsole("specify --output={file} to save " + cbDisk + "-byte disk image");

View file

@ -9,13 +9,10 @@ PDP-11 Machine Emulation Module (PDPjs)
Overview
---
PDPjs, our PDP-11 machine emulation module, is adapted from
PDPjs, our [PDP-11 Machine](/devices/pdp11/machine/) emulation module, is adapted from
the [PDP-11/70 Emulator (v1.4)](http://skn.noip.me/pdp11/pdp11.html) written by
Paul Nankervis.
See the list of available [PDP-11 Machines](/devices/pdp11/machine/), which includes a
[PDP-11/70 with Front Panel](/devices/pdp11/machine/1170/panel/debugger/) and [PDP-11/70 with VT100 Terminal](/devices/pdp11/machine/1170/vt100/debugger/).
PDPjs is currently comprised of the following non-shared components, as listed in
[package.json](../../package.json) (see the *pdp11Files* property):
@ -34,6 +31,7 @@ PDPjs is currently comprised of the following non-shared components, as listed i
* [serialport.js](/modules/pdp11/lib/serialport.js)
* [pc11.js](/modules/pdp11/lib/pc11.js)
* [disk.js](/modules/pdp11/lib/disk.js)
* [rk11.js](/modules/pdp11/lib/rk11.js)
* [rl11.js](/modules/pdp11/lib/rl11.js)
* [debugger.js](/modules/pdp11/lib/debugger.js)
* [computer.js](/modules/pdp11/lib/computer.js)

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

@ -1270,7 +1270,7 @@ if (DEBUGGER) {
if (this.sMessagePrev && sMessage == this.sMessagePrev) return;
this.sMessagePrev = sMessage;
var fRunning = false;
var fRunning;
if ((this.bitsMessage & MessagesPDP11.HALT) && this.cpu && (fRunning = this.cpu.isRunning()) || this.isBusy(true)) {
this.stopCPU();
if (fRunning) sMessage += " (cpu halted)";
@ -3000,10 +3000,11 @@ if (DEBUGGER) {
}
sDumpers += ",state,symbols";
this.println("dump memory commands:");
this.println("\tdb [a] [#] dump # bytes at address a");
this.println("\tdw [a] [#] dump # words at address a");
this.println("\tdd [a] [#] dump # dwords at address a");
this.println("\tdh [#] [#] dump # instructions from history");
this.println("\tdb [a] [n] dump n bytes at address a");
this.println("\tdw [a] [n] dump n words at address a");
this.println("\tdd [a] [n] dump n dwords at address a");
this.println("\tds [a] [n] dump n words at address a as JSON");
this.println("\tdh [p] [n] dump n instructions from history position p");
if (sDumpers.length) this.println("dump extension commands:\n\t" + sDumpers);
return;
}

View file

@ -476,6 +476,15 @@ var PDP11 = {
RLMP: 0o174406, // RL11 Multi-Purpose Register
RLBE: 0o174410, // RL11 Bus (Address) Extension Register (RLV12 controller only)
RKDS: 0o177400, // RK11 Drive Status Register
RKER: 0o177402, // RK11 Error Register
RKCS: 0o177404, // RK11 Control Status Register
RKWC: 0o177406, // RK11 Word Count Register
RKBA: 0o177410, // RK11 Bus Address Register
RKDA: 0o177412, // RK11 Disk Address Register
// NOTE: 177414 is unused
RKDB: 0o177416, // RK11 Data Buffer Register
LKS: 0o177546, // KW11-L Clock Status
PRS: 0o177550, // PC11 (and PR11) Reader Status Register
@ -483,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
@ -644,6 +653,84 @@ var PDP11 = {
BAUD: 600
},
},
RK11: { // RK11 Disk Controller
PRI: 5,
VEC: 0o220,
RKDS: { // 177400: Drive Status Register
SC: 0x000F, // Sector Counter
SCESA: 0x0010, // Sector Counter Equals Sector Address
WPS: 0x0020, // Write Protected Status (set if write-protected)
RRDY: 0x0040, // Read/Write/Seek Ready
DRDY: 0x0080, // Drive Ready
SOK: 0x0100, // Sector Counter OK
SIN: 0x0200, // Seek Incomplete
DRU: 0x0400, // Drive Unsafe
RK05: 0x0800, // RK05 is the selected disk drive (always set)
DPL: 0x1000, // Drive Power Low
ID: 0xE000, // Drive ID (logical drive number of an interrupting drive)
SHIFT: {
ID: 13
}
},
RKER: { // 177402: Error Register
WCE: 0x0001, // Write Check Error
CSE: 0x0002, // Checksum Error
UNUSED: 0x001C, // unused (returns zero)
NXS: 0x0020, // Non-Existent Sector
NXC: 0x0040, // Non-Existent Cylinder
NXD: 0x0080, // Non-Existent Disk
TE: 0x0100, // Timing Error
DLT: 0x0200, // Date Late
NXM: 0x0400, // Non-Existent Memory
PGE: 0x0800, // Programming Error
SKE: 0x1000, // Seek Error
WLO: 0x2000, // Write Lock-Out Violation
OVR: 0x4000, // Overrun
DRE: 0x8000 // Drive Error
},
RKCS: { // 177404: Control Status Register
GO: 0x0001, // Go (W/O)
FUNC: 0x000E, // Function Code (F2,F1,F0) (R/W)
MEX: 0x0030, // Memory Extension (R/W)
IE: 0x0040, // Interrupt Enable (R/W)
CRDY: 0x0080, // Controller Ready (R/O)
SSE: 0x0100, // Stop on Soft Error (R/W)
EXB: 0x0200, // Extra Bit (R/W)
FMT: 0x0400, // Format (R/W)
IBA: 0x0800, // Inhibit RKBA Increment (R/W)
SCP: 0x2000, // Search Complete (R/O)
HE: 0x4000, // Hard Error (R/O)
ERR: 0x8000, // Composite Error (R/O) (set when any RKER bit is set)
UNUSED: 0x1200, // unused
RMASK: 0xEFFE, // bits readable
WMASK: 0x0F7F, // bits writable
SHIFT: {
FUNC: 1,
MEX: 4
}
},
RKDA: { // 177412: Disk Address Register
SA: 0x000F, // Sector Address
HS: 0x0010, // Head Select (aka SUR: clear for upper disk head, set for lower)
CA: 0x1FE0, // Cylinder Address (aka CYL ADDR)
DS: 0xE000, // Drive Select (aka DR SEL)
SHIFT: {
HS: 4,
CA: 5,
DS: 13
}
},
FUNC: { // NOTE: These function codes are pre-shifted to read/write directly from/to RKCS.FUNC
CRESET: 0b0000, // Controller Reset
WRITE: 0b0010, // Write
READ: 0b0100, // Read
WCHK: 0b0110, // Write Check
SEEK: 0b1000, // Seek
RCHK: 0b1010, // Read Check
DRESET: 0b1100, // Drive Reset
WLOCK: 0b1110 // Write Lock
}
},
RL11: { // RL11 Disk Controller
PRI: 5,
VEC: 0o160,
@ -663,6 +750,7 @@ var PDP11 = {
WMASK: 0x03FE, // bits writable
SHIFT: {
FUNC: 1,
BAE: 4,
DS: 8
}
},

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));
@ -1167,6 +1167,10 @@ DevicePDP11.init = function()
device = new RL11(parmsDevice);
Component.bindComponentControls(device, eDevice, PDP11.APPCLASS);
break;
case 'rk11':
device = new RK11(parmsDevice);
Component.bindComponentControls(device, eDevice, PDP11.APPCLASS);
break;
}
}
};

View file

@ -73,7 +73,7 @@ if (NODE) {
*
* @constructor
* @extends Component
* @param {RL11} controller
* @param {RK11|RL11} controller
* @param {Object} drive
* @param {string} mode
*/

View file

@ -34,22 +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: 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,
@ -74,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,
@ -82,10 +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();

1296
modules/pdp11/lib/rk11.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -58,7 +58,7 @@ function RL11(parms)
* 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.configMount = parms['autoMount'] || {};
this.cAutoMount = 0;
/*
@ -256,23 +256,33 @@ RL11.prototype.initBus = function(cmp, bus, cpu, dbg)
this.cpu = cpu;
this.dbg = dbg;
this.configMount = this.cmp.getMachineParm('autoMount') || this.configMount;
if (this.configMount) {
if (typeof this.configMount == "string") {
var configMount = this.cmp.getMachineParm('autoMount');
if (configMount) {
if (typeof configMount == "string") {
try {
/*
* The most likely source of any exception will be right here, where we're parsing
* this JSON-encoded data.
*/
this.configMount = eval("(" + this.configMount + ")");
configMount = eval("(" + configMount + ")");
} catch (e) {
Component.error("RL11 auto-mount error: " + e.message + " (" + this.configMount + ")");
this.configMount = null;
Component.error(this.type + " auto-mount error: " + e.message + " (" + configMount + ")");
configMount = null;
}
}
}
/*
* 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
@ -280,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));
@ -442,22 +452,20 @@ RL11.prototype.saveController = function()
RL11.prototype.autoMount = function(fRemount)
{
if (!fRemount) this.cAutoMount = 0;
if (this.configMount) {
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;
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) + ")");
}
this.notice("Incorrect auto-mount settings for drive " + sDrive + " (" + JSON.stringify(configDrive) + ")");
}
return !!this.cAutoMount;
};
@ -980,7 +988,7 @@ RL11.prototype.processCommand = function()
RL11.prototype.endReadWrite = function(err, iCylinder, iHead, iSector, nWords, addr)
{
this.bar = addr & 0xffff;
this.csr = (this.csr & ~PDP11.RL11.RLCS.BAE) | ((addr >> 12) & PDP11.RL11.RLCS.BAE);
this.csr = (this.csr & ~PDP11.RL11.RLCS.BAE) | ((addr >> (16 - PDP11.RL11.RLCS.SHIFT.BAE)) & PDP11.RL11.RLCS.BAE);
this.ber = (addr >> 16) & PDP11.RL11.RLBE.MASK; // 22 bit mode
this.dar = (iCylinder << PDP11.RL11.RLDA.SHIFT.RW_CA) | (iHead? PDP11.RL11.RLDA.RW_HS : 0) | (iSector & PDP11.RL11.RLDA.RW_SA);
this.darInternal = this.dar;

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

@ -95,6 +95,7 @@ DiskAPI.DISK_FORMATS = {
/*
* Assorted DEC disk pack formats.
*/
2494464: [203,2,12,512], // RK03 single-platter disk cartridge: 203 tracks, 2 heads, 12 sectors/track, 512 bytes/sector, for a total of 2494464 bytes
5242880: [256,2,40,256], // RL01K single-platter disk cartridge: 256 tracks, 2 heads, 40 sectors/track, 256 bytes/sector, for a total of 5242880 bytes
10485760:[512,2,40,256] // RL02K single-platter disk cartridge: 512 tracks, 2 heads, 40 sectors/track, 256 bytes/sector, for a total of 10485760 bytes
};

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,