Merge branch 'master' into gh-pages

This commit is contained in:
Jeff Parsons 2017-01-03 15:39:30 -08:00
commit 90f8716a39
7 changed files with 246 additions and 19 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

@ -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:
<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" %}
*[@jeffpar](http://twitter.com/jeffpar)*
*Jan 3, 2017*

View file

@ -879,6 +879,7 @@ HTMLOut.prototype.findTokens = function(reTokens)
aParms.push(aMatch[1]); aParms.push(aMatch[1]);
} }
} }
//noinspection JSUnresolvedFunction
fnToken.call(this, sToken, sIndent, aParms); fnToken.call(this, sToken, sIndent, aParms);
} }
} }
@ -1968,6 +1969,12 @@ HTMLOut.prototype.processMachines = function(aMachines, buildOptions, done)
} }
} }
this.addFilesToHTML(asFiles, sScriptEmbed); this.addFilesToHTML(asFiles, sScriptEmbed);
if (infoMachine['sticky']) {
asFiles = [];
asFiles.push("/modules/shared/lib/sticky.js");
sScriptEmbed = '<script type="text/javascript">addStickyMachine("' + infoMachine['id'] + '")</script>';
this.addFilesToHTML(asFiles, sScriptEmbed);
}
if (buildOptions.id) { if (buildOptions.id) {
asFiles = []; asFiles = [];
asFiles.push("/modules/build/lib/build.js"); asFiles.push("/modules/build/lib/build.js");

View file

@ -367,7 +367,7 @@ MarkOut.aFMBooleanMachineProps = {
'autopower': "autoPower", 'autopower': "autoPower",
'autostart': "autoStart" '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() * convertMD()
@ -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,23 +1152,24 @@ 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";
sMachineType = sMachineType.replace("-dbg", ""); sMachineType = sMachineType.replace("-dbg", "");
sMachineXMLFile = machine['config'] || this.sMachineFile || "machine.xml"; sMachineXMLFile = machine['config'] || this.sMachineFile || "machine.xml";
sMachineXSLFile = machine['template'] || ""; sMachineXSLFile = machine['template'] || "";
@ -1175,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...
@ -1204,6 +1210,7 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
sMachineParms = aMachineParms[4] || ""; sMachineParms = aMachineParms[4] || "";
var aMachineOptions = sMachineOptions.split(','); var aMachineOptions = sMachineOptions.split(',');
var fDebugger = (aMachineOptions.indexOf("debugger") >= 0); 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 * 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, 'xsl': sMachineXSLFile,
'version': sMachineVersion,// eg, "1.10", "*" to select the current version, or "uncompiled"; "*" is the default '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 'debugger': fDebugger, // eg, true or false; false is the default
'sticky': fSticky, // eg, true or false; false is the default
'parms': sMachineParms} '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 = "<!-- 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

@ -0,0 +1,102 @@
/**
* @fileoverview Support for "sticky" machines
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a> (@jeffpar)
* @copyright © Jeff Parsons 2012-2017
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCjs. If not,
* see <http://www.gnu.org/licenses/gpl.html>.
*
* You are required to include the above copyright notice in every modified copy of this work
* and to display that copyright notice when the software starts running; see COPYRIGHT in
* <http://pcjs.org/modules/shared/lib/defines.js>.
*
* Some PCjs files also attempt to load external resource files, such as character-image files,
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
* for purposes of the GNU General Public License, and the author does not claim any copyright
* as to their contents.
*/
"use strict";
/**
* addStickyMachine(idMachine)
*
* @param {string} idMachine
*/
function addStickyMachine(idMachine)
{
var topMachine = -1;
var prevOnScroll = window.onscroll;
window.onscroll = function() {
/*
* 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
* 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);
if (machine) {
var machineSibling = machine.nextElementSibling;
if (machineSibling) {
if (topMachine < 0) {
topMachine = findTop(machine);
}
machine.className = machine.className.replace(/pcjs-machine-(floating|sticky) /g, '');
if (window.pageYOffset <= topMachine) {
machine.className = 'pcjs-machine-floating ' + machine.className;
if (machineSibling) machineSibling.style.paddingTop = 0;
} else {
machine.className = 'pcjs-machine-sticky ' + machine.className;
if (machineSibling) machineSibling.style.paddingTop = machine.offsetHeight + 'px';
}
if (prevOnScroll) prevOnScroll();
}
}
};
}
/**
* commandMachine(idMachine, typeComponent, sCommand, sValue)
*
* @param {string} idMachine
* @param {string} typeComponent
* @param {string} sCommand
* @param {string} [sValue]
*/
function commandMachine(idMachine, typeComponent, sCommand, sValue)
{
console.log("commandMachine('" + idMachine + "','" + typeComponent + "','" + sCommand + "','" + sValue + "')");
}
/**
* findTop(obj)
*
* @param {Object} obj
*/
function findTop(obj)
{
var curTop = 0;
if (typeof obj.offsetParent != 'undefined' && obj.offsetParent) {
while (obj.offsetParent) {
curTop += obj.offsetTop;
obj = obj.offsetParent;
}
curTop += obj.offsetTop;
}
else if (obj.y) {
curTop += obj.y;
}
return curTop;
}

View file

@ -228,7 +228,19 @@
.pcjs-copyright a { .pcjs-copyright a {
text-decoration: none; text-decoration: none;
} }
.pcjs-machine-floating {
position: relative;
z-index: auto;
background-color: transparent;
padding-right: 0;
}
.pcjs-machine-sticky {
position: fixed !important;
z-index: 1 !important;
background-color: #404040 !important;
padding-right: 16px !important;
top: 0 !important;
}
@media screen and (max-width: 800px) { @media screen and (max-width: 800px) {
.pcjs-textarea { .pcjs-textarea {
width: 100% !important; width: 100% !important;