Merge branch 'next-release'
This commit is contained in:
commit
31d634eb72
28 changed files with 652 additions and 509 deletions
|
|
@ -975,6 +975,7 @@ MarkOut.prototype.convertMDLinks = function(sBlock)
|
|||
} else {
|
||||
sURL = net.encodeURL(sURL, this.req, this.fDebug);
|
||||
}
|
||||
sURL = sURL.replace(/_/g, "%5F"); // this helps prevent emphasis detection in URLs
|
||||
sBlock = str.replaceAll(aMatch[0], '<' + sTag + ' ' + sType + '="' + sURL + '"' + sTitle + '>' + sText + '</' + sTag + '>', sBlock);
|
||||
}
|
||||
return sBlock;
|
||||
|
|
|
|||
|
|
@ -1043,7 +1043,7 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
|||
kbd.addActiveKey(simCode);
|
||||
};
|
||||
}(this, sBinding, Keyboard.SOFTCODES[sBinding]);
|
||||
var fnUp = function (kbd, sKey, simCode) {
|
||||
var fnUp = function(kbd, sKey, simCode) {
|
||||
return function onKeyboardBindingUp(event) {
|
||||
kbd.removeActiveKey(simCode);
|
||||
};
|
||||
|
|
@ -1655,7 +1655,7 @@ Keyboard.prototype.injectKeysFromBuffer = function(msDelay)
|
|||
this.addActiveKey(ch, true);
|
||||
}
|
||||
if (this.sInjectBuffer.length > 0) {
|
||||
setTimeout(function (kbd) {
|
||||
setTimeout(function(kbd) {
|
||||
return function onInjectKeyTimeout() {
|
||||
kbd.injectKeysFromBuffer(msDelay);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,29 +9,94 @@ PDP-11 Machine Emulation Module (PDPjs)
|
|||
|
||||
Overview
|
||||
---
|
||||
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.
|
||||
PDPjs, our [PDP-11 Machine](/devices/pdp11/machine/) emulation module, was written in 2016. It was adapted from
|
||||
the [PDP-11/70 Emulator (v1.4)](http://skn.noip.me/pdp11/pdp11.html) written by Paul Nankervis.
|
||||
|
||||
PDPjs is currently comprised of the following non-shared components, as listed in
|
||||
[package.json](../../package.json) (see the *pdp11Files* property):
|
||||
PDPjs is currently comprised of the following PDP-11 components, as listed in [package.json](../../package.json)
|
||||
(see the *pdp11Files* property):
|
||||
|
||||
* [bus.js](/modules/pdp11/lib/bus.js)
|
||||
* [computer.js](/modules/pdp11/lib/computer.js)
|
||||
* [cpu.js](/modules/pdp11/lib/cpu.js)
|
||||
* [cpuops.js](/modules/pdp11/lib/cpuops.js)
|
||||
* [cpustate.js](/modules/pdp11/lib/cpustate.js)
|
||||
* [debugger.js](/modules/pdp11/lib/debugger.js)
|
||||
* [defines.js](/modules/pdp11/lib/defines.js)
|
||||
* [device.js](/modules/pdp11/lib/device.js)
|
||||
* [disk.js](/modules/pdp11/lib/disk.js)
|
||||
* [keyboard.js](/modules/pdp11/lib/keyboard.js)
|
||||
* [memory.js](/modules/pdp11/lib/memory.js)
|
||||
* [messages.js](/modules/pdp11/lib/messages.js)
|
||||
* [panel.js](/modules/pdp11/lib/panel.js)
|
||||
* [bus.js](/modules/pdp11/lib/bus.js)
|
||||
* [device.js](/modules/pdp11/lib/device.js)
|
||||
* [memory.js](/modules/pdp11/lib/memory.js)
|
||||
* [cpu.js](/modules/pdp11/lib/cpu.js)
|
||||
* [cpustate.js](/modules/pdp11/lib/cpustate.js)
|
||||
* [cpuops.js](/modules/pdp11/lib/cpuops.js)
|
||||
* [rom.js](/modules/pdp11/lib/rom.js)
|
||||
* [ram.js](/modules/pdp11/lib/ram.js)
|
||||
* [keyboard.js](/modules/pdp11/lib/keyboard.js)
|
||||
* [serialport.js](/modules/pdp11/lib/serialport.js)
|
||||
* [pc11.js](/modules/pdp11/lib/pc11.js)
|
||||
* [disk.js](/modules/pdp11/lib/disk.js)
|
||||
* [ram.js](/modules/pdp11/lib/ram.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)
|
||||
* [rom.js](/modules/pdp11/lib/rom.js)
|
||||
* [serialport.js](/modules/pdp11/lib/serialport.js)
|
||||
|
||||
Since this module was written in 2016, it seemed appropriate to start adopting some of the more useful features of
|
||||
[ECMAScript](http://www.ecma-international.org/ecma-262/6.0/index.html) 2015 (aka ES6), including:
|
||||
|
||||
* Classes
|
||||
* *const* and *let*
|
||||
* Computed Properties
|
||||
* Default Parameters
|
||||
* Octal and Binary Constants
|
||||
* Template Literals
|
||||
- String Interpolation (i.e., ${*expr*})
|
||||
* New Built-in Methods
|
||||
- String.repeat()
|
||||
* *import* and *export*
|
||||
|
||||
However, I've still configured the Closure Compiler to "transpile" to ECMAScript 5 (aka ES5), because some people
|
||||
may still be using older browsers that don't support ES6 -- or at least the subset of ES6 features I'm currently
|
||||
using.
|
||||
|
||||
Eventually, I need to do some performance testing and determine whether the ES6 version performs any faster and/or
|
||||
consumes fewer resources than the ES5 version. If it does, then I should either bite the bullet and generate ES6 code,
|
||||
or generate both versions and use a loader that detects the browser's capabilities and loads the appropriate version.
|
||||
|
||||
Caveats
|
||||
-------
|
||||
|
||||
### Shared modules
|
||||
|
||||
All PCjs machines rely on shared modules that are normally stored in [/shared/lib](/shared/lib/). However, until
|
||||
ALL the machines have been converted to use ES6 classes, shared code must now exist in two flavors:
|
||||
[/shared/lib](/shared/lib/) and [/shared/es6](/shared/es6/).
|
||||
|
||||
Once all the other machines have been converted to use ES6 classes, the shared ES6 code will be folded back into
|
||||
[/shared/lib](/shared/lib/), and the temporary ES6 folder will go away. Obviously, there is incentive for me to do
|
||||
this sooner rather than later, since in the interim, I must make any changes to shared code in both places.
|
||||
|
||||
There's also a less obvious problem: if you load a web page that attempts to load two or more PCjs machines, one of
|
||||
which uses [/shared/lib](/shared/lib/) and another of which uses [/shared/es6](/shared/es6/), at least one of them will
|
||||
fail to start, because the two sets of shared code cannot coexist. Well, they *could* have coexisted if I had been
|
||||
willing to change the names of all the shared global objects (like **Component**), but I wasn't.
|
||||
|
||||
### *import* and *export*
|
||||
|
||||
With regard to *import* and *export* statements, the main reason I use them is to inform my development environment
|
||||
(WebStorm) about each file's dependencies, thereby preventing inspection warnings. And ultimately, I plan to make PDPjs
|
||||
run as a Node application, so explicitly declaring all imports and exports will be required, but for now, it's just
|
||||
a web application, so strictly speaking, they're not required.
|
||||
|
||||
When loading uncompiled PDPjs files into a web browser, the Node-based web server bundled with PCjs still relies on
|
||||
the `<script>` tag to load all JavaScript files, and as far as I know, no browser currently knows what to do with the
|
||||
*import* and *export* keywords under those conditions. Chrome, for example, will immediately throw an exception when
|
||||
it encounters a file containing them.
|
||||
|
||||
As a work-around, the bundled web server intercepts all requests for .js files and inserts line comments in front of
|
||||
every *import* and *export* statement, so that your web browser won't barf on them. The statements are completely
|
||||
superfluous anyway, since the web server generates `<script>` tags for all the necessary scripts, in the order they are
|
||||
listed in [package.json](../../package.json).
|
||||
|
||||
This work-around assumes that all *export* statements appear AFTER the object they're exporting; e.g.:
|
||||
|
||||
export default ComputerPDP11;
|
||||
|
||||
and NOT as part of the object declaration; e.g.:
|
||||
|
||||
export default class ComputerPDP11 extends Component { ... }
|
||||
|
||||
|
|
|
|||
|
|
@ -494,7 +494,7 @@ class BusPDP11 extends Component {
|
|||
var block = this.aBusBlocks[iBlock];
|
||||
info.cbTotal += block.size;
|
||||
if (block.size) {
|
||||
info.aBlocks.push(Usr.initBitFields(BlockInfoPDP11, iBlock, 0, 0, block.type));
|
||||
info.aBlocks.push(/** @type {BlockInfoPDP11} */ (Usr.initBitFields(BlockInfoPDP11, iBlock, 0, 0, block.type)));
|
||||
info.cBlocks++
|
||||
}
|
||||
iBlock++;
|
||||
|
|
@ -982,7 +982,7 @@ class BusPDP11 extends Component {
|
|||
var fnWriteWord = afn[3]? afn[3].bind(component) : null;
|
||||
|
||||
/*
|
||||
* As discussed in the IOController comments above, when handlers are being registered for these
|
||||
* As discussed in the IOController comments below, when handlers are being registered for these
|
||||
* BYTE-granular UNIBUS addresses, we must install custom fallback handlers for all BYTE accesses.
|
||||
*/
|
||||
if (addr >= PDP11.UNIBUS.R0SET0 && addr <= PDP11.UNIBUS.R6USER) {
|
||||
|
|
@ -1131,12 +1131,14 @@ class BusPDP11 extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
BusPDP11.IOPAGE_16BIT = 0xE000; /*000160000*/ // eg, PDP-11/20
|
||||
BusPDP11.IOPAGE_18BIT = 0x3E000; /*000760000*/ // eg, PDP-11/45
|
||||
BusPDP11.IOPAGE_16BIT = 0x00E000; /*000160000*/ // eg, PDP-11/20
|
||||
BusPDP11.IOPAGE_18BIT = 0x03E000; /*000760000*/ // eg, PDP-11/45
|
||||
BusPDP11.IOPAGE_22BIT = 0x3FE000; /*017760000*/ // eg, PDP-11/70
|
||||
BusPDP11.IOPAGE_LENGTH = 0x2000; // ie, 8Kb
|
||||
BusPDP11.IOPAGE_LENGTH = 0x002000; // ie, 8Kb
|
||||
BusPDP11.IOPAGE_MASK = BusPDP11.IOPAGE_LENGTH - 1;
|
||||
|
||||
BusPDP11.MASK_18BIT = 0x03FFFF; /*000777777*/
|
||||
|
||||
BusPDP11.UNIBUS_22BIT = 0x3C0000; /*017000000*/
|
||||
BusPDP11.MASK_22BIT = 0x3FFFFF; /*017777777*/
|
||||
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ import MemoryPDP11 from "./memory";
|
|||
* queue would constantly grow and shrink as requests were issued and dispatched, and as long as there was something
|
||||
* in the queue, the CPU was constantly examining it.
|
||||
*
|
||||
* Now we are trying something more efficient. First, for devices that require delays (like a serial port's receiver
|
||||
* and transmitter buffer registers that are supposed to "clock" the data in and out at a specific baud rate), the
|
||||
* Now we are trying something more efficient. First, for devices that require delays (like the SerialPort's receiver
|
||||
* and transmitter buffer registers, which are supposed to "clock" the data in and out at a specific baud rate), the
|
||||
* CPU offers timer services that will "fire" a callback after a specified delay, which are much more efficient than
|
||||
* requiring the CPU to dive into an interrupt queue and decrement delay counts on every instruction.
|
||||
*
|
||||
|
|
@ -337,11 +337,29 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
this.mmuLastPage = 0;
|
||||
this.mmuMask = 0x3ffff;
|
||||
|
||||
this.addrLast = 0; // this is queried by the Panel when it's not using its own ADDRESS register
|
||||
this.opLast = 0; // stores the PC and any auto-incs or auto-decs from the last opcode; used to update MMR1 and MMR2
|
||||
/*
|
||||
* This is queried and displayed by the Panel when it's not displaying its own ADDRESS register
|
||||
* (which takes precedence when, for example, you've manually halted the CPU and are independently
|
||||
* examining the contents of other addresses).
|
||||
*
|
||||
* We initialize it to whatever the current PC is, because according to @paulnank's pdp11.js: "Reset
|
||||
* displays next instruction address" and initMMU() is called on a RESET.
|
||||
*/
|
||||
this.addrLast = this.regsGen[7];
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
this.opLast = 0;
|
||||
|
||||
this.resetIRQs();
|
||||
|
||||
/*
|
||||
* As initCPU() explains, we shouldn't be calling this function until well after initBus() has been
|
||||
* called, but we still make absolutely sure we have Bus access.
|
||||
*/
|
||||
if (this.bus) {
|
||||
this.setMemoryAccess();
|
||||
this.addrInvalid = this.bus.getMemoryLimit(MemoryPDP11.TYPE.RAM);
|
||||
|
|
@ -479,8 +497,8 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
getMMR1()
|
||||
{
|
||||
/*
|
||||
* If updates to MMR1 have not been shut off (ie, MMR0.ABORT bits are clear),
|
||||
* then we are allowed to sync MMR1 with its real-time counterpart in opLast.
|
||||
* If updates to MMR1 have not been shut off (ie, MMR0.ABORT bits are clear), then we are allowed
|
||||
* to sync MMR1 with its real-time counterpart in opLast.
|
||||
*/
|
||||
if (!(this.regMMR0 & PDP11.MMR0.ABORT)) {
|
||||
this.regMMR1 = (this.opLast >> 16) & 0xffff;
|
||||
|
|
@ -501,8 +519,8 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
getMMR2()
|
||||
{
|
||||
/*
|
||||
* If updates to MMR2 have not been shut off (ie, MMR0.ABORT bits are clear),
|
||||
* then we are allowed to sync MMR2 with its real-time counterpart in opLast.
|
||||
* If updates to MMR2 have not been shut off (ie, MMR0.ABORT bits are clear), then we are allowed
|
||||
* to sync MMR2 with its real-time counterpart in opLast.
|
||||
*/
|
||||
if (!(this.regMMR0 & PDP11.MMR0.ABORT)) {
|
||||
this.regMMR2 = this.opLast & 0xffff;
|
||||
|
|
@ -530,14 +548,14 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
setMMR3(newMMR3)
|
||||
{
|
||||
/*
|
||||
* Don't allow the 11/45 to use 22-bit addressing or the UNIBUS map
|
||||
* Don't allow the 11/45 to use 22-bit addressing or the UNIBUS map.
|
||||
*/
|
||||
if (this.model < PDP11.MODEL_1170) {
|
||||
newMMR3 &= ~(PDP11.MMR3.MMU_22BIT | PDP11.MMR3.UNIBUS_MAP);
|
||||
}
|
||||
if (this.regMMR3 != newMMR3) {
|
||||
this.regMMR3 = newMMR3;
|
||||
this.mmuMask = (newMMR3 & PDP11.MMR3.MMU_22BIT)? 0x3fffff : 0x3ffff;
|
||||
this.mmuMask = (newMMR3 & PDP11.MMR3.MMU_22BIT)? BusPDP11.MASK_22BIT : BusPDP11.MASK_18BIT;
|
||||
this.setMemoryAccess();
|
||||
}
|
||||
}
|
||||
|
|
@ -817,6 +835,26 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
return pc;
|
||||
}
|
||||
|
||||
/**
|
||||
* branch(opCode)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} opCode
|
||||
* @param {boolean|number} condition
|
||||
*/
|
||||
branch(opCode, condition)
|
||||
{
|
||||
if (condition) {
|
||||
var off = ((opCode << 24) >> 23);
|
||||
if (DEBUG && DEBUGGER && this.dbg && off == -2) {
|
||||
this.dbg.stopInstruction("branch to self");
|
||||
}
|
||||
this.setPC(this.getPC() + off);
|
||||
this.nStepCycles -= 2;
|
||||
}
|
||||
this.nStepCycles -= (2 + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* getPC()
|
||||
*
|
||||
|
|
@ -2151,6 +2189,9 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
/**
|
||||
* getByteChecked(addr)
|
||||
*
|
||||
* This is the getByte() handler whenever the Debugger has one or more virtual memory READ breakpoints set;
|
||||
* otherwise, getByte() is bound to Bus.getByte().
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
|
|
@ -2166,6 +2207,9 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
/**
|
||||
* getWordChecked(addr)
|
||||
*
|
||||
* This is the getWord() handler whenever the Debugger has one or more virtual memory READ breakpoints set;
|
||||
* otherwise, getWord() is bound to Bus.getWord().
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
|
|
@ -2181,6 +2225,9 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
/**
|
||||
* setByteChecked(addr, data)
|
||||
*
|
||||
* This is the setByte() handler whenever the Debugger has one or more virtual memory WRITE breakpoints set;
|
||||
* otherwise, setByte() is bound to Bus.setByte().
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} addr
|
||||
* @param {number} data
|
||||
|
|
@ -2196,6 +2243,9 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
/**
|
||||
* setWordChecked(addr, data)
|
||||
*
|
||||
* This is the setWord() handler whenever the Debugger has one or more virtual memory WRITE breakpoints set;
|
||||
* otherwise, setWord() is bound to Bus.setWord().
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} addr
|
||||
* @param {number} data
|
||||
|
|
@ -2767,26 +2817,6 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* branch(opCode)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} opCode
|
||||
* @param {boolean|number} condition
|
||||
*/
|
||||
branch(opCode, condition)
|
||||
{
|
||||
if (condition) {
|
||||
var off = ((opCode << 24) >> 23);
|
||||
if (DEBUG && DEBUGGER && this.dbg && off == -2) {
|
||||
this.dbg.stopInstruction("branch to self");
|
||||
}
|
||||
this.setPC(this.getPC() + off);
|
||||
this.nStepCycles -= 2;
|
||||
}
|
||||
this.nStepCycles -= (2 + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* stepCPU(nMinCycles)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ import MessagesPDP11 from "./messages";
|
|||
* aCmds preprocessed commands (from sCmd)
|
||||
*
|
||||
* @typedef {{
|
||||
* addr:(number),
|
||||
* addr:(number|null),
|
||||
* fPhysical:(boolean),
|
||||
* fTemporary:(boolean),
|
||||
* nBase:(number|undefined),
|
||||
|
|
@ -203,7 +203,7 @@ class DebuggerPDP11 extends Debugger {
|
|||
* getAddr(dbgAddr, fWrite, nb)
|
||||
*
|
||||
* @this {DebuggerPDP11}
|
||||
* @param {DbgAddrPDP11|null|undefined} dbgAddr
|
||||
* @param {DbgAddrPDP11|null} [dbgAddr]
|
||||
* @param {boolean} [fWrite]
|
||||
* @param {number} [nb] number of bytes to check (1 or 2); default is 1
|
||||
* @return {number} is the corresponding linear address, or PDP11.ADDR_INVALID
|
||||
|
|
@ -221,14 +221,14 @@ class DebuggerPDP11 extends Debugger {
|
|||
* Returns a NEW DbgAddrPDP11 object, initialized with specified values and/or defaults.
|
||||
*
|
||||
* @this {DebuggerPDP11}
|
||||
* @param {number} [addr]
|
||||
* @param {number|null} [addr]
|
||||
* @param {boolean} [fPhysical]
|
||||
* @param {number} [nBase]
|
||||
* @return {DbgAddrPDP11}
|
||||
*/
|
||||
newAddr(addr, fPhysical, nBase)
|
||||
{
|
||||
return {addr: addr || 0, fPhysical: fPhysical || false, fTemporary: false, nBase: nBase};
|
||||
return {addr: addr || null, fPhysical: fPhysical || false, fTemporary: false, nBase: nBase};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2918,7 +2918,7 @@ class DebuggerPDP11 extends Debugger {
|
|||
* TODO: Tweak this output to accommodate 18-bit machines as well as 22-bit machines.
|
||||
*/
|
||||
var fPhysical = (dbgAddr.fPhysical || dbgAddr.addr > 0xffff);
|
||||
var a = this.cpu.getAddrInfo(dbgAddr.addr, fPhysical);
|
||||
var a = this.cpu.getAddrInfo(dbgAddr.addr || 0, fPhysical);
|
||||
this.println(Str.pad("", fPhysical? 12: 19) + Str.toBin(dbgAddr.addr, fPhysical? 22 : 17, 3) + " " + Str.toOct(dbgAddr.addr, 8));
|
||||
if (a.length < 6) {
|
||||
if (a.length > 2) {
|
||||
|
|
|
|||
|
|
@ -708,6 +708,7 @@ var PDP11 = {
|
|||
RKER: { // 177402: Error Register
|
||||
WCE: 0x0001, // Write Check Error
|
||||
CSE: 0x0002, // Checksum Error
|
||||
SE: 0x0003, // Soft Error bits (cleared at the start of a new function)
|
||||
UNUSED: 0x001C, // unused (returns zero)
|
||||
NXS: 0x0020, // Non-Existent Sector
|
||||
NXC: 0x0040, // Non-Existent Cylinder
|
||||
|
|
@ -719,7 +720,8 @@ var PDP11 = {
|
|||
SKE: 0x1000, // Seek Error
|
||||
WLO: 0x2000, // Write Lock-Out Violation
|
||||
OVR: 0x4000, // Overrun
|
||||
DRE: 0x8000 // Drive Error
|
||||
DRE: 0x8000, // Drive Error
|
||||
HE: 0x7FE0 // Hard Error bits (cleared only by Bus RESET or RK11 CRESET function)
|
||||
},
|
||||
RKCS: { // 177404: Control Status Register
|
||||
GO: 0x0001, // (000001) Go (W/O)
|
||||
|
|
|
|||
|
|
@ -35,11 +35,13 @@
|
|||
import Str from "../../shared/es6/strlib";
|
||||
import Web from "../../shared/es6/weblib";
|
||||
import Component from "../../shared/es6/component";
|
||||
import PDP11 from "./defines";
|
||||
import BusPDP11 from "./bus";
|
||||
import MemoryPDP11 from "./memory";
|
||||
import MessagesPDP11 from "./messages";
|
||||
import PC11 from "./pc11";
|
||||
import RL11 from "./rl11";
|
||||
import RK11 from "./rk11";
|
||||
|
||||
class DevicePDP11 extends Component {
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1020,7 +1020,7 @@ class PanelPDP11 extends Component {
|
|||
}
|
||||
|
||||
/*
|
||||
* Update the ADDRESS and DATA LEDs by selecting the appropriate values
|
||||
* Update the ADDRESS and DATA LEDs by selecting the appropriate values.
|
||||
*
|
||||
* 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".
|
||||
|
|
|
|||
|
|
@ -944,30 +944,20 @@ class RK11 extends Component {
|
|||
processCommand()
|
||||
{
|
||||
var fInterrupt = true;
|
||||
var fnReadWrite, sFunc = "";
|
||||
var fnReadWrite, func, sFunc = "";
|
||||
var iDrive = (this.regRKDA & PDP11.RK11.RKDA.DS) >> PDP11.RK11.RKDA.SHIFT.DS;
|
||||
var drive = this.aDrives[iDrive];
|
||||
var iCylinder, iHead, iSector, nWords, addr, inc;
|
||||
|
||||
this.regRKCS &= ~PDP11.RK11.RKCS.CRDY;
|
||||
var func = this.regRKCS & PDP11.RK11.RKCS.FUNC;
|
||||
this.regRKCS &= ~(PDP11.RK11.RKCS.CRDY | PDP11.RK11.RKCS.SCP);
|
||||
this.regRKER &= ~(PDP11.RK11.RKER.SE);
|
||||
|
||||
switch(func) {
|
||||
switch(func = this.regRKCS & PDP11.RK11.RKCS.FUNC) {
|
||||
|
||||
case PDP11.RK11.FUNC.CRESET:
|
||||
if (this.messageEnabled()) this.printMessage(this.type + ": CRESET(" + iDrive + ")", true);
|
||||
this.regRKER = 0;
|
||||
this.regRKER = this.regRKDA = 0;
|
||||
this.regRKCS = PDP11.RK11.RKCS.CRDY;
|
||||
this.regRKDA = 0;
|
||||
break;
|
||||
|
||||
case PDP11.RK11.FUNC.SEEK:
|
||||
iCylinder = (this.regRKDA & PDP11.RK11.RKDA.CA) >> PDP11.RK11.RKDA.SHIFT.CA;
|
||||
if (this.messageEnabled()) this.printMessage(this.type + ": SEEK(" + iCylinder + ")", true);
|
||||
if (iCylinder >= drive.nCylinders) {
|
||||
this.regRKER |= PDP11.RK11.RKER.DRE | PDP11.RK11.RKER.NXC;
|
||||
this.regRKCS |= PDP11.RK11.RKCS.HE | PDP11.RK11.RKCS.ERR;
|
||||
}
|
||||
break;
|
||||
|
||||
case PDP11.RK11.FUNC.RCHK:
|
||||
|
|
@ -997,21 +987,31 @@ class RK11 extends Component {
|
|||
if (this.messageEnabled()) this.printMessage(this.type + ": " + sFunc + "(" + iCylinder + ":" + iHead + ":" + iSector + ") " + Str.toOct(addr) + "--" + Str.toOct(addr + (nWords << 1)), true, true);
|
||||
|
||||
if (iCylinder >= drive.nCylinders) {
|
||||
this.regRKER |= PDP11.RK11.RKER.DRE | PDP11.RK11.RKER.NXC;
|
||||
this.regRKCS |= PDP11.RK11.RKCS.HE | PDP11.RK11.RKCS.ERR;
|
||||
this.regRKER |= PDP11.RK11.RKER.NXC;
|
||||
break;
|
||||
}
|
||||
if (iSector >= drive.nSectors) {
|
||||
this.regRKER |= PDP11.RK11.RKER.DRE | PDP11.RK11.RKER.NXS;
|
||||
this.regRKCS |= PDP11.RK11.RKCS.HE | PDP11.RK11.RKCS.ERR;
|
||||
this.regRKER |= PDP11.RK11.RKER.NXS;
|
||||
break;
|
||||
}
|
||||
|
||||
fInterrupt = fnReadWrite.call(this, drive, iCylinder, iHead, iSector, nWords, addr, inc, (func >= PDP11.RK11.FUNC.WCHK), this.doneReadWrite.bind(this));
|
||||
break;
|
||||
|
||||
case PDP11.RK11.FUNC.SEEK:
|
||||
iCylinder = (this.regRKDA & PDP11.RK11.RKDA.CA) >> PDP11.RK11.RKDA.SHIFT.CA;
|
||||
if (this.messageEnabled()) this.printMessage(this.type + ": SEEK(" + iCylinder + ")", true);
|
||||
if (iCylinder < drive.nCylinders) {
|
||||
this.regRKCS |= PDP11.RK11.RKCS.SCP;
|
||||
} else {
|
||||
this.regRKER |= PDP11.RK11.RKER.NXC;
|
||||
}
|
||||
break;
|
||||
|
||||
case PDP11.RK11.FUNC.DRESET:
|
||||
if (this.messageEnabled()) this.printMessage(this.type + ": DRESET(" + iDrive + ")");
|
||||
this.regRKER = this.regRKDA = 0;
|
||||
this.regRKCS = PDP11.RK11.RKCS.CRDY | PDP11.RK11.RKCS.SCP;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -1021,9 +1021,7 @@ class RK11 extends Component {
|
|||
|
||||
this.regRKDS = drive.status | (drive.disk? PDP11.RK11.RKDS.DRDY : 0) | (iDrive << PDP11.RK11.RKDS.SHIFT.ID) | (this.regRKDA & PDP11.RK11.RKDS.SC);
|
||||
|
||||
if (this.regRKER & PDP11.RK11.RKER.DRE) {
|
||||
if (this.messageEnabled()) this.printMessage(this.type + ": ERROR: " + Str.toOct(this.regRKER));
|
||||
}
|
||||
this.updateErrors();
|
||||
|
||||
if (fInterrupt) {
|
||||
this.regRKCS &= ~PDP11.RK11.RKCS.GO;
|
||||
|
|
@ -1139,8 +1137,6 @@ class RK11 extends Component {
|
|||
err = PDP11.RK11.RKER.NXM;
|
||||
break;
|
||||
}
|
||||
addr += inc;
|
||||
nWords--;
|
||||
if (!sector) {
|
||||
if (iCylinder >= disk.nCylinders) {
|
||||
err = PDP11.RK11.RKER.NXC;
|
||||
|
|
@ -1167,56 +1163,19 @@ class RK11 extends Component {
|
|||
break;
|
||||
}
|
||||
/*
|
||||
* TODO: Figure out why certain WCHK commands fail during the 11/70 CPU EXERCISER diagnostic,
|
||||
* once the test starts reading/writing with physical addresses > 177777. I think all the
|
||||
* UNIBUS address calculations are fine, so I'm at a loss to explain how a WCHK operation
|
||||
* can succeed when the diagnostic itself appears to alter the memory immediately after the
|
||||
* preceding WRITE operation.
|
||||
* NOTE: During the 11/70 CPU EXERCISER diagnostic, a number of WCHK requests will fail
|
||||
* when the test starts reading/writing with physical addresses > 177777. I'm pretty sure all
|
||||
* the UNIBUS address calculations are fine, and therefore those failures are expected.
|
||||
*
|
||||
* The following machine is already primed with the diagnostic, as well as the recommended
|
||||
* breakpoints and messages:
|
||||
*
|
||||
* http://www.pcjs.org/devices/pdp11/machine/1170/panel/debugger/cpuexer/
|
||||
*
|
||||
* If you're not using that machine, turn on RK11 messages ("m rk11 on"), set a breakpoint at
|
||||
* 033330 ("bp 033330") and then take a look at the first RK11 operation after that breakpoint;
|
||||
* it should be the first one using a UNIBUS address larger than 16 bits:
|
||||
*
|
||||
* RK11: WRITE(147:1:2) 00453610--00463610 @037772
|
||||
*
|
||||
* Use a breakpoint to halt the machine at 037772, and then use the Debugger's "da" command
|
||||
* to determine where UNIBUS address 00453610 is mapped (make sure you specify it as a physical
|
||||
* address using the % prefix):
|
||||
*
|
||||
* >> da %00453610
|
||||
* 00,101,011,110,001,000 00453610
|
||||
* UNIMAP[18]: 1,111,111,110,100,001,111,000 17764170
|
||||
* OFFSET: 1,011,110,001,000 00013610
|
||||
* PHYSICAL: 0,000,000,000,000,000,000,000 00000000
|
||||
*
|
||||
* So, you can see that the starting address for the WRITE is perfectly calculated to begin at
|
||||
* physical address 0. And the "da" command indicates virtual address 0 points to the same
|
||||
* memory:
|
||||
*
|
||||
* >> da 0
|
||||
* 00,000,000,000,000,000 00000000
|
||||
* 0,000,000,000,000 00000000
|
||||
* + KIPAR0: 0,000,000,000,000,000,000,000 00000000
|
||||
* & MMUMASK: 1,111,111,111,111,111,111,111 17777777
|
||||
* = PHYSICAL: 0,000,000,000,000,000,000,000 00000000
|
||||
*
|
||||
* Strangely, the diagnostic (its interrupt handler to be exact) is updating the next RKCS command
|
||||
* value at 001570, which is right in the middle of the first 2048 words of memory -- the same 2048
|
||||
* words that the diagnostic is about to WRITE and then WCHK. Since that value is changing between
|
||||
* the WRITE and the WCHK, how can WCHK succeed?
|
||||
*
|
||||
* For now, I just pretend that it does, by skipping the actual comparison.
|
||||
*
|
||||
* if (data != (b0 | (b1 << 8))) {
|
||||
* err = PDP11.RK11.RKER.WCE;
|
||||
* break;
|
||||
* }
|
||||
* Originally, those failures were causing me some grief because I was treating a WCE error like
|
||||
* any other error; ie, as a HARD error. That was wrong. Two errors (WCE and CSE) are soft
|
||||
* errors, so while they should still trigger the general-purpose RKCS ERR bit, they should NOT
|
||||
* trigger the RKCS HE (Hard Error) bit. This is all taken care of in updateErrors() now.
|
||||
*/
|
||||
if (data != (b0 | (b1 << 8))) {
|
||||
err = PDP11.RK11.RKER.WCE;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!disk.write(sector, ibSector++, data & 0xff) || !disk.write(sector, ibSector++, data >> 8)) {
|
||||
err = PDP11.RK11.RKER.NXS;
|
||||
|
|
@ -1224,6 +1183,8 @@ class RK11 extends Component {
|
|||
}
|
||||
}
|
||||
if (ibSector >= disk.cbSector) sector = null;
|
||||
addr += inc;
|
||||
nWords--;
|
||||
}
|
||||
return done? done(err, iCylinder, iHead, iSector, nWords, addr) : err;
|
||||
}
|
||||
|
|
@ -1246,13 +1207,39 @@ class RK11 extends Component {
|
|||
this.regRKCS = (this.regRKCS & ~PDP11.RK11.RKCS.MEX) | ((addr >> (16 - PDP11.RK11.RKCS.SHIFT.MEX)) & PDP11.RK11.RKCS.MEX);
|
||||
this.regRKWC = (0x10000 - nWords) & 0xffff;
|
||||
this.regRKDA = (this.regRKDA & ~PDP11.RK11.RKDA.SA) | (iSector & PDP11.RK11.RKDA.SA);
|
||||
if (err) {
|
||||
this.regRKER |= err | PDP11.RK11.RKER.DRE;
|
||||
this.regRKCS |= PDP11.RK11.RKCS.HE | PDP11.RK11.RKCS.ERR;
|
||||
}
|
||||
this.regRKER |= err;
|
||||
this.updateErrors();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* updateErrors()
|
||||
*
|
||||
* @this {RK11}
|
||||
*/
|
||||
updateErrors()
|
||||
{
|
||||
/*
|
||||
* Reflect RKER bits to RKCS bits as appropriate.
|
||||
*
|
||||
* TODO: I'm not entirely sure about the handling of the DRE bit here. DEC's RK11 documentation says:
|
||||
*
|
||||
* Sets if one of the drives in the system senses a loss of either AC or DC power and a function is
|
||||
* either initiated or in process while the selected drive is not ready or in some error condition.
|
||||
*
|
||||
* I'm not sure how to parse all the "ands" and "ors" in that sentence. For now, we're treating the DRE bit
|
||||
* much like the high error bit found in other hardware registers: we always set it if any lower error bits
|
||||
* are also set.
|
||||
*/
|
||||
this.regRKCS &= ~PDP11.RK11.RKCS.ERR;
|
||||
if (this.regRKER) {
|
||||
this.regRKER |= PDP11.RK11.RKER.DRE;
|
||||
this.regRKCS |= PDP11.RK11.RKCS.ERR;
|
||||
if (this.regRKER & PDP11.RK11.RKER.HE) this.regRKCS |= PDP11.RK11.RKCS.HE;
|
||||
if (this.messageEnabled()) this.printMessage(this.type + ": ERROR: " + Str.toOct(this.regRKER));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* readRKDS(addr)
|
||||
*
|
||||
|
|
|
|||
28
modules/shared/es6/README.md
Normal file
28
modules/shared/es6/README.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
Shared (ES6) Sources
|
||||
====================
|
||||
|
||||
This folder contains a mix of shared code, with some files used only by Node (server) modules,
|
||||
some used only by Browser (client) modules, and others used by both.
|
||||
|
||||
At the moment, only a few files are completely agnostic; eg: [strlib.js](strlib.js) and [usrlib.js](usrlib.js).
|
||||
One give-away is that neither contain references to any globals (although references to each other
|
||||
would be fine).
|
||||
|
||||
[netlib.js](netlib.js) is appropriate only for Node modules, because it contains code that relies on Node's
|
||||
global *Buffer* object, as indicated by:
|
||||
|
||||
/* global Buffer: false */
|
||||
|
||||
[weblib.js](weblib.js) is appropriate only for client modules, because it contains code that relies on the
|
||||
browser's global *window* object, as indicated by:
|
||||
|
||||
/* global window: true */
|
||||
|
||||
We declare *window* modifiable (true) so that [defines.js](defines.js) can set *global.window* to *false*
|
||||
when running within Node, allowing any other code to test the existence of *window* with a simple:
|
||||
|
||||
if (window) {...}
|
||||
|
||||
instead of:
|
||||
|
||||
if (typeof window !== "undefined") {...}
|
||||
|
|
@ -1027,7 +1027,7 @@ if (!Array.prototype.indexOf) {
|
|||
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
|
||||
*/
|
||||
if (!Array.isArray) {
|
||||
Array.isArray = function (arg) {
|
||||
Array.isArray = function(arg) {
|
||||
return Object.prototype.toString.call(arg) === '[object Array]';
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
"use strict";
|
||||
|
||||
import Str from "../../shared/es6/strlib";
|
||||
import Component from "../..shared/es6/component";
|
||||
import Component from "../../shared/es6/component";
|
||||
|
||||
/**
|
||||
* Debugger Address Object
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ class Net {
|
|||
var options = url.parse(sURL);
|
||||
options.method = "HEAD";
|
||||
options.path = options.pathname; // TODO: Determine the necessity of aliasing this
|
||||
var req = http.request(options, function (res)
|
||||
var req = http.request(options, function(res)
|
||||
{
|
||||
var err = null;
|
||||
var stat = null;
|
||||
|
|
@ -179,7 +179,7 @@ class Net {
|
|||
}
|
||||
done(err, stat);
|
||||
});
|
||||
req.on('error', function (err)
|
||||
req.on('error', function(err)
|
||||
{
|
||||
done(err, null);
|
||||
});
|
||||
|
|
@ -211,9 +211,9 @@ class Net {
|
|||
*/
|
||||
var sFile = "";
|
||||
var bufFile = null;
|
||||
http.get(sURL, function (res)
|
||||
http.get(sURL, function(res)
|
||||
{
|
||||
res.on('data', function (data)
|
||||
res.on('data', function(data)
|
||||
{
|
||||
if (sEncoding) {
|
||||
sFile += data;
|
||||
|
|
@ -241,7 +241,7 @@ class Net {
|
|||
* bufFile = buf;
|
||||
*/
|
||||
bufFile = Buffer.concat([bufFile, data], bufFile.length + data.length);
|
||||
}).on('end', function ()
|
||||
}).on('end', function()
|
||||
{
|
||||
/*
|
||||
* TODO: Decide what to do when res.statusCode is actually an error code (eg, 404), because
|
||||
|
|
@ -252,7 +252,7 @@ class Net {
|
|||
} else {
|
||||
done(new Error(sEncoding ? sFile : bufFile), res.statusCode, null);
|
||||
}
|
||||
}).on('error', function (err)
|
||||
}).on('error', function(err)
|
||||
{
|
||||
done(err, res.statusCode, null);
|
||||
});
|
||||
|
|
@ -279,12 +279,12 @@ class Net {
|
|||
* Either the documentation isn't quite right for url.parse() or http.request() (the big brother
|
||||
* of http.get), or one of those "options" properties is aliased to the other, or...?
|
||||
*/
|
||||
http.get(sURL, function (res)
|
||||
http.get(sURL, function(res)
|
||||
{
|
||||
res.on('data', function (data)
|
||||
res.on('data', function(data)
|
||||
{
|
||||
file.write(data);
|
||||
}).on('end', function ()
|
||||
}).on('end', function()
|
||||
{
|
||||
file.end();
|
||||
/*
|
||||
|
|
@ -295,7 +295,7 @@ class Net {
|
|||
* in such cases, the file content will likely just be an HTML error page.
|
||||
*/
|
||||
done(null, res.statusCode);
|
||||
}).on('error', function (err)
|
||||
}).on('error', function(err)
|
||||
{
|
||||
done(err, res.statusCode);
|
||||
});
|
||||
|
|
@ -329,7 +329,7 @@ class Net {
|
|||
var sBaseName = Str.getBaseName(sURL);
|
||||
var sFile = path.join(Net.sServerRoot, sURL);
|
||||
if (fAsync) {
|
||||
fs.readFile(sFile, {encoding: "utf8"}, function (err, s)
|
||||
fs.readFile(sFile, {encoding: "utf8"}, function(err, s)
|
||||
{
|
||||
/*
|
||||
* TODO: If err is set, is there an error code we should return (instead of -1)?
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class Proc {
|
|||
*
|
||||
* @return {{argc:number, argv:{}}}
|
||||
*/
|
||||
static getArgs = function ()
|
||||
static getArgs()
|
||||
{
|
||||
var argc = 0;
|
||||
var argv = {};
|
||||
|
|
|
|||
|
|
@ -450,7 +450,7 @@ class Str {
|
|||
*/
|
||||
static escapeHTML(sHTML)
|
||||
{
|
||||
return sHTML.replace(/[&<>"']/g, function (m)
|
||||
return sHTML.replace(/[&<>"']/g, function(m)
|
||||
{
|
||||
return Str.aHTMLEscapeMap[m];
|
||||
});
|
||||
|
|
@ -494,7 +494,7 @@ class Str {
|
|||
k = k.replace(/([\\[\]*{}().+?])/g, "\\$1");
|
||||
sMatch += (sMatch ? '|' : '') + k;
|
||||
}
|
||||
return s.replace(new RegExp('(' + sMatch + ')', "g"), function (m)
|
||||
return s.replace(new RegExp('(' + sMatch + ')', "g"), function(m)
|
||||
{
|
||||
return a[m];
|
||||
});
|
||||
|
|
@ -553,7 +553,10 @@ class Str {
|
|||
*/
|
||||
static toASCIICode(b)
|
||||
{
|
||||
var s = (b != Str.ASCII.CR && b != Str.ASCII.LF ? Str.aASCIICodes[b] : null);
|
||||
var s;
|
||||
if (b != Str.ASCII.CR && b != Str.ASCII.LF) {
|
||||
s = Str.aASCIICodes[b];
|
||||
}
|
||||
if (s) {
|
||||
s = '<' + s + '>';
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class Usr {
|
|||
var right = a.length;
|
||||
var found = 0;
|
||||
if (fnCompare === undefined) {
|
||||
fnCompare = function (a, b)
|
||||
fnCompare = function(a, b)
|
||||
{
|
||||
return a > b ? 1 : a < b ? -1 : 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ class Web {
|
|||
return [sResource, nErrorCode];
|
||||
}
|
||||
else if (fAsync && typeof resources == 'function') {
|
||||
resources(sURL, function (sResource, nErrorCode)
|
||||
resources(sURL, function(sResource, nErrorCode)
|
||||
{
|
||||
if (done) done(sURL, sResource, nErrorCode);
|
||||
});
|
||||
|
|
@ -190,7 +190,7 @@ class Web {
|
|||
|
||||
var xmlHTTP = (window.XMLHttpRequest ? new window.XMLHttpRequest() : new window.ActiveXObject("Microsoft.XMLHTTP"));
|
||||
if (fAsync) {
|
||||
xmlHTTP.onreadystatechange = function ()
|
||||
xmlHTTP.onreadystatechange = function()
|
||||
{
|
||||
if (xmlHTTP.readyState === 4) {
|
||||
/*
|
||||
|
|
@ -679,7 +679,7 @@ class Web {
|
|||
var match;
|
||||
var pl = /\+/g; // RegExp for replacing addition symbol with a space
|
||||
var search = /([^&=]+)=?([^&]*)/g;
|
||||
var decode = function (s)
|
||||
var decode = function(s)
|
||||
{
|
||||
return decodeURIComponent(s.replace(pl, " "));
|
||||
};
|
||||
|
|
@ -777,7 +777,7 @@ class Web {
|
|||
ms = msRepeat;
|
||||
}
|
||||
};
|
||||
e.onmousedown = function ()
|
||||
e.onmousedown = function()
|
||||
{
|
||||
// Web.log("onMouseDown()");
|
||||
if (!fIgnoreMouseEvents) {
|
||||
|
|
@ -787,7 +787,7 @@ class Web {
|
|||
}
|
||||
}
|
||||
};
|
||||
e.ontouchstart = function ()
|
||||
e.ontouchstart = function()
|
||||
{
|
||||
// Web.log("onTouchStart()");
|
||||
if (!timer) {
|
||||
|
|
@ -795,7 +795,7 @@ class Web {
|
|||
fnRepeat();
|
||||
}
|
||||
};
|
||||
e.onmouseup = e.onmouseout = function ()
|
||||
e.onmouseup = e.onmouseout = function()
|
||||
{
|
||||
// Web.log("onMouseUp()/onMouseOut()");
|
||||
if (timer) {
|
||||
|
|
@ -803,7 +803,7 @@ class Web {
|
|||
timer = null;
|
||||
}
|
||||
};
|
||||
e.ontouchend = e.ontouchcancel = function ()
|
||||
e.ontouchend = e.ontouchcancel = function()
|
||||
{
|
||||
// Web.log("onTouchEnd()/onTouchCancel()");
|
||||
if (timer) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Shared Sources
|
||||
===
|
||||
==============
|
||||
|
||||
This folder contains a mix of shared code, with some files used only by Node (server) modules,
|
||||
some used only by Browser (client) modules, and others used by both.
|
||||
|
|
@ -8,7 +8,7 @@ At the moment, only a few files are completely agnostic; eg: [strlib.js](strlib.
|
|||
One give-away is that neither contain references to any globals (although references to each other
|
||||
would be fine).
|
||||
|
||||
**netlib.js** is appropriate only for Node modules, because it contains code that relies on Node's
|
||||
[netlib.js](netlib.js) is appropriate only for Node modules, because it contains code that relies on Node's
|
||||
global *Buffer* object, as indicated by:
|
||||
|
||||
/* global Buffer: false */
|
||||
|
|
|
|||
|
|
@ -1081,7 +1081,7 @@ if (!Array.prototype.indexOf) {
|
|||
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
|
||||
*/
|
||||
if (!Array.isArray) {
|
||||
Array.isArray = function (arg) {
|
||||
Array.isArray = function(arg) {
|
||||
return Object.prototype.toString.call(arg) === '[object Array]';
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,6 @@
|
|||
var socket = io.connect();
|
||||
|
||||
// if we get an "info" emit from the socket server then console.log the data we receive
|
||||
socket.on('info', function (data) {
|
||||
socket.on('info', function(data) {
|
||||
console.log(data);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue