diff --git a/_posts/2016-08-19-connecting-an-ibm-pc-to-a-dec-vt100-terminal.md b/_posts/2016-08-19-connecting-an-ibm-pc-to-a-dec-vt100-terminal.md index a804fdf7f..8d1f7c7e2 100644 --- a/_posts/2016-08-19-connecting-an-ibm-pc-to-a-dec-vt100-terminal.md +++ b/_posts/2016-08-19-connecting-an-ibm-pc-to-a-dec-vt100-terminal.md @@ -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: - {% raw %} +{% raw %} {% include machine.html id="ibm5170" %} {% 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 requires adding a *connection* property (eg, `connection:"com2->vt100.serialPort"`) to the *parms* parameter passed to the diff --git a/_posts/2016-10-06-introducing-pdpjs-a-pdp-11-emulator.md b/_posts/2016-10-06-introducing-pdpjs-a-pdp-11-emulator.md index 7581e1506..9ae85d022 100644 --- a/_posts/2016-10-06-introducing-pdpjs-a-pdp-11-emulator.md +++ b/_posts/2016-10-06-introducing-pdpjs-a-pdp-11-emulator.md @@ -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 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 -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, 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 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. -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. +[PDPjs connected to a VT100 Terminal](/devices/pdp11/machine/1170/vt100/), by clicking the **Run** button on the +test machine. The test machine is running a custom [Boot Monitor](/apps/pdp11/boot/monitor/) included with +[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 some rigorous testing, so I'll be on the lookout for some comprehensive PDP-11 instruction tests. Or I'll write my own, diff --git a/_posts/2017-01-03-pdp-11-tutorials.md b/_posts/2017-01-03-pdp-11-tutorials.md new file mode 100644 index 000000000..b2a5097e5 --- /dev/null +++ b/_posts/2017-01-03-pdp-11-tutorials.md @@ -0,0 +1,61 @@ +--- +layout: post +title: PDP-11 Tutorials +date: 2017-01-03 15:00:00 +permalink: /blog/2017/01/03/ +machines: + - id: test1170 + type: pdp11 + debugger: true + config: /devices/pdp11/machine/1170/vt100/debugger/machine.xml + connection: dl11->vt100.serialPort + - id: vt100 + type: pc8080 + config: /devices/pc8080/machine/vt100/machine.xml + connection: serialPort->test1170.dl11 + sticky: top +--- + +Introducing PDP-11 tutorials. For more information, keep scrolling. + +{% include machine.html id="vt100" %} + +[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. + +In an effort to remedy that situation, I'm adding some new features to PCjs. The first feature is what I call +"Sticky Machines", and it's more a website feature than a machine feature. At the top of any PCjs webpage, in the +*machines* section, a machine can now have a *sticky* property. For now, the only supported value is "top"; e.g.: + + machines: + - id: vt100 + type: pc8080 + config: /devices/pc8080/machine/vt100/machine.xml + connection: serialPort->test1170.dl11 + sticky: top + +A sticky machine makes it easier to construct a tutorial page for a single machine, by preventing that machine from +scrolling off the top of the page; it "sticks" to the top instead. The rest of the page scrolls normally, allowing the +user to progress at their own pace through the text and/or images of an accompanying tutorial. + +The second feature is a generalized method for sending commands to components within a machine. For example, if we +want to send some keyboard commands to machine: + +{% raw %} + {% include machine-command.html type='button' label='Try It!' machine='vt100' component='Keyboard' command='sendString' value='Hello World' %} +{% endraw %} + +which should translate into a control that looks like: + + + +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" %} + +*[@jeffpar](http://twitter.com/jeffpar)* +*Jan 3, 2017* diff --git a/modules/htmlout/lib/htmlout.js b/modules/htmlout/lib/htmlout.js index 56cef16af..353e2a5bc 100644 --- a/modules/htmlout/lib/htmlout.js +++ b/modules/htmlout/lib/htmlout.js @@ -879,6 +879,7 @@ HTMLOut.prototype.findTokens = function(reTokens) aParms.push(aMatch[1]); } } + //noinspection JSUnresolvedFunction fnToken.call(this, sToken, sIndent, aParms); } } @@ -1968,6 +1969,12 @@ HTMLOut.prototype.processMachines = function(aMachines, buildOptions, done) } } this.addFilesToHTML(asFiles, sScriptEmbed); + if (infoMachine['sticky']) { + asFiles = []; + asFiles.push("/modules/shared/lib/sticky.js"); + sScriptEmbed = ''; + this.addFilesToHTML(asFiles, sScriptEmbed); + } if (buildOptions.id) { asFiles = []; asFiles.push("/modules/build/lib/build.js"); diff --git a/modules/markout/lib/markout.js b/modules/markout/lib/markout.js index ad64d8524..998e9c794 100644 --- a/modules/markout/lib/markout.js +++ b/modules/markout/lib/markout.js @@ -367,7 +367,7 @@ MarkOut.aFMBooleanMachineProps = { 'autopower': "autoPower", 'autostart': "autoStart" }; -MarkOut.aFMReservedMachineProps = ['id', 'name', 'type', 'debugger', 'config', 'template', 'uncompiled', 'autoMount', 'drives', 'parms']; +MarkOut.aFMReservedMachineProps = ['id', 'name', 'type', 'debugger', 'config', 'template', 'uncompiled', 'autoMount', 'drives', 'parms', 'sticky']; /** * convertMD() @@ -942,10 +942,12 @@ MarkOut.prototype.convertMDLinks = function(sBlock) * 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. * - * TODO: These replacements should use appropriate values from _config.yml; however, unless/until - * we start using Node again to host the public site, that's low priority. + * TODO: Any double-brace replacements should use appropriate values from _config.yml or the + * 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, "
$1$2$3");
var aMatch;
var re = /\[([^\[\]]*)]\((.*?)(?:\s*"(.*?)"\)|\))/g;
@@ -1150,23 +1152,24 @@ MarkOut.prototype.convertMDImageLinks = function(sBlock, sIndent)
*/
MarkOut.prototype.convertMDMachineLinks = function(sBlock)
{
- var aMatch, sReplacement;
+ var aMatch, sReplacement, machine;
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,
* (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=(["'])(.*?)\1\s*%}/g;
+ var reIncludes = /(.){%\s*include\s+machine\.html\s+id=(["'])(.*?)\2\s*%}/g;
while ((aMatch = reIncludes.exec(sBlock))) {
+ if (aMatch[1] == '\t') continue;
sReplacement = "";
- sMachineID = aMatch[2];
+ sMachineID = aMatch[3];
if (this.aMachineDefs[sMachineID]) {
- var machine = this.aMachineDefs[sMachineID];
+ machine = this.aMachineDefs[sMachineID];
sMachineType = machine['type'] || "PCx86";
sMachineOptions = ((sMachineType.indexOf("-dbg") > 0 || machine['debugger'] == "true")? "debugger" : "");
+ if (machine['sticky']) sMachineOptions += (sMachineOptions? "," : "") + "sticky";
sMachineType = sMachineType.replace("-dbg", "");
sMachineXMLFile = machine['config'] || this.sMachineFile || "machine.xml";
sMachineXSLFile = machine['template'] || "";
@@ -1175,11 +1178,14 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
sReplacement = machine['name'] || "Embedded PC";
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
}
- sBlock = sBlock.replace(/\{%\s*include\s+build\.html\s+id=(["'])(.*?)\1\s*%}/g, '');
+ /*
+ * Ditto for any Liquid-style machine build links.
+ */
+ sBlock = sBlock.replace(/\{%\s*include\s+machine-build\.html\s+id=(["'])(.*?)\1\s*%}/g, '');
/*
* Start looking for Markdown-style machine links now...
@@ -1204,6 +1210,7 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
sMachineParms = aMachineParms[4] || "";
var aMachineOptions = sMachineOptions.split(',');
var fDebugger = (aMachineOptions.indexOf("debugger") >= 0);
+ var fSticky = (aMachineOptions.indexOf("sticky") >= 0);
/*
* TODO: Consider validating the existence of this XML file and generating a more meaningful error if not found
@@ -1247,10 +1254,48 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
'xsl': sMachineXSLFile,
'version': sMachineVersion,// eg, "1.10", "*" to select the current version, or "uncompiled"; "*" is the default
'debugger': fDebugger, // eg, true or false; false is the default
+ 'sticky': fSticky, // eg, true or false; false is the default
'parms': sMachineParms}
);
}
+ /*
+ * 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 = "";
+ }
+ sBlock = sBlock.replace(aMatch[0], sReplacement);
+ reIncludes.lastIndex = 0; // reset lastIndex, since we just modified the string that reIncludes is iterating over
+ }
+
if (cMatches) {
sBlock = sBlock.replace(/^([\s\S]*)<\/p>$/g, "$1");
}
diff --git a/modules/shared/lib/sticky.js b/modules/shared/lib/sticky.js
new file mode 100644
index 000000000..baf393d2b
--- /dev/null
+++ b/modules/shared/lib/sticky.js
@@ -0,0 +1,102 @@
+/**
+ * @fileoverview Support for "sticky" machines
+ * @author Jeff Parsons (@jeffpar)
+ * @copyright © Jeff Parsons 2012-2017
+ *
+ * This file is part of PCjs, a computer emulation software project at