Worked around initConnection() failures due to machines that initialize more slowly, by also allowing each machine to invoke the other's initConnection(); if it turns out to be unnecessary, it's simply ignored

This commit is contained in:
Jeff Parsons 2016-08-19 12:35:18 -07:00
commit 983651610c
14 changed files with 1035 additions and 937 deletions

View file

@ -5,7 +5,7 @@ email: Jeff@pcjs.org
description: >
PCjs: Home of the original IBM PC emulator in a browser.
Classic computer simulations in JavaScript, including the IBM PC and other x86-based machines, 6502-based
machines such as the Ohio Scientific Challenger 1P, and 8080-based machines such as Space Invaders.
machines such as the Ohio Scientific Challenger 1P, and 8080-based machines such as Space Invaders and the DEC VT100 Terminal.
Includes an archive of historical PC software and publications.
baseurl: "" # "/pcjs" when using http://jeffpar.github.io or "" when using http://www.pcjs.org
url: "http://www.pcjs.org" # "http://jeffpar.github.io" or "http://www.pcjs.org"
@ -32,7 +32,7 @@ gems:
pcjs:
domain: pcjs.org # whereas site.url is used for linking purposes, site.pcjs.domain is used for display purposes
version: 1.23.3 # IMPORTANT: keep pcjs.version in sync with package.json:version
version: 1.24.0 # IMPORTANT: keep pcjs.version in sync with package.json:version
compiled: true # by default, the compiled pcjs.version scripts will be used (eg, pcx86.js or pcx86-dbg.js)
c1p_scripts: # if pcjs.compiled is false, the following scripts will be included instead, in the order listed
- /modules/shared/lib/defines.js
@ -94,8 +94,8 @@ pcjs:
- /modules/pcx86/lib/fdc.js
- /modules/pcx86/lib/hdc.js
- /modules/pcx86/lib/debugger.js
- /modules/pcx86/lib/state.js
- /modules/pcx86/lib/computer.js
- /modules/shared/lib/state.js
- /modules/shared/lib/embed.js
- /modules/shared/lib/save.js
pc8080_scripts:
@ -123,7 +123,7 @@ pcjs:
- /modules/pc8080/lib/video.js
- /modules/pc8080/lib/serialport.js
- /modules/pc8080/lib/debugger.js
- /modules/pc8080/lib/state.js
- /modules/pc8080/lib/computer.js
- /modules/shared/lib/state.js
- /modules/shared/lib/embed.js
- /modules/shared/lib/save.js

View file

@ -0,0 +1,85 @@
---
layout: post
title: Connecting an IBM PC to a DEC VT100 Terminal
date: 2016-08-19 10:00:00
permalink: /blog/2016/08/19/
machines:
- id: ibm5170
type: pcx86
connection: com2->vt100.serialPort
config: /devices/pcx86/machine/5170/ega/2048kb/rev3/machine.xml
- id: vt100
type: pc8080
connection: serialPort->ibm5170.com2
config: /devices/pc8080/machine/vt100/machine.xml
---
Now that you've had a chance to play with a standalone [VT100 Terminal](/devices/pc8080/machine/vt100/), not to mention
[Dual VT100 Terminals](/devices/pc8080/machine/vt100/dual/), it's time to take [PCjs Machines](/) to the next level, and
begin connecting PCs to terminals.
Below you'll find an 80286-based [IBM PC AT](/devices/pcx86/machine/5170/ega/2048kb/rev3/) connected to an 8080-based
[VT100 Terminal](/devices/pc8080/machine/vt100/) via the PC's **COM2** serial port.
At this point, the connection is *very* thin. The [PCx86](/modules/pcx86/) [SerialPort](/modules/pcx86/lib/serialport.js)
and [PC8080](/modules/pc8080/) [SerialPort](/modules/pc8080/lib/serialport.js) each export exactly two methods:
- *initConnection()*
- *receiveData()*
The *receiveData()* method always returns *success*, because both SerialPort components buffer all received data.
For now, the VT100 SerialPort component avoids overflowing the VT100 firmware's buffers by automatically throttling the flow
of data whenever the firmware, in desperation, issues an XOFF. This "Auto XOFF" behavior will eventually be superseded by
additional interfaces that provide conventional RS-232 signal-based flow control.
What can you do with these machines? Click on the PC screen and type:
DIR > COM2
or any command redirected to COM2. The output should appear on the VT100's screen.
A more interesting example:
CTTY COM2
redirects all *stdout* (screen) and *stdin* (keyboard) I/O to the VT100. Then click on the VT100 screen to switch focus,
and you can now interact with the PC via the terminal. The `CTTY CON` command will restore control to the PC.
More useful scenarios include machines running PC-based debuggers that communicate via a serial port, such as the
OS/2 kernel debugger, Windows debuggers like `WDEB386`, some versions of `SYMDEB`, and so on. PC-to-PC configurations will
also be possible, enabling live demonstrations of classic communication software packages such as `CROSSTALK`.
I've also made it incredibly easy to put machines like this on any PCjs web page. For this blog post, all I had to do
was add the following Front Matter to the top of the Markdown file:
machines:
- id: ibm5170
type: pcx86
connection: com2->vt100.serialPort
config: /devices/pcx86/machine/5170/ega/2048kb/rev3/machine.xml
- id: vt100
type: pc8080
connection: serialPort->ibm5170.com2
config: /devices/pc8080/machine/vt100/machine.xml
and then embed the machines in the post, each with a single line:
{% raw %}
{% include machine.html id="ibm5170" %}
{% include machine.html id="vt100" %}
{% endraw %}
For people rolling their own web pages, [the basics](/docs/pcx86/) haven't changed, and adding a serial connection merely
requires adding a *connection* property (eg, `connection:"com2->vt100.serialPort"`) to the *parms* parameter passed to the
*embedPCx86()* interface. A similar property (eg, `connection:"serialPort->ibm5170.com2"`) must also be added to the *parms*
passed to *embedPC8080()*.
For the more adventurous, a [Dual Debugger Configuration](/devices/pcx86/machine/5170/ega/2048kb/rev3/debugger/vt100/)
is also available.
{% include machine.html id="ibm5170" %}
{% include machine.html id="vt100" %}
*[@jeffpar](http://twitter.com/jeffpar)*
*Aug 19, 2016*

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -16,7 +16,7 @@ one of several JavaScript Machines in the [PCjs Project](https://github.com/jeff
includes:
* [PCx86](/docs/pcx86/), an x86-based IBM PC and PC-compatible emulator
* [PC8080](/modules/pc8080/), a 8080-based machine emulator (e.g., [Space Invaders](/devices/pc8080/machine/invaders/), [VT100 Terminal](/devices/pc8080/machine/vt100/))
* [PC8080](/modules/pc8080/), an 8080-based machine emulator (e.g., [Space Invaders](/devices/pc8080/machine/invaders/), [VT100 Terminal](/devices/pc8080/machine/vt100/))
* [C1Pjs](/docs/c1pjs/), a 6502-based emulation of the Ohio Scientific [Challenger 1P](/devices/c1p/)
All PCjs computer simulations are written entirely in [JavaScript](/modules/). No Flash, Java or other plugins are

View file

@ -136,6 +136,7 @@ function SerialPort8080(parmsSerial) {
* Export all functions required by initConnection(); currently, this is the bare minimum, with no flow control.
*/
this['exports'] = {
'connect': this.initConnection,
'receiveData': this.receiveData
};
}
@ -428,24 +429,33 @@ SerialPort8080.prototype.initBus = function(cmp, bus, cpu, dbg)
*/
SerialPort8080.prototype.initConnection = function()
{
var sConnection = this.cmp.getMachineParm("connection");
if (sConnection) {
var asParts = sConnection.split('->');
if (asParts.length == 2) {
var sSourceID = str.trim(asParts[0]);
if (sSourceID != this.idComponent) return; // this connection string is intended for another instance
var sTargetID = str.trim(asParts[1]);
this.connection = Component.getComponentByID(sTargetID);
if (this.connection) {
var exports = this.connection['exports'];
if (exports) {
this.sendData = exports['receiveData'];
this.status(this.idMachine + '.' + sSourceID + " connected to " + sTargetID);
return;
if (!this.connection) {
var sConnection = this.cmp.getMachineParm("connection");
if (sConnection) {
var asParts = sConnection.split('->');
if (asParts.length == 2) {
var sSourceID = str.trim(asParts[0]);
if (sSourceID != this.idComponent) return; // this connection string is intended for another instance
var sTargetID = str.trim(asParts[1]);
this.connection = Component.getComponentByID(sTargetID);
if (this.connection) {
var exports = this.connection['exports'];
if (exports) {
var fnConnect = exports['connect'];
if (fnConnect) fnConnect.call(this.connection);
this.sendData = exports['receiveData'];
if (this.sendData) {
this.status(this.idMachine + '.' + sSourceID + " connected to " + sTargetID);
return;
}
}
}
}
/*
* Changed from notice() to status() because sometimes a connection fails simply because one of us is a laggard.
*/
this.status("Unable to establish connection: " + sConnection);
}
this.notice("Unable to establish connection: " + sConnection);
}
};

View file

@ -146,6 +146,7 @@ function SerialPort(parmsSerial) {
* Export all functions required by initConnection(); currently, this is the bare minimum, with no flow control.
*/
this['exports'] = {
'connect': this.initConnection,
'receiveData': this.receiveData
};
}
@ -489,24 +490,33 @@ SerialPort.prototype.initBus = function(cmp, bus, cpu, dbg)
*/
SerialPort.prototype.initConnection = function()
{
var sConnection = this.cmp.getMachineParm("connection");
if (sConnection) {
var asParts = sConnection.split('->');
if (asParts.length == 2) {
var sSourceID = str.trim(asParts[0]);
if (sSourceID != this.idComponent) return; // this connection string is intended for another instance
var sTargetID = str.trim(asParts[1]);
this.connection = Component.getComponentByID(sTargetID);
if (this.connection) {
var exports = this.connection['exports'];
if (exports) {
this.sendData = exports['receiveData'];
this.status(this.idMachine + '.' + sSourceID + " connected to " + sTargetID);
return;
if (!this.connection) {
var sConnection = this.cmp.getMachineParm("connection");
if (sConnection) {
var asParts = sConnection.split('->');
if (asParts.length == 2) {
var sSourceID = str.trim(asParts[0]);
if (sSourceID != this.idComponent) return; // this connection string is intended for another instance
var sTargetID = str.trim(asParts[1]);
this.connection = Component.getComponentByID(sTargetID);
if (this.connection) {
var exports = this.connection['exports'];
if (exports) {
var fnConnect = exports['connect'];
if (fnConnect) fnConnect.call(this.connection);
this.sendData = exports['receiveData'];
if (this.sendData) {
this.status(this.idMachine + '.' + sSourceID + " connected to " + sTargetID);
return;
}
}
}
}
/*
* Changed from notice() to status() because sometimes a connection fails simply because one of us is a laggard.
*/
this.status("Unable to establish connection: " + sConnection);
}
this.notice("Unable to establish connection: " + sConnection);
}
};

View file

@ -226,17 +226,9 @@ Component.subclass = function(subclass, superclass, methods, statics)
* various resources used by that machine.
*/
if (window) {
if (!window['PCjs']) {
window['PCjs'] = {
'Machines': {},
'Components': []
};
}
/*
* Alias the new global objects above to their original property names, to minimize code changes.
*/
Component.machines = window['PCjs']['Machines'];
Component.components = window['PCjs']['Components'];
if (!window['PCjs']) window['PCjs'] = {};
Component.machines = window['PCjs']['Machines'] || (window['PCjs']['Machines'] = {});
Component.components = window['PCjs']['Components'] || (window['PCjs']['Components'] = []);
}
else {
/*

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long