diff --git a/devices/pc8080/machine/README.md b/devices/pc8080/machine/README.md index 1ad417684..570bf11dc 100644 --- a/devices/pc8080/machine/README.md +++ b/devices/pc8080/machine/README.md @@ -10,5 +10,7 @@ PC8080 Machine Configurations The following 8080-based machines are currently available: * [8080 Exerciser](exerciser/) -* [Space Invaders (1978)](invaders/) -* [DEC VT100 Terminal](vt100/) +* [Space Invaders (1978)](invaders/) (with [Debugger](invaders/debugger/)) +* [DEC VT100 Terminal](vt100/) (with [Debugger](vt100/debugger/)) + +And check out the [Dual VT100 Terminals](vt100/dual/) for a demo of two PCjs machines communicating over a *virtual* serial connection. diff --git a/devices/pc8080/machine/vt100/README.md b/devices/pc8080/machine/vt100/README.md index 64e69f098..082be948c 100644 --- a/devices/pc8080/machine/vt100/README.md +++ b/devices/pc8080/machine/vt100/README.md @@ -3,9 +3,8 @@ layout: page title: DEC VT100 Terminal permalink: /devices/pc8080/machine/vt100/ machines: - - type: pc8080 - id: vt100 - debugger: true + - id: vt100 + type: pc8080 --- DEC VT100 Terminal diff --git a/devices/pc8080/machine/vt100/debugger/README.md b/devices/pc8080/machine/vt100/debugger/README.md index 6c1844423..ec8efda2f 100644 --- a/devices/pc8080/machine/vt100/debugger/README.md +++ b/devices/pc8080/machine/vt100/debugger/README.md @@ -3,8 +3,8 @@ layout: page title: DEC VT100 Terminal with Debugger permalink: /devices/pc8080/machine/vt100/debugger/ machines: - - type: pc8080 - id: vt100 + - id: vt100 + type: pc8080 debugger: true --- @@ -51,6 +51,36 @@ keys. The following keys have special meaning inside SET-UP Mode. - SHIFT-S: Save SET-UP Features - SHIFT-R: Restore SET-UP Features +### SET-UP B Options + +#### Block 1 + +- 1---: Smooth scroll +- -1--: Auto-Repeat +- --1-: Light Screen Background +- ---1: Block Cursor + +#### Block 2 + +- 1---: Margin Bell +- -1--: Key-click +- --1-: ANSI +- ---1: Auto-XON/XOFF + +#### Block 3 + +- 1---: UK Pound Symbol +- -1--: Wrap-Around +- --1-: New Line +- ---1: Interlace + +#### Block 4 + +- 1---: Even Parity +- -1--: Parity Enable +- --1-: 8 Data Bits +- ---1: 50Hz Power + VT100 Memory Usage ------------------ diff --git a/devices/pc8080/machine/vt100/debugger/machine.xml b/devices/pc8080/machine/vt100/debugger/machine.xml index a4349b299..3a2f8364f 100644 --- a/devices/pc8080/machine/vt100/debugger/machine.xml +++ b/devices/pc8080/machine/vt100/debugger/machine.xml @@ -27,7 +27,7 @@ SET-UP - + TEST RECEIVER diff --git a/devices/pc8080/machine/vt100/dual/README.md b/devices/pc8080/machine/vt100/dual/README.md new file mode 100644 index 000000000..1e17eafc6 --- /dev/null +++ b/devices/pc8080/machine/vt100/dual/README.md @@ -0,0 +1,27 @@ +--- +layout: page +title: Dual VT100 Terminals +permalink: /devices/pc8080/machine/vt100/dual/ +machines: + - id: vt100a + type: pc8080 + config: /devices/pc8080/machine/vt100/machine.xml + connection: serialPort=vt100b.serialPort + - id: vt100b + type: pc8080 + config: /devices/pc8080/machine/vt100/machine.xml + connection: serialPort=vt100a.serialPort +--- + +Dual VT100 Terminals +-------------------- + +This is a test of two [VT100 Terminals](../) whose serial ports are *virtually* connected, so anything you type in the first +terminal should appear on the screen of the second, and vice versa. Click or tap on the screen of the desired terminal before +you begin typing. + +If either terminal becomes LOCKED, press the SET-UP key (mapped to your F9 function key) twice to unlock it. + +{% include machine.html id="vt100a" %} + +{% include machine.html id="vt100b" %} diff --git a/devices/pc8080/machine/vt100/machine.xml b/devices/pc8080/machine/vt100/machine.xml index 378bca98e..c9df17c00 100644 --- a/devices/pc8080/machine/vt100/machine.xml +++ b/devices/pc8080/machine/vt100/machine.xml @@ -27,7 +27,7 @@ SET-UP - + TEST RECEIVER diff --git a/modules/pc8080/lib/chipset.js b/modules/pc8080/lib/chipset.js index 67cdff3ab..f1c702245 100644 --- a/modules/pc8080/lib/chipset.js +++ b/modules/pc8080/lib/chipset.js @@ -220,16 +220,16 @@ ChipSet.SI1978 = { */ ChipSet.VT100 = { MODEL: 100.0, - FLAGS_BUFFER: { + FLAGS: { PORT: 0x42, // read-only - XMIT: 0x01, // PUSART transmit buffer empty if SET + UART_XMIT: 0x01, // PUSART transmit buffer empty if SET NO_AVO: 0x02, // AVO present if CLEAR NO_GFX: 0x04, // VT125 graphics board present if CLEAR OPTION: 0x08, // OPTION present if SET NO_EVEN: 0x10, // EVEN FIELD active if CLEAR NVR_DATA: 0x20, // NVR DATA if SET NVR_CLK: 0x40, // NVR CLOCK if SET - KBD_XMIT: 0x80 // KBD XMIT BUFFER empty if SET + KBD_XMIT: 0x80 // KBD transmit buffer empty if SET }, BRIGHTNESS: { PORT: 0x42, // write-only @@ -424,6 +424,7 @@ ChipSet.prototype.initBus = function(cmp, bus, cpu, dbg) this.dbg = dbg; this.cmp = cmp; this.kbd = /** @type {Keyboard} */ (cmp.getMachineComponent("Keyboard")); + this.serial = /** @type {SerialPort} */ (cmp.getMachineComponent("SerialPort")); this.video = /** @type {Video} */ (cmp.getMachineComponent("Video")); bus.addPortInputTable(this, this.config.portsInput); bus.addPortOutputTable(this, this.config.portsOutput); @@ -474,7 +475,7 @@ ChipSet.SI1978.INIT = [ ChipSet.VT100.INIT = [ [ ChipSet.VT100.BRIGHTNESS.INIT, - ChipSet.VT100.FLAGS_BUFFER.NO_AVO | ChipSet.VT100.FLAGS_BUFFER.NO_GFX + ChipSet.VT100.FLAGS.NO_AVO | ChipSet.VT100.FLAGS.NO_GFX ], [ ChipSet.VT100.DC011.INITCOLS, @@ -531,7 +532,7 @@ ChipSet.prototype.save = function() state.set(0, [this.bStatus0, this.bStatus1, this.bStatus2, this.wShiftData, this.bShiftCount, this.bSound1, this.bSound2]); break; case ChipSet.VT100.MODEL: - state.set(0, [this.bBrightness, this.bFlagsBuffer]); + state.set(0, [this.bBrightness, this.bFlags]); state.set(1, [this.bDC011Cols, this.bDC011Rate]); state.set(2, [this.bDC012Scroll, this.bDC012Blink, this.bDC012Reverse, this.bDC012Attr]); state.set(3, [this.dNVRAddr, this.wNVRData, this.bNVRLatch, this.bNVROut, this.aNVRWords]); @@ -555,31 +556,31 @@ ChipSet.prototype.restore = function(data) if (data && (a = data[0]) && a.length) { switch(this.config.MODEL) { case ChipSet.SI1978.MODEL: - this.bStatus0 = a[0]; - this.bStatus1 = a[1]; - this.bStatus2 = a[2]; - this.wShiftData = a[3]; - this.bShiftCount = a[4]; - this.bSound1 = a[5]; - this.bSound2 = a[6]; + this.bStatus0 = a[0]; + this.bStatus1 = a[1]; + this.bStatus2 = a[2]; + this.wShiftData = a[3]; + this.bShiftCount = a[4]; + this.bSound1 = a[5]; + this.bSound2 = a[6]; return true; case ChipSet.VT100.MODEL: - this.bBrightness = a[0]; - this.bFlagsBuffer = a[1]; + this.bBrightness = a[0]; + this.bFlags = a[1]; a = data[1]; - this.bDC011Cols = a[0]; - this.bDC011Rate = a[1]; + this.bDC011Cols = a[0]; + this.bDC011Rate = a[1]; a = data[2]; - this.bDC012Scroll = a[0]; - this.bDC012Blink = a[1]; + this.bDC012Scroll = a[0]; + this.bDC012Blink = a[1]; this.bDC012Reverse = a[2]; - this.bDC012Attr = a[3]; + this.bDC012Attr = a[3]; a = data[3]; - this.dNVRAddr = a[0]; // 20-bit address - this.wNVRData = a[1]; // 14-bit word - this.bNVRLatch = a[2]; // 1 byte - this.bNVROut = a[3]; // 1 bit - this.aNVRWords = a[4]; // 100 14-bit words + this.dNVRAddr = a[0]; // 20-bit address + this.wNVRData = a[1]; // 14-bit word + this.bNVRLatch = a[2]; // 1 byte + this.bNVROut = a[3]; // 1 bit + this.aNVRWords = a[4]; // 100 14-bit words return true; } } @@ -883,36 +884,40 @@ ChipSet.prototype.doNVRCommand = function() }; /** - * inVT100FlagsBuffer(port, addrFrom) + * inVT100Flags(port, addrFrom) * * @this {ChipSet} * @param {number} port (0x42) * @param {number} [addrFrom] (not defined if the Debugger is trying to read the specified port) * @return {number} simulated port value */ -ChipSet.prototype.inVT100FlagsBuffer = function(port, addrFrom) +ChipSet.prototype.inVT100Flags = function(port, addrFrom) { /* * The NVR_CLK bit is driven by LBA7 (ie, bit 7 from Line Buffer Address generation); see the DC011 discussion above. */ - var b = this.bFlagsBuffer; - b &= ~ChipSet.VT100.FLAGS_BUFFER.NVR_CLK; + var b = this.bFlags; + b &= ~ChipSet.VT100.FLAGS.NVR_CLK; if (this.getVT100LBA(7)) { - b |= ChipSet.VT100.FLAGS_BUFFER.NVR_CLK; - if (b != this.bFlagsBuffer) { + b |= ChipSet.VT100.FLAGS.NVR_CLK; + if (b != this.bFlags) { this.doNVRCommand(); } } - b &= ~ChipSet.VT100.FLAGS_BUFFER.NVR_DATA; + b &= ~ChipSet.VT100.FLAGS.NVR_DATA; if (this.bNVROut) { - b |= ChipSet.VT100.FLAGS_BUFFER.NVR_DATA; + b |= ChipSet.VT100.FLAGS.NVR_DATA; } - b &= ~ChipSet.VT100.FLAGS_BUFFER.KBD_XMIT; - if (this.kbd && !this.kbd.checkBusy()) { - b |= ChipSet.VT100.FLAGS_BUFFER.KBD_XMIT; + b &= ~ChipSet.VT100.FLAGS.KBD_XMIT; + if (this.kbd && this.kbd.isTransmitterReady()) { + b |= ChipSet.VT100.FLAGS.KBD_XMIT; } - this.bFlagsBuffer = b; - this.printMessageIO(port, null, addrFrom, "FLAGS.BUFFER", b); + b &= ~ChipSet.VT100.FLAGS.UART_XMIT; + if (this.serial && this.serial.isTransmitterReady()) { + b |= ChipSet.VT100.FLAGS.UART_XMIT; + } + this.bFlags = b; + this.printMessageIO(port, null, addrFrom, "FLAGS", b); return b; }; @@ -967,6 +972,7 @@ ChipSet.prototype.outVT100DC012 = function(port, b, addrFrom) break; case 0x1: this.bDC012Scroll = (this.bDC012Scroll & ~0xC) | (bOpt << 2); + if (this.video) this.video.updateScrollOffset(this.bDC012Scroll); break; case 0x2: switch(bOpt) { @@ -1013,7 +1019,7 @@ ChipSet.prototype.outVT100DC011 = function(port, b, addrFrom) this.bDC011Cols = b; if (this.video) { var nCols = (this.bDC011Cols == ChipSet.VT100.DC011.COLS132? 132 : 80); - var nRows = (nCols > 80 && (this.bFlagsBuffer & ChipSet.VT100.FLAGS_BUFFER.NO_AVO)? 14 : 24); + var nRows = (nCols > 80 && (this.bFlags & ChipSet.VT100.FLAGS.NO_AVO)? 14 : 24); this.video.updateDimensions(nCols, nRows); } } @@ -1039,7 +1045,7 @@ ChipSet.SI1978.portsOutput = { }; ChipSet.VT100.portsInput = { - 0x42: ChipSet.prototype.inVT100FlagsBuffer + 0x42: ChipSet.prototype.inVT100Flags }; ChipSet.VT100.portsOutput = { diff --git a/modules/pc8080/lib/keyboard.js b/modules/pc8080/lib/keyboard.js index 3daed04d3..4cb30932e 100644 --- a/modules/pc8080/lib/keyboard.js +++ b/modules/pc8080/lib/keyboard.js @@ -870,17 +870,17 @@ Keyboard.prototype.checkSoftKeysToRelease = function() }; /** - * checkBusy() + * isTransmitterReady() * - * Called whenever a ChipSet circuit needs the keyboard's UART status. - * Currently, we have no busy conditions (our virtual keyboard is infinitely fast). + * Called whenever a ChipSet circuit needs the Keyboard UART's transmitter status. + * Currently, we have no busy conditions (our virtual keyboard transmitter is infinitely fast). * * @this {Keyboard} - * @return {boolean} + * @return {boolean} (true if ready, false if not) */ -Keyboard.prototype.checkBusy = function() +Keyboard.prototype.isTransmitterReady = function() { - return false; + return true; }; /** diff --git a/modules/pc8080/lib/serialport.js b/modules/pc8080/lib/serialport.js index 36a781cea..4e8e6d720 100644 --- a/modules/pc8080/lib/serialport.js +++ b/modules/pc8080/lib/serialport.js @@ -36,7 +36,6 @@ if (NODE) { var web = require("../../shared/lib/weblib"); var Component = require("../../shared/lib/component"); var Messages = require("./messages"); - var ChipSet = require("./chipset"); var State = require("./state"); } @@ -86,7 +85,7 @@ function SerialPort(parmsSerial) { this.consoleOutput = null; /** - * controlIOBuffer is a DOM element, if any, bound to the port (currently used for output only; see echoByte()). + * controlIOBuffer is a DOM element bound to the port (currently used for output only; see transmitByte()). * * @type {Object} */ @@ -94,7 +93,7 @@ function SerialPort(parmsSerial) { /* * If controlIOBuffer is being used AND 'tabSize' is set, then we make an attempt to monitor the characters - * being echoed via echoByte(), maintain a logical column position, and convert any tabs into the appropriate + * being echoed via transmitByte(), maintain a logical column position, and convert any tabs into the appropriate * number of spaces. * * charBOL, if nonzero, is a character to automatically output at the beginning of every line. This probably @@ -115,6 +114,27 @@ function SerialPort(parmsSerial) { */ Component.bindExternalControl(this, sBinding, SerialPort.sIOBuffer); } + + /* + * Define a setTimeout() function that receiveData() can use when there's more data to receive. + */ + this.fnCheckDataReceived = function(serial) { + return function() { + serial.receiveData(); + } + }(this); + + /* + * No connection until initBus() invokes initConnection(). + */ + this.connection = this.sendByte = null; + + /* + * Export all functions required by initConnection(); currently, this is the bare minimum, with no flow control. + */ + this['exports'] = { + 'receiveByte': this.receiveByte + }; } /* @@ -122,7 +142,7 @@ function SerialPort(parmsSerial) { * property {number} iAdapter * property {number} portBase * property {number} nIRQ - * property {Object} controlIOBuffer is a DOM element, if any, bound to the port (for rudimentary output; see echoByte()) + * property {Object} controlIOBuffer is a DOM element bound to the port (for rudimentary output; see transmitByte()) * * NOTE: This class declaration started as a way of informing the code inspector of the controlIOBuffer property, * which remained undefined until a setBinding() call set it later, but I've since decided that explicitly @@ -146,7 +166,7 @@ SerialPort.UART8251 = { PARITY_ENABLE: 0x10, EVEN_PARITY: 0x20, STOP_BITS: 0xC0, // 00=invalid, 01=1, 10=1.5, 11=2 - INIT: 0x8E // 16x baudrate, 8 data bits, no parity, 1.5 stop bits + INIT: 0x8E // 16x baud rate, 8 data bits, no parity, 1.5 stop bits }, /* * Format of COMMAND byte written to CONTROL port 0x1 @@ -177,9 +197,10 @@ SerialPort.UART8251 = { INIT: 0x85 // XMIT_READY | XMIT_EMPTY | DSR }, /* - * Format of BAUDRATE byte written to port 0x2 + * Format of BAUDRATES byte written to port 0x2 * - * Each nibble is an index (0x0-0xF) into a set internal CPU clock divisors that yield the following baud rates: + * Each nibble is an index (0x0-0xF) into a set of internal CPU clock divisors that yield the + * following baud rates: * * Index Divisor Baud Rate * ----- ------- --------- @@ -199,15 +220,15 @@ SerialPort.UART8251 = { * 0xD 36 4800 * 0xE 18 9600 (default) * 0xF 9 19200 - * - * TODO: I'm not really sure at this point which nibble is for the XMIT rate and which is for the RECV - * rate; I just made a random guess. */ - BAUDRATE: { - XMIT_RATE: 0x0F, - RECV_RATE: 0xF0, + BAUDRATES: { + RECV_RATE: 0x0F, + XMIT_RATE: 0xF0, INIT: 0xEE // default to 9600 (0xE) for both XMIT and RECV - } + }, + BAUDTABLE: [ + 50, 75, 110, 134.5, 150, 200, 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 9600, 19200 + ] }; SerialPort.UART8251.INIT = [ @@ -217,7 +238,7 @@ SerialPort.UART8251.INIT = [ SerialPort.UART8251.STATUS.INIT, SerialPort.UART8251.MODE.INIT, SerialPort.UART8251.COMMAND.INIT, - SerialPort.UART8251.BAUDRATE.INIT + SerialPort.UART8251.BAUDRATES.INIT ]; /* @@ -272,7 +293,7 @@ SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control, sValue) if (keyCode === 0x08 || event.ctrlKey && keyCode >= 0x41 && keyCode <= 0x5A) { if (event.preventDefault) event.preventDefault(); if (keyCode > 0x40) keyCode -= 0x40; - serial.sendByteIn(keyCode); + serial.receiveByte(keyCode); } return true; }; @@ -284,7 +305,7 @@ SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control, sValue) */ event = event || window.event; var keyCode = event.which || event.keyCode; - // serial.sendRBR([keyCode]); + serial.receiveByte(keyCode); /* * Since we're going to remove the "readonly" attribute from the