Added support for automatic scripts (ie, autoScript)

This commit is contained in:
Jeff Parsons 2017-07-25 12:52:36 -07:00 committed by Jeff Parsons
commit 30df38738c
34 changed files with 3895 additions and 3580 deletions

View file

@ -114,7 +114,7 @@ function MarkOut(sMD, sIndent, req, aParms, fDebug, sMachineFile, fAutoHeading)
this.aMachines = []; // this keeps track of embedded machines on the page
this.buildOptions = {}; // this keeps track of any build options specified on the page
this.aMachineDefs = {}; // this keeps track of any machine definitions at the top of the file (as part of any Jekyll "Front Matter")
this.aCommandDefs = {}; // this keeps track of any command definitions at the top of the file (as part of any Jekyll "Front Matter")
this.aScriptDefs = {}; // this keeps track of any script definitions at the top of the file (as part of any Jekyll "Front Matter")
}
/*
@ -439,6 +439,24 @@ MarkOut.prototype.convertMD = function(sIndent)
*/
sMD = sMD.replace(aMatch[0], "");
/*
* Extract script definitions first, if any, from the Front Matter.
*/
var aScriptDefs = aMatch[1].match(/\nscripts:([\s\S]*?)\n([^\s]|$)/);
if (aScriptDefs) {
var reScript = /[ \t]+([a-z0-9$@_-]+):\s*\|/gi;
var aScripts = aScriptDefs[1].split(reScript);
/*
* Since the preceding RegExp contains a capture group (representing the name of the script),
* it will be "spliced" into the split results. So, aScripts[0] will be whatever characters precede
* the first command (which we can ignore), followed by the first script name, followed by one or more
* command lines, followed by the second script name, followed by one or more command lines, and so on.
*/
for (var iScript = 1; iScript < aScripts.length; iScript += 2) {
this.aScriptDefs[aScripts[iScript]] = aScripts[iScript+1].replace(/\n[ \t]*/g, " ").trim().replace(/"/g, "&quot;");
}
}
/*
* Extract machine definitions, if any, from the Front Matter.
*/
@ -448,7 +466,7 @@ MarkOut.prototype.convertMD = function(sIndent)
var asMachines = aMachineDefs[1].split(/\n[ \t]+-\s*/);
for (var iMachine = 0; iMachine < asMachines.length; iMachine++) {
if (!asMachines[iMachine]) continue;
var id = null, iProp, sProp;
var id = null, iProp, sProp, sValue;
var aOptions, aaOptions = [], machine = {};
var reOption = /([ \t]*)([^\s]+):[ \t]*([^\n]*)/g;
while (aOptions = reOption.exec(asMachines[iMachine])) {
@ -456,15 +474,16 @@ MarkOut.prototype.convertMD = function(sIndent)
}
for (var iOption = 0; iOption < aaOptions.length; iOption++) {
aOptions = aaOptions[iOption];
var sSpace = aOptions[1], sName = aOptions[2].toLowerCase(), sValue = aOptions[3];
if (sName == 'automount') sName = 'autoMount'; // for backward compatibility
if (sName == 'autotype') {
sName = 'autoType';
var sSpace = aOptions[1];
sProp = aOptions[2];
sValue = aOptions[3];
if (sProp == 'automount') sProp = 'autoMount'; // for backward compatibility
if (sProp == 'autoType' || sProp == 'autoScript') {
sValue = sValue.replace(/\\/g, "&#92;"); // automatically "double" any backslashes
}
if (!id && sName == 'id') {
if (!id && sProp == 'id') {
id = sValue;
} else if (sName == 'autoMount') {
} else if (sProp == 'autoMount') {
/*
* I take a simplistic approach to parsing the object definition associated with "autoMount",
* because I know it only consists of 1 or more drive letters, each of which may be followed
@ -501,7 +520,7 @@ MarkOut.prototype.convertMD = function(sIndent)
sValue += '}';
iOption = iProp - 1;
}
machine[sName] = sValue;
machine[sProp] = sValue;
}
/*
* Any "non-reserved" properties are now merged into the 'parms' property; 'autoMount'
@ -515,7 +534,17 @@ MarkOut.prototype.convertMD = function(sIndent)
machine['parms'] += MarkOut.aFMBooleanMachineProps[sProp] + ':' + machine[sProp] + ',';
continue;
}
machine['parms'] += sProp + ':"' + machine[sProp] + '",';
sValue = machine[sProp];
if (sProp == "autoScript") {
sValue = this.aScriptDefs[sValue] || sValue;
/*
* Don't try to optimize out the second replace(); it's not there simply to replace
* the single backslashes that were added to all &quot; entities. It's there to escape
* ALL backslashes in the string (ie, the ones we just added, and any others).
*/
sValue = sValue.replace(/(&quot;)/g, "\\$1").replace(/\\/g, "\\\\");
}
machine['parms'] += sProp + ':"' + sValue + '",';
}
}
var sDrives = machine['drives'];
@ -535,21 +564,6 @@ MarkOut.prototype.convertMD = function(sIndent)
}
}
var aCommandDefs = aMatch[1].match(/\ncommands:([\s\S]*?)\n([^\s]|$)/);
if (aCommandDefs) {
var reCommand = /[ \t]+([a-z0-9$@_-]+):\s*\|/gi;
var aCommands = aCommandDefs[1].split(reCommand);
/*
* Since the preceding RegExp contains a capture group (representing the name of the command),
* it will be "spliced" into the split results. So, aCommands[0] will be whatever characters precede
* the first command (which we can ignore), followed by the first command name, followed by one or more
* command lines, followed by the second command name, followed by one or more command lines, and so on.
*/
for (var iCommand = 1; iCommand < aCommands.length; iCommand += 2) {
this.aCommandDefs[aCommands[iCommand]] = aCommands[iCommand+1].replace(/\n[ \t]*/g, " ").trim().replace(/"/g, "&quot;");
}
}
/*
* If a "title" element existed in the Front Matter, this code auto-generates a top-level heading
* with the same value.
@ -1331,8 +1345,8 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
if (this.aMachineDefs[sMachineID]) {
var sCommand = findParm(aParms, 'command');
var sValue = findParm(aParms, 'value');
if (this.aCommandDefs[sCommand]) {
sValue = this.aCommandDefs[sCommand];
if (this.aScriptDefs[sCommand]) {
sValue = this.aScriptDefs[sCommand];
sCommand = "script";
} else if (sValue) {
sValue = sValue.replace(/"/g, "&quot;");

View file

@ -900,6 +900,8 @@ class Computer extends Component {
}
this.nPowerChange = 0;
Component.processScript(this.idMachine, this.getMachineParm('autoScript'));
}
/**

View file

@ -149,6 +149,8 @@ class Keyboard extends Component {
*/
this.autoInject = null;
this.autoType = parmsKbd['autoType'];
this.fDOSReady = false;
this.fnDOSReady = this.fnInjectReady = null;
/*
* HACK: We set fAllDown to false to ignore all down/up events for keys not explicitly marked as ONDOWN;
@ -385,7 +387,15 @@ class Keyboard extends Component {
intDOS(addr)
{
var AH = (this.cpu.regEAX >> 8) & 0xff;
if (AH == 0x0A) this.injectInit(this.autoType);
if (AH == 0x0A) {
this.fDOSReady = true;
if (this.fnDOSReady) {
this.fnDOSReady();
this.fnDOSReady = null;
} else {
this.injectInit(this.autoType);
}
}
return true;
}
@ -891,7 +901,6 @@ class Keyboard extends Component {
* Make sure the auto-injection buffer is empty (an injection could have been in progress on any reset after the first).
*/
this.sInjectBuffer = "";
this.fnCallReady = null;
return true;
}
@ -1026,9 +1035,9 @@ class Keyboard extends Component {
this.addActiveKey(charCode, true);
}
if (!this.sInjectBuffer.length) {
if (this.fnCallReady) {
this.fnCallReady();
this.fnCallReady = null;
if (this.fnInjectReady) {
this.fnInjectReady();
this.fnInjectReady = null;
}
} else {
setTimeout(function(kbd) {
@ -1040,19 +1049,35 @@ class Keyboard extends Component {
}
/**
* waitReady(fnCallReady)
* waitReady(fnCallReady, sOption)
*
* @this {Keyboard}
* @param {function()|null} fnCallReady
* @param {string} [sOption]
* @return {boolean} false if wait required, true otherwise
*/
waitReady(fnCallReady)
waitReady(fnCallReady, sOption)
{
if (this.sInjectBuffer.length > 0) {
if (!this.fnCallReady) this.fnCallReady = fnCallReady;
return false;
var fReady = false;
switch(sOption) {
case "DOS":
if (this.fDOSReady) {
fReady = true;
} else {
this.fnDOSReady = fnCallReady;
}
break;
default:
if (!this.sInjectBuffer.length) {
fReady = true;
} else {
this.fnInjectReady = fnCallReady;
}
break;
}
return true;
return fReady;
}
/**

View file

@ -848,7 +848,7 @@ class Component {
}
if (!fSuccess) {
Component.alertUser("Script error: '" + sCommand + "' command " + (fnCommand? " failed" : " unrecognized"));
Component.alertUser("Script error: '" + sCommand + (fnCommand? " failed" : " unrecognized"));
break;
}
}