Added support for machine command links to assist in the creation of online tutorials; however, the commandMachine() function isn't wired to anything yet, and this only works with the Node web server (updating the Jekyll environment is next)

This commit is contained in:
Jeff Parsons 2017-01-03 10:58:25 -08:00 committed by Jeff Parsons
commit 6f36583704
5 changed files with 106 additions and 43 deletions

View file

@ -64,10 +64,10 @@ was add the following Front Matter to the top of the Markdown file:
and then embed the machines in the post, each with a single line: and then embed the machines in the post, each with a single line:
{% raw %} {% raw %}
{% include machine.html id="ibm5170" %} {% include machine.html id="ibm5170" %}
{% include machine.html id="vt100" %} {% include machine.html id="vt100" %}
{% endraw %} {% endraw %}
For people rolling their own web pages, [the basics](/docs/pcx86/) haven't changed, and adding a serial connection merely 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 requires adding a *connection* property (eg, `connection:"com2->vt100.serialPort"`) to the *parms* parameter passed to the

View file

@ -24,9 +24,9 @@ beginning of the PDP-11 line: the PDP-11/20.
I'm starting with the top-of-the-line PDP-11/70 largely because the core of the emulator is being adapted from the I'm starting with the top-of-the-line PDP-11/70 largely because the core of the emulator is being adapted from the
JavaScript [PDP-11/70 Emulator (v1.3)](http://skn.noip.me/pdp11/pdp11.html) written by JavaScript [PDP-11/70 Emulator (v1.3)](http://skn.noip.me/pdp11/pdp11.html) written by
Paul Nankervis, who has generously given permission to use his code in PCjs. Since his emulator is a fully functional Paul Nankervis, who has generously given permission to use his code in PCjs. Since his emulator is a fully functional
11/70, it made sense to start there and work backwards, factoring out features as needed. 11/70, it made sense to start there and work backwards, disabling features according to the model.
The code has already undergone a lot of refactoring. Opcodes are now decoded by function tables rather than a single The code has already undergone a lot of refactoring. Opcodes are now decoded by function tables rather than a single
switch statement, and every opcode is implemented with a discrete function. Other refactoring includes flag management, switch statement, and every opcode is implemented with a discrete function. Other refactoring includes flag management,
interrupt management, and device management. interrupt management, and device management.
@ -37,9 +37,9 @@ component, [device.js](/modules/pdp11/lib/device.js), which has now been convert
The first new device component is [serial.js](/modules/pdp11/lib/serial.js), which is currently the The first new device component is [serial.js](/modules/pdp11/lib/serial.js), which is currently the
only means PDPjs has of communicating with the outside world. So you can try only means PDPjs has of communicating with the outside world. So you can try
[PDPjs connected to a VT100 Terminal](/devices/pdp11/machine/1170/vt100/), by clicking the **Run** button on the test machine. [PDPjs connected to a VT100 Terminal](/devices/pdp11/machine/1170/vt100/), by clicking the **Run** button on the
The test machine is running [custom boot code](/apps/pdp11/boot/test/), adapted from boot code written by Paul, but due to the test machine. The test machine is running a custom [Boot Monitor](/apps/pdp11/boot/monitor/) included with
lack of other device support, nothing can be booted yet. [Paul's emulator](http://skn.noip.me/pdp11/), but due to the lack of other device support, nothing can be booted yet.
Obviously PDPjs is very much a work-in-progress. Before I proceed much farther, I really want to put the CPU through Obviously PDPjs is very much a work-in-progress. Before I proceed much farther, I really want to put the CPU through
some rigorous testing, so I'll be on the lookout for some comprehensive PDP-11 instruction tests. Or I'll write my own, some rigorous testing, so I'll be on the lookout for some comprehensive PDP-11 instruction tests. Or I'll write my own,

View file

@ -16,39 +16,44 @@ machines:
sticky: top sticky: top
--- ---
Introducing PDP-11 tutorials! Introducing PDP-11 tutorials. For more information, keep scrolling.
{% include machine.html id="vt100" %} {% include machine.html id="vt100" %}
[PDPjs](/devices/pdp11/machine/) is the newest addition to the PCjs family of emulators, joining PCx86, PC8080, and C1Pjs. [PDPjs](/devices/pdp11/machine/) is able to run a variety of old DEC operating systems, such as RT-11 and RSTS/E,
and while there are manuals available online, thanks to the efforts of those who operate and contribute to websites
like [bitsavers.org](http://bitsavers.org), I suspect most people don't have a lot of interest or time to spend
reading old manuals.
While PDPjs may eventually support a range of DEC PDP machines, my current focus is on the PDP-11, starting with the In an effort to remedy that situation, I'm adding some new features to PCjs. The first feature is what I call
PDP-11/70. From there, I'll work backwards to support other PDP-11 models, such as the PDP-11/45, until I reach the "Sticky Machines", and it's more a website feature than a machine feature. At the top of any PCjs webpage, in the
beginning of the PDP-11 line: the PDP-11/20. *machines* section, a machine can now have a *sticky* property. For now, the only supported value is "top"; e.g.:
I'm starting with the top-of-the-line PDP-11/70 largely because the core of the emulator is being adapted from the machines:
JavaScript [PDP-11/70 Emulator (v1.3)](http://skn.noip.me/pdp11/pdp11.html) written by - id: vt100
Paul Nankervis, who has generously given permission to use his code in PCjs. Since his emulator is a fully functional type: pc8080
11/70, it made sense to start there and work backwards, factoring out features as needed. config: /devices/pc8080/machine/vt100/machine.xml
connection: serialPort->test1170.dl11
sticky: top
The code has already undergone a lot of refactoring. Opcodes are now decoded by function tables rather than a single A sticky machine makes it easier to construct a tutorial page for a single machine, by preventing that machine from
switch statement, and every opcode is implemented with a discrete function. Other refactoring includes flag management, scrolling off the top of the page; it "sticks" to the top instead. The rest of the page scrolls normally, allowing the
interrupt management, and device management. user to progress at their own pace through the text and/or images of an accompanying tutorial.
Most of the work remaining is in device management. Like other PCjs emulators, PDPjs has a Bus component, The second feature is a generalized method for sending commands to components within a machine. For example, if we
[bus.js](/modules/pdp11/lib/bus.js), that allows separate device components to register I/O handlers for specific want to send some keyboard commands to machine:
UNIBUS addresses. During the initial port, I moved all of Paul's original device management code into one "catch-all"
component, [device.js](/modules/pdp11/lib/device.js), which has now been converted to the new I/O registration model.
The first new device component is [serial.js](/modules/pdp11/lib/serial.js), which is currently the {% raw %}
only means PDPjs has of communicating with the outside world. So you can try {% include machine-command.html type='button' label='Try It!' machine='vt100' component='Keyboard' command='sendString' value='Hello World' %}
[PDPjs connected to a VT100 Terminal](/devices/pdp11/machine/1170/vt100/), by clicking the **Run** button on the test machine. {% endraw %}
The test machine is running [custom boot code](/apps/pdp11/boot/test/), adapted from boot code written by Paul, but due to the
lack of other device support, nothing can be booted yet.
Obviously PDPjs is very much a work-in-progress. Before I proceed much farther, I really want to put the CPU through which should translate into a control that looks like:
some rigorous testing, so I'll be on the lookout for some comprehensive PDP-11 instruction tests. Or I'll write my own,
and compare results across 1 or 2 other PDP-11 emulators. <button type="button" onclick="commandMachine('vt100','Keyboard','sendString','Hello World')">Try It!</button>
In fact, let's try it now. {% include machine-command.html type='button' label='Try It!' machine='vt100' component='Keyboard' command='sendString' value='Hello World' %}
Obviously, every component we want to control will need to be updated to export the necessary functions.
{% include machine.html id="test1170" %} {% include machine.html id="test1170" %}

View file

@ -942,10 +942,12 @@ MarkOut.prototype.convertMDLinks = function(sBlock)
* Before we start replacing Markdown links, see if there are any Liquid-style replacements * Before we start replacing Markdown links, see if there are any Liquid-style replacements
* (in case this Markdown file is part of a Jekyll installation) and remove them. * (in case this Markdown file is part of a Jekyll installation) and remove them.
* *
* TODO: These replacements should use appropriate values from _config.yml; however, unless/until * TODO: Any double-brace replacements should use appropriate values from _config.yml or the
* we start using Node again to host the public site, that's low priority. * page's Front Matter; however, unless/until we start using Node again to host the public site,
* that's low priority.
*/ */
sBlock = sBlock.replace(/\{([\{%]).*?\1}/g, ""); sBlock = sBlock.replace(/([^\t])\{([\{%]).*?\2}/g, "$1");
sBlock = sBlock.replace(/(\{)([\{%])(.*?\2})/g, "<pre><code>$1$2$3</code></pre>");
var aMatch; var aMatch;
var re = /\[([^\[\]]*)]\((.*?)(?:\s*"(.*?)"\)|\))/g; var re = /\[([^\[\]]*)]\((.*?)(?:\s*"(.*?)"\)|\))/g;
@ -1150,21 +1152,21 @@ MarkOut.prototype.convertMDImageLinks = function(sBlock, sIndent)
*/ */
MarkOut.prototype.convertMDMachineLinks = function(sBlock) MarkOut.prototype.convertMDMachineLinks = function(sBlock)
{ {
var aMatch, sReplacement; var aMatch, sReplacement, machine;
var sMachineType, sMachineID, sMachineXMLFile, sMachineXSLFile, sMachineVersion, sMachineOptions, sMachineParms; var sMachineType, sMachineID, sMachineXMLFile, sMachineXSLFile, sMachineVersion, sMachineOptions, sMachineParms;
/* /*
* Before we start looking for Markdown-style machine links, see if there are any Liquid-style machines, * Before we start looking for Markdown-style machine links, see if there are any Liquid-style machines,
* (in case this Markdown file is part of a Jekyll installation) and convert them to Markdown-style links. * (in case this Markdown file is part of a Jekyll installation) and convert them to Markdown-style links.
*/ */
var reIncludes = /(.){%\s*include\s+machine\.html\s+id=(["'])(.*?)\2\s*%}/g;
var reIncludes = /\{%\s*include\s+machine\.html\s+id=(["'])(.*?)\1\s*%}/g;
while ((aMatch = reIncludes.exec(sBlock))) { while ((aMatch = reIncludes.exec(sBlock))) {
if (aMatch[1] == '\t') continue;
sReplacement = ""; sReplacement = "";
sMachineID = aMatch[2]; sMachineID = aMatch[3];
if (this.aMachineDefs[sMachineID]) { if (this.aMachineDefs[sMachineID]) {
var machine = this.aMachineDefs[sMachineID]; machine = this.aMachineDefs[sMachineID];
sMachineType = machine['type'] || "PCx86"; sMachineType = machine['type'] || "PCx86";
sMachineOptions = ((sMachineType.indexOf("-dbg") > 0 || machine['debugger'] == "true")? "debugger" : ""); sMachineOptions = ((sMachineType.indexOf("-dbg") > 0 || machine['debugger'] == "true")? "debugger" : "");
if (machine['sticky']) sMachineOptions += (sMachineOptions? "," : "") + "sticky"; if (machine['sticky']) sMachineOptions += (sMachineOptions? "," : "") + "sticky";
@ -1176,11 +1178,14 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
sReplacement = machine['name'] || "Embedded PC"; sReplacement = machine['name'] || "Embedded PC";
sReplacement = "[" + sReplacement + "](" + sMachineXMLFile + ' "' + sMachineType + '!' + sMachineID + '!' + sMachineXSLFile + '!!' + sMachineOptions + '!' + sMachineParms + '")'; sReplacement = "[" + sReplacement + "](" + sMachineXMLFile + ' "' + sMachineType + '!' + sMachineID + '!' + sMachineXSLFile + '!!' + sMachineOptions + '!' + sMachineParms + '")';
} }
sBlock = sBlock.replace(aMatch[0], sReplacement); sBlock = sBlock.replace(aMatch[0].substr(1), sReplacement);
reIncludes.lastIndex = 0; // reset lastIndex, since we just modified the string that reIncludes is iterating over reIncludes.lastIndex = 0; // reset lastIndex, since we just modified the string that reIncludes is iterating over
} }
sBlock = sBlock.replace(/\{%\s*include\s+build\.html\s+id=(["'])(.*?)\1\s*%}/g, '<div class="buildpc" id=$1$2$1></div>'); /*
* Ditto for any Liquid-style machine build links.
*/
sBlock = sBlock.replace(/\{%\s*include\s+machine-build\.html\s+id=(["'])(.*?)\1\s*%}/g, '<div class="buildpc" id=$1$2$1></div>');
/* /*
* Start looking for Markdown-style machine links now... * Start looking for Markdown-style machine links now...
@ -1254,6 +1259,43 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
); );
} }
/*
* Last but not least, see if there are any Liquid-style machine command links that need to be converted.
*/
reIncludes = /([ \t]*)\{%\s*include\s+machine-command\.html\s+(.*?)\s*%}/g;
var findParm = function(aParms, sParm) {
sParm += '=';
for (var i = 0; i < aParms.length; i++) {
if (aParms[i].indexOf(sParm) == 0) {
return aParms[i].slice(sParm.length+1, -1);
}
}
return "";
};
while ((aMatch = reIncludes.exec(sBlock))) {
if (aMatch[1] == '\t') continue;
var aParms = aMatch[2].match(/[a-z]+=(["']).*?\1/g);
if (!aParms) continue;
sReplacement = "";
sMachineID = findParm(aParms, 'machine');
var sControl = findParm(aParms, 'type') || 'button';
var sControlType = sControl == 'button'? ' type="button"' : '';
if (this.aMachineDefs[sMachineID]) {
sReplacement = '<' + sControl + sControlType + ' onclick="commandMachine(';
sReplacement += "'" + sMachineID + "',";
sReplacement += "'" + findParm(aParms, 'component') + "',";
sReplacement += "'" + findParm(aParms, 'command') + "',";
sReplacement += "'" + findParm(aParms, 'value') + "')";
sReplacement += '">' + (findParm(aParms, 'label') || 'Try It!') + '</' + sControl + '>';
} else {
sReplacement = "<!-- Unrecognized machine ID: " + sMachineID + " -->";
}
sBlock = sBlock.replace(aMatch[0], sReplacement);
reIncludes.lastIndex = 0; // reset lastIndex, since we just modified the string that reIncludes is iterating over
}
if (cMatches) { if (cMatches) {
sBlock = sBlock.replace(/^<p>([\s\S]*)<\/p>$/g, "$1"); sBlock = sBlock.replace(/^<p>([\s\S]*)<\/p>$/g, "$1");
} }

View file

@ -29,7 +29,7 @@
"use strict"; "use strict";
/** /**
* addStickYMachine(idMachine) * addStickyMachine(idMachine)
* *
* @param {string} idMachine * @param {string} idMachine
*/ */
@ -41,7 +41,10 @@ function addStickyMachine(idMachine)
/* /*
* TODO: Determine if/when we can cache the machine and machineSibling elements; we already * TODO: Determine if/when we can cache the machine and machineSibling elements; we already
* know we can't cache them when addStickyMachine() is first called, because that currently * know we can't cache them when addStickyMachine() is first called, because that currently
* happens *before* embed.js replaces the placeholder machine DIV with the *real* machine DIV. * happens before embed.js replaces the placeholder machine <div> with the real machine <div>.
*
* Placement of the addStickyMachine() call is irrelevant, because embed.js asynchronously
* reads all the XML files that define the machine *before* replacing the <div>.
*/ */
var machine = document.getElementById(idMachine); var machine = document.getElementById(idMachine);
if (machine) { if (machine) {
@ -70,6 +73,19 @@ function addStickyMachine(idMachine)
}; };
} }
/**
* commandMachine(idMachine, typeComponent, sCommand, sValue)
*
* @param {string} idMachine
* @param {string} typeComponent
* @param {string} sCommand
* @param {string} [sValue]
*/
function commandMachine(idMachine, typeComponent, sCommand, sValue)
{
}
/** /**
* findTop(obj) * findTop(obj)
* *