Created PDP-10 skeleton
This commit is contained in:
parent
b1d16c979a
commit
ba7de38fbf
40 changed files with 12282 additions and 35 deletions
89
Gruntfile.js
89
Gruntfile.js
|
|
@ -114,6 +114,7 @@ module.exports = function(grunt) {
|
|||
* @property {Array.<string>} c1pJSFiles
|
||||
* @property {Array.<string>} pcX86Files
|
||||
* @property {Array.<string>} pc8080Files
|
||||
* @property {Array.<string>} pdp10Files
|
||||
* @property {Array.<string>} pdp11Files
|
||||
* @property {Array.<string>} closureCompilerExterns
|
||||
*/
|
||||
|
|
@ -122,6 +123,7 @@ module.exports = function(grunt) {
|
|||
var tmpC1Pjs = "./tmp/c1pjs/" + pkg.version + "/c1p.js";
|
||||
var tmpPCx86 = "./tmp/pcx86/" + pkg.version + "/pcx86.js";
|
||||
var tmpPC8080 = "./tmp/pc8080/" + pkg.version + "/pc8080.js";
|
||||
var tmpPDP10 = "./tmp/pdpjs/" + pkg.version + "/pdp10.js";
|
||||
var tmpPDP11 = "./tmp/pdpjs/" + pkg.version + "/pdp11.js";
|
||||
|
||||
grunt.initConfig({
|
||||
|
|
@ -176,6 +178,19 @@ module.exports = function(grunt) {
|
|||
src: pkg.pc8080Files,
|
||||
dest: "./versions/pc8080/" + pkg.version + "/pc8080-dbg.js"
|
||||
},
|
||||
"pdp10.js": {
|
||||
src: pkg.pdp10Files,
|
||||
dest: "./versions/pdpjs/" + pkg.version + "/pdp10.js",
|
||||
options: {
|
||||
process: function(src, filepath) {
|
||||
return (path.basename(filepath) == "debugger.js"? "" : src);
|
||||
}
|
||||
}
|
||||
},
|
||||
"pdp10-dbg.js": {
|
||||
src: pkg.pdp10Files,
|
||||
dest: "./versions/pdpjs/" + pkg.version + "/pdp10-dbg.js"
|
||||
},
|
||||
"pdp11.js": {
|
||||
src: pkg.pdp11Files,
|
||||
dest: "./versions/pdpjs/" + pkg.version + "/pdp11.js",
|
||||
|
|
@ -243,6 +258,24 @@ module.exports = function(grunt) {
|
|||
}
|
||||
}
|
||||
},
|
||||
"tmp-pdp10": {
|
||||
src: pkg.pdp10Files,
|
||||
dest: tmpPDP10,
|
||||
options: {
|
||||
banner: '"use strict";\n\n',
|
||||
process: function(src, filepath) {
|
||||
return "/**\n * @copyright " + filepath.replace(/^\./, "http://pcjs.org") + " (C) Jeff Parsons 2012-2017\n */\n\n" +
|
||||
src.replace(/(^|\n)[ \t]*(['"])use strict\2;?/g, '')
|
||||
.replace(/^(import|export)[ \t]+[^\n]*\n/gm, '')
|
||||
.replace(/^[ \t]*var\s+\S+\s*=\s*require\((['"]).*?\1\);/gm, '')
|
||||
.replace(/^[ \t]*(if\s+\(NODE\)\s*|)module\.exports\s*=\s*\S+;/gm, '')
|
||||
.replace(/\/\*\*\s*\*\s*@fileoverview[\s\S]*?\*\/\s*/g, '')
|
||||
.replace(/[ \t]*if\s*\(NODE\)\s*(\{[^}]*}|[^\n]*)(\n|$)/gm, '')
|
||||
.replace(/[ \t]*if\s*\(typeof\s+module\s*!==\s*(['"])undefined\1\)\s*(\{[^}]*}|[^\n]*)(\n|$)/gm, '')
|
||||
.replace(/[ \t]*[A-Za-z_][A-Za-z0-9_.]*\.assert\([^\n]*\);[^\n]*/g, '');
|
||||
}
|
||||
}
|
||||
},
|
||||
"tmp-pdp11": {
|
||||
src: pkg.pdp11Files,
|
||||
dest: tmpPDP11,
|
||||
|
|
@ -281,11 +314,14 @@ module.exports = function(grunt) {
|
|||
src: pkg.pc8080Files,
|
||||
dest: tmpPC8080
|
||||
},
|
||||
"pdp10.js": {
|
||||
src: pkg.pdp10Files,
|
||||
dest: tmpPDP10
|
||||
},
|
||||
"pdp11.js": {
|
||||
src: pkg.pdp11Files,
|
||||
dest: tmpPDP11
|
||||
}
|
||||
|
||||
},
|
||||
closureCompiler: {
|
||||
options: {
|
||||
|
|
@ -293,7 +329,7 @@ module.exports = function(grunt) {
|
|||
compilerFile: "./bin/compiler.jar",
|
||||
|
||||
// [OPTIONAL] set to true if you want to check if files were modified before starting compilation
|
||||
checkModified: grunt.option("rebuild")? false : true,
|
||||
checkModified: !grunt.option("rebuild"),
|
||||
|
||||
// [OPTIONAL] Set Closure Compiler Directives here
|
||||
compilerOpts: {
|
||||
|
|
@ -420,6 +456,33 @@ module.exports = function(grunt) {
|
|||
src: tmpPC8080,
|
||||
dest: "./versions/pc8080/" + pkg.version + "/pc8080-dbg.js"
|
||||
},
|
||||
"pdp10.js": {
|
||||
TEMPcompilerOpts: {
|
||||
create_source_map: "./tmp/pdpjs/" + pkg.version + "/pdp10.map",
|
||||
define: ["\"APPVERSION='" + pkg.version + "'\"",
|
||||
"\"SITEHOST='www.pcjs.org'\"", "COMPILED=true", "DEBUG=false", "DEBUGGER=false"],
|
||||
output_wrapper: "\"(function(){%output%})();//# sourceMappingURL=/tmp/pdpjs/" + pkg.version + "/pdp10.map\""
|
||||
// output_wrapper: "\"(function(){%output%})();\""
|
||||
},
|
||||
// src: pkg.pdp10Files,
|
||||
src: tmpPDP10,
|
||||
dest: "./versions/pdpjs/" + pkg.version + "/pdp10.js"
|
||||
},
|
||||
"pdp10-dbg.js": {
|
||||
/*
|
||||
* Technically, this is the one case we don't need to override the default 'define' settings, but maybe it's best to be explicit.
|
||||
*/
|
||||
TEMPcompilerOpts: {
|
||||
create_source_map: "./tmp/pdpjs/" + pkg.version + "/pdp10-dbg.map",
|
||||
define: ["\"APPVERSION='" + pkg.version + "'\"",
|
||||
"\"SITEHOST='www.pcjs.org'\"", "COMPILED=true", "DEBUG=false", "DEBUGGER=true"],
|
||||
output_wrapper: "\"(function(){%output%})();//# sourceMappingURL=/tmp/pdpjs/" + pkg.version + "/pdp10-dbg.map\""
|
||||
// output_wrapper: "\"(function(){%output%})();\""
|
||||
},
|
||||
// src: pkg.pdp10Files,
|
||||
src: tmpPDP10,
|
||||
dest: "./versions/pdpjs/" + pkg.version + "/pdp10-dbg.js"
|
||||
},
|
||||
"pdp11.js": {
|
||||
TEMPcompilerOpts: {
|
||||
create_source_map: "./tmp/pdpjs/" + pkg.version + "/pdp11.map",
|
||||
|
|
@ -522,6 +585,26 @@ module.exports = function(grunt) {
|
|||
}
|
||||
}
|
||||
},
|
||||
"pdp10": {
|
||||
files: [
|
||||
{
|
||||
cwd: "modules/shared/templates/",
|
||||
src: ["common.css", "common.xsl", "components.*", "document.css", "document.xsl", "machine.xsl", "manifest.xsl", "outline.xsl"],
|
||||
dest: "versions/pdpjs/<%= pkg.version %>/",
|
||||
expand: true
|
||||
}
|
||||
],
|
||||
options: {
|
||||
process: function(content, srcPath) {
|
||||
var s = content.replace(/(<xsl:variable name="APPCLASS">)[^<]*(<\/xsl:variable>)/g, "$1pdp10$2");
|
||||
s = s.replace(/(<xsl:variable name="APPNAME">)[^<]*(<\/xsl:variable>)/g, "$1PDPjs$2");
|
||||
s = s.replace(/(<xsl:variable name="APPVERSION">)[^<]*(<\/xsl:variable>)/g, "$1" + pkg.version + "$2");
|
||||
s = s.replace(/"[^"]*\/?(common.css|common.xsl|components.css|components.xsl|document.css|document.xsl)"/g, '"/versions/pdpjs/' + pkg.version + '/$1"');
|
||||
s = s.replace(/[ \t]*\/\*[^*][\s\S]*?\*\//g, "").replace(/[ \t]*<!--[^@]*?-->[ \t]*\n?/g, "");
|
||||
return s;
|
||||
}
|
||||
}
|
||||
},
|
||||
"pdp11": {
|
||||
files: [
|
||||
{
|
||||
|
|
@ -667,7 +750,7 @@ module.exports = function(grunt) {
|
|||
|
||||
grunt.loadTasks("modules/grunts/prepjs/tasks");
|
||||
|
||||
grunt.registerTask("preCompiler", grunt.option("rebuild")? ["concat:tmp-c1pjs", "concat:tmp-pcx86", "concat:tmp-pc8080", "concat:tmp-pdp11"] : ["newer:concat:tmp-c1pjs", "newer:concat:tmp-pcx86", "newer:concat:tmp-pc8080", "newer:concat:tmp-pdp11"]);
|
||||
grunt.registerTask("preCompiler", grunt.option("rebuild")? ["concat:tmp-c1pjs", "concat:tmp-pcx86", "concat:tmp-pc8080", "concat:tmp-pdp10", "concat:tmp-pdp11"] : ["newer:concat:tmp-c1pjs", "newer:concat:tmp-pcx86", "newer:concat:tmp-pc8080", "newer:concat:tmp-pdp10", "newer:concat:tmp-pdp11"]);
|
||||
|
||||
grunt.registerTask("compile", ["preCompiler", "closureCompiler", "replace:fix-source-maps"]);
|
||||
|
||||
|
|
|
|||
|
|
@ -718,7 +718,7 @@ case "^":d=f^e;break;case "|":d=f|e;break;case "&&":d=f&&e?1:0;break;case "||":d
|
|||
function Px(a,b,c){var d;if(b){b=Qx(a,b);for(var e=0,f=!1,g=b,h=[],k=[],m=b.split(/(\|\||&&|\||^|&|!=|==|>=|>>>|>>|>|<=|<<|<|-|\+|%|\/|\*)/);e<m.length;){var p=m[e++],w=p.length,p=ra(p);if(!p){f=!0;break}p=Rx(a,p,null,!1===c);if(void 0===p){f=!0;c=!1;break}h.push(p);if(e==m.length)break;var p=m[e++],u=p.length;k.length&&Sx[p]<Sx[k[k.length-1]]&&Ox(h,k,1);k.push(p);b=b.substr(w+u)}Ox(h,k)&&1==h.length||(f=!0);f?c&&a.P("error parsing '"+g+"' at character "+(g.length-b.length)):(d=h.pop(),c&&Tx(a,null,
|
||||
d))}return d}function Qx(a,b){for(var c,d=/\{(.*?)\}/;(c=b.match(d))&&!(0<=c[1].indexOf("{"));){var e=Px(a,c[1]);b=b.replace("{"+c[1]+"}",null!=e?Ux(a,e):"undefined")}for(;(c=b.match(/\[(.*?)]/))&&!(0<=c[1].indexOf("["));)b=a.Pk(b,c[1]);for(;c=b.match(/\$([a-z]+)/i);){d=null;switch(c[1].toLowerCase()){case "ops":d=a.W-a.ga}if(null==d)break;b=b.replace(c[0],d.toString())}return b}
|
||||
function Rx(a,b,c,d){var e;null!=b?(e=a.gh(b),0<=e?e=a.hh(e):(e=a.da[b],null==e&&(e=ga(b,a.ya))),null!=e||d||a.P("invalid "+(c?c:"value")+": "+b)):d||a.P("missing "+(c||"value"));return e}function Tx(a,b,c){var d,e=!1;void 0!==c&&(e=!0,d=q(c,0,!0)+" "+c+". "+ja(c,0,!0)+" "+ia(c,4,!0),32<=c&&127>c&&(d+=" '"+String.fromCharCode(c)+"'"));a.P((null!=b?b+": ":"")+d);return e}function Vx(a,b){if(b)return Tx(a,b,a.da[b]);var c=0;for(b in a.da)Tx(a,b,a.da[b]),c++;return 0<c}
|
||||
function Ux(a,b){switch(a.ya){case 8:a=ja(b,NaN);break;case 10:a=b.toString();break;default:a=q(b,NaN)}return a}var Sx={"||":0,"&&":1,"|":2,"^":3,"&":4,"!=":5,"==":5,">=":6,">":6,"<=":6,"<":6,">>>":7,">>":7,"<<":7,"-":8,"+":8,"%":9,"/":9,"*":9};
|
||||
function Ux(a,b){var c;c=void 0===c?0:c;switch(a.ya){case 8:a=ja(b,3*c);break;case 10:a=b.toString();break;default:a=q(b,2*c)}return a}var Sx={"||":0,"&&":1,"|":2,"^":3,"&":4,"!=":5,"==":5,">=":6,">":6,"<=":6,"<":6,">>>":7,">>":7,"<<":7,"-":8,"+":8,"%":9,"/":9,"*":9};
|
||||
function Wx(a){Nx.call(this,a);this.oa=4;this.ha=5;this.wa=1048575;this.K=Xx(this);this.Ga=Xx(this);this.Z=Xx(this);this.D=[];this.B=this.Y=this.M=[];Yx(this);this.ta=0;Zx(this);this.za={};$x(this,a.messages);this.Ca=a.commands;var b=this;window?void 0===window.pcx86&&(window.pcx86=function(a){return ay(b,a)}):void 0===global.pcx86&&(global.pcx86=function(a){return ay(b,a)})}ba(Wx,Nx);l=Wx.prototype;
|
||||
l.uc=function(a,b,c,d){this.ka=b;this.H=c;this.pa=a;this.Za=Ob(a,"FDC");this.Ha=Ob(a,"HDC");this.Tc=Ob(a,"FPU");this.G=Ob(a,"Mouse");(a=qd(a,"messages"))&&$x(this,a);this.ha=b.I>>2;this.wa=b.N;this.Ta=new Kd(this.H,7,"DBG");this.ma=by;80186<=this.H.ca&&(this.ma=by.slice(),this.ma[15]=cy,80286<=this.H.ca&&(this.ma[15]=dy,80386<=this.H.ca&&(this.oa=8)));rl(this,64,function(a){ey(d,d.H.sc,a[0])});rl(this,4,function(a){if(a=a[0]){var b=Rx(d,a);if(void 0===b)d.P("invalid selector: "+a);else if(a=fy(d,
|
||||
b,gy),d.P("dumpSel("+ka(a?a.U:b)+"): %"+q(a?a.Lb:null,d.ha)),a){var c,b=!1;if(a.type&4096)a.type&2048?(c="code"+(a.type&512?",readable":",execonly"),a.type&1024&&(c+=",conforming")):(c="data"+(a.type&512?",writable":",readonly"),a.type&1024&&(c+=",expdown")),a.type&256&&(c+=",accessed");else{var e=hy[a.type];e&&(c=e[0],b=e[1])}!c||a.jb&32768||(c+=",not present");d.P((b?"seg="+ka(a.ua&65535)+" off="+ka(a.Ka):"base="+q(a.ua,d.ha)+" limit="+iy(a.Ka))+" type="+r(a.type>>8)+" ("+c+") ext="+ka(a.ext&-65296)+
|
||||
|
|
|
|||
717
modules/pdp10/lib/bus.js
Normal file
717
modules/pdp10/lib/bus.js
Normal file
|
|
@ -0,0 +1,717 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP-10 Bus component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @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";
|
||||
|
||||
if (NODE) {
|
||||
var Str = require("../../shared/lib/strlib");
|
||||
var Usr = require("../../shared/lib/usrlib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var State = require("../../shared/lib/state");
|
||||
var PDP10 = require("./defines");
|
||||
var MemoryPDP10 = require("./memory");
|
||||
var MessagesPDP10 = require("./messages");
|
||||
}
|
||||
|
||||
/*
|
||||
* Data types used by scanMemory()
|
||||
*/
|
||||
|
||||
/**
|
||||
* This defines the BlockInfo bit fields used by scanMemory() when it creates the aBlocks array.
|
||||
*
|
||||
* @typedef {{
|
||||
* num: BitField,
|
||||
* count: BitField,
|
||||
* btmod: BitField,
|
||||
* type: BitField
|
||||
* }}
|
||||
*/
|
||||
var BlockInfoPDP10 = Usr.defineBitFields({num:20, count:8, btmod:1, type:3});
|
||||
|
||||
/**
|
||||
* BusInfoPDP10 object definition (returned by scanMemory())
|
||||
*
|
||||
* cbTotal: total bytes allocated
|
||||
* cBlocks: total Memory blocks allocated
|
||||
* aBlocks: array of allocated Memory block numbers
|
||||
*
|
||||
* @typedef {{
|
||||
* cbTotal: number,
|
||||
* cBlocks: number,
|
||||
* aBlocks: Array.<BlockInfoPDP10>
|
||||
* }}
|
||||
*/
|
||||
var BusInfoPDP10;
|
||||
|
||||
class BusPDP10 extends Component {
|
||||
/**
|
||||
* BusPDP10(parmsBus, cpu, dbg)
|
||||
*
|
||||
* The BusPDP10 component manages physical memory and I/O address spaces.
|
||||
*
|
||||
* The BusPDP10 component has no UI elements, so it does not require an init() handler,
|
||||
* but it still inherits from the Component class and must be allocated like any
|
||||
* other device component. It's currently allocated by the Computer's init() handler,
|
||||
* which then calls the initBus() method of all the other components.
|
||||
*
|
||||
* For memory beyond the simple needs of the ROM and RAM components (ie, memory-mapped
|
||||
* devices), the address space must still be allocated through the BusPDP10 component via
|
||||
* addMemory(). If the component needs something more than simple read/write storage,
|
||||
* it must provide a custom controller.
|
||||
*
|
||||
* @param {Object} parmsBus
|
||||
* @param {CPUStatePDP10} cpu
|
||||
* @param {DebuggerPDP10} dbg
|
||||
*/
|
||||
constructor(parmsBus, cpu, dbg)
|
||||
{
|
||||
super("Bus", parmsBus, MessagesPDP10.BUS);
|
||||
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
|
||||
/*
|
||||
* Supported values for nBusWidth: 18 (default). This represents the maximum size of the bus for the
|
||||
* life of the machine, regardless what memory management mode the CPU has enabled.
|
||||
*/
|
||||
this.nBusWidth = +parmsBus['busWidth'] || 18;
|
||||
|
||||
/*
|
||||
* Compute all BusPDP10 memory block parameters now, based on the width of the bus.
|
||||
*
|
||||
* Note that all PCjs machines divide their address space into blocks, using a block size appropriate for
|
||||
* the machine's bus width. This allows us to efficiently allocate the entire address space, by reusing blocks
|
||||
* as appropriate, and to define to different address behaviors on a block-granular level.
|
||||
*/
|
||||
this.addrTotal = 1 << this.nBusWidth;
|
||||
this.nBusMask = (this.addrTotal - 1);
|
||||
this.nBlockSize = 16384;
|
||||
this.nBlockShift = Math.log2(this.nBlockSize); // ES6 ALERT (alternatively: Math.log(this.nBlockSize) / Math.LN2)
|
||||
this.nBlockLen = this.nBlockSize >> 2;
|
||||
this.nBlockLimit = this.nBlockSize - 1;
|
||||
this.nBlockTotal = (this.addrTotal / this.nBlockSize) | 0;
|
||||
this.nBlockMask = this.nBlockTotal - 1;
|
||||
this.assert(this.nBlockMask <= BlockInfoPDP10.num.mask);
|
||||
|
||||
/*
|
||||
* Define all the properties to be initialized by initMemory()
|
||||
*/
|
||||
this.aBusBlocks = [];
|
||||
|
||||
/*
|
||||
* We're ready to allocate empty Memory blocks to span the entire physical address space.
|
||||
*/
|
||||
this.initMemory();
|
||||
|
||||
this.setReady();
|
||||
}
|
||||
|
||||
/**
|
||||
* initMemory()
|
||||
*
|
||||
* Allocate enough (empty) Memory blocks to span the entire physical address space.
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
*/
|
||||
initMemory()
|
||||
{
|
||||
var block = new MemoryPDP10(this);
|
||||
block.copyBreakpoints(this.dbg);
|
||||
|
||||
this.aBusBlocks = new Array(this.nBlockTotal);
|
||||
for (var iBlock = 0; iBlock < this.nBlockTotal; iBlock++) {
|
||||
this.aBusBlocks[iBlock] = block;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
*/
|
||||
reset()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* getWidth()
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @return {number}
|
||||
*/
|
||||
getWidth()
|
||||
{
|
||||
return this.nBusWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {Object|null} data (always null because we supply no powerDown() handler)
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
powerUp(data, fRepower)
|
||||
{
|
||||
if (!fRepower) {
|
||||
if (!data) {
|
||||
this.reset();
|
||||
} else {
|
||||
if (!this.restore(data)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
powerDown(fSave, fShutdown)
|
||||
{
|
||||
return fSave? this.save() : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* save()
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @return {Object|null}
|
||||
*/
|
||||
save()
|
||||
{
|
||||
var state = new State(this);
|
||||
state.set(0, this.saveMemory());
|
||||
return state.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* restore(data)
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {Object} data
|
||||
* @return {boolean} true if restore successful, false if not
|
||||
*/
|
||||
restore(data)
|
||||
{
|
||||
return this.restoreMemory(data[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* addMemory(addr, size, type)
|
||||
*
|
||||
* Adds new Memory blocks to the specified address range. Any Memory blocks previously
|
||||
* added to that range must first be removed via removeMemory(); otherwise, you'll get
|
||||
* an allocation conflict error. This helps prevent address calculation errors, redundant
|
||||
* allocations, etc.
|
||||
*
|
||||
* We've relaxed some of the original requirements (ie, that addresses must start at a
|
||||
* block-granular address, or that sizes must be equal to exactly one or more blocks),
|
||||
* because machines with large block sizes can make it impossible to load certain ROMs at
|
||||
* their required addresses. Every allocation still allocates a whole number of blocks.
|
||||
*
|
||||
* Even so, BusPDP10 memory management does NOT provide a general-purpose heap. Most memory
|
||||
* allocations occur during machine initialization and never change. In particular, there
|
||||
* is NO support for removing partial-block allocations.
|
||||
*
|
||||
* Each Memory block keeps track of a start address (addr) and length (used), indicating
|
||||
* the used space within the block; any free space that precedes or follows that used space
|
||||
* can be allocated later, by simply extending the beginning or ending of the previously used
|
||||
* space. However, any holes that might have existed between the original allocation and an
|
||||
* extension are subsumed by the extension.
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} addr is the starting physical address of the request
|
||||
* @param {number} size of the request, in bytes
|
||||
* @param {number} type is one of the MemoryPDP10.TYPE constants
|
||||
* @return {boolean} true if successful, false if not
|
||||
*/
|
||||
addMemory(addr, size, type)
|
||||
{
|
||||
var addrNext = addr;
|
||||
var sizeLeft = size;
|
||||
var iBlock = addrNext >>> this.nBlockShift;
|
||||
|
||||
while (sizeLeft > 0 && iBlock < this.aBusBlocks.length) {
|
||||
|
||||
var block = this.aBusBlocks[iBlock];
|
||||
var addrBlock = iBlock * this.nBlockSize;
|
||||
var sizeBlock = this.nBlockSize - (addrNext - addrBlock);
|
||||
if (sizeBlock > sizeLeft) sizeBlock = sizeLeft;
|
||||
|
||||
if (block && block.size) {
|
||||
if (block.type == type) {
|
||||
/*
|
||||
* Where there is already a similar block with a non-zero size, we allow the allocation only if:
|
||||
*
|
||||
* 1) addrNext + sizeLeft <= block.addr (the request precedes the used portion of the current block), or
|
||||
* 2) addrNext >= block.addr + block.used (the request follows the used portion of the current block)
|
||||
*/
|
||||
if (addrNext + sizeLeft <= block.addr) {
|
||||
block.used += (block.addr - addrNext);
|
||||
block.addr = addrNext;
|
||||
return true;
|
||||
}
|
||||
if (addrNext >= block.addr + block.used) {
|
||||
var sizeAvail = block.size - (addrNext - addrBlock);
|
||||
if (sizeAvail > sizeLeft) sizeAvail = sizeLeft;
|
||||
block.used = addrNext - block.addr + sizeAvail;
|
||||
addrNext = addrBlock + this.nBlockSize;
|
||||
sizeLeft -= sizeAvail;
|
||||
iBlock++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return this.reportError(BusPDP10.ERROR.RANGE_INUSE, addrNext, sizeLeft);
|
||||
}
|
||||
|
||||
var blockNew = new MemoryPDP10(this, addrNext, sizeBlock, this.nBlockSize, type);
|
||||
blockNew.copyBreakpoints(this.dbg, block);
|
||||
this.aBusBlocks[iBlock++] = blockNew;
|
||||
|
||||
addrNext = addrBlock + this.nBlockSize;
|
||||
sizeLeft -= sizeBlock;
|
||||
}
|
||||
|
||||
if (sizeLeft <= 0) {
|
||||
this.status("Added " + (size >> 10) + "Kb " + MemoryPDP10.TYPE_NAMES[type] + " at " + Str.toOct(addr));
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.reportError(BusPDP10.ERROR.RANGE_INVALID, addr, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* cleanMemory(addr, size)
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} addr
|
||||
* @param {number} size
|
||||
* @return {boolean} true if all blocks were clean, false if dirty; all blocks are cleaned in the process
|
||||
*/
|
||||
cleanMemory(addr, size)
|
||||
{
|
||||
var fClean = true;
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
while (size > 0 && iBlock < this.aBusBlocks.length) {
|
||||
if (this.aBusBlocks[iBlock].fDirty) {
|
||||
this.aBusBlocks[iBlock].fDirty = fClean = false;
|
||||
this.aBusBlocks[iBlock].fDirtyEver = true;
|
||||
}
|
||||
size -= this.nBlockSize;
|
||||
iBlock++;
|
||||
}
|
||||
return fClean;
|
||||
}
|
||||
|
||||
/**
|
||||
* zeroMemory(addr, size, pattern)
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} addr
|
||||
* @param {number} size
|
||||
* @param {number} [pattern]
|
||||
*/
|
||||
zeroMemory(addr, size, pattern)
|
||||
{
|
||||
var off = addr & this.nBlockLimit;
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
while (size > 0 && iBlock < this.aBusBlocks.length) {
|
||||
this.aBusBlocks[iBlock].zero(off, size, pattern);
|
||||
size -= this.nBlockSize;
|
||||
iBlock++;
|
||||
off = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* scanMemory(info, addr, size)
|
||||
*
|
||||
* Returns a BusInfoPDP10 object for the specified address range.
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {BusInfoPDP10} [info] previous BusInfoPDP10, if any
|
||||
* @param {number} [addr] starting address of range (0 if none provided)
|
||||
* @param {number} [size] size of range, in bytes (up to end of address space if none provided)
|
||||
* @return {BusInfoPDP10} updated info (or new info if no previous info provided)
|
||||
*/
|
||||
scanMemory(info, addr, size)
|
||||
{
|
||||
if (addr == null) addr = 0;
|
||||
if (size == null) size = (this.addrTotal - addr) | 0;
|
||||
if (info == null) info = {cbTotal: 0, cBlocks: 0, aBlocks: []};
|
||||
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
var iBlockMax = ((addr + size - 1) >>> this.nBlockShift);
|
||||
|
||||
info.cbTotal = 0;
|
||||
info.cBlocks = 0;
|
||||
while (iBlock <= iBlockMax) {
|
||||
var block = this.aBusBlocks[iBlock];
|
||||
info.cbTotal += block.size;
|
||||
if (block.size) {
|
||||
info.aBlocks.push(/** @type {BlockInfoPDP10} */ (Usr.initBitFields(BlockInfoPDP10, iBlock, 0, 0, block.type)));
|
||||
info.cBlocks++
|
||||
}
|
||||
iBlock++;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* removeMemory(addr, size)
|
||||
*
|
||||
* Replaces every block in the specified address range with empty Memory blocks that ignore all reads/writes.
|
||||
*
|
||||
* TODO: Update the removeMemory() interface to reflect the relaxed requirements of the addMemory() interface.
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} addr
|
||||
* @param {number} size
|
||||
* @return {boolean} true if successful, false if not
|
||||
*/
|
||||
removeMemory(addr, size)
|
||||
{
|
||||
if (!(addr & this.nBlockLimit) && size && !(size & this.nBlockLimit)) {
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
while (size > 0) {
|
||||
var blockOld = this.aBusBlocks[iBlock];
|
||||
var blockNew = new MemoryPDP10(this, addr);
|
||||
blockNew.copyBreakpoints(this.dbg, blockOld);
|
||||
this.aBusBlocks[iBlock++] = blockNew;
|
||||
addr = iBlock * this.nBlockSize;
|
||||
size -= this.nBlockSize;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return this.reportError(BusPDP10.ERROR.RANGE_INVALID, addr, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* getMemoryBlocks(addr, size)
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} addr is the starting physical address
|
||||
* @param {number} size of the request, in bytes
|
||||
* @return {Array} of Memory blocks
|
||||
*/
|
||||
getMemoryBlocks(addr, size)
|
||||
{
|
||||
var aBlocks = [];
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
while (size > 0 && iBlock < this.aBusBlocks.length) {
|
||||
aBlocks.push(this.aBusBlocks[iBlock++]);
|
||||
size -= this.nBlockSize;
|
||||
}
|
||||
return aBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* setMemoryBlocks(addr, size, aBlocks, type)
|
||||
*
|
||||
* If no type is specified, then specified address range uses all the provided blocks as-is;
|
||||
* this form of setMemoryBlocks() is used for complete physical aliases.
|
||||
*
|
||||
* Otherwise, new blocks are allocated with the specified type; the underlying memory from the
|
||||
* provided blocks is still used, but the new blocks may have different access to that memory.
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} addr is the starting physical address
|
||||
* @param {number} size of the request, in bytes
|
||||
* @param {Array} aBlocks as returned by getMemoryBlocks()
|
||||
* @param {number} [type] is one of the MemoryPDP10.TYPE constants
|
||||
*/
|
||||
setMemoryBlocks(addr, size, aBlocks, type)
|
||||
{
|
||||
var i = 0;
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
while (size > 0 && iBlock < this.aBusBlocks.length) {
|
||||
var block = aBlocks[i++];
|
||||
this.assert(block);
|
||||
if (!block) break;
|
||||
if (type !== undefined) {
|
||||
var blockNew = new MemoryPDP10(this, addr);
|
||||
blockNew.clone(block, type, this.dbg);
|
||||
block = blockNew;
|
||||
}
|
||||
this.aBusBlocks[iBlock++] = block;
|
||||
size -= this.nBlockSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* getWord(addr)
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} addr is a physical address
|
||||
* @return {number} word (16-bit) value at that address
|
||||
*/
|
||||
getWord(addr)
|
||||
{
|
||||
var off = addr & this.nBlockLimit;
|
||||
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
|
||||
return this.aBusBlocks[iBlock].readWord(off, addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* setWord(addr, w)
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} addr is a physical address
|
||||
* @param {number} w is the word (16-bit) value to write
|
||||
*/
|
||||
setWord(addr, w)
|
||||
{
|
||||
var off = addr & this.nBlockLimit;
|
||||
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
|
||||
this.aBusBlocks[iBlock].writeWord(off, w, addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* getBlockDirect(addr)
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} addr is a physical address
|
||||
* @return {MemoryPDP10}
|
||||
*/
|
||||
getBlockDirect(addr)
|
||||
{
|
||||
return this.aBusBlocks[(addr & this.nBusMask) >>> this.nBlockShift];
|
||||
}
|
||||
|
||||
/**
|
||||
* getWordDirect(addr)
|
||||
*
|
||||
* This is used for device I/O and Debugger physical memory requests, not the CPU.
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} addr is a physical address
|
||||
* @return {number} word (16-bit) value at that address
|
||||
*/
|
||||
getWordDirect(addr)
|
||||
{
|
||||
var w;
|
||||
var off = addr & this.nBlockLimit;
|
||||
var block = this.getBlockDirect(addr);
|
||||
w = block.readWordDirect(off, addr);
|
||||
return w;
|
||||
}
|
||||
|
||||
/**
|
||||
* setWordDirect(addr, w)
|
||||
*
|
||||
* This is used for device I/O and Debugger physical memory requests, not the CPU.
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} addr is a physical address
|
||||
* @param {number} w is the word (16-bit) value to write (we truncate it to 16 bits to be safe)
|
||||
*/
|
||||
setWordDirect(addr, w)
|
||||
{
|
||||
var off = addr & this.nBlockLimit;
|
||||
var block = this.getBlockDirect(addr);
|
||||
block.writeWordDirect(off, w & 0xffff, addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* addMemBreak(addr, fWrite)
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} addr
|
||||
* @param {boolean} fWrite is true for a memory write breakpoint, false for a memory read breakpoint
|
||||
*/
|
||||
addMemBreak(addr, fWrite)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
this.aBusBlocks[iBlock].addBreakpoint(addr & this.nBlockLimit, fWrite);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* removeMemBreak(addr, fWrite)
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} addr
|
||||
* @param {boolean} fWrite is true for a memory write breakpoint, false for a memory read breakpoint
|
||||
*/
|
||||
removeMemBreak(addr, fWrite)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
this.aBusBlocks[iBlock].removeBreakpoint(addr & this.nBlockLimit, fWrite);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* saveMemory(fAll)
|
||||
*
|
||||
* The only memory blocks we save are those marked as dirty, but most likely all of RAM will have been marked dirty,
|
||||
* and even if our dirty-memory flags were as smart as our dirty-sector flags (ie, were set only when a write changed
|
||||
* what was already there), it's unlikely that would reduce the number of RAM blocks we must save/restore. At least
|
||||
* all the ROM blocks should be clean (except in the unlikely event that the Debugger was used to modify them).
|
||||
*
|
||||
* All dirty blocks will be stored in a single array, as pairs of block numbers and data arrays, like so:
|
||||
*
|
||||
* [iBlock0, [dw0, dw1, ...], iBlock1, [dw0, dw1, ...], ...]
|
||||
*
|
||||
* In a normal 4Kb block, there will be 1K DWORD values in the data array. Remember that each DWORD is a signed 32-bit
|
||||
* integer (because they are formed using bit-wise operator rather than floating-point math operators), so don't be
|
||||
* surprised to see negative numbers in the data.
|
||||
*
|
||||
* The above example assumes "uncompressed" data arrays. If we choose to use "compressed" data arrays, the data arrays
|
||||
* will look like:
|
||||
*
|
||||
* [count0, dw0, count1, dw1, ...]
|
||||
*
|
||||
* where each count indicates how many times the following DWORD value occurs. A data array length less than 1K indicates
|
||||
* that it's compressed, since we'll only store them in compressed form if they actually shrank, and we'll use State
|
||||
* helper methods compress() and decompress() to create and expand the compressed data arrays.
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {boolean} [fAll] (true to save all non-ROM memory blocks, regardless of their dirty flags)
|
||||
* @return {Array} a
|
||||
*/
|
||||
saveMemory(fAll)
|
||||
{
|
||||
var i = 0;
|
||||
var a = [];
|
||||
|
||||
for (var iBlock = 0; iBlock < this.nBlockTotal; iBlock++) {
|
||||
var block = this.aBusBlocks[iBlock];
|
||||
/*
|
||||
* We have to check both fDirty and fDirtyEver, because we may have called cleanMemory() on some of
|
||||
* the memory blocks (eg, video memory), and while cleanMemory() will clear a dirty block's fDirty flag,
|
||||
* it also sets the dirty block's fDirtyEver flag, which is left set for the lifetime of the machine.
|
||||
*/
|
||||
if (fAll && block.type != MemoryPDP10.TYPE.ROM || block.fDirty || block.fDirtyEver) {
|
||||
a[i++] = iBlock;
|
||||
a[i++] = State.compress(block.save());
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* restoreMemory(a)
|
||||
*
|
||||
* This restores the contents of all Memory blocks; called by CPUState.restore().
|
||||
*
|
||||
* In theory, we ONLY have to save/restore block contents. Other block attributes,
|
||||
* like the type, the memory controller (if any), and the active memory access functions,
|
||||
* should already be restored, since every component (re)allocates all the memory blocks
|
||||
* it was using when it's restored. And since the CPU is guaranteed to be the last
|
||||
* component to be restored, all those blocks (and their attributes) should be in place now.
|
||||
*
|
||||
* See saveMemory() for more information on how the memory block contents are saved.
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {Array} a
|
||||
* @return {boolean} true if successful, false if not
|
||||
*/
|
||||
restoreMemory(a)
|
||||
{
|
||||
var i;
|
||||
for (i = 0; i < a.length - 1; i += 2) {
|
||||
var iBlock = a[i];
|
||||
var adw = a[i+1];
|
||||
if (adw && adw.length < this.nBlockLen) {
|
||||
adw = State.decompress(adw, this.nBlockLen);
|
||||
}
|
||||
var block = this.aBusBlocks[iBlock];
|
||||
if (!block || !block.restore(adw)) {
|
||||
/*
|
||||
* Either the block to restore hasn't been allocated, indicating a change in the machine
|
||||
* configuration since it was last saved (the most likely explanation) or there's some internal
|
||||
* inconsistency (eg, the block size is wrong).
|
||||
*/
|
||||
Component.error("Unable to restore memory block " + iBlock);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* getMemoryLimit(type)
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} type is one of the MemoryPDP10.TYPE constants
|
||||
* @return {number} (the limiting address of the specified memory type, zero if none)
|
||||
*/
|
||||
getMemoryLimit(type)
|
||||
{
|
||||
var addr = 0;
|
||||
for (var iBlock = 0; iBlock < this.aBusBlocks.length; iBlock++) {
|
||||
var block = this.aBusBlocks[iBlock];
|
||||
if (block.type == type) {
|
||||
addr = block.addr + block.used;
|
||||
}
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* reportError(errNum, addr, size, fQuiet)
|
||||
*
|
||||
* @this {BusPDP10}
|
||||
* @param {number} errNum
|
||||
* @param {number} addr
|
||||
* @param {number} size
|
||||
* @param {boolean} [fQuiet] (true if any error should be quietly logged)
|
||||
* @return {boolean} false
|
||||
*/
|
||||
reportError(errNum, addr, size, fQuiet)
|
||||
{
|
||||
var sError = "Memory block error (" + errNum + ": " + Str.toHex(addr) + "," + Str.toHex(size) + ")";
|
||||
if (fQuiet) {
|
||||
if (this.dbg) {
|
||||
this.dbg.message(sError);
|
||||
} else {
|
||||
this.log(sError);
|
||||
}
|
||||
} else {
|
||||
Component.error(sError);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BusPDP10.ERROR = {
|
||||
RANGE_INUSE: 1,
|
||||
RANGE_INVALID: 2
|
||||
};
|
||||
|
||||
if (NODE) module.exports = BusPDP10;
|
||||
1640
modules/pdp10/lib/computer.js
Normal file
1640
modules/pdp10/lib/computer.js
Normal file
File diff suppressed because it is too large
Load diff
1274
modules/pdp10/lib/cpu.js
Normal file
1274
modules/pdp10/lib/cpu.js
Normal file
File diff suppressed because it is too large
Load diff
802
modules/pdp10/lib/cpustate.js
Normal file
802
modules/pdp10/lib/cpustate.js
Normal file
|
|
@ -0,0 +1,802 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP-10 CPU component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @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";
|
||||
|
||||
if (NODE) {
|
||||
var Str = require("../../shared/lib/strlib");
|
||||
var Web = require("../../shared/lib/weblib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var State = require("../../shared/lib/state");
|
||||
var PDP10 = require("./defines");
|
||||
var BusPDP10 = require("./bus");
|
||||
var CPUPDP10 = require("./cpu");
|
||||
var MessagesPDP10 = require("./messages");
|
||||
var MemoryPDP10 = require("./memory");
|
||||
}
|
||||
|
||||
/*
|
||||
* Overview of Device Interrupt Support
|
||||
*
|
||||
* Originally, the CPU maintained a queue of requested interrupts. Entries in this queue recorded a device's
|
||||
* priority, vector, and delay (ie, a number of instructions to execute before dispatching the interrupt). This
|
||||
* queue would constantly grow and shrink as requests were issued and dispatched, and as long as there was something
|
||||
* in the queue, the CPU was constantly examining it.
|
||||
*
|
||||
* Now we are trying something more efficient. First, for devices that require delays (like the SerialPort's receiver
|
||||
* and transmitter buffer registers, which are supposed to "clock" the data in and out at a specific baud rate), the
|
||||
* CPU offers timer services that will "fire" a callback after a specified delay, which are much more efficient than
|
||||
* requiring the CPU to dive into an interrupt queue and decrement delay counts on every instruction.
|
||||
*
|
||||
* Second, devices that generate interrupts will allocate an IRQ object during initialization; we will no longer
|
||||
* be creating and destroying interrupt event objects and inserting/deleting them in a constantly changing queue.
|
||||
* Each IRQ contains properties that never change (eg, the vector and priority), along with a "next" pointer that's
|
||||
* only used when the IRQ is active.
|
||||
*
|
||||
* When a device decides it's time to interrupt (either at the end of some I/O operation or when a timer has fired),
|
||||
* it will simply set the IRQ, which basically means that the IRQ will be linked onto a list of active IRQs, in
|
||||
* priority order, so that when the CPU is ready to acknowledge interrupts, it need only check the top of the active
|
||||
* IRQ list.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* vector: number,
|
||||
* priority: number,
|
||||
* message: number,
|
||||
* name: (string|null),
|
||||
* next: (IRQ|null)
|
||||
* }}
|
||||
*/
|
||||
var IRQ;
|
||||
|
||||
/**
|
||||
* @class CPUStatePDP10
|
||||
* @unrestricted
|
||||
*/
|
||||
class CPUStatePDP10 extends CPUPDP10 {
|
||||
/**
|
||||
* CPUStatePDP10(parmsCPU)
|
||||
*
|
||||
* The CPUStatePDP10 class uses the following (parmsCPU) properties:
|
||||
*
|
||||
* model: a number (eg, 1001) that should match one of the PDP10.MODEL_* values
|
||||
* addrReset: reset address (default is 0)
|
||||
*
|
||||
* This extends the CPU class and passes any remaining parmsCPU properties to the CPU class
|
||||
* constructor, along with a default speed (cycles per second) based on the specified (or default)
|
||||
* CPU model number.
|
||||
*
|
||||
* @param {Object} parmsCPU
|
||||
*/
|
||||
constructor(parmsCPU)
|
||||
{
|
||||
var nCyclesDefault = 0;
|
||||
var model = +parmsCPU['model'] || PDP10.MODEL_KA10;
|
||||
|
||||
switch(model) {
|
||||
case PDP10.MODEL_KA10:
|
||||
default:
|
||||
nCyclesDefault = 6666667;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* ES6 ALERT: Classes cannot access "this" until all superclasses have been initialized as well.
|
||||
*/
|
||||
super(parmsCPU, nCyclesDefault);
|
||||
|
||||
this.model = model;
|
||||
this.addrReset = +parmsCPU['addrReset'] || 0;
|
||||
|
||||
/** @type {IRQ|null} */
|
||||
this.irqNext = null; // the head of the active IRQ list, in priority order
|
||||
|
||||
/** @type {Array.<IRQ>} */
|
||||
this.aIRQs = []; // list of all IRQs, active or not (to be used for auto-configuration)
|
||||
|
||||
this.flags.complete = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* Called once the Bus has been initialized.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {ComputerPDP10} cmp
|
||||
* @param {BusPDP10} bus
|
||||
* @param {CPUPDP10} cpu
|
||||
* @param {DebuggerPDP10} dbg
|
||||
*/
|
||||
initBus(cmp, bus, cpu, dbg)
|
||||
{
|
||||
super.initBus(cmp, bus, cpu, dbg);
|
||||
}
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
*/
|
||||
reset()
|
||||
{
|
||||
this.status("Model " + this.model);
|
||||
if (this.flags.running) this.stopCPU();
|
||||
this.initCPU();
|
||||
this.resetCycles();
|
||||
this.clearError(); // clear any fatal error/exception that setError() may have flagged
|
||||
super.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* initCPU()
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
*/
|
||||
initCPU()
|
||||
{
|
||||
this.regPC = this.pcLast = this.addrReset;
|
||||
|
||||
/*
|
||||
* This is queried and displayed by the Panel when it's not displaying its own ADDRESS register
|
||||
* (which takes precedence when, for example, you've manually halted the CPU and are independently
|
||||
* examining the contents of other addresses).
|
||||
*
|
||||
* We initialize it to the current PC.
|
||||
*/
|
||||
this.addrLast = this.regPC;
|
||||
|
||||
/*
|
||||
* opFlags contains various conditions that stepCPU() needs to be aware of.
|
||||
*/
|
||||
this.opFlags = 0;
|
||||
|
||||
this.setMemoryAccess();
|
||||
|
||||
this.resetIRQs();
|
||||
}
|
||||
|
||||
/**
|
||||
* setMemoryAccess()
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
*/
|
||||
setMemoryAccess()
|
||||
{
|
||||
this.readWord = this.readWordFromPhysical;
|
||||
this.writeWord = this.writeWordToPhysical;
|
||||
}
|
||||
|
||||
/**
|
||||
* setReset(addr, fStart, bUnit, addrStack)
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} addr
|
||||
* @param {boolean} [fStart] (true if a "startable" image was just loaded, false if not)
|
||||
* @param {number} [bUnit] (boot unit #)
|
||||
* @param {number} [addrStack]
|
||||
*/
|
||||
setReset(addr, fStart, bUnit, addrStack)
|
||||
{
|
||||
this.addrReset = addr;
|
||||
|
||||
this.setPC(addr);
|
||||
|
||||
if (fStart) {
|
||||
if (!this.flags.powered) {
|
||||
this.flags.autoStart = true;
|
||||
}
|
||||
else if (!this.flags.running) {
|
||||
this.startCPU();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this.dbg && this.flags.powered) {
|
||||
/*
|
||||
* TODO: Review the decision to always stop the CPU if the Debugger is loaded. Note that
|
||||
* when stopCPU() stops a running CPU, the Debugger gets notified, so no need to notify it again.
|
||||
*
|
||||
* TODO: There are more serious problems to deal with if another component is slamming a new PC down
|
||||
* the CPU's throat (presumably while also dropping some new code into RAM) while the CPU is running;
|
||||
* we should probably force a complete reset, but for now, it's up to the user to hit the reset button
|
||||
* themselves.
|
||||
*/
|
||||
if (!this.stopCPU() && !this.cmp.flags.reset) {
|
||||
this.dbg.updateStatus();
|
||||
this.cmp.updateDisplays(-1);
|
||||
}
|
||||
}
|
||||
else if (fStart === false) {
|
||||
this.stopCPU();
|
||||
}
|
||||
}
|
||||
if (!this.isRunning() && this.panel) this.panel.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* getChecksum()
|
||||
*
|
||||
* TODO: Implement
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @return {number} a 32-bit summation of key elements of the current CPU state (used by the CPU checksum code)
|
||||
*/
|
||||
getChecksum()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* save()
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @return {Object|null}
|
||||
*/
|
||||
save()
|
||||
{
|
||||
var state = new State(this);
|
||||
state.set(0, [
|
||||
this.regPC,
|
||||
this.pcLast,
|
||||
this.addrLast,
|
||||
this.opFlags
|
||||
]);
|
||||
state.set(1, []);
|
||||
state.set(2, [this.nTotalCycles, this.getSpeed(), this.flags.autoStart]);
|
||||
state.set(3, this.saveIRQs());
|
||||
state.set(4, this.saveTimers());
|
||||
return state.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* restore(data)
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {Object} data
|
||||
* @return {boolean} true if restore successful, false if not
|
||||
*/
|
||||
restore(data)
|
||||
{
|
||||
/*
|
||||
* ES6 ALERT: A handy destructuring assignment, which makes it easy to perform the inverse
|
||||
* of what save() does when it collects a bunch of object properties into an array.
|
||||
*/
|
||||
[
|
||||
this.regPC,
|
||||
this.pcLast,
|
||||
this.addrLast,
|
||||
this.opFlags
|
||||
] = data[0];
|
||||
|
||||
var a = data[2];
|
||||
this.nTotalCycles = a[0];
|
||||
this.setSpeed(a[1]);
|
||||
this.flags.autoStart = a[2];
|
||||
|
||||
this.restoreIRQs(data[3]);
|
||||
this.restoreTimers(data[4]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* getOpcode()
|
||||
*
|
||||
* NOTE: This function is nothing more than a convenience, and we fully expect it to be inlined at runtime.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @return {number}
|
||||
*/
|
||||
getOpcode()
|
||||
{
|
||||
var pc = this.regPC;
|
||||
var opCode = this.readWord(pc);
|
||||
this.regPC = (pc + 1) % PDP10.ADDR_LIMIT;
|
||||
return opCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* advancePC(off)
|
||||
*
|
||||
* NOTE: This function is nothing more than a convenience, and we fully expect it to be inlined at runtime.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} off
|
||||
* @return {number} (original PC)
|
||||
*/
|
||||
advancePC(off)
|
||||
{
|
||||
var pc = this.regPC;
|
||||
this.regPC = (pc + off) % PDP10.ADDR_LIMIT;
|
||||
return pc;
|
||||
}
|
||||
|
||||
/**
|
||||
* getPC()
|
||||
*
|
||||
* NOTE: This function is nothing more than a convenience, and we fully expect it to be inlined at runtime.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @return {number}
|
||||
*/
|
||||
getPC()
|
||||
{
|
||||
return this.regPC;
|
||||
}
|
||||
|
||||
/**
|
||||
* getLastAddr()
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @return {number}
|
||||
*/
|
||||
getLastAddr()
|
||||
{
|
||||
return this.addrLast;
|
||||
}
|
||||
|
||||
/**
|
||||
* getLastPC()
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @return {number}
|
||||
*/
|
||||
getLastPC()
|
||||
{
|
||||
return this.pcLast;
|
||||
}
|
||||
|
||||
/**
|
||||
* setPC()
|
||||
*
|
||||
* NOTE: Unlike other PCjs emulators, such as PCx86, where all PC updates MUST go through the setPC()
|
||||
* function, this function is nothing more than a convenience, because in the PDP-11, the PC can be loaded
|
||||
* like any other general register. We fully expect this function to be inlined at runtime.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} addr
|
||||
*/
|
||||
setPC(addr)
|
||||
{
|
||||
this.regPC = addr % PDP10.ADDR_LIMIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* addIRQ(vector, priority, message)
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} vector (-1 for floating vector)
|
||||
* @param {number} priority
|
||||
* @param {number} [message]
|
||||
* @return {IRQ}
|
||||
*/
|
||||
addIRQ(vector, priority, message)
|
||||
{
|
||||
var irq = {vector: vector, priority: priority, message: message || 0, name: null, next: null};
|
||||
this.aIRQs.push(irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
/**
|
||||
* insertIRQ(irq)
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {IRQ} irq
|
||||
*/
|
||||
insertIRQ(irq)
|
||||
{
|
||||
if (irq != this.irqNext) {
|
||||
var irqPrev = this.irqNext;
|
||||
if (!irqPrev || irqPrev.priority <= irq.priority) {
|
||||
irq.next = irqPrev;
|
||||
this.irqNext = irq;
|
||||
} else {
|
||||
do {
|
||||
var irqNext = irqPrev.next;
|
||||
if (!irqNext || irqNext.priority <= irq.priority) {
|
||||
irq.next = irqNext;
|
||||
irqPrev.next = irq;
|
||||
break;
|
||||
}
|
||||
irqPrev = irqNext;
|
||||
} while (irqPrev);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* See the writeXCSR() function for an explanation of why signalling an IRQ hardware interrupt
|
||||
* should be done using IRQ_DELAY rather than setting IRQ directly.
|
||||
*/
|
||||
this.opFlags |= PDP10.OPFLAG.IRQ_DELAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* removeIRQ(irq)
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {IRQ} irq
|
||||
*/
|
||||
removeIRQ(irq)
|
||||
{
|
||||
var irqPrev = this.irqNext;
|
||||
if (irqPrev == irq) {
|
||||
this.irqNext = irq.next;
|
||||
} else {
|
||||
while (irqPrev) {
|
||||
var irqNext = irqPrev.next;
|
||||
if (irqNext == irq) {
|
||||
irqPrev.next = irqNext.next;
|
||||
break;
|
||||
}
|
||||
irqPrev = irqNext;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* We could also set irq.next to null now, but strictly speaking, that shouldn't be necessary.
|
||||
*
|
||||
* Last but not least, if there's still an IRQ on the active IRQ list, we need to make sure IRQ_DELAY
|
||||
* is still set.
|
||||
*/
|
||||
if (this.irqNext) {
|
||||
this.opFlags |= PDP10.OPFLAG.IRQ_DELAY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* setIRQ(irq)
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {IRQ|null} irq
|
||||
*/
|
||||
setIRQ(irq)
|
||||
{
|
||||
if (irq) {
|
||||
this.insertIRQ(irq);
|
||||
if (irq.message && this.messageEnabled(irq.message | MessagesPDP10.INT)) {
|
||||
this.printMessage("setIRQ(vector=" + Str.toOct(irq.vector) + ",priority=" + irq.priority + ")", true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clearIRQ(irq)
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {IRQ|null} irq
|
||||
*/
|
||||
clearIRQ(irq)
|
||||
{
|
||||
if (irq) {
|
||||
this.removeIRQ(irq);
|
||||
if (irq.message && this.messageEnabled(irq.message | MessagesPDP10.INT)) {
|
||||
this.printMessage("clearIRQ(vector=" + Str.toOct(irq.vector) + ",priority=" + irq.priority + ")", true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* findIRQ(vector)
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} vector
|
||||
* @return {IRQ|null}
|
||||
*/
|
||||
findIRQ(vector)
|
||||
{
|
||||
for (var i = 0; i < this.aIRQs.length; i++) {
|
||||
var irq = this.aIRQs[i];
|
||||
if (irq.vector === vector) return irq;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* checkIRQs(priority)
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} priority
|
||||
* @return {IRQ|null}
|
||||
*/
|
||||
checkIRQs(priority)
|
||||
{
|
||||
return (this.irqNext && this.irqNext.priority > priority)? this.irqNext : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* resetIRQs(priority)
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
*/
|
||||
resetIRQs()
|
||||
{
|
||||
this.irqNext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* saveIRQs()
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @return {Array.<number>}
|
||||
*/
|
||||
saveIRQs()
|
||||
{
|
||||
var aIRQVectors = [];
|
||||
var irq = this.irqNext;
|
||||
while (irq) {
|
||||
aIRQVectors.push(irq.vector);
|
||||
irq = irq.next;
|
||||
}
|
||||
return aIRQVectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* restoreIRQs(aIRQVectors)
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {Array.<number>} aIRQVectors
|
||||
*/
|
||||
restoreIRQs(aIRQVectors)
|
||||
{
|
||||
for (var i = aIRQVectors.length - 1; i >= 0; i--) {
|
||||
var irq = this.findIRQ(aIRQVectors[i]);
|
||||
this.assert(irq != null);
|
||||
if (irq) {
|
||||
irq.next = this.irqNext;
|
||||
this.irqNext = irq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* checkInterrupts()
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @return {boolean} true if an interrupt was dispatched, false if not
|
||||
*/
|
||||
checkInterrupts()
|
||||
{
|
||||
var fInterrupt = false;
|
||||
|
||||
if (this.opFlags & PDP10.OPFLAG.IRQ) {
|
||||
|
||||
// var vector = PDP10.TRAP.PIRQ;
|
||||
// var priority = (this.regPIR & PDP10.PSW.PRI) >> PDP10.PSW.SHIFT.PRI;
|
||||
//
|
||||
// var irq = this.checkIRQs(priority);
|
||||
// if (irq) {
|
||||
// vector = irq.vector;
|
||||
// priority = irq.priority;
|
||||
// }
|
||||
//
|
||||
// if (this.dispatchInterrupt(vector, priority)) {
|
||||
// if (irq) this.removeIRQ(irq);
|
||||
// fInterrupt = true;
|
||||
// }
|
||||
|
||||
if (!this.irqNext) {
|
||||
this.opFlags &= ~PDP10.OPFLAG.IRQ;
|
||||
}
|
||||
}
|
||||
else if (this.opFlags & PDP10.OPFLAG.IRQ_DELAY) {
|
||||
/*
|
||||
* We know that IRQ (bit 2) is clear, so since IRQ_DELAY (bit 0) is set, incrementing opFlags
|
||||
* will eventually transform IRQ_DELAY into IRQ, without affecting any other (higher) bits.
|
||||
*/
|
||||
this.opFlags++;
|
||||
}
|
||||
return fInterrupt;
|
||||
}
|
||||
|
||||
/**
|
||||
* dispatchInterrupt(vector, priority)
|
||||
*
|
||||
* TODO: The process of dispatching an interrupt MUST cost some cycles; either trap() needs to assess
|
||||
* that cost, or we do.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} vector
|
||||
* @param {number} priority
|
||||
* @return {boolean} (true if dispatched, false if not)
|
||||
*/
|
||||
dispatchInterrupt(vector, priority)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* isWaiting()
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @return {boolean} (true if OPFLAG.WAIT is set, false otherwise)
|
||||
*/
|
||||
isWaiting()
|
||||
{
|
||||
return !!(this.opFlags & PDP10.OPFLAG.WAIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* readWordFromPhysical(addr)
|
||||
*
|
||||
* This is a handler set up by setMemoryAccess(). All calls should go through readWord().
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readWordFromPhysical(addr)
|
||||
{
|
||||
return this.bus.getWord(this.addrLast = addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* writeWordToPhysical(addr, data)
|
||||
*
|
||||
* This is a handler set up by setMemoryAccess(). All calls should go through writeWord().
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} addr
|
||||
* @param {number} data
|
||||
*/
|
||||
writeWordToPhysical(addr, data)
|
||||
{
|
||||
this.bus.setWord(this.addrLast = addr, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* stepCPU(nMinCycles)
|
||||
*
|
||||
* NOTE: Single-stepping should not be confused with the Trap flag; single-stepping is a Debugger
|
||||
* operation that's completely independent of Trap status. The CPU can go in and out of Trap mode,
|
||||
* in and out of h/w interrupt service routines (ISRs), etc, but from the Debugger's perspective,
|
||||
* they're all one continuous stream of instructions that can be stepped or run at will. Moreover,
|
||||
* stepping vs. running should never change the behavior of the simulation.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} nMinCycles (0 implies a single-step, and therefore breakpoints should be ignored)
|
||||
* @return {number} of cycles executed; 0 indicates a pre-execution condition (ie, an execution breakpoint
|
||||
* was hit), -1 indicates a post-execution condition (eg, a read or write breakpoint was hit), and a positive
|
||||
* number indicates successful completion of that many cycles (which should always be >= nMinCycles).
|
||||
*/
|
||||
stepCPU(nMinCycles)
|
||||
{
|
||||
/*
|
||||
* The Debugger uses complete to determine if the instruction completed (true) or was interrupted
|
||||
* by a breakpoint or some other exceptional condition (false). NOTE: this does NOT include JavaScript
|
||||
* exceptions, which stepCPU() expects the caller to catch using its own exception handler.
|
||||
*
|
||||
* The CPU relies on the use of stopCPU() rather than complete, because the CPU never single-steps
|
||||
* (ie, nMinCycles is always some large number), whereas the Debugger does. And conversely, when the
|
||||
* Debugger is single-stepping (even when performing multiple single-steps), fRunning is never set,
|
||||
* so stopCPU() would have no effect as far as the Debugger is concerned.
|
||||
*/
|
||||
this.flags.complete = true;
|
||||
|
||||
/*
|
||||
* nDebugCheck is 1 if we want the Debugger's checkInstruction() to check every instruction,
|
||||
* -1 if we want it to check just the first instruction, and 0 if there's no need for any checks.
|
||||
*/
|
||||
var nDebugCheck = (DEBUGGER && this.dbg)? (this.dbg.checksEnabled()? 1 : (this.flags.starting? -1 : 0)) : 0;
|
||||
|
||||
/*
|
||||
* nDebugState is needed only when nDebugCheck is non-zero; it is -1 if this is a single-step, 0 if
|
||||
* this is the start of a new run, and 1 if this is a continuation of a previous run. It is used by
|
||||
* checkInstruction() to determine if it should skip breakpoint checks and/or HALT instructions (ie,
|
||||
* if nDebugState is <= zero).
|
||||
*/
|
||||
var nDebugState = (!nMinCycles)? -1 : (this.flags.starting? 0 : 1);
|
||||
this.flags.starting = false; // we've moved beyond "starting" and have officially "started" now
|
||||
|
||||
/*
|
||||
* We move the minimum cycle count to nStepCycles (the number of cycles left to step), so that other
|
||||
* functions have the ability to force that number to zero (eg, stopCPU()), and thus we don't have to check
|
||||
* any other criteria to determine whether we should continue stepping or not.
|
||||
*/
|
||||
this.nBurstCycles = this.nStepCycles = nMinCycles;
|
||||
|
||||
/*
|
||||
* And finally, move the nDebugCheck state to an OPFLAG bit, so that the loop need check only one variable.
|
||||
*/
|
||||
this.opFlags = (this.opFlags & ~PDP10.OPFLAG.DEBUGGER) | (nDebugCheck? PDP10.OPFLAG.DEBUGGER : 0);
|
||||
|
||||
do {
|
||||
if (this.opFlags) {
|
||||
/*
|
||||
* NOTE: We still check DEBUGGER to ensure that this code will be compiled out of existence in
|
||||
* non-DEBUGGER builds.
|
||||
*/
|
||||
if (DEBUGGER && (this.opFlags & PDP10.OPFLAG.DEBUGGER)) {
|
||||
if (this.dbg.checkInstruction(this.getPC(), nDebugState)) {
|
||||
this.stopCPU();
|
||||
break;
|
||||
}
|
||||
if (!++nDebugCheck) this.opFlags &= ~PDP10.OPFLAG.DEBUGGER;
|
||||
if (!nDebugState) nDebugState++;
|
||||
}
|
||||
/*
|
||||
* If we're in the IRQ or WAIT state, check for any pending interrupts.
|
||||
*
|
||||
* NOTE: It's no coincidence that we're checking this BEFORE any pending traps, because in rare
|
||||
* cases (including some presented by those pesky "TRAP TEST" diagnostics), the process of dispatching
|
||||
* an interrupt can trigger a TRAP_SP stack overflow condition, which must be dealt with BEFORE we
|
||||
* execute the first instruction of the interrupt handler.
|
||||
*/
|
||||
if ((this.opFlags & (PDP10.OPFLAG.IRQ_MASK | PDP10.OPFLAG.WAIT)) /* && nDebugState >= 0 */) {
|
||||
if (this.checkInterrupts()) {
|
||||
if ((this.opFlags & PDP10.OPFLAG.DEBUGGER) && this.dbg.checkInstruction(this.getPC(), nDebugState)) {
|
||||
this.stopCPU();
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Since an interrupt was just dispatched, altering the normal flow of time and changing
|
||||
* the future as we knew it, let's break out immediately if we're single-stepping, so that
|
||||
* the Debugger gets to see the first instruction of the interrupt handler. NOTE: This
|
||||
* assumes that we've still commented out the nDebugState check above that used to bypass
|
||||
* checkInterrupts() when single-stepping.
|
||||
*/
|
||||
if (nDebugState < 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.opFlags &= PDP10.OPFLAG.PRESERVE;
|
||||
|
||||
var opCode = this.getOpcode();
|
||||
|
||||
// this.decode(opCode);
|
||||
|
||||
} while (this.nStepCycles > 0);
|
||||
|
||||
return (this.flags.complete? this.nBurstCycles - this.nStepCycles : (this.flags.complete === false? -1 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* CPUStatePDP10.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "cpu", extracting the
|
||||
* JSON-encoded parameters for the CPUStatePDP10 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor (which in turn invokes the CPU constructor)
|
||||
* to create a CPUStatePDP10 component, and then binding any associated HTML controls to the
|
||||
* new component.
|
||||
*/
|
||||
static init()
|
||||
{
|
||||
var aeCPUs = Component.getElementsByClass(document, PDP10.APPCLASS, "cpu");
|
||||
for (var iCPU = 0; iCPU < aeCPUs.length; iCPU++) {
|
||||
var eCPU = aeCPUs[iCPU];
|
||||
var parmsCPU = Component.getComponentParms(eCPU);
|
||||
var cpu = new CPUStatePDP10(parmsCPU);
|
||||
Component.bindComponentControls(cpu, eCPU, PDP10.APPCLASS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize every CPU module on the page
|
||||
*/
|
||||
Web.onInit(CPUStatePDP10.init);
|
||||
|
||||
if (NODE) module.exports = CPUStatePDP10;
|
||||
3558
modules/pdp10/lib/debugger.js
Normal file
3558
modules/pdp10/lib/debugger.js
Normal file
File diff suppressed because it is too large
Load diff
148
modules/pdp10/lib/defines.js
Normal file
148
modules/pdp10/lib/defines.js
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/**
|
||||
* @fileoverview PDP10-specific compile-time definitions.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @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";
|
||||
|
||||
/**
|
||||
* @define {string}
|
||||
*/
|
||||
var APPCLASS = "pdp10"; // this @define is the default application class (eg, "pcx86", "c1pjs")
|
||||
|
||||
/**
|
||||
* APPNAME is used more for display purposes than anything else now. APPCLASS is what matters in terms
|
||||
* of folder and file names, CSS styles, etc.
|
||||
*
|
||||
* @define {string}
|
||||
*/
|
||||
var APPNAME = "PDPjs"; // this @define is the default application name (eg, "PCx86", "C1Pjs")
|
||||
|
||||
/**
|
||||
* WARNING: DEBUGGER needs to accurately reflect whether or not the Debugger component is (or will be) loaded.
|
||||
* In the compiled case, we rely on the Closure Compiler to override DEBUGGER as appropriate. When it's *false*,
|
||||
* nearly all of debugger.js will be conditionally removed by the compiler, reducing it to little more than a
|
||||
* "type skeleton", which also solves some type-related warnings we would otherwise have if we tried to remove
|
||||
* debugger.js from the compilation process altogether.
|
||||
*
|
||||
* However, when we're in "development mode" and running uncompiled code in debugger-less configurations,
|
||||
* I would like to skip loading debugger.js altogether. When doing that, we must ALSO arrange for an additional file
|
||||
* (nodebugger.js) to be loaded immediately after this file, which *explicitly* overrides DEBUGGER with *false*.
|
||||
*
|
||||
* @define {boolean}
|
||||
*/
|
||||
var DEBUGGER = true; // this @define is overridden by the Closure Compiler to remove Debugger-related support
|
||||
|
||||
/*
|
||||
* Combine all the shared globals and machine-specific globals into one machine-specific global object,
|
||||
* which all machine components should start using; eg: "if (PDP10.DEBUG) ..." instead of "if (DEBUG) ...".
|
||||
*/
|
||||
var PDP10 = {
|
||||
APPCLASS: APPCLASS,
|
||||
APPNAME: APPNAME,
|
||||
APPVERSION: APPVERSION, // shared
|
||||
COMPILED: COMPILED, // shared
|
||||
CSSCLASS: CSSCLASS, // shared
|
||||
DEBUG: DEBUG, // shared
|
||||
DEBUGGER: DEBUGGER,
|
||||
MAXDEBUG: MAXDEBUG, // shared
|
||||
PRIVATE: PRIVATE, // shared
|
||||
SITEHOST: SITEHOST, // shared
|
||||
XMLVERSION: XMLVERSION, // shared
|
||||
|
||||
/*
|
||||
* CPU model numbers (supported)
|
||||
*
|
||||
* The 11/20 includes the 11/10, which is not identified separately because there was
|
||||
* nothing functionally different about it.
|
||||
*
|
||||
* The 11/40 added the MODE bits to the PSW (but only KERNEL=00 and USER=11) and 18-bit
|
||||
* addressing via an MMU; there was still only one register set.
|
||||
*
|
||||
* The 11/45 added REGSET bit to the PSW (to support a second register set), SUPER=01
|
||||
* mode to the existing KERNEL=00 and USER=11 modes, separate I/D spaces, and other MMU
|
||||
* extensions (eg, MMR1 and MMR3).
|
||||
*
|
||||
* The 11/70 added 22-bit addressing and corresponding extensions to the MMU.
|
||||
*/
|
||||
MODEL_KA10: 1001,
|
||||
|
||||
/*
|
||||
* This constant is used to mark points in the code where the physical address being returned
|
||||
* is invalid and should not be used.
|
||||
*
|
||||
* In a 32-bit CPU, -1 (ie, 0xffffffff) could actually be a valid address, so consider changing
|
||||
* ADDR_INVALID to NaN or null (which is also why all ADDR_INVALID tests should use strict equality
|
||||
* operators).
|
||||
*
|
||||
* The main reason I'm NOT using NaN or null now is my concern that, by mixing non-numbers
|
||||
* (specifically, values outside the range of signed 32-bit integers), performance may suffer.
|
||||
*
|
||||
* WARNING: Like many of the properties defined here, ADDR_INVALID is a common constant, which the
|
||||
* Closure Compiler will happily inline (with or without @const annotations; in fact, I've yet to
|
||||
* see a @const annotation EVER improve automatic inlining). However, if you don't make ABSOLUTELY
|
||||
* certain that this file is included BEFORE the first reference to any of these properties, that
|
||||
* automatic inlining will no longer occur.
|
||||
*/
|
||||
ADDR_INVALID: -1,
|
||||
ADDR_LIMIT: Math.pow(2, 18),
|
||||
DATA_INVALID: 0,
|
||||
DATA_LIMIT: Math.pow(2, 36),
|
||||
/*
|
||||
* Assorted common opcodes
|
||||
*/
|
||||
OPCODE: {
|
||||
HALT: 0o000000000000, // TODO: Resolve
|
||||
INVALID: 0o777777777777 // TODO: Resolve
|
||||
},
|
||||
/*
|
||||
* Internal operation state flags
|
||||
*/
|
||||
OPFLAG: {
|
||||
IRQ_DELAY: 0x0001, // incremented until it becomes IRQ
|
||||
IRQ: 0x0002, // time to call checkInterrupts()
|
||||
IRQ_MASK: 0x0003,
|
||||
DEBUGGER: 0x0004, // set if the Debugger wants to perform checks
|
||||
WAIT: 0x0008, // WAIT operation in progress
|
||||
PRESERVE: 0x000F, // OPFLAG bits to preserve prior to the next instruction
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Combine all the shared globals and machine-specific globals into one machine-specific global object,
|
||||
* which all machine components should start using; eg: "if (PDP10.DEBUGGER)" instead of "if (DEBUGGER)".
|
||||
*/
|
||||
PDP10.APPCLASS = APPCLASS;
|
||||
PDP10.APPNAME = APPNAME;
|
||||
PDP10.DEBUGGER = DEBUGGER;
|
||||
|
||||
if (NODE) {
|
||||
global.APPCLASS = APPCLASS;
|
||||
global.APPNAME = APPNAME;
|
||||
global.DEBUGGER = DEBUGGER;
|
||||
global.PDP10 = PDP10;
|
||||
module.exports = PDP10;
|
||||
}
|
||||
172
modules/pdp10/lib/device.js
Normal file
172
modules/pdp10/lib/device.js
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/**
|
||||
* @fileoverview Implements PDP-10 device support.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @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";
|
||||
|
||||
if (NODE) {
|
||||
var Str = require("../../shared/lib/strlib");
|
||||
var Web = require("../../shared/lib/weblib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var State = require("../../shared/lib/state");
|
||||
var PDP10 = require("./defines");
|
||||
var BusPDP10 = require("./bus");
|
||||
var MemoryPDP10 = require("./memory");
|
||||
var MessagesPDP10 = require("./messages");
|
||||
}
|
||||
|
||||
class DevicePDP10 extends Component {
|
||||
/**
|
||||
* DevicePDP10(parmsDevice)
|
||||
*
|
||||
* @param {Object} parmsDevice
|
||||
*/
|
||||
constructor(parmsDevice)
|
||||
{
|
||||
super("Device", parmsDevice, MessagesPDP10.DEVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {DevicePDP10}
|
||||
* @param {ComputerPDP10} cmp
|
||||
* @param {BusPDP10} bus
|
||||
* @param {CPUStatePDP10} cpu
|
||||
* @param {DebuggerPDP10} dbg
|
||||
*/
|
||||
initBus(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.bus = bus;
|
||||
this.cmp = cmp;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
|
||||
this.setReady();
|
||||
}
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {DevicePDP10}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
powerUp(data, fRepower)
|
||||
{
|
||||
if (!fRepower) {
|
||||
if (!data) {
|
||||
this.reset();
|
||||
} else {
|
||||
if (!this.restore(data)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* @this {DevicePDP10}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
powerDown(fSave, fShutdown)
|
||||
{
|
||||
return fSave? this.save() : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {DevicePDP10}
|
||||
*/
|
||||
reset()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* save()
|
||||
*
|
||||
* This implements save support for the DevicePDP10 component.
|
||||
*
|
||||
* @this {DevicePDP10}
|
||||
* @return {Object}
|
||||
*/
|
||||
save()
|
||||
{
|
||||
var state = new State(this);
|
||||
return state.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* restore(data)
|
||||
*
|
||||
* This implements restore support for the DevicePDP10 component.
|
||||
*
|
||||
* @this {DevicePDP10}
|
||||
* @param {Object} data
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
restore(data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* DevicePDP10.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "device", extracting the
|
||||
* JSON-encoded parameters for the DevicePDP10 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a DevicePDP10 component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*/
|
||||
static init()
|
||||
{
|
||||
var aeDevice = Component.getElementsByClass(document, PDP10.APPCLASS, "device");
|
||||
for (var iDevice = 0; iDevice < aeDevice.length; iDevice++) {
|
||||
var device;
|
||||
var eDevice = aeDevice[iDevice];
|
||||
var parmsDevice = Component.getComponentParms(eDevice);
|
||||
switch(parmsDevice['type']) {
|
||||
case 'default':
|
||||
device = new DevicePDP10(parmsDevice);
|
||||
Component.bindComponentControls(device, eDevice, PDP10.APPCLASS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize all the DevicePDP10 modules on the page.
|
||||
*/
|
||||
Web.onInit(DevicePDP10.init);
|
||||
|
||||
if (NODE) module.exports = DevicePDP10;
|
||||
645
modules/pdp10/lib/memory.js
Normal file
645
modules/pdp10/lib/memory.js
Normal file
|
|
@ -0,0 +1,645 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP-10 Memory component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @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";
|
||||
|
||||
if (NODE) {
|
||||
var Component = require("../../shared/lib/component");
|
||||
var Int36 = require("../../shared/lib/int36");
|
||||
var PDP10 = require("./defines");
|
||||
var MessagesPDP10 = require("./messages");
|
||||
}
|
||||
|
||||
/**
|
||||
* @class DataView
|
||||
* @property {function(number,boolean):number} getUint8
|
||||
* @property {function(number,number,boolean)} setUint8
|
||||
* @property {function(number,boolean):number} getUint16
|
||||
* @property {function(number,number,boolean)} setUint16
|
||||
* @property {function(number,boolean):number} getInt32
|
||||
* @property {function(number,number,boolean)} setInt32
|
||||
*/
|
||||
class MemoryPDP10 {
|
||||
/**
|
||||
* MemoryPDP10(bus, addr, used, size, type)
|
||||
*
|
||||
* The Bus component allocates Memory objects so that each has a memory buffer with a
|
||||
* block-granular starting address and an address range equal to bus.nBlockSize; however,
|
||||
* the size of any given Memory object's underlying buffer can be either zero or bus.nBlockSize;
|
||||
* memory read/write functions for empty (buffer-less) blocks are mapped to readNone/writeNone.
|
||||
*
|
||||
* The Bus allocates empty blocks for the entire address space during initialization, so that
|
||||
* any reads/writes to undefined addresses will have no effect. Later, the ROM and RAM
|
||||
* components will ask the Bus to allocate memory for specific ranges, and the Bus will allocate
|
||||
* as many new blockSize Memory objects as the ranges require. Partial Memory blocks could
|
||||
* also be supported in theory, but in practice, they're not.
|
||||
*
|
||||
* WARNING: Since Memory blocks are low-level objects that have no UI requirements, they
|
||||
* do not inherit from the Component class, so if you want to use any Component class methods,
|
||||
* such as Component.assert(), use the corresponding Debugger methods instead (assuming a debugger
|
||||
* is available).
|
||||
*
|
||||
* @param {BusPDP10} bus
|
||||
* @param {number|null} [addr] of lowest used address in block
|
||||
* @param {number} [used] portion of block in words (0 for none)
|
||||
* @param {number} [size] of block's buffer in words (0 for none)
|
||||
* @param {number} [type] is one of the MemoryPDP10.TYPE constants (default is MemoryPDP10.TYPE.NONE)
|
||||
*/
|
||||
constructor(bus, addr, used, size, type)
|
||||
{
|
||||
var a, i;
|
||||
this.bus = bus;
|
||||
this.id = (MemoryPDP10.idBlock += 2);
|
||||
this.adw = null;
|
||||
this.offset = 0;
|
||||
this.addr = addr;
|
||||
this.used = used;
|
||||
this.size = size || 0;
|
||||
this.type = type || MemoryPDP10.TYPE.NONE;
|
||||
this.fReadOnly = (type == MemoryPDP10.TYPE.ROM);
|
||||
this.dbg = null;
|
||||
this.readBits = this.readBitsDirect = this.readNone;
|
||||
this.readWord = this.readWordDirect = this.readWordDefault;
|
||||
this.writeBits = this.writeBitsDirect = this.writeNone;
|
||||
this.writeWord = this.writeWordDirect = this.writeWordDefault;
|
||||
this.cReadBreakpoints = this.cWriteBreakpoints = 0;
|
||||
this.copyBreakpoints(); // initialize the block's Debugger info; the caller will reinitialize
|
||||
|
||||
/*
|
||||
* TODO: Study the impact of dirty block tracking. The original purposes were to allow saveMemory()
|
||||
* to save only dirty blocks, and to enable the Video component to quickly detect changes to the video buffer.
|
||||
*
|
||||
* However, a quick test with dirty block tracking disabled didn't yield a noticeable improvement in performance,
|
||||
* so I think the overhead of our block-based architecture is swamping the impact of these micro-updates.
|
||||
*/
|
||||
this.fDirty = this.fDirtyEver = false;
|
||||
|
||||
/*
|
||||
* For empty memory blocks, all we need to do is ensure all access functions are mapped to "none" handlers.
|
||||
*/
|
||||
if (!this.size) {
|
||||
this.setAccess();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the normal case: allocate a buffer that provides a word of data per address;
|
||||
* no controller is required because our default memory access functions (see afnMemory)
|
||||
* know how to deal with this simple 1-1 mapping of addresses to words.
|
||||
*
|
||||
* TODO: Consider initializing the memory array to random (or pseudo-random) values in DEBUG
|
||||
* mode; pseudo-random might be best, to help make any bugs reproducible.
|
||||
*/
|
||||
a = this.aw = new Array(this.size);
|
||||
for (i = 0; i < a.length; i++) a[i] = 0;
|
||||
this.setAccess(MemoryPDP10.afnMemory);
|
||||
}
|
||||
|
||||
/**
|
||||
* init(addr)
|
||||
*
|
||||
* Quick reinitializer when reusing a Memory block.
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} addr
|
||||
*/
|
||||
init(addr)
|
||||
{
|
||||
this.addr = addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* clone(mem, type, dbg)
|
||||
*
|
||||
* Converts the current Memory block (this) into a clone of the given Memory block (mem),
|
||||
* and optionally overrides the current block's type with the specified type.
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {MemoryPDP10} mem
|
||||
* @param {number} [type]
|
||||
* @param {DebuggerPDP10} [dbg]
|
||||
*/
|
||||
clone(mem, type, dbg)
|
||||
{
|
||||
/*
|
||||
* Original memory block IDs are even; cloned memory block IDs are odd;
|
||||
* the original ID of the current block is lost, but that's OK, since it was presumably
|
||||
* produced merely to become a clone.
|
||||
*/
|
||||
this.id = mem.id | 0x1;
|
||||
this.used = mem.used;
|
||||
this.size = mem.size;
|
||||
if (type) {
|
||||
this.type = type;
|
||||
this.fReadOnly = (type == MemoryPDP10.TYPE.ROM);
|
||||
}
|
||||
this.aw = mem.aw;
|
||||
this.setAccess(MemoryPDP10.afnMemory);
|
||||
this.copyBreakpoints(dbg, mem);
|
||||
}
|
||||
|
||||
/**
|
||||
* save()
|
||||
*
|
||||
* This gets the contents of a Memory block as an array of numeric values; used by Bus.saveMemory(),
|
||||
* which in turn is called by CPUState.save().
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @return {Array.<number>|null}
|
||||
*/
|
||||
save()
|
||||
{
|
||||
return this.aw;
|
||||
}
|
||||
|
||||
/**
|
||||
* restore(aw)
|
||||
*
|
||||
* This restores the contents of a Memory block from an array of numeric values; used by Bus.restoreMemory(),
|
||||
* which is called by CPUState.restore(), after all other components have been restored and thus all Memory
|
||||
* blocks have been allocated by their respective components.
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {Array.<number>|null} aw
|
||||
* @return {boolean} true if successful, false if block size mismatch
|
||||
*/
|
||||
restore(aw)
|
||||
{
|
||||
if (aw && this.size == aw.length) {
|
||||
this.aw = aw;
|
||||
this.fDirty = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* zero(off, len, pattern)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} [off] (optional starting word offset within block)
|
||||
* @param {number} [len] (optional maximum number of words; default is the entire block)
|
||||
* @param {number} [pattern]
|
||||
*/
|
||||
zero(off, len, pattern)
|
||||
{
|
||||
var i;
|
||||
off = off || 0;
|
||||
pattern = Int36.validate(pattern || 0);
|
||||
/*
|
||||
* NOTE: If len happens to be larger than the block, that's OK, because we also bounds-check the index.
|
||||
*/
|
||||
if (len === undefined) len = this.size;
|
||||
Component.assert(off >= 0 && off < this.size);
|
||||
for (i = off; len-- && i < this.size; i++) this.writeWordDirect(off, pattern, this.addr + off);
|
||||
}
|
||||
|
||||
/**
|
||||
* setAccess(afn, fDirect)
|
||||
*
|
||||
* The afn parameter should be a 4-entry function table containing two bits handlers and
|
||||
* two word handlers. See the static afnMemory table for an example.
|
||||
*
|
||||
* If no function table is specified, a default is selected based on the Memory type;
|
||||
* similarly, any undefined entries in the table are filled with default handlers that fall
|
||||
* back to the bits handlers, and if one or both bits handlers are undefined, they default
|
||||
* to handlers that simply ignore the access.
|
||||
*
|
||||
* fDirect indicates that both the default AND the direct handlers should be updated. Direct
|
||||
* handlers normally match the default handlers, except when "checked" handlers are installed;
|
||||
* this allows "checked" handlers to know where to dispatch the call after performing checks.
|
||||
* Examples of checks are read/write breakpoints, but it's really up to the Debugger to decide
|
||||
* what the check consists of.
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {Array.<function()>} [afn] function table
|
||||
* @param {boolean} [fDirect] (true to update direct access functions as well; default is true)
|
||||
*/
|
||||
setAccess(afn, fDirect)
|
||||
{
|
||||
if (!afn) {
|
||||
Component.assert(this.type == MemoryPDP10.TYPE.NONE);
|
||||
afn = MemoryPDP10.afnNone;
|
||||
}
|
||||
this.setReadAccess(afn, fDirect);
|
||||
this.setWriteAccess(afn, fDirect);
|
||||
}
|
||||
|
||||
/**
|
||||
* setReadAccess(afn, fDirect)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {Array.<function()>} afn
|
||||
* @param {boolean} [fDirect]
|
||||
*/
|
||||
setReadAccess(afn, fDirect)
|
||||
{
|
||||
if (!fDirect || !this.cReadBreakpoints) {
|
||||
this.readBits = afn[0] || this.readNone;
|
||||
this.readWord = afn[2] || this.readWordDefault;
|
||||
}
|
||||
if (fDirect || fDirect === undefined) {
|
||||
this.readBitsDirect = afn[0] || this.readNone;
|
||||
this.readWordDirect = afn[2] || this.readWordDefault;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* setWriteAccess(afn, fDirect)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {Array.<function()>} afn
|
||||
* @param {boolean} [fDirect]
|
||||
*/
|
||||
setWriteAccess(afn, fDirect)
|
||||
{
|
||||
if (!fDirect || !this.cWriteBreakpoints) {
|
||||
this.writeBits = !this.fReadOnly && afn[1] || this.writeNone;
|
||||
this.writeWord = !this.fReadOnly && afn[3] || this.writeWordDefault;
|
||||
}
|
||||
if (fDirect || fDirect === undefined) {
|
||||
this.writeBitsDirect = afn[1] || this.writeNone;
|
||||
this.writeWordDirect = afn[3] || this.writeWordDefault;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* resetReadAccess()
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
*/
|
||||
resetReadAccess()
|
||||
{
|
||||
this.readBits = this.readBitsDirect;
|
||||
this.readWord = this.readWordDirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* resetWriteAccess()
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
*/
|
||||
resetWriteAccess()
|
||||
{
|
||||
this.writeBits = this.fReadOnly? this.writeNone : this.writeBitsDirect;
|
||||
this.writeWord = this.fReadOnly? this.writeWordDefault : this.writeWordDirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* printAddr(sMessage)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {string} sMessage
|
||||
*/
|
||||
printAddr(sMessage)
|
||||
{
|
||||
if (DEBUG && this.dbg && this.dbg.messageEnabled(MessagesPDP10.MEMORY)) {
|
||||
this.dbg.printMessage(sMessage + ' ' + (this.addr != null? ('@' + this.dbg.toStrBase(this.addr)) : '#' + this.id), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* addBreakpoint(off, fWrite)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} off
|
||||
* @param {boolean} fWrite
|
||||
*/
|
||||
addBreakpoint(off, fWrite)
|
||||
{
|
||||
if (!fWrite) {
|
||||
if (this.cReadBreakpoints++ === 0) {
|
||||
this.setReadAccess(MemoryPDP10.afnChecked, false);
|
||||
}
|
||||
if (DEBUG) this.printAddr("read breakpoint added to memory block");
|
||||
}
|
||||
else {
|
||||
if (this.cWriteBreakpoints++ === 0) {
|
||||
this.setWriteAccess(MemoryPDP10.afnChecked, false);
|
||||
}
|
||||
if (DEBUG) this.printAddr("write breakpoint added to memory block");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* removeBreakpoint(off, fWrite)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} off
|
||||
* @param {boolean} fWrite
|
||||
*/
|
||||
removeBreakpoint(off, fWrite)
|
||||
{
|
||||
if (!fWrite) {
|
||||
if (--this.cReadBreakpoints === 0) {
|
||||
this.resetReadAccess();
|
||||
if (DEBUG) this.printAddr("all read breakpoints removed from memory block");
|
||||
}
|
||||
Component.assert(this.cReadBreakpoints >= 0);
|
||||
}
|
||||
else {
|
||||
if (--this.cWriteBreakpoints === 0) {
|
||||
this.resetWriteAccess();
|
||||
if (DEBUG) this.printAddr("all write breakpoints removed from memory block");
|
||||
}
|
||||
Component.assert(this.cWriteBreakpoints >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* copyBreakpoints(dbg, mem)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {DebuggerPDP10} [dbg]
|
||||
* @param {MemoryPDP10} [mem] (outgoing MemoryPDP10 block to copy breakpoints from, if any)
|
||||
*/
|
||||
copyBreakpoints(dbg, mem)
|
||||
{
|
||||
this.dbg = dbg;
|
||||
this.cReadBreakpoints = this.cWriteBreakpoints = 0;
|
||||
if (mem) {
|
||||
if ((this.cReadBreakpoints = mem.cReadBreakpoints)) {
|
||||
this.setReadAccess(MemoryPDP10.afnChecked, false);
|
||||
}
|
||||
if ((this.cWriteBreakpoints = mem.cWriteBreakpoints)) {
|
||||
this.setWriteAccess(MemoryPDP10.afnChecked, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* readNone(off)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readNone(off, addr)
|
||||
{
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP10.MEMORY) /* && !off */) {
|
||||
this.dbg.printMessage("attempt to read invalid address " + this.dbg.toStrBase(addr), true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* writeNone(v, off, addr)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} v
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeNone(v, off, addr)
|
||||
{
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP10.MEMORY) /* && !off */) {
|
||||
this.dbg.printMessage("attempt to write " + this.dbg.toStrBase(v) + " to invalid addresses " + this.dbg.toStrBase(addr), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* readWordDefault(off, addr)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readWordDefault(off, addr)
|
||||
{
|
||||
return this.readWord(off, addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* writeWordDefault(w, off, addr)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} w
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeWordDefault(w, off, addr)
|
||||
{
|
||||
this.writeWord(w, off, addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* readBitsMemory(offBits, lenBits, off, addr)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} offBits (the bit position of the right-most bit of the result, using modern bit numbering)
|
||||
* @param {number} lenBits
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readBitsMemory(offBits, lenBits, off, addr)
|
||||
{
|
||||
var w = this.aw[off];
|
||||
if (offBits + lenBits <= 32) {
|
||||
w = (w >> offBits) & ((1 << lenBits) - 1);
|
||||
} else {
|
||||
w = Math.trunc(w / Math.pow(2, offBits)) % Math.pow(2, lenBits);
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
/**
|
||||
* readWordMemory(off, addr)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readWordMemory(off, addr)
|
||||
{
|
||||
return this.aw[off];
|
||||
}
|
||||
|
||||
/**
|
||||
* writeBitsMemory(offBits, lenBits, bits, off, addr)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} offBits (the bit position of the right-most bit of the result, using modern bit numbering)
|
||||
* @param {number} lenBits
|
||||
* @param {number} bits (only the right-most lenBits of bits are used, so this value doesn't need to be pre-masked)
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeBitsMemory(offBits, lenBits, bits, off, addr)
|
||||
{
|
||||
var w = this.aw[off];
|
||||
if (offBits + lenBits <= 32) {
|
||||
var bitsMask = ((1 << lenBits) - 1) << offBits;
|
||||
w = (w & ~bitsMask) | ((bits << offBits) & bitsMask);
|
||||
} else {
|
||||
var shiftBits = Math.pow(2, offBits);
|
||||
bits %= Math.pow(2, lenBits);
|
||||
var v = (w % Math.pow(2, offBits + lenBits));
|
||||
w = (w - v) + (bits * shiftBits) + (v % shiftBits);
|
||||
}
|
||||
if (this.aw[off] != w) {
|
||||
this.aw[off] = w;
|
||||
this.fDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* writeWordMemory(w, off, addr)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} w
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeWordMemory(w, off, addr)
|
||||
{
|
||||
this.aw[off] = w;
|
||||
this.fDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* readBitsChecked(offBits, lenBits, off, addr)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} offBits (the bit position of the right-most bit of the result, using modern bit numbering)
|
||||
* @param {number} lenBits
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readBitsChecked(offBits, lenBits, off, addr)
|
||||
{
|
||||
if (DEBUGGER && this.dbg && this.addr != null) {
|
||||
this.dbg.checkMemoryRead(this.addr + off);
|
||||
}
|
||||
return this.readBitsDirect(offBits, lenBits, off, addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* readWordChecked(off, addr)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readWordChecked(off, addr)
|
||||
{
|
||||
if (DEBUGGER && this.dbg && this.addr != null) {
|
||||
this.dbg.checkMemoryRead(this.addr + off, 2);
|
||||
}
|
||||
return this.readWordDirect(off, addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* writeBitsChecked(offBits, lenBits, bits, off, addr)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} offBits (the bit position of the right-most bit of the result, using modern bit numbering)
|
||||
* @param {number} lenBits
|
||||
* @param {number} bits
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeBitsChecked(offBits, lenBits, bits, off, addr)
|
||||
{
|
||||
if (DEBUGGER && this.dbg && this.addr != null) {
|
||||
this.dbg.checkMemoryWrite(this.addr + off);
|
||||
}
|
||||
if (this.fReadOnly) this.writeNone(bits, off, addr); else this.writeBitsDirect(offBits, lenBits, bits, off, addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* writeWordChecked(w, off, addr)
|
||||
*
|
||||
* @this {MemoryPDP10}
|
||||
* @param {number} w
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeWordChecked(w, off, addr)
|
||||
{
|
||||
if (DEBUGGER && this.dbg && this.addr != null) {
|
||||
this.dbg.checkMemoryWrite(this.addr + off, 2)
|
||||
}
|
||||
if (this.fReadOnly) this.writeNone(w, off, addr); else this.writeWordDirect(w, off, addr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic memory types
|
||||
*
|
||||
* RAM is the most conventional memory type, providing full read/write capability. ROM is equally
|
||||
* conventional, except that the fReadOnly property is set. ROM can be written using the Bus setWordDirect()
|
||||
* interface (which in turn uses the Memory writeWordDirect() interface), allowing the ROM component to
|
||||
* initialize its own memory.
|
||||
*/
|
||||
MemoryPDP10.TYPE = {
|
||||
NONE: 0,
|
||||
RAM: 1,
|
||||
ROM: 2
|
||||
};
|
||||
MemoryPDP10.TYPE_COLORS = ["black", "blue", "green"];
|
||||
MemoryPDP10.TYPE_NAMES = ["NONE", "RAM", "ROM"];
|
||||
|
||||
/*
|
||||
* Last used block ID (used for debugging only)
|
||||
*/
|
||||
MemoryPDP10.idBlock = 0;
|
||||
|
||||
/*
|
||||
* This is the effective definition of afnNone, but we need not fully define it, because setAccess()
|
||||
* uses these defaults when any of the 4 handlers (ie, 2 bits handlers and 2 word handlers) are undefined.
|
||||
*
|
||||
MemoryPDP10.afnNone = [
|
||||
MemoryPDP10.prototype.readNone,
|
||||
MemoryPDP10.prototype.writeNone,
|
||||
MemoryPDP10.prototype.readWordDefault,
|
||||
MemoryPDP10.prototype.writeWordDefault
|
||||
];
|
||||
*/
|
||||
MemoryPDP10.afnNone = [];
|
||||
|
||||
MemoryPDP10.afnMemory = [
|
||||
MemoryPDP10.prototype.readBitsMemory,
|
||||
MemoryPDP10.prototype.writeBitsMemory,
|
||||
MemoryPDP10.prototype.readWordMemory,
|
||||
MemoryPDP10.prototype.writeWordMemory
|
||||
];
|
||||
|
||||
MemoryPDP10.afnChecked = [
|
||||
MemoryPDP10.prototype.readBitsChecked,
|
||||
MemoryPDP10.prototype.writeBitsChecked,
|
||||
MemoryPDP10.prototype.readWordChecked,
|
||||
MemoryPDP10.prototype.writeWordChecked
|
||||
];
|
||||
|
||||
if (NODE) module.exports = MemoryPDP10;
|
||||
104
modules/pdp10/lib/messages.js
Normal file
104
modules/pdp10/lib/messages.js
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* @fileoverview Defines PDP-10 message categories.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @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";
|
||||
|
||||
var MessagesPDP10 = {
|
||||
CPU: 0x00000001,
|
||||
TRAP: 0x00000002,
|
||||
FAULT: 0x00000004,
|
||||
INT: 0x00000008,
|
||||
BUS: 0x00000010,
|
||||
MEMORY: 0x00000020,
|
||||
MMU: 0x00000040,
|
||||
ROM: 0x00000080,
|
||||
DEVICE: 0x00000100,
|
||||
PANEL: 0x00000200,
|
||||
KEYBOARD: 0x00000400,
|
||||
KEYS: 0x00000800,
|
||||
PAPER: 0x00001000,
|
||||
READ: 0x00004000,
|
||||
WRITE: 0x00008000,
|
||||
SERIAL: 0x00100000,
|
||||
TIMER: 0x00200000,
|
||||
SPEAKER: 0x01000000,
|
||||
COMPUTER: 0x02000000,
|
||||
LOG: 0x10000000,
|
||||
WARN: 0x20000000,
|
||||
BUFFER: 0x40000000,
|
||||
HALT: 0x80000000|0
|
||||
};
|
||||
|
||||
/*
|
||||
* Message categories supported by the messageEnabled() function and other assorted message
|
||||
* functions. Each category has a corresponding bit value that can be combined (ie, OR'ed) as
|
||||
* needed. The Debugger's message command ("m") is used to turn message categories on and off,
|
||||
* like so:
|
||||
*
|
||||
* m port on
|
||||
* m port off
|
||||
* ...
|
||||
*
|
||||
* NOTE: The order of these categories can be rearranged, alphabetized, etc, as desired; just be
|
||||
* aware that changing the bit values could break saved Debugger states (not a huge concern, just
|
||||
* something to be aware of).
|
||||
*/
|
||||
MessagesPDP10.CATEGORIES = {
|
||||
"cpu": MessagesPDP10.CPU,
|
||||
"trap": MessagesPDP10.TRAP,
|
||||
"fault": MessagesPDP10.FAULT,
|
||||
"int": MessagesPDP10.INT,
|
||||
"bus": MessagesPDP10.BUS,
|
||||
"memory": MessagesPDP10.MEMORY,
|
||||
"mmu": MessagesPDP10.MMU,
|
||||
"rom": MessagesPDP10.ROM,
|
||||
"device": MessagesPDP10.DEVICE,
|
||||
"panel": MessagesPDP10.PANEL,
|
||||
"keyboard": MessagesPDP10.KEYBOARD, // "kbd" is also allowed as shorthand for "keyboard"; see doMessages()
|
||||
"key": MessagesPDP10.KEYS, // using "key" instead of "keys", since the latter is a method on JavasScript objects
|
||||
"paper": MessagesPDP10.PAPER,
|
||||
"read": MessagesPDP10.READ,
|
||||
"write": MessagesPDP10.WRITE,
|
||||
"serial": MessagesPDP10.SERIAL,
|
||||
"timer": MessagesPDP10.TIMER,
|
||||
"speaker": MessagesPDP10.SPEAKER,
|
||||
"computer": MessagesPDP10.COMPUTER,
|
||||
"log": MessagesPDP10.LOG,
|
||||
"warn": MessagesPDP10.WARN,
|
||||
/*
|
||||
* Now we turn to message actions rather than message types; for example, setting "halt"
|
||||
* on or off doesn't enable "halt" messages, but rather halts the CPU on any message above.
|
||||
*
|
||||
* Similarly, "m buffer on" turns on message buffering, deferring the display of all messages
|
||||
* until "m buffer off" is issued.
|
||||
*/
|
||||
"buffer": MessagesPDP10.BUFFER,
|
||||
"halt": MessagesPDP10.HALT
|
||||
};
|
||||
|
||||
if (NODE) module.exports = MessagesPDP10;
|
||||
43
modules/pdp10/lib/nodebugger.js
Normal file
43
modules/pdp10/lib/nodebugger.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* @fileoverview Compile-time definitions for Debugger-less configurations.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @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";
|
||||
|
||||
/*
|
||||
* WARNING: DEBUGGER needs to accurately reflect whether or not the Debugger component is (or will be) loaded.
|
||||
* In the compiled case, we rely on the Closure Compiler to override DEBUGGER as appropriate. When it's *false*,
|
||||
* nearly all of debugger.js will be conditionally removed by the compiler, reducing it to little more than a
|
||||
* "type skeleton", which also solves some type-related warnings we would otherwise have if we tried to remove
|
||||
* debugger.js from the compilation process altogether.
|
||||
*
|
||||
* However, when we're in "development mode" and running uncompiled code in debugger-less configurations,
|
||||
* I would still like to skip loading debugger.js altogether. To do that, we must arrange for this additional file,
|
||||
* nodebugger.js, to be loaded immediately after defines.js, *explicitly* overriding the previously defined value
|
||||
* of DEBUGGER with *false*.
|
||||
*/
|
||||
DEBUGGER = false;
|
||||
1266
modules/pdp10/lib/panel.js
Normal file
1266
modules/pdp10/lib/panel.js
Normal file
File diff suppressed because it is too large
Load diff
413
modules/pdp10/lib/ram.js
Normal file
413
modules/pdp10/lib/ram.js
Normal file
|
|
@ -0,0 +1,413 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP-10 RAM component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @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";
|
||||
|
||||
if (NODE) {
|
||||
var Str = require("../../shared/lib/strlib");
|
||||
var Web = require("../../shared/lib/weblib");
|
||||
var DumpAPI = require("../../shared/lib/dumpapi");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var PDP10 = require("./defines");
|
||||
var MemoryPDP10 = require("./memory");
|
||||
var MessagesPDP10 = require("./messages");
|
||||
}
|
||||
|
||||
class RAMPDP10 extends Component {
|
||||
/**
|
||||
* RAMPDP10(parmsRAM)
|
||||
*
|
||||
* The RAMPDP10 component expects the following (parmsRAM) properties:
|
||||
*
|
||||
* addr: starting physical address of RAM (default is 0)
|
||||
* size: amount of RAM, in bytes (default is 0, which means defer to motherboard switch settings)
|
||||
* file: name of optional data file to load into RAM (default is "")
|
||||
* load: optional file load address (overrides any load address specified in the data file; default is null)
|
||||
* exec: optional file exec address (overrides any exec address specified in the data file; default is null)
|
||||
*
|
||||
* NOTE: We make a note of the specified size, but no memory is initially allocated for the RAM until the
|
||||
* Computer component calls powerUp().
|
||||
*
|
||||
* @param {Object} parmsRAM
|
||||
*/
|
||||
constructor(parmsRAM)
|
||||
{
|
||||
super("RAM", parmsRAM);
|
||||
|
||||
this.abInit = null;
|
||||
this.aSymbols = null;
|
||||
|
||||
this.addrRAM = +parmsRAM['addr'];
|
||||
this.sizeRAM = +parmsRAM['size'];
|
||||
|
||||
this.addrLoad = parmsRAM['load'];
|
||||
this.addrExec = parmsRAM['exec'];
|
||||
if (this.addrLoad != null) this.addrLoad = +this.addrLoad;
|
||||
if (this.addrExec != null) this.addrExec = +this.addrExec;
|
||||
|
||||
this.fInstalled = (!!this.sizeRAM); // 0 is the default value for 'size' when none is specified
|
||||
this.fAllocated = false;
|
||||
|
||||
this.sFilePath = parmsRAM['file'];
|
||||
this.sFileName = Str.getBaseName(this.sFilePath);
|
||||
|
||||
if (this.sFilePath) {
|
||||
var sFileURL = this.sFilePath;
|
||||
if (DEBUG) this.log('load("' + sFileURL + '")');
|
||||
/*
|
||||
* If the selected data file has a ".json" extension, then we assume it's pre-converted
|
||||
* JSON-encoded data, so we load it as-is; ditto for ROM files with a ".hex" extension.
|
||||
* Otherwise, we ask our server-side converter to return the file in a JSON-compatible format.
|
||||
*/
|
||||
var sFileExt = Str.getExtension(this.sFileName);
|
||||
if (sFileExt != DumpAPI.FORMAT.JSON && sFileExt != DumpAPI.FORMAT.HEX) {
|
||||
sFileURL = Web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + this.sFilePath + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES + '&' + DumpAPI.QUERY.DECIMAL + '=true';
|
||||
}
|
||||
var ram = this;
|
||||
Web.getResource(sFileURL, null, true, function doneLoad(sURL, sResponse, nErrorCode) {
|
||||
ram.finishLoad(sURL, sResponse, nErrorCode);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {RAMPDP10}
|
||||
* @param {ComputerPDP10} cmp
|
||||
* @param {BusPDP10} bus
|
||||
* @param {CPUStatePDP10} cpu
|
||||
* @param {DebuggerPDP10} dbg
|
||||
*/
|
||||
initBus(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.initRAM();
|
||||
}
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {RAMPDP10}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
powerUp(data, fRepower)
|
||||
{
|
||||
if (this.aSymbols) {
|
||||
if (this.dbg) {
|
||||
this.dbg.addSymbols(this.id, this.addrRAM, this.sizeRAM, this.aSymbols);
|
||||
}
|
||||
/*
|
||||
* Our only role in the handling of symbols is to hand them off to the Debugger at our
|
||||
* first opportunity. Now that we've done that, our copy of the symbols, if any, are toast.
|
||||
*/
|
||||
delete this.aSymbols;
|
||||
}
|
||||
if (!fRepower) {
|
||||
/*
|
||||
* Since we use the Bus to allocate all our memory, memory contents are already restored for us,
|
||||
* so we don't save any state, and therefore no state should be restored. Just do a reset().
|
||||
*/
|
||||
this.assert(!data);
|
||||
this.reset();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* @this {RAMPDP10}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
powerDown(fSave, fShutdown)
|
||||
{
|
||||
/*
|
||||
* The Computer powers down the CPU first, at which point CPUState state is saved,
|
||||
* which includes the Bus state, and since we use the Bus component to allocate all
|
||||
* our memory, memory contents are already saved for us, so we don't need the usual
|
||||
* save logic.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* finishLoad(sURL, sData, nErrorCode)
|
||||
*
|
||||
* @this {RAMPDP10}
|
||||
* @param {string} sURL
|
||||
* @param {string} sData
|
||||
* @param {number} nErrorCode (response from server if anything other than 200)
|
||||
*/
|
||||
finishLoad(sURL, sData, nErrorCode)
|
||||
{
|
||||
if (nErrorCode) {
|
||||
this.notice("Unable to load RAM resource (error " + nErrorCode + ": " + sURL + ")");
|
||||
this.sFilePath = null;
|
||||
}
|
||||
else {
|
||||
Component.addMachineResource(this.idMachine, sURL, sData);
|
||||
var resource = Web.parseMemoryResource(sURL, sData);
|
||||
if (resource) {
|
||||
this.abInit = resource.aBytes;
|
||||
this.aSymbols = resource.aSymbols;
|
||||
if (this.addrLoad == null) this.addrLoad = resource.addrLoad;
|
||||
if (this.addrExec == null) this.addrExec = resource.addrExec;
|
||||
} else {
|
||||
this.sFilePath = null;
|
||||
}
|
||||
}
|
||||
this.initRAM();
|
||||
}
|
||||
|
||||
/**
|
||||
* initRAM()
|
||||
*
|
||||
* This function is called by both initBus() and finishLoad(), but it cannot copy the initial data into place
|
||||
* until after initBus() has received the Bus component AND finishLoad() has received the data. When both those
|
||||
* criteria are satisfied, the component becomes "ready".
|
||||
*
|
||||
* @this {RAMPDP10}
|
||||
*/
|
||||
initRAM()
|
||||
{
|
||||
if (!this.bus) return;
|
||||
|
||||
if (!this.fAllocated && this.sizeRAM) {
|
||||
if (this.bus.addMemory(this.addrRAM, this.sizeRAM, MemoryPDP10.TYPE.RAM)) {
|
||||
this.fAllocated = true;
|
||||
} else {
|
||||
this.sizeRAM = 0; // don't bother trying again (it just results in redundant error messages)
|
||||
}
|
||||
}
|
||||
if (!this.isReady()) {
|
||||
if (!this.fAllocated) {
|
||||
Component.error("No RAM allocated");
|
||||
}
|
||||
else if (this.sFilePath) {
|
||||
/*
|
||||
* Too early...
|
||||
*/
|
||||
if (!this.abInit || !this.bus) return;
|
||||
|
||||
if (this.loadImage(this.abInit, this.addrLoad, this.addrExec, this.addrRAM)) {
|
||||
this.status('Loaded image "' + this.sFileName + '"');
|
||||
} else {
|
||||
this.notice('Error loading image "' + this.sFileName + '"');
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: We now retain this data, so that reset() can return the RAM to its predefined state.
|
||||
*
|
||||
* delete this.abInit;
|
||||
*/
|
||||
}
|
||||
this.setReady();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {RAMPDP10}
|
||||
*/
|
||||
reset()
|
||||
{
|
||||
if (this.fAllocated) {
|
||||
/*
|
||||
* TODO: Add a configuration parameter for selecting the byte pattern on reset?
|
||||
* Note that when memory blocks are originally created, they are currently always
|
||||
* zero-initialized, so this would only affect resets.
|
||||
*/
|
||||
this.bus.zeroMemory(this.addrRAM, this.sizeRAM, 0);
|
||||
if (this.abInit) {
|
||||
this.loadImage(this.abInit, this.addrLoad, this.addrExec, this.addrRAM, !this.dbg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* loadImage(aBytes, addrLoad, addrExec, addrInit, fStart)
|
||||
*
|
||||
* If the array contains a PAPER tape image in the "Absolute Format," load it as specified
|
||||
* by the format; otherwise, load it as-is using the address(es) supplied.
|
||||
*
|
||||
* @this {RAMPDP10}
|
||||
* @param {Array|Uint8Array} aBytes
|
||||
* @param {number|null} [addrLoad]
|
||||
* @param {number|null} [addrExec] (this CAN override any starting address INSIDE the image)
|
||||
* @param {number|null} [addrInit]
|
||||
* @param {boolean} [fStart]
|
||||
* @return {boolean} (true if loaded, false if not)
|
||||
*/
|
||||
loadImage(aBytes, addrLoad, addrExec, addrInit, fStart)
|
||||
{
|
||||
var fStop = false;
|
||||
var fLoaded = false;
|
||||
/*
|
||||
* Data on tapes in the "Absolute Format" is organized into blocks; each block begins with
|
||||
* a 6-byte header:
|
||||
*
|
||||
* 2-byte signature (0x0001)
|
||||
* 2-byte block length (N + 6, because it includes the 6-byte header)
|
||||
* 2-byte load address
|
||||
*
|
||||
* followed by N data bytes. If N is zero, then the 2-byte load address is the exec address,
|
||||
* unless the address is odd (usually 1). DEC's Absolute Loader jumps to the exec address
|
||||
* in former case, halts in the latter.
|
||||
*
|
||||
* All values are stored "little endian" (low byte followed by high byte), just like the
|
||||
* PDP-11's memory architecture.
|
||||
*
|
||||
* After the data bytes, there is a single checksum byte. The 8-bit sum of all the bytes in
|
||||
* the block (including the header bytes and checksum byte) should be zero.
|
||||
*
|
||||
* ANOMALIES: Tape files don't always begin with a signature word, so I allow any number of
|
||||
* leading zeros before the first signature. Tape files don't always end cleanly either, so as
|
||||
* soon as I see an invalid signature, I break out of the loop without signalling an error, as
|
||||
* long as at least ONE block was successfully processed. In fact, it's possible that as
|
||||
* soon as a block with ZERO data bytes is encountered, processing is supposed to stop, but
|
||||
* I haven't examined enough tapes (or the Absolute Loader code) to know for sure.
|
||||
*/
|
||||
if (addrLoad == null) {
|
||||
var off = 0, fError = false;
|
||||
while (off < aBytes.length - 1) {
|
||||
var w = (aBytes[off] & 0xff) | ((aBytes[off+1] & 0xff) << 8);
|
||||
if (!w) { // ignore pairs of leading zeros
|
||||
off += 2;
|
||||
continue;
|
||||
}
|
||||
if (!(w & 0xff)) { // as well as single bytes of zero
|
||||
off++;
|
||||
continue;
|
||||
}
|
||||
var offBlock = off;
|
||||
if (w != 0x0001) {
|
||||
this.printMessage("invalid signature (" + Str.toHexWord(w) + ") at offset " + Str.toHexWord(offBlock), MessagesPDP10.PAPER);
|
||||
break;
|
||||
}
|
||||
if (off + 6 >= aBytes.length) {
|
||||
this.printMessage("invalid block at offset " + Str.toHexWord(offBlock), MessagesPDP10.PAPER);
|
||||
break;
|
||||
}
|
||||
off += 2;
|
||||
var checksum = w;
|
||||
var len = (aBytes[off++] & 0xff) | ((aBytes[off++] & 0xff) << 8);
|
||||
var addr = (aBytes[off++] & 0xff) | ((aBytes[off++] & 0xff) << 8);
|
||||
checksum += (len & 0xff) + (len >> 8) + (addr & 0xff) + (addr >> 8);
|
||||
var offData = off, cbData = len -= 6;
|
||||
while (len > 0 && off < aBytes.length) {
|
||||
checksum += aBytes[off++] & 0xff;
|
||||
len--;
|
||||
}
|
||||
if (len != 0 || off >= aBytes.length) {
|
||||
this.printMessage("insufficient data for block at offset " + Str.toHexWord(offBlock), MessagesPDP10.PAPER);
|
||||
break;
|
||||
}
|
||||
checksum += aBytes[off++] & 0xff;
|
||||
if (checksum & 0xff) {
|
||||
this.printMessage("invalid checksum (" + Str.toHexByte(checksum) + ") for block at offset " + Str.toHexWord(offBlock), MessagesPDP10.PAPER);
|
||||
break;
|
||||
}
|
||||
if (!cbData) {
|
||||
if (addr & 0x1) {
|
||||
fStop = true;
|
||||
} else {
|
||||
if (addrExec == null) addrExec = addr;
|
||||
}
|
||||
if (addrExec != null) this.printMessage("starting address: " + Str.toHexWord(addrExec), MessagesPDP10.PAPER);
|
||||
} else {
|
||||
this.printMessage("loading " + Str.toHexWord(cbData) + " bytes at " + Str.toHexWord(addr) + "-" + Str.toHexWord(addr + cbData), MessagesPDP10.PAPER);
|
||||
while (cbData--) {
|
||||
this.bus.setWordDirect(addr++, aBytes[offData++]);
|
||||
}
|
||||
}
|
||||
fLoaded = true;
|
||||
}
|
||||
}
|
||||
if (!fLoaded) {
|
||||
if (addrLoad == null) addrLoad = addrInit;
|
||||
if (addrLoad != null) {
|
||||
for (var i = 0; i < aBytes.length; i++) {
|
||||
this.bus.setWordDirect(addrLoad + i, aBytes[i]);
|
||||
}
|
||||
fLoaded = true;
|
||||
}
|
||||
}
|
||||
if (fLoaded) {
|
||||
/*
|
||||
* Set the start address to whatever the caller provided, or failing that, whatever start
|
||||
* address was specified inside the image.
|
||||
*
|
||||
* For example, the diagnostic "MAINDEC-11-D0AA-PB" doesn't include a start address inside the
|
||||
* image, but we know that the directions for that diagnostic say to "Start and Restart at 200",
|
||||
* so we have manually inserted an "exec":128 in the JSON containing the image.
|
||||
*/
|
||||
if (addrExec == null || fStop) {
|
||||
this.cpu.stopCPU();
|
||||
fStart = false;
|
||||
}
|
||||
if (addrExec != null) {
|
||||
this.cpu.setReset(addrExec, fStart);
|
||||
}
|
||||
}
|
||||
return fLoaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* RAMPDP10.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "ram", extracting the
|
||||
* JSON-encoded parameters for the RAMPDP10 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a RAMPDP10 component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*/
|
||||
static init()
|
||||
{
|
||||
var aeRAM = Component.getElementsByClass(document, PDP10.APPCLASS, "ram");
|
||||
for (var iRAM = 0; iRAM < aeRAM.length; iRAM++) {
|
||||
var eRAM = aeRAM[iRAM];
|
||||
var parmsRAM = Component.getComponentParms(eRAM);
|
||||
var ram = new RAMPDP10(parmsRAM);
|
||||
Component.bindComponentControls(ram, eRAM, PDP10.APPCLASS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize all the RAMPDP10 modules on the page.
|
||||
*/
|
||||
Web.onInit(RAMPDP10.init);
|
||||
|
||||
if (NODE) module.exports = RAMPDP10;
|
||||
339
modules/pdp10/lib/rom.js
Normal file
339
modules/pdp10/lib/rom.js
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP-10 ROM component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @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";
|
||||
|
||||
if (NODE) {
|
||||
var Str = require("../../shared/lib/strlib");
|
||||
var Web = require("../../shared/lib/weblib");
|
||||
var DumpAPI = require("../../shared/lib/dumpapi");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var PDP10 = require("./defines");
|
||||
var BusPDP10 = require("./bus");
|
||||
var MemoryPDP10 = require("./memory");
|
||||
var MessagesPDP10 = require("./messages");
|
||||
}
|
||||
|
||||
class ROMPDP10 extends Component {
|
||||
/**
|
||||
* ROMPDP10(parmsROM)
|
||||
*
|
||||
* The ROMPDP10 component expects the following (parmsROM) properties:
|
||||
*
|
||||
* addr: physical address of ROM
|
||||
* size: amount of ROM, in bytes
|
||||
* alias: physical alias address (null if none)
|
||||
* file: name of ROM data file
|
||||
*
|
||||
* NOTE: The ROM data will not be copied into place until the Bus is ready (see initBus()) AND
|
||||
* the ROM data file has finished loading (see finishLoad()).
|
||||
*
|
||||
* Also, while the size parameter may seem redundant, I consider it useful to confirm that the ROM
|
||||
* you received is the ROM you expected.
|
||||
*
|
||||
* @param {Object} parmsROM
|
||||
*/
|
||||
constructor(parmsROM)
|
||||
{
|
||||
super("ROM", parmsROM, MessagesPDP10.ROM);
|
||||
|
||||
this.abInit = null;
|
||||
this.aSymbols = null;
|
||||
|
||||
this.addrROM = +parmsROM['addr'];
|
||||
this.sizeROM = +parmsROM['size'];
|
||||
this.fRetainROM = false;
|
||||
|
||||
/*
|
||||
* The new 'alias' property can now be EITHER a single physical address (like 'addr') OR an array of
|
||||
* physical addresses; eg:
|
||||
*
|
||||
* [0xf0000,0xffff0000,0xffff8000]
|
||||
*
|
||||
* We could have overloaded 'addr' to accomplish the same thing, but I think it's better to have any
|
||||
* aliased locations listed under a separate property.
|
||||
*
|
||||
* Most ROMs are not aliased, in which case the 'alias' property should have the default value of null.
|
||||
*/
|
||||
this.addrAlias = parmsROM['alias'];
|
||||
if (typeof this.addrAlias == "string") {
|
||||
this.addrAlias = eval(this.addrAlias);
|
||||
}
|
||||
|
||||
this.sFilePath = parmsROM['file'];
|
||||
this.sFileName = Str.getBaseName(this.sFilePath);
|
||||
|
||||
if (this.sFilePath) {
|
||||
var sFileURL = this.sFilePath;
|
||||
if (DEBUG) this.log('load("' + sFileURL + '")');
|
||||
/*
|
||||
* If the selected ROM file has a ".json" extension, then we assume it's pre-converted
|
||||
* JSON-encoded ROM data, so we load it as-is; ditto for ROM files with a ".hex" extension.
|
||||
* Otherwise, we ask our server-side ROM converter to return the file in a JSON-compatible format.
|
||||
*/
|
||||
var sFileExt = Str.getExtension(this.sFileName);
|
||||
if (sFileExt != DumpAPI.FORMAT.JSON && sFileExt != DumpAPI.FORMAT.HEX) {
|
||||
sFileURL = Web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + this.sFilePath + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES + '&' + DumpAPI.QUERY.DECIMAL + '=true';
|
||||
}
|
||||
var rom = this;
|
||||
Web.getResource(sFileURL, null, true, function doneLoad(sURL, sResponse, nErrorCode) {
|
||||
rom.finishLoad(sURL, sResponse, nErrorCode);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {ROMPDP10}
|
||||
* @param {ComputerPDP10} cmp
|
||||
* @param {BusPDP10} bus
|
||||
* @param {CPUStatePDP10} cpu
|
||||
* @param {DebuggerPDP10} dbg
|
||||
*/
|
||||
initBus(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.initROM();
|
||||
}
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {ROMPDP10}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
powerUp(data, fRepower)
|
||||
{
|
||||
if (this.aSymbols) {
|
||||
if (this.dbg) {
|
||||
this.dbg.addSymbols(this.id, this.addrROM, this.sizeROM, this.aSymbols);
|
||||
}
|
||||
/*
|
||||
* Our only role in the handling of symbols is to hand them off to the Debugger at our
|
||||
* first opportunity. Now that we've done that, our copy of the symbols, if any, are toast.
|
||||
*/
|
||||
delete this.aSymbols;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* Since we have nothing to do on powerDown(), and no state to return, we could simply omit
|
||||
* this function. But it doesn't hurt anything, and maybe we'll use our state to save something
|
||||
* useful down the road, like user-defined symbols (ie, symbols that the Debugger may have
|
||||
* created, above and beyond those symbols we automatically loaded, if any, along with the ROM).
|
||||
*
|
||||
* @this {ROMPDP10}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
powerDown(fSave, fShutdown)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* finishLoad(sURL, sData, nErrorCode)
|
||||
*
|
||||
* @this {ROMPDP10}
|
||||
* @param {string} sURL
|
||||
* @param {string} sData
|
||||
* @param {number} nErrorCode (response from server if anything other than 200)
|
||||
*/
|
||||
finishLoad(sURL, sData, nErrorCode)
|
||||
{
|
||||
if (nErrorCode) {
|
||||
this.notice("Unable to load ROM resource (error " + nErrorCode + ": " + sURL + ")");
|
||||
this.sFilePath = null;
|
||||
}
|
||||
else {
|
||||
Component.addMachineResource(this.idMachine, sURL, sData);
|
||||
var resource = Web.parseMemoryResource(sURL, sData);
|
||||
if (resource) {
|
||||
this.abInit = resource.aBytes;
|
||||
this.aSymbols = resource.aSymbols;
|
||||
} else {
|
||||
this.sFilePath = null;
|
||||
}
|
||||
}
|
||||
this.initROM();
|
||||
}
|
||||
|
||||
/**
|
||||
* initROM()
|
||||
*
|
||||
* This function is called by both initBus() and finishLoad(), but it cannot copy the initial data into place
|
||||
* until after initBus() has received the Bus component AND finishLoad() has received the data. When both those
|
||||
* criteria are satisfied, the component becomes "ready".
|
||||
*
|
||||
* @this {ROMPDP10}
|
||||
*/
|
||||
initROM()
|
||||
{
|
||||
if (!this.isReady()) {
|
||||
if (this.sFilePath) {
|
||||
/*
|
||||
* Too early...
|
||||
*/
|
||||
if (!this.abInit || !this.bus) return;
|
||||
|
||||
/*
|
||||
* If no explicit size was specified, then use whatever the actual size is.
|
||||
*/
|
||||
if (!this.sizeROM) {
|
||||
this.sizeROM = this.abInit.length;
|
||||
}
|
||||
if (this.abInit.length != this.sizeROM) {
|
||||
/*
|
||||
* Note that setError() sets the component's fError flag, which in turn prevents setReady() from
|
||||
* marking the component ready. TODO: Revisit this decision. On the one hand, it sounds like a
|
||||
* good idea to stop the machine in its tracks whenever a setError() occurs, but there may also be
|
||||
* times when we'd like to forge ahead anyway.
|
||||
*/
|
||||
this.setError("ROM size (" + Str.toHexLong(this.abInit.length) + ") does not match specified size (" + Str.toHexLong(this.sizeROM) + ")");
|
||||
}
|
||||
else if (this.addROM(this.addrROM)) {
|
||||
|
||||
var aliases = [];
|
||||
if (typeof this.addrAlias == "number") {
|
||||
aliases.push(this.addrAlias);
|
||||
} else if (this.addrAlias != null && this.addrAlias.length) {
|
||||
aliases = this.addrAlias;
|
||||
}
|
||||
for (var i = 0; i < aliases.length; i++) {
|
||||
this.cloneROM(aliases[i]);
|
||||
}
|
||||
/*
|
||||
* We used to hang onto the initial ROM data so that we could restore any bytes the CPU overwrote,
|
||||
* using memory write-notification handlers, but with the introduction of read-only memory blocks, that's
|
||||
* no longer necessary.
|
||||
*
|
||||
* TODO: Consider an option to retain the ROM data, and give the user some way of restoring ROMs.
|
||||
* That may be useful for "resumable" machines that save/restore all dirty block of memory, regardless
|
||||
* whether they're ROM or RAM. However, the only way to modify a machine's ROM is with the Debugger,
|
||||
* and Debugger users should know better.
|
||||
*/
|
||||
if (!this.fRetainROM) {
|
||||
delete this.abInit;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setReady();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* addROM(addr)
|
||||
*
|
||||
* @this {ROMPDP10}
|
||||
* @param {number} addr
|
||||
* @return {boolean}
|
||||
*/
|
||||
addROM(addr)
|
||||
{
|
||||
if (this.bus.addMemory(addr, this.sizeROM, MemoryPDP10.TYPE.ROM)) {
|
||||
if (DEBUG) this.log("addROM(): copying ROM to " + Str.toHexLong(addr) + " (" + Str.toHexLong(this.abInit.length) + " bytes)");
|
||||
var i;
|
||||
for (i = 0; i < this.abInit.length; i++) {
|
||||
this.bus.setWordDirect(addr + i, this.abInit[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't need to report an error here, because addMemory() already takes care of that.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* cloneROM(addr)
|
||||
*
|
||||
* For ROMs with one or more alias addresses, we used to call addROM() for each address. However,
|
||||
* that obviously wasted memory, since each alias was an independent copy, and if you used the
|
||||
* Debugger to edit the ROM in one location, the changes would not appear in the other location(s).
|
||||
*
|
||||
* Now that the Bus component provides low-level getMemoryBlocks() and setMemoryBlocks() methods
|
||||
* to manually get and set the blocks of any memory range, it is now possible to create true aliases.
|
||||
*
|
||||
* @this {ROMPDP10}
|
||||
* @param {number} addr
|
||||
*/
|
||||
cloneROM(addr)
|
||||
{
|
||||
var aBlocks = this.bus.getMemoryBlocks(this.addrROM, this.sizeROM);
|
||||
this.bus.setMemoryBlocks(addr, this.sizeROM, aBlocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* ROMPDP10.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "rom", extracting the
|
||||
* JSON-encoded parameters for the ROMPDP10 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a ROMPDP10 component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*/
|
||||
static init()
|
||||
{
|
||||
var aeROM = Component.getElementsByClass(document, PDP10.APPCLASS, "rom");
|
||||
for (var iROM = 0; iROM < aeROM.length; iROM++) {
|
||||
var eROM = aeROM[iROM];
|
||||
var parmsROM = Component.getComponentParms(eROM);
|
||||
var rom = new ROMPDP10(parmsROM);
|
||||
Component.bindComponentControls(rom, eROM, PDP10.APPCLASS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: There's currently no need for this component to have a reset() function, since
|
||||
* once the ROM data is loaded, it can't be changed, so there's nothing to reinitialize.
|
||||
*
|
||||
* OK, well, I take that back, because the Debugger, if installed, has the ability to modify
|
||||
* ROM contents, so in that case, having a reset() function that restores the original ROM data
|
||||
* might be useful; then again, it might not, depending on what you're trying to debug.
|
||||
*
|
||||
* If we do add reset(), then we'll want to change initROM() to hang onto the original
|
||||
* ROM data; currently, we release it after copying it into the read-only memory allocated
|
||||
* via bus.addMemory().
|
||||
*/
|
||||
|
||||
/*
|
||||
* Initialize all the ROMPDP10 modules on the page.
|
||||
*/
|
||||
Web.onInit(ROMPDP10.init);
|
||||
|
||||
if (NODE) module.exports = ROMPDP10;
|
||||
670
modules/pdp10/lib/serial.js
Normal file
670
modules/pdp10/lib/serial.js
Normal file
|
|
@ -0,0 +1,670 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP-10 SerialPort component
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @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";
|
||||
|
||||
if (NODE) {
|
||||
var Str = require("../../shared/lib/strlib");
|
||||
var Web = require("../../shared/lib/weblib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var Keys = require("../../shared/lib/keys");
|
||||
var State = require("../../shared/lib/state");
|
||||
var PDP10 = require("./defines");
|
||||
var MessagesPDP10 = require("./messages");
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the Closure Compiler treats ES6 classes as @struct rather than @dict by default,
|
||||
* it deters us from defining named properties on our components; eg:
|
||||
*
|
||||
* this['exports'] = {...}
|
||||
*
|
||||
* results in an error:
|
||||
*
|
||||
* Cannot do '[]' access on a struct
|
||||
*
|
||||
* So, in order to define 'exports', we must override the @struct assumption by annotating
|
||||
* the class as @unrestricted (or @dict). Note that this must be done both here and in the
|
||||
* Component class, because otherwise the Compiler won't allow us to *reference* the named
|
||||
* property either.
|
||||
*
|
||||
* TODO: Consider marking ALL our classes unrestricted, because otherwise it forces us to
|
||||
* define every single property the class uses in its constructor, which results in a fair
|
||||
* bit of redundant initialization, since many properties aren't (and don't need to be) fully
|
||||
* initialized until the appropriate init(), reset(), restore(), etc. function is called.
|
||||
*
|
||||
* The upside, however, may be that since the structure of the class is completely defined by
|
||||
* the constructor, JavaScript engines may be able to optimize and run more efficiently.
|
||||
*
|
||||
* @unrestricted
|
||||
*/
|
||||
class SerialPortPDP10 extends Component {
|
||||
/**
|
||||
* SerialPortPDP10(parmsSerial)
|
||||
*
|
||||
* The SerialPort component has the following component-specific (parmsSerial) properties:
|
||||
*
|
||||
* adapter: adapter number; 0 if not defined (the PCx86 SerialPort component uses this
|
||||
* value to set the device's internal COM number, which in turn determines other properties,
|
||||
* such as I/O ports and IRQ; for the PDP-10, this currently has no defined use)
|
||||
*
|
||||
* binding: name of a control (based on its "binding" attribute) to bind to this port's I/O
|
||||
*
|
||||
* tabSize: set to a non-zero number to convert tabs to spaces (applies only to output to
|
||||
* the above binding); default is 0 (no conversion)
|
||||
*
|
||||
* upperCase: if true, all received input is upper-cased; it is normally the responsibility
|
||||
* of the sending device to ensure this, but sometimes it's more convenient to enforce
|
||||
* on the receiving end.
|
||||
*
|
||||
* @param {Object} parmsSerial
|
||||
*/
|
||||
constructor(parmsSerial)
|
||||
{
|
||||
super("SerialPort", parmsSerial, MessagesPDP10.SERIAL);
|
||||
|
||||
this.iAdapter = +parmsSerial['adapter'];
|
||||
this.fUpperCase = parmsSerial['upperCase'];
|
||||
if (typeof this.fUpperCase == "string") this.fUpperCase = (this.fUpperCase == "true");
|
||||
/**
|
||||
* consoleOutput becomes a string that records serial port output if the 'binding' property is set to the
|
||||
* reserved name "console". Nothing is written to the console, however, until a linefeed (0x0A) is output
|
||||
* or the string length reaches a threshold (currently, 1024 characters).
|
||||
*
|
||||
* @type {string|null}
|
||||
*/
|
||||
this.consoleOutput = null;
|
||||
|
||||
/**
|
||||
* controlIOBuffer is a DOM element bound to the port (currently used for output only; see transmitByte()).
|
||||
*
|
||||
* Example: CTTY COM2
|
||||
*
|
||||
* The CTTY DOS command redirects all CON I/O to the specified serial port (eg, COM2), which it assumes is
|
||||
* connected to a serial terminal, and therefore anything it *transmits* via COM2 will be displayed by the
|
||||
* terminal. It further assumes that anything typed on such a terminal is NOT displayed, so as DOS *receives*
|
||||
* serial input, DOS *transmits* the appropriate characters back to the terminal via COM2.
|
||||
*
|
||||
* As a result, controlIOBuffer only needs to be updated by the transmitByte() function.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
this.controlIOBuffer = null;
|
||||
|
||||
/*
|
||||
* If controlIOBuffer is being used AND 'tabSize' is set, then we make an attempt to monitor the characters
|
||||
* being echoed via transmitByte(), maintain a logical column position, and convert any tabs into the appropriate
|
||||
* number of spaces.
|
||||
*
|
||||
* charBOL, if nonzero, is a character to automatically output at the beginning of every line. This probably
|
||||
* isn't generally useful; I use it internally to preformat serial output.
|
||||
*/
|
||||
this.tabSize = +parmsSerial['tabSize'];
|
||||
this.charBOL = +parmsSerial['charBOL'];
|
||||
this.iLogicalCol = 0;
|
||||
this.fNullModem = true;
|
||||
|
||||
this.abReceive = [];
|
||||
|
||||
var sBinding = parmsSerial['binding'];
|
||||
if (sBinding == "console") {
|
||||
this.consoleOutput = "";
|
||||
} else {
|
||||
/*
|
||||
* NOTE: If sBinding is not the name of a valid Control Panel DOM element, this call does nothing.
|
||||
*/
|
||||
Component.bindExternalControl(this, sBinding, SerialPortPDP10.sIOBuffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* No connection until initConnection() is called.
|
||||
*/
|
||||
this.sDataReceived = "";
|
||||
this.connection = this.sendData = this.updateStatus = null;
|
||||
|
||||
/*
|
||||
* Export all functions required by initConnection().
|
||||
*/
|
||||
this['exports'] = {
|
||||
'connect': this.initConnection,
|
||||
'receiveData': this.receiveData,
|
||||
'receiveStatus': this.receiveStatus,
|
||||
'setConnection': this.setConnection
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setBinding(sType, sBinding, control, sValue)
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
* @param {string|null} sType is the type of the HTML control (eg, "button", "textarea", "register", "flag", "rled", etc)
|
||||
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "buffer")
|
||||
* @param {Object} control is the HTML control DOM object (eg, HTMLButtonElement)
|
||||
* @param {string} [sValue] optional data value
|
||||
* @return {boolean} true if binding was successful, false if unrecognized binding request
|
||||
*/
|
||||
setBinding(sType, sBinding, control, sValue)
|
||||
{
|
||||
var serial = this;
|
||||
|
||||
switch (sBinding) {
|
||||
case SerialPortPDP10.sIOBuffer:
|
||||
this.bindings[sBinding] = this.controlIOBuffer = control;
|
||||
|
||||
/*
|
||||
* An onkeydown handler is required for certain keys that browsers tend to consume themselves;
|
||||
* for example, BACKSPACE is often defined as going back to the previous web page, and certain
|
||||
* CTRL keys are often used for browser shortcuts (usually on Windows-based browsers).
|
||||
*
|
||||
* NOTE: We don't bother with a keyUp handler, because for the most part, we're only intercepting
|
||||
* keys that require special treatment; in general, we're content with keyPress events.
|
||||
*/
|
||||
control.onkeydown = function onKeyDown(event) {
|
||||
event = event || window.event;
|
||||
var bASCII = 0;
|
||||
var keyCode = event.keyCode;
|
||||
/*
|
||||
* Perform the same remapping of BACKSPACE and DELETE that our VT100 emulation performs,
|
||||
* for PCjs-wide consistency; see the KEYMAP table in /modules/pc8080/lib/keyboard.js for
|
||||
* the rationale. Ditto for ALT-DELETE; see onKeyDown() in /modules/pc8080/lib/keyboard.js
|
||||
* for details.
|
||||
*
|
||||
* NOTE: keyDown (and keyUp) events supply us with KEYCODE values, which are NOT the same as
|
||||
* ASCII values, which is why we are comparing with KEYCODE values but assigning ASCII values,
|
||||
* because receiveData() requires ASCII values.
|
||||
*/
|
||||
if (keyCode == Keys.KEYCODE.BS) {
|
||||
bASCII = event.altKey? Keys.ASCII.CTRL_H : Keys.ASCII.DEL;
|
||||
}
|
||||
else if (keyCode == Keys.KEYCODE.DEL) {
|
||||
bASCII = Keys.ASCII.CTRL_H;
|
||||
}
|
||||
else if (event.ctrlKey && keyCode >= Keys.ASCII.A && keyCode <= Keys.ASCII.Z) {
|
||||
bASCII = keyCode - (Keys.ASCII.A - Keys.ASCII.CTRL_A);
|
||||
}
|
||||
if (bASCII) {
|
||||
if (event.preventDefault) event.preventDefault();
|
||||
serial.receiveData(bASCII);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
control.onkeypress = function onKeyPress(event) {
|
||||
/*
|
||||
* NOTE: Unlike keyDown events, keyPress events generally supply us with ASCII values,
|
||||
* despite the fact that, as above, they come to us via the keyCode property. Yes, it's
|
||||
* brilliant (or rather, the opposite of brilliant), but that's life.
|
||||
*/
|
||||
event = event || window.event;
|
||||
/*
|
||||
* Not sure why COMMAND-key combinations are coming through here (on Safari at least),
|
||||
* but in any case, let's make sure we don't act on them.
|
||||
*/
|
||||
if (!event.metaKey) {
|
||||
var bASCII = event.which || event.keyCode;
|
||||
/*
|
||||
* Perform the same remapping of ALT-ENTER (to LINE-FEED) that our VT100 emulation performs,
|
||||
* for PCjs-wide consistency; see onKeyDown() in /modules/pc8080/lib/keyboard.js for details.
|
||||
*/
|
||||
if (event.altKey) {
|
||||
if (bASCII == Keys.ASCII.CTRL_M) {
|
||||
bASCII = Keys.ASCII.CTRL_J;
|
||||
}
|
||||
}
|
||||
serial.receiveData(bASCII);
|
||||
/*
|
||||
* Since we're going to remove the "readonly" attribute from the <textarea> control
|
||||
* (so that the soft keyboard activates on iOS), instead of calling preventDefault() for
|
||||
* selected keys (eg, the SPACE key, whose default behavior is to scroll the page), we must
|
||||
* now call it for *all* keys, so that the keyCode isn't added to the control immediately,
|
||||
* on top of whatever the machine is echoing back, resulting in double characters.
|
||||
*/
|
||||
if (event.preventDefault) event.preventDefault();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
control.onpaste = function onKeyPress(event) {
|
||||
if (event.stopPropagation) event.stopPropagation();
|
||||
if (event.preventDefault) event.preventDefault();
|
||||
var clipboardData = event.clipboardData || window.clipboardData;
|
||||
if (clipboardData) {
|
||||
/*
|
||||
* NOTE: Multiple lines of pasted text will (at least on macOS) contain LFs instead of CRs;
|
||||
* this is dealt with in receiveData() whenever it receives a string of characters.
|
||||
*/
|
||||
serial.receiveData(clipboardData.getData('Text'));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Now that we've added an onkeypress handler that calls preventDefault() for ALL keys, the control
|
||||
* itself no longer needs the "readonly" attribute; we primarily need to remove it for iOS browsers,
|
||||
* so that the soft keyboard will activate, but it shouldn't hurt to remove the attribute for all browsers.
|
||||
*/
|
||||
control.removeAttribute("readonly");
|
||||
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
* @param {ComputerPDP10} cmp
|
||||
* @param {BusPDP10} bus
|
||||
* @param {CPUStatePDP10} cpu
|
||||
* @param {DebuggerPDP10} dbg
|
||||
*/
|
||||
initBus(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.cmp = cmp;
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
|
||||
this.setReady();
|
||||
}
|
||||
|
||||
/**
|
||||
* initConnection(fNullModem)
|
||||
*
|
||||
* If a machine 'connection' parameter exists of the form "{sourcePort}->{targetMachine}.{targetPort}",
|
||||
* and "{sourcePort}" matches our idComponent, then look for a component with id "{targetMachine}.{targetPort}".
|
||||
*
|
||||
* If the target component is found, then verify that it has exported functions with the following names:
|
||||
*
|
||||
* receiveData(data): called when we have data to transmit; aliased internally to sendData(data)
|
||||
* receiveStatus(pins): called when our control signals have changed; aliased internally to updateStatus(pins)
|
||||
*
|
||||
* For now, we're not going to worry about communication in the other direction, because when the target component
|
||||
* performs its own initConnection(), it will find our receiveData() and receiveStatus() functions, at which point
|
||||
* communication in both directions should be established, and the circle of life complete.
|
||||
*
|
||||
* For added robustness, if the target machine initializes much more slowly than we do, and our connection attempt
|
||||
* fails, that's OK, because when it finally initializes, its initConnection() will call our initConnection();
|
||||
* if we've already initialized, no harm done.
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
* @param {boolean} [fNullModem] (caller's null-modem setting, to ensure our settings are in agreement)
|
||||
*/
|
||||
initConnection(fNullModem)
|
||||
{
|
||||
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.fNullModem);
|
||||
this.sendData = exports['receiveData'];
|
||||
if (this.sendData) {
|
||||
this.fNullModem = fNullModem;
|
||||
this.updateStatus = exports['receiveStatus'];
|
||||
this.status("Connected " + this.idMachine + '.' + sSourceID + " 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
powerUp(data, fRepower)
|
||||
{
|
||||
if (!fRepower) {
|
||||
|
||||
/*
|
||||
* This is as late as we can currently wait to make our first inter-machine connection attempt;
|
||||
* even so, the target machine's initialization process may still be ongoing, so any connection
|
||||
* may be not fully resolved until the target machine performs its own initConnection(), which will
|
||||
* in turn invoke our initConnection() again.
|
||||
*/
|
||||
this.initConnection(this.fNullModem);
|
||||
|
||||
if (!data) {
|
||||
this.reset();
|
||||
} else {
|
||||
if (!this.restore(data)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
powerDown(fSave, fShutdown)
|
||||
{
|
||||
return fSave? this.save() : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
*/
|
||||
reset()
|
||||
{
|
||||
this.initState();
|
||||
}
|
||||
|
||||
/**
|
||||
* save()
|
||||
*
|
||||
* This implements save support for the SerialPort component.
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
* @return {Object}
|
||||
*/
|
||||
save()
|
||||
{
|
||||
var state = new State(this);
|
||||
state.set(0, this.saveRegisters());
|
||||
return state.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* restore(data)
|
||||
*
|
||||
* This implements restore support for the SerialPort component.
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
* @param {Object} data
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
restore(data)
|
||||
{
|
||||
return this.initState(data[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* initState(a)
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
* @param {Array} [a]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
initState(a)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* saveRegisters()
|
||||
*
|
||||
* Basically, the inverse of initState().
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
* @return {Array}
|
||||
*/
|
||||
saveRegisters()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* receiveData(data)
|
||||
*
|
||||
* This replaces the old sendRBR() function, which expected an Array of bytes. We still support that,
|
||||
* but in order to support connections with other SerialPort components (ie, the PC8080 SerialPort), we
|
||||
* have added support for numbers and strings as well.
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
* @param {number|string|Array} data
|
||||
* @return {boolean} true if received, false if not
|
||||
*/
|
||||
receiveData(data)
|
||||
{
|
||||
if (typeof data == "number") {
|
||||
this.abReceive.push(data);
|
||||
}
|
||||
else if (typeof data == "string") {
|
||||
var bASCII = 0, bASCIIPrev;
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
bASCIIPrev = bASCII;
|
||||
bASCII = data.charCodeAt(i);
|
||||
/*
|
||||
* NOTE: Multiple lines of pasted text will (at least on macOS) contain LFs instead of CRs;
|
||||
* we convert them to CRs below. Windows may do something different, but in the worst case,
|
||||
* even if we receive CR/LF pairs, this code should keep the CRs and lose the LFs.
|
||||
*/
|
||||
if (bASCII == Str.ASCII.LF) {
|
||||
if (bASCIIPrev == Str.ASCII.CR) continue;
|
||||
bASCII = Str.ASCII.CR;
|
||||
}
|
||||
this.abReceive.push(bASCII);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.abReceive = this.abReceive.concat(data);
|
||||
}
|
||||
|
||||
return true; // for now, return true regardless, since we're buffering everything anyway
|
||||
}
|
||||
|
||||
/**
|
||||
* receiveByte()
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
* @return {number} (0x00-0xff if byte available, -1 if not)
|
||||
*/
|
||||
receiveByte()
|
||||
{
|
||||
var b = -1;
|
||||
if (this.abReceive.length) {
|
||||
/*
|
||||
* Here, as elsewhere (eg, the PC11 component), even if I trusted all incoming data
|
||||
* to be byte values (which I don't), there's also the risk that it could be signed data
|
||||
* (eg, -128 to 127, instead of 0 to 255). Both risks are good reasons to always mask
|
||||
* the data assigned to RBUF with 0xff.
|
||||
*/
|
||||
b = this.abReceive.shift() & 0xff;
|
||||
this.printMessage("receiveByte(" + Str.toHexByte(b) + ")");
|
||||
if (this.fUpperCase) {
|
||||
/*
|
||||
* Automatically transform lower-case ASCII codes to upper-case; fUpperCase should
|
||||
* only be set when a terminal or some sort of pseudo-display is being used and we don't
|
||||
* trust it to have its CAPS-LOCK setting correct.
|
||||
*/
|
||||
if (b >= 0x61 && b < 0x7A) b -= 0x20;
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* receiveStatus(pins)
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
* @param {number} pins
|
||||
*/
|
||||
receiveStatus(pins)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* setConnection(component, fn)
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
* @param {Object|null} component
|
||||
* @param {function(number)} fn
|
||||
* @return {boolean}
|
||||
*/
|
||||
setConnection(component, fn)
|
||||
{
|
||||
if (!this.connection) {
|
||||
this.connection = component;
|
||||
this.sendData = fn;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* transmitByte(b)
|
||||
*
|
||||
* @this {SerialPortPDP10}
|
||||
* @param {number} b
|
||||
* @return {boolean} true if transmitted, false if not
|
||||
*/
|
||||
transmitByte(b)
|
||||
{
|
||||
var fTransmitted = false;
|
||||
|
||||
if (MAXDEBUG) this.printMessage("transmitByte(" + Str.toHexByte(b) + ")");
|
||||
|
||||
if (this.sendData) {
|
||||
if (this.sendData.call(this.connection, b)) {
|
||||
fTransmitted = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Why do DEC diagnostics like to output bytes with bit 7 set?
|
||||
*/
|
||||
b &= 0x7F;
|
||||
|
||||
if (this.controlIOBuffer) {
|
||||
if (b == 0x0D) {
|
||||
this.iLogicalCol = 0;
|
||||
}
|
||||
else if (b == 0x08) {
|
||||
this.controlIOBuffer.value = this.controlIOBuffer.value.slice(0, -1);
|
||||
/*
|
||||
* TODO: Back up the correct number of columns if the character erased was a tab.
|
||||
*/
|
||||
if (this.iLogicalCol > 0) this.iLogicalCol--;
|
||||
}
|
||||
else if (b) {
|
||||
/*
|
||||
* RT-11 outputs lots of NULL characters, at least after a "D 56=5015" (0x0A0D) command has
|
||||
* been issued, hence the "if (b)" check above.
|
||||
*
|
||||
* TODO: Also consider a check for Keys.ASCII.CTRL_C, because by default, RT-11 outputs "raw"
|
||||
* CTRL_C characters, which we capture below and render as <ETX>. RT-11 does this for other keys
|
||||
* as well, such as CTRL_K (<VT>) and CTRL_L (<FF>).
|
||||
*/
|
||||
var s = Str.toASCIICode(b); // formerly: String.fromCharCode(b);
|
||||
var nChars = s.length; // formerly: (b >= 0x20? 1 : 0);
|
||||
if (b < 0x20 && nChars == 1) nChars = 0;
|
||||
if (b == 0x09) {
|
||||
var tabSize = this.tabSize || 8;
|
||||
nChars = tabSize - (this.iLogicalCol % tabSize);
|
||||
if (this.tabSize) s = Str.pad("", nChars);
|
||||
}
|
||||
if (this.charBOL && !this.iLogicalCol && nChars) s = String.fromCharCode(this.charBOL) + s;
|
||||
this.controlIOBuffer.value += s;
|
||||
this.controlIOBuffer.scrollTop = this.controlIOBuffer.scrollHeight;
|
||||
this.iLogicalCol += nChars;
|
||||
}
|
||||
fTransmitted = true;
|
||||
}
|
||||
else if (this.consoleOutput != null) {
|
||||
if (b == 0x0A || this.consoleOutput.length >= 1024) {
|
||||
this.println(this.consoleOutput);
|
||||
this.consoleOutput = "";
|
||||
}
|
||||
if (b != 0x0A) {
|
||||
this.consoleOutput += String.fromCharCode(b);
|
||||
}
|
||||
fTransmitted = true;
|
||||
}
|
||||
|
||||
return fTransmitted;
|
||||
}
|
||||
|
||||
/**
|
||||
* SerialPortPDP10.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "serial", extracting the
|
||||
* JSON-encoded parameters for the SerialPort constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a SerialPort component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*/
|
||||
static init()
|
||||
{
|
||||
var aeSerial = Component.getElementsByClass(document, PDP10.APPCLASS, "serial");
|
||||
for (var iSerial = 0; iSerial < aeSerial.length; iSerial++) {
|
||||
var eSerial = aeSerial[iSerial];
|
||||
var parmsSerial = Component.getComponentParms(eSerial);
|
||||
var serial = new SerialPortPDP10(parmsSerial);
|
||||
Component.bindComponentControls(serial, eSerial, PDP10.APPCLASS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal name used for the I/O buffer control, if any, that we bind to the SerialPort.
|
||||
*
|
||||
* Alternatively, if SerialPort wants to use another component's control (eg, the Panel's
|
||||
* "print" control), it can specify the name of that control with the 'binding' property.
|
||||
*
|
||||
* For that binding to succeed, we also need to know the target component; for now, that's
|
||||
* been hard-coded to "Panel", in part because that's one of the few components we can rely
|
||||
* upon initializing before we do, but it would be a simple matter to include a component type
|
||||
* or ID as part of the 'binding' property as well, if we need more flexibility later.
|
||||
*/
|
||||
SerialPortPDP10.sIOBuffer = "buffer";
|
||||
|
||||
/*
|
||||
* Initialize every SerialPort module on the page.
|
||||
*/
|
||||
Web.onInit(SerialPortPDP10.init);
|
||||
|
||||
if (NODE) module.exports = SerialPortPDP10;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 Bus component.
|
||||
* @fileoverview Implements the PDP-11 Bus component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 Computer component.
|
||||
* @fileoverview Implements the PDP-11 Computer component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview Controls the PDP11 CPU component.
|
||||
* @fileoverview Controls the PDP-11 CPU component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview Implements PDP11 opcode handlers.
|
||||
* @fileoverview Implements PDP-11 opcode handlers.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 CPU component.
|
||||
* @fileoverview Implements the PDP-11 CPU component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 Debugger component.
|
||||
* @fileoverview Implements the PDP-11 Debugger component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview PDP11-specific compile-time definitions.
|
||||
* @fileoverview PDP-11 compile-time definitions.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview Implements PDP11 device support.
|
||||
* @fileoverview Implements PDP-11 device support.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview Implements a generic disk drive controller
|
||||
* @fileoverview Implements a generic disk drive controller.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 Keyboard component.
|
||||
* @fileoverview Implements the PDP-11 Keyboard component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 Memory component.
|
||||
* @fileoverview Implements the PDP-11 Memory component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
@ -738,8 +738,8 @@ class MemoryPDP11 {
|
|||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @param {number} b
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeByteChecked(off, b, addr)
|
||||
{
|
||||
|
|
@ -754,8 +754,8 @@ class MemoryPDP11 {
|
|||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @param {number} w
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeWordChecked(off, w, addr)
|
||||
{
|
||||
|
|
@ -859,8 +859,8 @@ class MemoryPDP11 {
|
|||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @param {number} b
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeByteLE(off, b, addr)
|
||||
{
|
||||
|
|
@ -876,8 +876,8 @@ class MemoryPDP11 {
|
|||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @param {number} w
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeWordBE(off, w, addr)
|
||||
{
|
||||
|
|
@ -893,8 +893,8 @@ class MemoryPDP11 {
|
|||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @param {number} w
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeWordLE(off, w, addr)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview Defines PDP11 message categories.
|
||||
* @fileoverview Defines PDP-11 message categories.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 Panel component.
|
||||
* @fileoverview Implements the PDP-11 Panel component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 RAM component.
|
||||
* @fileoverview Implements the PDP-11 RAM component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 ROM component.
|
||||
* @fileoverview Implements the PDP-11 ROM component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @copyright © Jeff Parsons 2012-2017
|
||||
*
|
||||
|
|
|
|||
|
|
@ -657,11 +657,11 @@ class Debugger extends Component {
|
|||
* @param {boolean} [fStripLeadingZeros]
|
||||
* @return {string}
|
||||
*/
|
||||
toStrBase(n, nBytes, fStripLeadingZeros) {
|
||||
toStrBase(n, nBytes = 0, fStripLeadingZeros = false) {
|
||||
var s;
|
||||
switch(this.nBase) {
|
||||
case 8:
|
||||
s = Str.toOct(n, nBytes * 3 - (nBytes > 2? 1 : 0));
|
||||
s = Str.toOct(n, nBytes * 3 /* - (nBytes > 2? 1 : 0) */);
|
||||
break;
|
||||
case 10:
|
||||
s = n.toString();
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
var DEBUG = true;
|
||||
|
||||
/*
|
||||
From the "PDP-10 System Reference Manual", May 1968, p. 1-4:
|
||||
|
||||
|
|
@ -116,13 +114,13 @@ var DEBUG = true;
|
|||
events in previous instructions.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @class Int36
|
||||
* @property {number} value
|
||||
* @property {number|null} extended
|
||||
* @property {number|null} remainder
|
||||
* @property {number} error
|
||||
* @unrestricted
|
||||
*
|
||||
* The 'value' property stores the 36-bit value as an unsigned integer. When the value should be
|
||||
* interpreted as a signed quantity, subtract BIT36 whenever value > MAXPOS.
|
||||
|
|
@ -140,7 +138,6 @@ var DEBUG = true;
|
|||
* to the 'extended' portion as the "low order part" and the 'value' portion as the "high order part",
|
||||
* presumably because they number the left-most significant bit 0.
|
||||
*/
|
||||
|
||||
class Int36 {
|
||||
/**
|
||||
* Int36(obj, extended)
|
||||
|
|
@ -280,7 +277,7 @@ class Int36 {
|
|||
* @param {boolean} [fUnsigned] (default is signed for radix 10, unsigned for any other radix)
|
||||
* @return {string}
|
||||
*/
|
||||
toString(radix = 10, fUnsigned)
|
||||
toString(radix = 10, fUnsigned = false)
|
||||
{
|
||||
if (radix == 10) {
|
||||
return this.toDecimal(fUnsigned);
|
||||
|
|
|
|||
29
package.json
29
package.json
|
|
@ -174,6 +174,35 @@
|
|||
"./modules/shared/lib/state.js",
|
||||
"./modules/shared/lib/embed.js"
|
||||
],
|
||||
"pdp10Files": [
|
||||
"./modules/shared/lib/defines.js",
|
||||
"./modules/shared/lib/diskapi.js",
|
||||
"./modules/shared/lib/dumpapi.js",
|
||||
"./modules/shared/lib/reportapi.js",
|
||||
"./modules/shared/lib/userapi.js",
|
||||
"./modules/shared/lib/int36.js",
|
||||
"./modules/shared/lib/keys.js",
|
||||
"./modules/shared/lib/component.js",
|
||||
"./modules/shared/lib/strlib.js",
|
||||
"./modules/shared/lib/usrlib.js",
|
||||
"./modules/shared/lib/weblib.js",
|
||||
"./modules/pdp10/lib/defines.js",
|
||||
"./modules/pdp10/lib/messages.js",
|
||||
"./modules/pdp10/lib/panel.js",
|
||||
"./modules/pdp10/lib/bus.js",
|
||||
"./modules/pdp10/lib/device.js",
|
||||
"./modules/pdp10/lib/memory.js",
|
||||
"./modules/pdp10/lib/cpu.js",
|
||||
"./modules/pdp10/lib/cpustate.js",
|
||||
"./modules/pdp10/lib/rom.js",
|
||||
"./modules/pdp10/lib/ram.js",
|
||||
"./modules/pdp10/lib/serial.js",
|
||||
"./modules/shared/lib/debugger.js",
|
||||
"./modules/pdp10/lib/debugger.js",
|
||||
"./modules/pdp10/lib/computer.js",
|
||||
"./modules/shared/lib/state.js",
|
||||
"./modules/shared/lib/embed.js"
|
||||
],
|
||||
"pdp11Files": [
|
||||
"./modules/shared/lib/defines.js",
|
||||
"./modules/shared/lib/diskapi.js",
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ function Vf(a,b,c){var d;if(b){b=Wf(a,b);for(var e=0,f=!1,g=b,h=[],k=[],p=b.spli
|
|||
d))}return d}function Wf(a,b){for(var c,d=/\{(.*?)\}/;(c=b.match(d))&&!(0<=c[1].indexOf("{"));){var e=Vf(a,c[1]);b=b.replace("{"+c[1]+"}",null!=e?$f(a,e):"undefined")}for(;(c=b.match(/\[(.*?)]/))&&!(0<=c[1].indexOf("["));)b=b.replace("["+c[1]+"]","unimplemented");for(;c=b.match(/\$([a-z]+)/i);){d=null;switch(c[1].toLowerCase()){case "ops":d=a.N-a.Y}if(null==d)break;b=b.replace(c[0],d.toString())}return b}
|
||||
function Xf(a,b,c,d){var e;null!=b?(e=a.Kb(b),0<=e?e=a.Lb(e):(e=a.S[b],null==e&&(e=Ha(b,a.na))),null!=e||d||a.i("invalid "+(c?c:"value")+": "+b)):d||a.i("missing "+(c||"value"));return e}
|
||||
function Zf(a,b,c){var d,e=!1;if(void 0!==c){e=!0;d=c;var f=4,g="";if(!f||4<f)f=4;for(var h=0;h<f;h++){g&&(g=","+g);var k=d&255,p=void 0,u=8,v="";u?32<u&&(u=32):u=32;for(var B=null==k||isNaN(k),y=p=p||u;0<u--;)y||(v=","+v,y=p),v=(B?"?":k&1?"1":"0")+v,k>>=1,y--;g=v+g;d>>=8}d=t(c,0,!0)+" "+c+". "+Ia(c,0,!0)+" "+("0b"+g);32<=c&&127>c&&(d+=" '"+String.fromCharCode(c)+"'")}a.i((null!=b?b+": ":"")+d);return e}
|
||||
function ag(a,b){if(b)return Zf(a,b,a.S[b]);var c=0;for(b in a.S)Zf(a,b,a.S[b]),c++;return 0<c}function $f(a,b){switch(a.na){case 8:a=Ia(b,NaN);break;case 10:a=b.toString();break;default:a=t(b,NaN)}return a}var Yf={"||":0,"&&":1,"|":2,"^":3,"&":4,"!=":5,"==":5,">=":6,">":6,"<=":6,"<":6,">>>":7,">>":7,"<<":7,"-":8,"+":8,"%":9,"/":9,"*":9};
|
||||
function ag(a,b){if(b)return Zf(a,b,a.S[b]);var c=0;for(b in a.S)Zf(a,b,a.S[b]),c++;return 0<c}function $f(a,b){var c;c=void 0===c?0:c;switch(a.na){case 8:a=Ia(b,3*c);break;case 10:a=b.toString();break;default:a=t(b,2*c)}return a}var Yf={"||":0,"&&":1,"|":2,"^":3,"&":4,"!=":5,"==":5,">=":6,">":6,"<=":6,"<":6,">>>":7,">>":7,"<<":7,"-":8,"+":8,"%":9,"/":9,"*":9};
|
||||
function bg(a){Tf.call(this,a);this.style=cg;this.F=dg();this.la=dg();this.O=dg();this.A=[];this.j=this.I=this.w=[];eg(this);this.W=0;fg(this);this.Z={};gg(this,a.messages);this.ea=a.commands;var b=this;window?void 0===window.pc8080&&(window.pc8080=function(a){return Fc(b,a)}):void 0===global.pc8080&&(global.pc8080=function(a){return Fc(b,a)})}aa(bg,Tf);l=bg.prototype;
|
||||
l.Ea=function(a,b,c,d){this.H=b;this.u=c;this.R=a;(a=Bc(a,"messages"))&&gg(this,a);this.ga=hg;Sd(this,64,function(a){a:{var b=d.H.b,c=a[0],e=a=0,k=b.length;if(c){a=ig(jg(d,c));if(-1===a){d.i("invalid address: "+c);break a}e=a>>>d.H.g;k=1}d.i("blockid physical blockaddr used size type");d.i("-------- --------- ---------- ------ ------ ----");for(var c=-1,p=0;k--;){var u=b[e];u.type==c?p++||d.i("..."):(c=u.type,p=Ib[c],u&&d.i(t(u.id)+" %"+t(e<<d.H.g)+" %%"+t(u.J)+" "+x(u.pb)+" "+
|
||||
x(u.size)+" "+p),c!=dc&&(c=-1),p=0);a+=d.H.w;e++}}});Aa(this)};
|
||||
|
|
|
|||
|
|
@ -718,7 +718,7 @@ case "^":d=f^e;break;case "|":d=f|e;break;case "&&":d=f&&e?1:0;break;case "||":d
|
|||
function Px(a,b,c){var d;if(b){b=Qx(a,b);for(var e=0,f=!1,g=b,h=[],k=[],m=b.split(/(\|\||&&|\||^|&|!=|==|>=|>>>|>>|>|<=|<<|<|-|\+|%|\/|\*)/);e<m.length;){var p=m[e++],w=p.length,p=ra(p);if(!p){f=!0;break}p=Rx(a,p,null,!1===c);if(void 0===p){f=!0;c=!1;break}h.push(p);if(e==m.length)break;var p=m[e++],u=p.length;k.length&&Sx[p]<Sx[k[k.length-1]]&&Ox(h,k,1);k.push(p);b=b.substr(w+u)}Ox(h,k)&&1==h.length||(f=!0);f?c&&a.P("error parsing '"+g+"' at character "+(g.length-b.length)):(d=h.pop(),c&&Tx(a,null,
|
||||
d))}return d}function Qx(a,b){for(var c,d=/\{(.*?)\}/;(c=b.match(d))&&!(0<=c[1].indexOf("{"));){var e=Px(a,c[1]);b=b.replace("{"+c[1]+"}",null!=e?Ux(a,e):"undefined")}for(;(c=b.match(/\[(.*?)]/))&&!(0<=c[1].indexOf("["));)b=a.Pk(b,c[1]);for(;c=b.match(/\$([a-z]+)/i);){d=null;switch(c[1].toLowerCase()){case "ops":d=a.W-a.ga}if(null==d)break;b=b.replace(c[0],d.toString())}return b}
|
||||
function Rx(a,b,c,d){var e;null!=b?(e=a.gh(b),0<=e?e=a.hh(e):(e=a.da[b],null==e&&(e=ga(b,a.ya))),null!=e||d||a.P("invalid "+(c?c:"value")+": "+b)):d||a.P("missing "+(c||"value"));return e}function Tx(a,b,c){var d,e=!1;void 0!==c&&(e=!0,d=q(c,0,!0)+" "+c+". "+ja(c,0,!0)+" "+ia(c,4,!0),32<=c&&127>c&&(d+=" '"+String.fromCharCode(c)+"'"));a.P((null!=b?b+": ":"")+d);return e}function Vx(a,b){if(b)return Tx(a,b,a.da[b]);var c=0;for(b in a.da)Tx(a,b,a.da[b]),c++;return 0<c}
|
||||
function Ux(a,b){switch(a.ya){case 8:a=ja(b,NaN);break;case 10:a=b.toString();break;default:a=q(b,NaN)}return a}var Sx={"||":0,"&&":1,"|":2,"^":3,"&":4,"!=":5,"==":5,">=":6,">":6,"<=":6,"<":6,">>>":7,">>":7,"<<":7,"-":8,"+":8,"%":9,"/":9,"*":9};
|
||||
function Ux(a,b){var c;c=void 0===c?0:c;switch(a.ya){case 8:a=ja(b,3*c);break;case 10:a=b.toString();break;default:a=q(b,2*c)}return a}var Sx={"||":0,"&&":1,"|":2,"^":3,"&":4,"!=":5,"==":5,">=":6,">":6,"<=":6,"<":6,">>>":7,">>":7,"<<":7,"-":8,"+":8,"%":9,"/":9,"*":9};
|
||||
function Wx(a){Nx.call(this,a);this.oa=4;this.ha=5;this.wa=1048575;this.K=Xx(this);this.Ga=Xx(this);this.Z=Xx(this);this.D=[];this.B=this.Y=this.M=[];Yx(this);this.ta=0;Zx(this);this.za={};$x(this,a.messages);this.Ca=a.commands;var b=this;window?void 0===window.pcx86&&(window.pcx86=function(a){return ay(b,a)}):void 0===global.pcx86&&(global.pcx86=function(a){return ay(b,a)})}ba(Wx,Nx);l=Wx.prototype;
|
||||
l.uc=function(a,b,c,d){this.ka=b;this.H=c;this.pa=a;this.Za=Ob(a,"FDC");this.Ha=Ob(a,"HDC");this.Tc=Ob(a,"FPU");this.G=Ob(a,"Mouse");(a=qd(a,"messages"))&&$x(this,a);this.ha=b.I>>2;this.wa=b.N;this.Ta=new Kd(this.H,7,"DBG");this.ma=by;80186<=this.H.ca&&(this.ma=by.slice(),this.ma[15]=cy,80286<=this.H.ca&&(this.ma[15]=dy,80386<=this.H.ca&&(this.oa=8)));rl(this,64,function(a){ey(d,d.H.sc,a[0])});rl(this,4,function(a){if(a=a[0]){var b=Rx(d,a);if(void 0===b)d.P("invalid selector: "+a);else if(a=fy(d,
|
||||
b,gy),d.P("dumpSel("+ka(a?a.U:b)+"): %"+q(a?a.Lb:null,d.ha)),a){var c,b=!1;if(a.type&4096)a.type&2048?(c="code"+(a.type&512?",readable":",execonly"),a.type&1024&&(c+=",conforming")):(c="data"+(a.type&512?",writable":",readonly"),a.type&1024&&(c+=",expdown")),a.type&256&&(c+=",accessed");else{var e=hy[a.type];e&&(c=e[0],b=e[1])}!c||a.jb&32768||(c+=",not present");d.P((b?"seg="+ka(a.ua&65535)+" off="+ka(a.Ka):"base="+q(a.ua,d.ha)+" limit="+iy(a.Ka))+" type="+r(a.type>>8)+" ("+c+") ext="+ka(a.ext&-65296)+
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
<xsl:variable name="MACHINETYPE">pc</xsl:variable>
|
||||
<xsl:variable name="CSSCLASS">pcjs</xsl:variable>
|
||||
<xsl:variable name="APPCLASS">pdp11</xsl:variable>
|
||||
<xsl:variable name="APPCLASS">pdp10</xsl:variable>
|
||||
<xsl:variable name="APPNAME">PDPjs</xsl:variable>
|
||||
<xsl:variable name="APPVERSION">1.34.0</xsl:variable>
|
||||
<xsl:variable name="SITEHOST">www.pcjs.org</xsl:variable>
|
||||
|
|
|
|||
204
versions/pdpjs/1.34.0/pdp10-dbg.js
Normal file
204
versions/pdpjs/1.34.0/pdp10-dbg.js
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
(function(){/*
|
||||
http://pcjs.org/modules/shared/lib/diskapi.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/dumpapi.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/reportapi.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/userapi.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/keys.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/strlib.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/weblib.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/messages.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/device.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/rom.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/ram.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/computer.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/state.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/embed.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/defines.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/int36.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/component.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/usrlib.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/defines.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/panel.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/bus.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/memory.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/cpu.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/cpustate.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/serial.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/debugger.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/debugger.js (C) Jeff Parsons 2012-2017
|
||||
*/
|
||||
var l,aa="function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){if(c.get||c.set)throw new TypeError("ES3 does not support getters and setters.");a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)},ba="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global?global:this;function ea(){ea=function(){};ba.Symbol||(ba.Symbol=fa)}var ga=0;function fa(a){return"jscomp_symbol_"+(a||"")+ga++}
|
||||
function ha(){ea();var a=ba.Symbol.iterator;a||(a=ba.Symbol.iterator=ba.Symbol("iterator"));"function"!=typeof Array.prototype[a]&&aa(Array.prototype,a,{configurable:!0,writable:!0,value:function(){return oa(this)}});ha=function(){}}function oa(a){var b=0;return pa(function(){return b<a.length?{done:!1,value:a[b++]}:{done:!0}})}function pa(a){ha();a={next:a};a[ba.Symbol.iterator]=function(){return this};return a}
|
||||
function q(a,b){function c(){}c.prototype=b.prototype;a.prototype=new c;a.prototype.constructor=a;for(var d in b)if(Object.defineProperties){var e=Object.getOwnPropertyDescriptor(b,d);e&&Object.defineProperty(a,d,e)}else a[d]=b[d]}function qa(a,b){if(b){var c=ba;a=a.split(".");for(var d=0;d<a.length-1;d++){var e=a[d];e in c||(c[e]={});c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&aa(c,a,{configurable:!0,writable:!0,value:b})}}
|
||||
qa("Math.trunc",function(a){return a?a:function(a){a=Number(a);if(isNaN(a)||Infinity===a||-Infinity===a||!a)return a;var b=Math.floor(Math.abs(a));return 0>a?-b:b}});qa("Math.log2",function(a){return a?a:function(a){return Math.log(a)/Math.LN2}});
|
||||
var ra=Math.pow(2,36),sa=-Math.pow(2,35),t={Yb:0,nb:1,$b:2,ac:3,bc:4,cc:5,dc:6,ec:7,Ya:8,fc:9,ob:10,gc:11,hc:12,pb:13,ic:14,jc:15,kc:16,lc:17,mc:18,nc:19,oc:20,pc:21,qc:22,rc:23,sc:24,tc:25,uc:26," ":32,"!":33,'"':34,"#":35,$:36,"%":37,"&":38,"'":39,"(":40,")":41,"*":42,"+":43,",":44,"-":45,".":46,"/":47,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57,":":58,";":59,"<":60,"=":61,">":62,"?":63,"@":64,Xa:65,Xb:66,Zb:67,vc:68,E:69,wc:70,xc:71,yc:72,zc:73,Ac:74,Bc:75,Cc:76,Dc:77,Ec:78,Fc:79,Gc:80,Q:81,
|
||||
Hc:82,Ic:83,Jc:84,Kc:85,Lc:86,Mc:87,Nc:88,Oc:89,sb:90,"[":91,"\\":92,"]":93,"^":94,_:95,"`":96,Pc:97,Qc:98,Rc:99,d:100,e:101,Sc:102,Tc:103,Uc:104,Vc:105,Wc:106,k:107,Xc:108,Yc:109,n:110,Zc:111,p:112,q:113,r:114,$c:115,t:116,bd:117,cd:118,dd:119,x:120,y:121,z:122,"{":123,"|":124,"}":125,"~":126,qb:127};
|
||||
function u(a,b,c){this.type=a;b||(b={id:"",name:""});this.id=b.id||"";this.name=b.name;this.$a=b.comment;this.rb=b;this.exports={};this.F=this.bindings={};a=this.id.indexOf(".");0>a?this.Ja=this.id:(this.Ka=this.id.substr(0,a),this.Ja=this.id.substr(a+1));this.i={ready:!1,Ea:!1,Ua:!1,S:!1,error:!1};this.Na=null;this.i.error=!1;this.V=c||0;this.A=this.b=this.B=this.C=this.ja=null;v.push(this)}function ta(a,b,c){ua[a]&&b&&(ua[a][b]=c)}function va(){return Date.now()||+new Date}
|
||||
function x(a){window&&window.alert(a)}function xa(a){var b=!1;window&&(b=window.confirm(a));return b}function y(a){var b,c=[];a&&(a=0<(b=a.indexOf("."))?a.substr(0,b+1):"");for(b=0;b<v.length;b++){var d=v[b];a&&d.id.indexOf(a)||c.push(d)}return c}function Ca(a){if(void 0!==a){var b;for(b=0;b<v.length;b++)if(v[b].id===a)return v[b]}return null}
|
||||
function z(a,b){var c;if(void 0!==a){var d;b&&(b=0<(d=b.indexOf("."))?b.substr(0,d+1):"");for(d=0;d<v.length;d++)if(c)c==v[d]&&(c=null);else if(!(a!=v[d].type||b&&v[d].id.indexOf(b)))return v[d]}return null}function A(a){var b=null;if(a=a.getAttribute("data-value"))try{b=eval("("+a+")")}catch(c){x(c.message+" ("+a+")")}return b}
|
||||
function B(a,b){var c=C;b=D(b.parentNode,c+"-control");for(var d=0;d<b.length;d++)for(var e=b[d].childNodes,f=0;f<e.length;f++){var g=e[f];if(1===g.nodeType){var h=g.getAttribute("class");if(h)for(var k=h.split(" "),m=0;m<k.length;m++)switch(h=k[m],h){case c+"-binding":(h=A(g))&&h.binding&&a.da(h.type,h.binding,g,h.value),m=k.length}}}}
|
||||
function D(a,b,c){c&&(b+="-"+c+"-object");if(a.getElementsByClassName)return a.getElementsByClassName(b);var d;c=[];a=a.getElementsByTagName("*");var e=new RegExp("(^| )"+b+"( |$)");b=0;for(d=a.length;b<d;b++)e.test(a[b].className)&&c.push(a[b]);return c}
|
||||
function Da(a){for(var b=!0,c=Ea[a];c&&c.length;){var d=c.splice(0,1)[0],e=d[0],f=null;0<=Fa.indexOf(e)&&(f=function(){return function(){Da(a)}}());var g=Ga[e];if(g)if(!f)b=g(d[1],d[2],d[3]);else{if(!g(f,d[1],d[2],d[3]))break}else{var b=!1,h=z(d[1],a);if(h)if(g=Ha[e])b=g(h,d[2],d[3]);else{var k=h.exports;if(k&&(g=k[e]))if(b=!0,!f)b=g.call(h,d[2],d[3]);else if(!g.call(h,f,d[2],d[3]))break}}if(!b){x("Script error: "+e+(g?" failed":" unrecognized"));break}}c&&!c.length&&delete Ea[a];return b}l=u.prototype;
|
||||
l.toString=function(){return this.name?this.name:this.id||this.type};
|
||||
l.da=function(a,b,c){switch(b){case "clear":return this.F[b]||(this.F[b]=c,c.onclick=function(a){return function(){a.F.print&&(a.F.print.value="")}}(this)),!0;case "print":return this.F[b]||(this.ja=this.F[b]=c,c.value="",this.f=function(a){return function(b,c){8192<a.value.length&&(a.value=a.value.substr(a.value.length-4096));a.value+=(void 0!==c?c+": ":"")+(b||"")+"\n";a.scrollTop=a.scrollHeight}}(c),this.Y=function(a){this.f(a,this.Ja)}),!0;default:return!1}};l.log=function(){};l.f=function(){};
|
||||
l.status=function(a){this.f(this.Ja+": "+a)};l.Y=function(a,b,c){c=c||this.type;b||x((c?c+": ":"")+a)};function Ia(a,b){a.i.error=!0;a.Y(b)}function Ja(a){return a.i.error?(a.f(a.toString()+" error"),!0):!1}function Ka(a,b){b&&(a.i.ready?b():a.Na=b);return a.i.ready}function F(a){if(!a.i.error&&(a.i.ready=!0,a.i.ready)){var b=a.Na;a.Na=null;b&&b()}}function La(a,b){a.i.Ea&&(b?a.i.Ua=!0:void 0===b&&a.f(a.toString()+" busy"));return a.i.Ea}
|
||||
function Ma(a,b){if(a.i.Ua)return a.i.Ea=!1,a.i.Ua=!1;if(a.i.error)return a.f(a.toString()+" error"),!1;a.i.Ea=b;return a.i.Ea}l.la=function(){return this.i.S=!0};l.ca=function(a,b){b&&(this.i.S=!1);return!0};function Na(a,b){if(a.A){a===a.A?b|=0:b=b||a.V;var c=a.A.V&b;return!!b&&c===b||!!(c&a.A.fb)}return!1}function G(a,b,c){a.A&&(!0===c||Na(a,c|0))&&a.A.message(b,void 0)}
|
||||
window&&(window.PCjs||(window.PCjs={}),window.PCjs.Machines||(window.PCjs.Machines={}),window.PCjs.Components||(window.PCjs.Components=[]),window.PCjs.Commands||(window.PCjs.Commands={}));
|
||||
var ua=window?window.PCjs.Machines:{},v=window?window.PCjs.Components:[],Ea=window?window.PCjs.Commands:{},Fa=["hold","sleep","wait"],Ga={alert:function(a){x(a);return!0},sleep:function(a,b){setTimeout(a,+b);return!1}},Ha={select:function(a,b,c){var d=!1;if(a=a.bindings[b])for(b=0;b<a.options.length;b++)if(a.options[b].textContent==c){a.selectedIndex!=b&&(a.selectedIndex=b);d=!0;break}return d}};
|
||||
Array.prototype.indexOf||(Array.prototype.indexOf=function(a,b){b=b||0;for(var c=this.length;b<c;b++)if(this[b]===a)return b;return-1});Array.isArray||(Array.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)});
|
||||
Function.prototype.bind||(Function.prototype.bind=function(a){function b(){return e.apply(this instanceof c&&a?this:a,d.concat(Array.prototype.slice.call(arguments)))}function c(){}if("function"!=typeof this)throw new TypeError("Function.prototype.bind: non-callable object");var d=Array.prototype.slice.call(arguments,1),e=this;c.prototype=this.prototype;b.prototype=new c;return b});
|
||||
function Oa(a,b){var c;if(a){b||(b=10);var d=a.charAt(0),e=0<a.indexOf(",");e&&(a=a.replace(/,/g,""));"#"==d?(b=8,d=null):"$"==d&&(b=16,d=null);null==d?a=a.substr(1):("0"==d&&(d=a.charAt(1),"b"==d&&e&&(b=2,d=null),"o"==d?(b=8,d=null):"x"==d&&(b=16,d=null)),null==d?a=a.substr(2):(d=a.charAt(a.length-1).toLowerCase(),"y"==d?(b=2,d=null):"."==d?(b=10,d=null):"h"==d&&(b=16,d=null),null==d&&(a=a.substr(0,a.length-1))));var f,d=a;((e=b)&&10!=e?16==e?d.match(/^[0-9a-f]+$/i):8==e?d.match(/^[0-7]+$/):2==e&&
|
||||
d.match(/^[01]+$/):d.match(/^[0-9]+$/))&&!isNaN(f=parseInt(a,b))&&(c=f|0)}return c}function Pa(a,b,c){var d="";b?11<b&&(b=11):b=a&-16777216?11:a&-65536?8:6;if(null==a||isNaN(a))for(;0<b--;)d="?"+d;else for(;0<b--;)d=String.fromCharCode((a&7)+48)+d,a>>=3;return(c?"0o":"")+d}function H(a,b,c){var d="";b?8<b&&(b=8):b=a&-65536?8:4;if(null==a||isNaN(a))for(;0<b--;)d="?"+d;else for(;0<b--;){var e=a&15,e=e+(0<=e&&9>=e?48:55),d=String.fromCharCode(e)+d;a>>=4}return(c?"0x":"")+d}
|
||||
function I(a){return H(a,4,!0)}function Qa(a){var b=a,c=a.lastIndexOf("/");0<=c&&(b=a.substr(c+1));c=b.indexOf("&");0<c&&(b=b.substr(0,c));return b}function Ra(a){var b="",c=a.lastIndexOf(".");0<=c&&(b=a.substr(c+1).toLowerCase());return b}function Sa(){var a=Ta();return-1!==a.indexOf("pcjs.org",a.length-8)}function Va(a){return a.replace(/[&<>"']/g,function(a){return gb[a]})}function hb(a,b){return(a+" ").slice(0,b)}
|
||||
function ib(a){return String.prototype.trim?a.trim():a.replace(/^\s+|\s+$/g,"")}var gb={"&":"&","<":"<",">":">",'"':""","'":"'"};function jb(a,b,c){var d=0,e=a.length,f=0;for(c||(c=function(a,b){return a>b?1:a<b?-1:0});d<e;){var g=d+e>>1,h;h=c(b,a[g]);0<h?d=g+1:(e=g,f=!h)}return f?d:~d}function kb(){function a(a){return(10>a?"0":"")+a}var b=new Date;return b.getFullYear()+"-"+a(b.getMonth()+1)+"-"+a(b.getDate())+" "+a(b.getHours())+":"+a(b.getMinutes())+":"+a(b.getSeconds())}
|
||||
function J(a,b,c,d){c=void 0===c?!1:c;var e=0,f=null,g=null;if("object"==typeof resources&&(f=resources[a]))return d&&d(a,f,e),[f,e];if(c&&"function"==typeof resources)return resources(a,function(b,c){d&&d(a,b,c)}),g;var h=window.XMLHttpRequest?new window.XMLHttpRequest:new window.ActiveXObject("Microsoft.XMLHTTP");c&&(h.onreadystatechange=function(){4===h.readyState&&(f=h.responseText,200==h.status||!h.status&&f.length&&"file:"==(window?window.location.protocol:"file:")||(e=h.status||-1),d&&d(a,
|
||||
f,e))});if(b&&"object"==typeof b){var k="",m;for(m in b)b.hasOwnProperty(m)&&(k&&(k+="&"),k+=m+"="+encodeURIComponent(b[m]));k=k.replace(/%20/g,"+");h.open("POST",a,c);h.setRequestHeader("Content-type","application/x-www-form-urlencoded");h.send(k)}else h.open("GET",a,c),"bytes"==b&&h.overrideMimeType("text/plain; charset=x-user-defined"),h.send();c||(f=h.responseText,200!=h.status&&(e=h.status||-1),d&&d(a,f,e),g=[f,e]);return g}
|
||||
function lb(a,b){var c,d={W:null,R:null,ia:null,ha:null};if("["==b.charAt(0)||"{"==b.charAt(0))try{var e,f,g;if("<"==b.substr(0,1))throw Error(b);g=0>b.indexOf("0x")&&'["'!=b.substr(0,2)?JSON.parse(b.replace(/([a-z]+):/gm,'"$1":').replace(/\/\/[^\n]*/gm,"")):eval("("+b+")");d.ia=g.load;d.ha=g.exec;if(e=g.bytes)d.W=e;else if(e=g.words)for(d.W=Array(2*e.length),f=c=0;c<e.length;c++)d.W[f++]=e[c]&255,d.W[f++]=e[c]>>8&255;else if(e=g.data)for(d.W=Array(4*e.length),f=c=0;c<e.length;c++)d.W[f++]=e[c]&255,
|
||||
d.W[f++]=e[c]>>8&255,d.W[f++]=e[c]>>16&255,d.W[f++]=e[c]>>24&255;else d.W=g;d.R=g.symbols;d.W.length?1==d.W.length&&(x(d.W[0]),d=null):(x("Empty resource: "+a),d=null)}catch(h){x("Resource data error ("+a+"): "+h.message),d=null}else{e=[];b=b.replace(/\n/gm," ").replace(/ +$/,"").split(" ");for(c=0;c<b.length;c++){f=parseInt(b[c],16);if(isNaN(f)){x("Resource data error ("+a+"): invalid hex byte ("+b[c]+")");break}e.push(f&255)}c==b.length&&(d.W=e)}return d}
|
||||
function Ta(){return"http://"+(window?window.location.host:"www.pcjs.org")}function mb(){if(null==nb){var a=!1;if(window)try{window.localStorage.setItem("PCjs.localStorage","PCjs.localStorage"),a="PCjs.localStorage"==window.localStorage.getItem("PCjs.localStorage"),window.localStorage.removeItem("PCjs.localStorage")}catch(b){a=!1}nb=a}return nb}function ob(a){var b;if(window)try{b=window.localStorage.getItem(a)}catch(c){}return b}
|
||||
function pb(a,b){try{return window.localStorage.setItem(a,b),!0}catch(c){}return!1}function qb(a){if(window){var b=window?window.navigator.userAgent:"";return"iOS"==a&&!!b.match(/(iPod|iPhone|iPad)/)&&!!b.match(/AppleWebKit/)||"MSIE"==a&&!!b.match(/(MSIE|Trident)/)||0<=b.indexOf(a)}return!1}
|
||||
function rb(a){if(!sb){var b,c={};if(window){b||(b=window.location.search.substr(1));for(var d,e=/\+/g,f=/([^&=]+)=?([^&]*)/g;d=f.exec(b);)c[decodeURIComponent(d[1].replace(e," "))]=decodeURIComponent(d[2].replace(e," "))}sb=c}return sb[a]}function tb(a,b,c){function d(){--a;0<=a&&(b()||(a=0));0<a?setTimeout(d,0):c()}d()}
|
||||
function ub(a,b){function c(){b(100===d)&&(e=setTimeout(c,d),d=100)}var d=0,e=null,f=!1;a.onmousedown=function(){f||e||(d=500,c())};a.ontouchstart=function(){e||(d=500,c())};a.onmouseup=a.onmouseout=function(){e&&(clearTimeout(e),e=null)};a.ontouchend=a.ontouchcancel=function(){e&&(clearTimeout(e),e=null);f=!0}}function vb(a,b){if(window){var c=window[a];window[a]="function"!==typeof c?b:function(){c&&c();b()}}}function L(a){wb.init.push(a)}
|
||||
function xb(a){if(yb)try{for(var b=0;b<a.length;b++)a[b]()}catch(c){x(""+("An unexpected exception occurred:\n\n"+c.message+"\n\nPlease send this information to support@pcjs.org. Thanks."))}}function zb(a){!yb&&a?(yb=!0,Ab&&Bb("init"),Gb&&Bb("show")):yb=a}function Bb(a){wb[a]&&xb(wb[a])}var sb=null,wb={init:[],show:[],exit:[]},Ab=!1,Gb=!1,yb=!0,nb=null;vb("onload",function(){Ab=!0;xb(wb.init)});vb("onpageshow",function(){Gb=!0;xb(wb.show)});vb(qb("Opera")||qb("iOS")?"onunload":"onbeforeunload",function(){xb(wb.exit)});
|
||||
var C="pdp10",Hb="PDPjs",Ib=Math.pow(2,18),Jb=Math.pow(2,36),C="pdp10",Hb="PDPjs",M={cpu:1,trap:2,fault:4,"int":8,bus:16,memory:32,mmu:64,rom:128,device:256,panel:512,keyboard:1024,key:2048,paper:4096,read:16384,write:32768,serial:1048576,timer:2097152,speaker:16777216,computer:33554432,log:268435456,warn:536870912,buffer:1073741824,halt:-2147483648};
|
||||
function Kb(a,b){u.call(this,"Panel",a,512);this.I=this.K=0;this.U=b;this.j=this.H=this.v=this.w=0;this.L=this.M=-1;this.N=this.O=this.G=!1;this.P=Lb;this.D={};this.g={START:[1,1,!0,!1,this.Eb],STEP:[1,1,!1,!1,this.Fb],ENABLE:[1,1,!1,!1,this.zb],CONT:[1,1,!0,!1,this.xb],DEP:[0,0,!0,!1,this.yb],EXAM:[1,1,!0,!1,this.Ab],LOAD:[1,1,!0,!1,this.Cb],TEST:[0,0,!0,!1,this.Bb]};for(a=0;22>a;a++)this.g["S"+a]=[0,0,!1,!1,this.Db,a];this.A=this.b=this.B=this.C=null;this.exports={hold:this.vb,toggle:this.Qb,reset:this.Mb,
|
||||
set:this.Pb};F(this)}q(Kb,u);l=Kb.prototype;l.reset=function(a){this.stop();a&&Mb(this,this.w=0)};
|
||||
l.da=function(a,b,c,d){if(this.C&&this.C.da(a,b,c,d)||this.b&&this.b.da(a,b,c,d)||this.A&&this.A.da(a,b,c,d))return!0;switch(b){case "R0":case "R1":case "R2":case "R3":case "R4":case "R5":case "R6":case "R7":case "NF":case "ZF":case "VF":case "CF":case "PS":return this.F[b]=c,this.K++,!0;default:return"led"==a||"rled"==a?(this.F[b]=c,this.D[b]=d?1:0,this.K++,!0):"switch"==a?(void 0===this.g[b]&&(this.g[b]=[d?1:0,d?1:0]),this.F[b]=c,a=c.parentElement||c,a=a.parentElement||a,a.onmousedown=function(a,
|
||||
b){return function(){Nb(a,b)}}(this,b),a.onmouseup=a.onmouseout=function(a,b){return function(){Ob(a,b)}}(this,b),a.ontouchstart=function(a,b){return function(c){Nb(a,b);c.preventDefault()}}(this,b),a.ontouchend=function(a,b){return function(){Ob(a,b)}}(this,b),!0):u.prototype.da.call(this,a,b,c,d)}};l.ta=function(a,b,c,d){this.C=a;this.B=b;this.b=c;this.A=d;Pb(this);Qb(this)};l.la=function(a,b){if(!b)if(this.U&&Rb(),!a)this.reset(!0);else if(!this.restore(a))return!1;return!0};
|
||||
l.ca=function(a){return a?this.save():!0};l.save=function(){var a=new N(this);a.set(0,[this.j,this.w,this.v]);return a.data()};l.restore=function(a){if(a=a[0])Sb(this,this.j=a[0]),Mb(this,this.w=a[1]),Tb(this,a[2]);return!0};l.Mb=function(){for(var a in this.g){var b=this.g[a];b[1]=b[0]}Qb(this);return!0};function Ub(a,b,c){if(a=a.F[b])a.style.backgroundColor=c?"#ff0000":"#000000"}function Pb(a,b){for(var c in a.D)Ub(a,c,null!=b?b:a.D[c])}
|
||||
function Vb(a,b,c){if(a=a.F[b])a.style.marginTop=c?"0px":"20px",a.style.backgroundColor=c?"#00ff00":"#228B22"}function Qb(a){for(var b in a.g)Vb(a,b,a.g[b][1])}l.vb=function(a,b,c){if(Nb(this,b)){if(c){var d=this;setTimeout(function(){Ob(d,b);a&&a()},+c);return!1}Ob(this,b)}return!0};l.Pb=function(a,b){if("SR"==a)return Tb(this,Oa(b,8));var c=this.g[a];return c?(c[1]=+b?1:0,Vb(this,a,c[1]),!0):!1};l.Qb=function(a){return Nb(this,a)?(Ob(this,a),!0):!1};
|
||||
function Nb(a,b){var c=a.g[b];return c?(Vb(a,b,c[1]=1-c[1]),c[3]=!0,c[4]&&c[4].call(a,c[1],c[5]),b!=Wb&&(a.N=b==Xb,a.O=b==Yb),!0):!1}function Ob(a,b){var c=a.g[b];c&&(c[2]&&c[3]&&(Vb(a,b,c[1]=c[0]),c[4]&&c[4].call(a,c[1],c[5])),c[3]=!1)}l.Eb=function(a){a||this.b.i.J||(this.b.g=this.j%Ib,this.g[Zb]&&this.g[Zb][1]&&$b(this.b))};l.Fb=function(){};l.zb=function(a){a||this.b.T()};
|
||||
l.xb=function(a){if(!a&&!this.b.i.J)if(this.g[Zb]&&this.g[Zb][1])$b(this.b);else{if((a=this.A)&&!La(a,!0))Ma(a,!0),ac(a,0,null),Ma(a,!1);else try{var b=this.b.Ia(1);0<b&&(bc(this.b,b),cc(this.b,b,!0),dc(this.b,b))}catch(c){"number"!=typeof c&&Ia(this.b,c.stack||c.message)}this.stop();this.C&&P(this.C)}};l.yb=function(a){a&&!this.b.i.J&&(this.N&&ec(this),a=Mb(this,this.w=this.v),this.P==Lb?fc(this.B,this.j,a):this.b.Ca(this.j,a))};
|
||||
l.Ab=function(a){if(!a&&!this.b.i.J){var b;this.O&&ec(this);this.P==Lb?b=gc(this.B,this.j):b=this.b.pa(this.j);Mb(this,this.w=b)}};l.Cb=function(a){a||this.b.i.J||Sb(this,this.v)};l.Bb=function(a){a?(this.G=!0,Pb(this,!0)):(this.G=!1,Pb(this),Tb(this,0))};l.Db=function(a,b){this.v=a?this.v|1<<b:this.v&~(1<<b)};function ec(a){var b=1,c=a.B.v;a.g[Wb]&&a.g[Wb][1]||(b=-b);Sb(a,a.j&~c|a.j+b&c)}
|
||||
function Sb(a,b){a.j=b&a.B.v;if(a.L!==a.j){a.L=a.j;b=a.L;for(var c=0;22>c;c++){var d=a,e="A"+c,f=b&1<<c;d.D[e]=f;d.G||Ub(d,e,f)}}}function Mb(a,b){a.H=b%Jb;if(a.M!==a.H){a.M=a.H;b=a.M;for(var c=0;16>c;c++){var d=a,e="D"+c,f=b&1<<c;d.D[e]=f;d.G||Ub(d,e,f)}}return a.H}function Tb(a,b){a.v=b|0;for(b=0;22>b;b++)a.g["S"+b][1]=a.v&1<<b?1:0;Qb(a);return!0}l.stop=function(){Sb(this,this.b.g)};
|
||||
function Rb(){for(var a=D(document,C,"panel"),b=0;b<a.length;b++){var c=a[b],d=A(c),e=Ca(d.id);e||(e=new Kb(d,!0));B(e,c)}}var Lb=7,Xb="DEP",Zb="ENABLE",Yb="EXAM",Wb="STEP";L(Rb);function hc(a,b,c){u.call(this,"Bus",a,16);this.b=b;this.A=c;this.G=+a.busWidth||18;this.D=1<<this.G;this.v=this.D-1;this.j=Math.log2(16384);this.w=this.D/16384|0;this.g=[];a=new V;ic(a,this.A);this.g=Array(this.w);for(b=0;b<this.w;b++)this.g[b]=a;F(this)}q(hc,u);l=hc.prototype;l.reset=function(){};
|
||||
l.la=function(a,b){if(!b)if(!a)this.reset();else if(!this.restore(a))return!1;return!0};l.ca=function(a){return a?this.save():!0};l.save=function(){var a=new N(this);a.set(0,jc(this));return a.data()};l.restore=function(a){a:{a=a[0];var b;for(b=0;b<a.length-1;b+=2){var c=a[b],d=a[b+1];if(d&&4096>d.length){for(var e=0,f=Array(4096),g=0;g<d.length-1;)for(var h=d[g++],k=d[g++];h--;)f[e++]=k;d=f}e=this.g[c];if(!e||!e.restore(d)){x("Unable to restore memory block "+c);a=!1;break a}}a=!0}return a};
|
||||
function kc(a,b,c,d){for(var e=b,f=c,g=e>>>a.j;0<f&&g<a.g.length;){var h=a.g[g],k=16384*g,m=16384-(e-k);m>f&&(m=f);if(h&&h.size){if(h.type==d){if(e+f<=h.u)return h.Ga+=h.u-e,h.u=e,!0;if(e>=h.u+h.Ga){m=h.size-(e-k);m>f&&(m=f);h.Ga=e-h.u+m;e=k+16384;f-=m;g++;continue}}return lc(mc,e,f)}e=new V(0,e,m,16384,d);ic(e,a.A,h);a.g[g++]=e;e=k+16384;f-=m}return 0>=f?(a.status("Added "+(c>>10)+"Kb "+nc[d]+" at "+Pa(b)),!0):lc(oc,b,c)}function gc(a,b){return a.g[(b&a.v)>>>a.j].Oa(b&16383,b)}
|
||||
function fc(a,b,c){a.g[(b&a.v)>>>a.j].Ha(b&16383,c&65535,b)}function jc(a){for(var b=0,c=[],d=0;d<a.w;d++){var e=a.g[d];if(e.Ma||e.tb){c[b++]=d;var f=b++;if(e=e.save()){for(var g=0,h=0,k=[];g<e.length;){for(var m=e[g],p=g+1;p<e.length&&e[p]===m;)p++;k[h++]=p-g;k[h++]=m;g=p}k.length<e.length&&(e=k)}c[f]=e}}return c}function lc(a,b,c){x("Memory block error ("+a+": "+H(b)+","+H(c)+")");return!1}var mc=1,oc=2;function Fc(a){u.call(this,"Device",a,256)}q(Fc,u);l=Fc.prototype;
|
||||
l.ta=function(a,b,c,d){this.B=b;this.C=a;this.b=c;this.A=d;F(this)};l.la=function(a,b){if(!b)if(!a)this.reset();else if(!this.restore(a))return!1;return!0};l.ca=function(a){return a?this.save():!0};l.reset=function(){};l.save=function(){return(new N(this)).data()};l.restore=function(){return!0};L(function(){for(var a=D(document,C,"device"),b=0;b<a.length;b++){var c,d=a[b];c=A(d);switch(c.type){case "default":c=new Fc(c),B(c,d)}}});
|
||||
function V(a,b,c,d,e){this.id=Gc+=2;this.u=b;this.Ga=c;this.size=d||0;this.type=e||Hc;this.F=e==Ic;this.b=null;this.g=this.mb;this.pa=this.Oa=this.Wa;this.j=this.Sa;this.Ca=this.Ha=this.Ta;this.B=this.C=0;ic(this);this.Ma=this.tb=!1;if(this.size){a=this.A=Array(this.size);for(b=0;b<a.length;b++)a[b]=0;Jc(this,Kc)}else Jc(this)}l=V.prototype;l.save=function(){return this.A};l.restore=function(a){return a&&this.size==a.length?(this.A=a,this.Ma=!0):!1};
|
||||
function Jc(a,b){b||(b=Lc);Mc(a,b,void 0);Nc(a,b,void 0)}function Mc(a,b,c){c&&a.B||(a.pa=b[2]||a.Wa);if(c||void 0===c)a.g=b[0]||a.mb,a.Oa=b[2]||a.Wa}function Nc(a,b,c){c&&a.C||(a.Ca=!a.F&&b[3]||a.Ta);if(c||void 0===c)a.j=b[1]||a.Sa,a.Ha=b[3]||a.Ta}l.ua=function(a,b){b?this.C++||Nc(this,Oc,!1):this.B++||Mc(this,Oc,!1)};function Pc(a,b){b?--a.C||(a.Ca=a.F?a.Ta:a.Ha):--a.B||(a.pa=a.Oa)}function ic(a,b,c){a.b=b;a.B=a.C=0;c&&((a.B=c.B)&&Mc(a,Oc,!1),(a.C=c.C)&&Nc(a,Oc,!1))}
|
||||
l.mb=function(a,b){this.b&&Na(this.b,32)&&G(this.b,"attempt to read invalid address "+W(this.b,b),!0);return 0};l.Sa=function(a,b,c){this.b&&Na(this.b,32)&&G(this.b,"attempt to write "+W(this.b,a)+" to invalid addresses "+W(this.b,c),!0)};l.Wa=function(a,b){return this.pa(a,b)};l.Ta=function(a,b,c){this.Ca(a,b,c)};l.Hb=function(a,b,c){c=this.A[c];return c=32>=a+b?c>>a&(1<<b)-1:Math.trunc(c/Math.pow(2,a))%Math.pow(2,b)};l.Kb=function(a){return this.A[a]};
|
||||
l.Tb=function(a,b,c,d){var e=this.A[d];if(32>=a+b)var f=(1<<b)-1<<a,e=e&~f|c<<a&f;else f=Math.pow(2,a),c%=Math.pow(2,b),a=e%Math.pow(2,a+b),e=e-a+c*f+a%f;this.A[d]!=e&&(this.A[d]=e,this.Ma=!0)};l.Vb=function(a,b){this.A[b]=a;this.Ma=!0};l.Gb=function(a,b,c,d){if(this.b&&null!=this.u){var e=this.b;Qc(e,this.u+c,1,e.K)&&e.T(!1)}return this.g(a,b,c,d)};l.Ib=function(a,b){if(this.b&&null!=this.u){var c=this.b;Qc(c,this.u+a,2,c.K)&&c.T(!1)}return this.Oa(a,b)};
|
||||
l.Sb=function(a,b,c,d,e){if(this.b&&null!=this.u){var f=this.b;Qc(f,this.u+d,1,f.G)&&f.T(!1)}this.F?this.Sa(c,0,e):this.j(a,b,c,d,e)};l.Ub=function(a,b,c){if(this.b&&null!=this.u){var d=this.b;Qc(d,this.u+b,2,d.G)&&d.T(!1)}this.F?this.Sa(a,0,c):this.Ha(a,b,c)};var Hc=0,Ic=2,nc=["NONE","RAM","ROM"],Gc=0,Lc=[],Kc=[V.prototype.Hb,V.prototype.Tb,V.prototype.Kb,V.prototype.Vb],Oc=[V.prototype.Gb,V.prototype.Sb,V.prototype.Ib,V.prototype.Ub];
|
||||
function Rc(a,b){u.call(this,"CPU",a,1);b=+a.cycles||b;var c=+a.multiplier||1;this.wa=0;this.za=b;this.N=c;this.xa=Math.round(this.za/1E4)/100;this.M=this.xa*this.N;this.ya=this.oa=this.O=this.ra=0;this.i.J=this.i.Ra=!1;this.i.X=a.autoStart;"string"==typeof this.i.X&&(this.i.X="true"==this.i.X);this.i.Ba=!1;this.ma=this.U=0;this.na=+a.csStart;this.P=+a.csInterval;this.Z=+a.csStop;this.D=[];this.bb=this.Nb.bind(this);this.K=this.w=this.H=this.I=this.v=this.ga=this.G=this.aa=this.Aa=this.L=0;this.qa=
|
||||
null;F(this)}q(Rc,u);l=Rc.prototype;l.ta=function(a,b,c,d){this.C=a;this.B=b;this.A=d;this.qa=a.v;for(a=0;a<Sc.length;a++)(b=this.F[Sc[a]])&&this.C.da(null,Sc[a],b);F(this)};l.reset=function(){};l.save=function(){return null};l.restore=function(){return!1};
|
||||
l.la=function(a,b){var c=Tc(this.C,"autoStart");null!=c?this.i.X="true"==c?!0:"false"==c?!1:!!c:null==this.i.X&&(this.i.X=!this.A&&void 0===this.F.run);if(!b){if(a){Uc(this);if(!this.restore(a))return!1;Vc(this)}else this.reset();this.A?(a=this.A,b=this.i.X,a.xa=!0,a.f("Type ? for help with PDPjs Debugger commands"),Wc(a),b||Xc(a),a.sa&&(b=a.sa,a.sa=null,Yc(a,b))):this.status("No debugger detected");this.i.X||this.f("CPU will not be auto-started "+(this.qa?"(click Run to start)":"(type 'go' to start)"))}return!0};
|
||||
l.ca=function(a){return a?this.save():!0};l.X=function(){return this.i.J?!0:this.i.X?($b(this),!0):!1};l.ib=function(){return 0};function Vc(a){void 0===a.na&&(a.na=0);void 0===a.P&&(a.P=-1);void 0===a.Z&&(a.Z=-1);a.i.Ba=0<=a.na&&0<a.P;a.i.Ba&&(a.ma=0,a.U=a.na-a.K)}function dc(a,b){if(a.i.Ba){var c=!1;a.ma=a.ma+a.ib()|0;a.U-=b;0>=a.U&&(a.U+=a.P,c=!0);0<=a.Z&&a.Z<=Zc(a)&&(a.P=a.Z=-1,Vc(a),a.T(),c=!0);c&&a.f(Zc(a)+" cycles: checksum="+H(a.ma))}}
|
||||
l.da=function(a,b,c){var d=this;switch(b){case "power":case "reset":return this.F[b]=c,!0;case "run":return this.F[b]=c,c.onclick=function(){var a;if(a=d.C)if(a=d.C,a.i.S)a=!0;else{var b=null,c,h=y(a.id);for(c=0;c<h.length&&(b=h[c],b===a||b.i.ready);c++);if(c==h.length)for(c=0;c<h.length&&(b=h[c],b===a||b.i.S);c++);c==h.length&&(b=a);x("The "+b.type+" component ("+b.id+") is not "+(b.i.ready?"powered yet":"ready yet"+(b.Na?" (waiting for notification)":""))+".");a=!1}a&&(d.i.J?d.T():$b(d))},!0;case "speed":return this.F[b]=
|
||||
c,!0;case "setSpeed":return this.F[b]=c,c.onclick=function(){$c(d,d.N<<1,!0)},c.textContent=this.M.toFixed(2)+"Mhz",!0}return!1};function cc(a,b,c){a.K+=b;c&&(a.H=a.I=0)}function ad(a,b){var c=1;b&&1<a.N&&a.L&&(c=a.L/a.xa);a.ya=Math.round(1E3/bd);a.oa=Math.floor(a.za/bd*c);b||(a.O=a.oa);a.ra=0}function Zc(a){return a.K+a.w+a.H-a.I}function Uc(a){a.L=0;a.Aa=0;a.K=a.w=a.H=a.I=0;Vc(a);$c(a,1)}
|
||||
function $c(a,b,c){var d=!1;if(void 0!==b){.8>a.L/a.M?b=1:d=!0;a.N=b;b=a.xa*a.N;if(a.M!=b){a.M=b;b=a.M.toFixed(2)+"Mhz";var e=a.F.setSpeed;e&&(e.textContent=b);a.f("target speed: "+b)}c&&a.C&&cd(a.C)}cc(a,a.w);a.w=0;a.v=va();a.G=0;ad(a);return d}function dd(a,b){for(var c=a.D.length-1;0<=c;c--){var d=a.D[c];0>d[0]||b>d[0]&&(b=d[0])}return b}function ed(a){for(var b=[],c=0;c<a.D.length;c++)b.push(a.D[c][0]);return b}
|
||||
function bc(a,b){for(var c=a.D.length-1;0<=c;c--){var d=a.D[c];0>d[0]||(d[0]-=b,0>=d[0]&&(d[0]=-1,d[1]()))}}function fd(a,b){var c=a.H-=a.I;a.I=0;b&&(a.H=0);return c}
|
||||
l.Nb=function(){if(this.i.J){this.ra>=this.za&&ad(this,!0);this.aa=0;this.ga=va();if(this.G){var a=this.ga-this.G;a>this.ya&&(this.v+=a,this.v>this.ga&&(this.v=this.ga))}try{do{var b=dd(this,this.i.Ba?1:this.oa);try{this.Ia(b)}catch(e){if("number"!=typeof e)throw e;}b=fd(this,!0);this.aa+=b;this.w+=b;dc(this,b);bc(this,b);this.O-=b;if(0>=this.O){this.O+=this.oa;++this.Aa>=gd&&(this.C&&P(this.C,void 0),this.Aa=0);break}}while(this.i.J)}catch(e){this.T();this.C&&this.C.stop(va(),Zc(this));Ia(this,e.stack||
|
||||
e.message);return}if(this.i.J){a=setTimeout;b=this.bb;this.G=va();var c=this.ya;this.aa&&(c=Math.round(c*this.aa/this.oa));var c=c-(this.G-this.ga),d=this.G-this.v;d&&(this.L=Math.round(this.w/(10*d))/100,864E5<=d&&(this.K=0,$c(this)));if(0>c||this.L<this.M)-1E3>c&&(this.v-=c),c=0;this.ra+=this.aa;this.G+=c;a(b,c)}}};
|
||||
function $b(a,b){if(!Ja(a))if(a.i.J)a.f(a.toString()+" busy");else{$c(a);a.i.J=!0;a.i.Ra=!0;var c=a.F.run;c&&(c.textContent="Halt");a.C&&(b&&cd(a.C,!0),a.C.start(a.v,Zc(a)));a.A||a.status("Started");setTimeout(a.bb,0)}}l.Ia=function(){return 0};l.T=function(a){var b=!1;if(this.i.J){fd(this);cc(this,this.w);this.w=0;this.i.J=!1;if(b=this.F.run)b.textContent="Run";this.C&&this.C.stop(va(),Zc(this));b=!0;this.A||this.status("Stopped")}this.i.complete=a;return b};var bd=30,gd=15,Sc=["power","reset"];
|
||||
function hd(a){var b=+a.model||1001;Rc.call(this,a,6666667);this.ab=b;this.La=+a.addrReset||0;this.fa=null;this.Da=[];this.i.complete=!1}q(hd,Rc);l=hd.prototype;l.reset=function(){this.status("Model "+this.ab);this.i.J&&this.T();this.ea=this.g=this.sa=this.La;this.j=0;this.pa=this.Jb;this.Ca=this.Wb;this.fa=null;Uc(this);this.i.error=!1;Rc.prototype.reset.call(this)};l.ib=function(){return 0};
|
||||
l.save=function(){var a=new N(this);a.set(0,[this.g,this.sa,this.ea,this.j]);a.set(1,[]);a.set(2,[this.K,this.N,this.i.X]);a.set(3,id(this));a.set(4,ed(this));return a.data()};
|
||||
l.restore=function(a){var b;b=a[0];ha();ea();ha();var c=b[Symbol.iterator];b=c?c.call(b):oa(b);this.g=b.next().value;this.sa=b.next().value;this.ea=b.next().value;this.j=b.next().value;b=a[2];this.K=b[0];$c(this,b[1]);this.i.X=b[2];b=a[3];for(c=b.length-1;0<=c;c--){var d;a:{for(d=0;d<this.Da.length;d++){var e=this.Da[d];if(e.Rb===b[c]){d=e;break a}}d=null}d&&(d.next=this.fa,this.fa=d)}a=a[4];for(b=0;b<this.D.length&&b<a.length;b++)this.D[b][0]=a[b];return!0};
|
||||
function id(a){var b=[];for(a=a.fa;a;)b.push(a.Rb),a=a.next;return b}l.Jb=function(a){var b=this.B;a=this.ea=a;return b.g[(a&b.v)>>>b.j].pa(a&16383,a)};l.Wb=function(a,b){var c=this.B;a=this.ea=a;c.g[(a&c.v)>>>c.j].Ca(a&16383,b,a)};
|
||||
l.Ia=function(a){this.i.complete=!0;var b=this.A?jd(this.A)?1:this.i.Ra?-1:0:0,c=a?this.i.Ra?0:1:-1;this.i.Ra=!1;this.H=this.I=a;this.j=this.j&-5|(b?4:0);do{if(this.j){if(this.j&4){if(kd(this.A,this.g,c)){this.T();break}++b||(this.j&=-5);c||c++}if(a=this.j&11)this.j&2?this.fa||(this.j&=-3):this.j&1&&this.j++,a=!1;if(a){if(this.j&4&&kd(this.A,this.g,c)){this.T();break}if(0>c)break}}this.j&=15;a=this.g;this.pa(a);this.g=(a+1)%Ib}while(0<this.I);return this.i.complete?this.H-this.I:!1===this.i.complete?
|
||||
-1:0};L(function(){for(var a=D(document,C,"cpu"),b=0;b<a.length;b++){var c=a[b],d=A(c),d=new hd(d);B(d,c)}});
|
||||
function ld(a){u.call(this,"ROM",a,128);this.R=this.g=null;this.D=+a.addr;this.v=+a.size;this.j=a.alias;"string"==typeof this.j&&(this.j=eval(this.j));this.w=a.file;this.G=Qa(this.w);if(this.w){a=this.w;var b=Ra(this.G);"json"!=b&&"hex"!=b&&(a=Ta()+"/api/v1/dump?file="+this.w+"&format=bytes&decimal=true");var c=this;J(a,null,!0,function(a,b,f){f?(c.Y("Unable to load ROM resource (error "+f+": "+a+")"),c.w=null):(ta(c.Ka,a,b),(a=lb(a,b))?(c.g=a.W,c.R=a.R):c.w=null);md(c)})}}q(ld,u);
|
||||
ld.prototype.ta=function(a,b,c,d){this.B=b;this.b=c;this.A=d;md(this)};ld.prototype.la=function(){this.R&&(this.A&&nd(this.A,this.id,this.D,this.v,this.R),delete this.R);return!0};ld.prototype.ca=function(){return!0};
|
||||
function md(a){if(!Ka(a)){if(a.w){if(!a.g||!a.B)return;a.v||(a.v=a.g.length);if(a.g.length!=a.v)Ia(a,"ROM size ("+H(a.g.length,8,!0)+") does not match specified size ("+H(a.v,8,!0)+")");else{var b;b=a.D;if(kc(a.B,b,a.v,Ic)){var c;for(c=0;c<a.g.length;c++)fc(a.B,b+c,a.g[c]);b=!0}else b=!1;if(b){b=[];"number"==typeof a.j?b.push(a.j):null!=a.j&&a.j.length&&(b=a.j);for(c=0;c<b.length;c++){for(var d=a,e=b[c],f=d.B,g=d.v,h=[],k=d.D>>>f.j;0<g&&k<f.g.length;)h.push(f.g[k++]),g-=16384;f=d.B;d=d.v;g=0;for(e>>>=
|
||||
f.j;0<d&&e<f.g.length;){k=h[g++];if(!k)break;f.g[e++]=k;d-=16384}}delete a.g}}}F(a)}}L(function(){for(var a=D(document,C,"rom"),b=0;b<a.length;b++){var c=a[b],d=A(c),d=new ld(d);B(d,c)}});
|
||||
function od(a){u.call(this,"RAM",a);this.R=this.j=null;this.v=+a.addr;this.w=+a.size;this.ia=a.load;this.ha=a.exec;null!=this.ia&&(this.ia=+this.ia);null!=this.ha&&(this.ha=+this.ha);this.D=!1;this.g=a.file;this.G=Qa(this.g);if(this.g){a=this.g;var b=Ra(this.G);"json"!=b&&"hex"!=b&&(a=Ta()+"/api/v1/dump?file="+this.g+"&format=bytes&decimal=true");var c=this;J(a,null,!0,function(a,b,f){f?(c.Y("Unable to load RAM resource (error "+f+": "+a+")"),c.g=null):(ta(c.Ka,a,b),(a=lb(a,b))?(c.j=a.W,c.R=a.R,null==
|
||||
c.ia&&(c.ia=a.ia),null==c.ha&&(c.ha=a.ha)):c.g=null);pd(c)})}}q(od,u);od.prototype.ta=function(a,b,c,d){this.B=b;this.b=c;this.A=d;pd(this)};od.prototype.la=function(a,b){this.R&&(this.A&&nd(this.A,this.id,this.v,this.w,this.R),delete this.R);b||this.reset();return!0};od.prototype.ca=function(){return!0};
|
||||
function pd(a){if(a.B&&(!a.D&&a.w&&(kc(a.B,a.v,a.w,1)?a.D=!0:a.w=0),!Ka(a))){if(!a.D)x("No RAM allocated");else if(a.g){if(!a.j||!a.B)return;qd(a,a.j,a.ia,a.ha,a.v)?a.status('Loaded image "'+a.G+'"'):a.Y('Error loading image "'+a.G+'"')}F(a)}}
|
||||
od.prototype.reset=function(){if(this.D){for(var a=this.B,b=this.v,c=this.w,d=b&16383,b=b>>>a.j;0<c&&b<a.g.length;){var e=a.g[b],f=c,g=0,h,d=d||0,g=g||0;0>g&&g>=sa&&(g+=ra);g=Math.trunc(Math.abs(g))%ra;void 0===f&&(f=e.size);for(h=d;f--&&h<e.size;h++)e.Ha(d,g,e.u+d);c-=16384;b++;d=0}this.j&&qd(this,this.j,this.ia,this.ha,this.v,!this.A)}};
|
||||
function qd(a,b,c,d,e,f){var g=!1,h=!1;if(null==c)for(var k=0;k<b.length-1;){var m=b[k]&255|(b[k+1]&255)<<8;if(m)if(m&255){var p=k;if(1!=m){G(a,"invalid signature ("+I(m)+") at offset "+I(p),4096);break}if(k+6>=b.length){G(a,"invalid block at offset "+I(p),4096);break}for(var k=k+2,n=b[k++]&255|(b[k++]&255)<<8,r=b[k++]&255|(b[k++]&255)<<8,m=m+((n&255)+(n>>8)+(r&255)+(r>>8)),Q=k,wa=n-=6;0<n&&k<b.length;)m+=b[k++]&255,n--;if(n||k>=b.length){G(a,"insufficient data for block at offset "+I(p),4096);break}m+=
|
||||
b[k++]&255;if(m&255){G(a,"invalid checksum ("+H(m,2,!0)+") for block at offset "+I(p),4096);break}if(wa)for(G(a,"loading "+I(wa)+" bytes at "+I(r)+"-"+I(r+wa),4096);wa--;)fc(a.B,r++,b[Q++]);else r&1?g=!0:null==d&&(d=r),null!=d&&G(a,"starting address: "+I(d),4096);h=!0}else k++;else k+=2}if(!h&&(null==c&&(c=e),null!=c)){for(h=0;h<b.length;h++)fc(a.B,c+h,b[h]);h=!0}if(h){if(null==d||g)a.b.T(),f=!1;null!=d&&(a=a.b,a.La=d,a.g=d%Ib,f?a.i.S?a.i.J||$b(a):a.i.X=!0:a.A&&a.i.S?a.T()||a.C.i.reset||(Wc(a.A),
|
||||
P(a.C,-1)):!1===f&&a.T(),!a.i.J&&a.qa&&a.qa.stop())}return h}L(function(){for(var a=D(document,C,"ram"),b=0;b<a.length;b++){var c=a[b],d=A(c),d=new od(d);B(d,c)}});
|
||||
function rd(a){u.call(this,"SerialPort",a,1048576);this.w=a.upperCase;"string"==typeof this.w&&(this.w="true"==this.w);this.v=!0;this.j=[];var b=a.binding;if("console"!=b){var c;a=sd;b&&(void 0===c&&(c="Panel"),(c=z(c,this.id))&&(b=c.F[b])&&this.da(null,a,b))}this.g=this.D=null;this.exports={connect:this.lb,receiveData:this.Pa,receiveStatus:this.Lb,setConnection:this.Ob}}q(rd,u);l=rd.prototype;
|
||||
l.da=function(a,b,c){var d=this;switch(b){case sd:return this.F[b]=c,c.onkeydown=function(a){a=a||window.event;var b=0,c=a.keyCode;8==c?b=a.altKey?t.Ya:t.qb:46==c?b=t.Ya:a.ctrlKey&&c>=t.Xa&&c<=t.sb&&(b=c-(t.Xa-t.nb));b&&(a.preventDefault&&a.preventDefault(),d.Pa(b));return!0},c.onkeypress=function(a){a=a||window.event;if(!a.metaKey){var b=a.which||a.keyCode;a.altKey&&b==t.pb&&(b=t.ob);d.Pa(b);a.preventDefault&&a.preventDefault()}return!0},c.onpaste=function(a){a.stopPropagation&&a.stopPropagation();
|
||||
a.preventDefault&&a.preventDefault();(a=a.clipboardData||window.clipboardData)&&d.Pa(a.getData("Text"))},c.removeAttribute("readonly"),!0}return!1};l.ta=function(a,b,c,d){this.C=a;this.B=b;this.b=c;this.A=d;F(this)};
|
||||
l.lb=function(a){if(!this.g){var b=Tc(this.C,"connection");if(b){var c=b.split("->");if(2==c.length){var d=ib(c[0]);if(d!=this.Ja)return;c=ib(c[1]);if(this.g=Ca(c)){var e=this.g.exports;if(e){var f=e.connect;f&&f.call(this.g,this.v);if(this.D=e.receiveData){this.v=a;this.status("Connected "+this.Ka+"."+d+" to "+c);return}}}}this.status("Unable to establish connection: "+b)}}};l.la=function(a,b){if(!b)if(this.lb(this.v),!a)this.reset();else if(!this.restore(a))return!1;return!0};
|
||||
l.ca=function(a){return a?this.save():!0};l.reset=function(){};l.save=function(){var a=new N(this);a.set(0,[]);return a.data()};l.restore=function(){return!0};l.Pa=function(a){if("number"==typeof a)this.j.push(a);else if("string"==typeof a)for(var b=0,c,d=0;d<a.length;d++){c=b;b=a.charCodeAt(d);if(10==b){if(13==c)continue;b=13}this.j.push(b)}else this.j=this.j.concat(a);return!0};l.Lb=function(){};l.Ob=function(a,b){return this.g?!1:(this.g=a,this.D=b,!0)};var sd="buffer";
|
||||
L(function(){for(var a=D(document,C,"serial"),b=0;b<a.length;b++){var c=a[b],d=A(c),d=new rd(d);B(d,c)}});function td(a){u.call(this,"Debugger",a);this.ba=+a.base||16;this.ma=!1;this.D=0;this.O=!1;this.w=-1;this.v=[];this.Z={}}q(td,u);td.prototype.jb=function(){return-1};td.prototype.kb=function(){};
|
||||
function ud(a,b,c,d){if(c)if(b){0>a.w&&a.v.length&&(a.w=0);if(0>a.w||b!=a.v[a.w])a.v.splice(0,0,b),a.w=0;a.w--}else a.O?b="end":b=a.v[a.w+1];a=[];if(b){b=b.replace(/""/g,"'");c=0;var e=null;d=d||";";for(var f=0;f<=b.length;f++){var g=b.charAt(f);if('"'==g||"'"==g)e?g==e&&(e=null):e=g;else if(g==d&&!e||!g)a.push(ib(b.substring(c,f))),c=f+1}}return a}
|
||||
function vd(a,b,c){for(c=c||-1;c--&&b.length;){var d=b.pop();if(2>a.length)return!1;var e=a.pop(),f=a.pop();switch(d){case "*":d=f*e;break;case "/":if(!e)return!1;d=f/e;break;case "%":if(!e)return!1;d=f%e;break;case "+":d=f+e;break;case "-":d=f-e;break;case "<<":d=f<<e;break;case ">>":d=f>>e;break;case ">>>":d=f>>>e;break;case "<":d=f<e?1:0;break;case "<=":d=f<=e?1:0;break;case ">":d=f>e?1:0;break;case ">=":d=f>=e?1:0;break;case "==":d=f==e?1:0;break;case "!=":d=f!=e?1:0;break;case "&":d=f&e;break;
|
||||
case "^":d=f^e;break;case "|":d=f|e;break;case "&&":d=f&&e?1:0;break;case "||":d=f||e?1:0;break;default:return!1}a.push(d|0)}return!0}
|
||||
function wd(a,b,c){var d;if(b){b=xd(a,b);for(var e=0,f=!1,g=b,h=[],k=[],m=b.split(/(\|\||&&|\||^|&|!=|==|>=|>>>|>>|>|<=|<<|<|-|\+|%|\/|\*)/);e<m.length;){var p=m[e++],n=p.length,p=ib(p);if(!p){f=!0;break}p=yd(a,p,null,!1===c);if(void 0===p){f=!0;c=!1;break}h.push(p);if(e==m.length)break;var p=m[e++],r=p.length;k.length&&zd[p]<zd[k[k.length-1]]&&vd(h,k,1);k.push(p);b=b.substr(n+r)}vd(h,k)&&1==h.length||(f=!0);f?c&&a.f("error parsing '"+g+"' at character "+(g.length-b.length)):(d=h.pop(),c&&Ad(a,null,
|
||||
d))}return d}function xd(a,b){for(var c,d=a.ma?"(":"{",e=a.ma?")":"}",f=new RegExp(a.ma?"\\((.*?)\\)":"\\{(.*?)\\}");(c=b.match(f))&&!(0<=c[1].indexOf(d));){var g=wd(a,c[1]);b=b.replace(d+c[1]+e,null!=g?W(a,g):"undefined")}for(;(c=b.match(/\[(.*?)]/))&&!(0<=c[1].indexOf("["));)b=b.replace("["+c[1]+"]","unimplemented");for(a=b;b=a.match(/\$([a-z]+)/i);){c=null;switch(b[1].toLowerCase()){case "ops":c=0}if(null==c)break;a=a.replace(b[0],c.toString())}return a}
|
||||
function yd(a,b,c,d){var e;null!=b?(e=a.jb(b),0<=e?e=a.kb(e):(e=a.Z[b],null==e&&(e=Oa(b,a.ba))),null!=e||d||a.f("invalid "+(c?c:"value")+": "+b)):d||a.f("missing "+(c||"value"));return e}
|
||||
function Ad(a,b,c){var d,e=!1;if(void 0!==c){e=!0;d=c;var f=4,g="";if(!f||4<f)f=4;for(var h=0;h<f;h++){g&&(g=","+g);var k=d&255,m=void 0,p=8,n="";p?32<p&&(p=32):p=32;for(var r=null==k||isNaN(k),Q=m=m||p;0<p--;)Q||(n=","+n,Q=m),n=(r?"?":k&1?"1":"0")+n,k>>=1,Q--;g=n+g;d>>=8}d=H(c,0,!0)+" "+c+". "+Pa(c,0,!0)+" "+("0b"+g);32<=c&&127>c&&(d+=" '"+String.fromCharCode(c)+"'")}a.f((null!=b?b+": ":"")+d);return e}
|
||||
function Bd(a,b){if(b)return Ad(a,b,a.Z[b]);var c=0;for(b in a.Z)Ad(a,b,a.Z[b]),c++;return 0<c}function W(a,b,c){c=void 0===c?0:c;switch(a.ba){case 8:a=Pa(b,3*c);break;case 10:a=b.toString();break;default:a=H(b,2*c)}return a}var zd={"||":0,"&&":1,"|":2,"^":3,"&":4,"!=":5,"==":5,">=":6,">":6,"<=":6,"<":6,">>>":7,">>":7,"<<":7,"-":8,"+":8,"%":9,"/":9,"*":9};
|
||||
function Cd(a){td.call(this,a);this.xa=!1;this.ma=!0;this.I=X();this.wa=X();this.N=X();this.H=[];this.g=this.K=this.G=[];Dd(this);this.P=this.ga=0;this.j=[];this.qa=void 0;Ed(this);this.A=this;this.na={};this.V=this.fb=0;this.U=null;this.M=[];Fd(this,a.messages);this.sa=a.commands;this.Aa=Gd;this.La=[];this.L=0;this.Da=this.ra=null;this.D=this.za=this.ya=this.fa=this.oa=0;this.ea=this.aa=null;var b=this;window?void 0===window[C]&&(window[C]=function(a){return Yc(b,a)}):void 0===global[C]&&(global[C]=
|
||||
function(a){return Yc(b,a)})}q(Cd,td);function Y(a){a=a&&a.u;null==a&&(a=-1);return a}function X(a,b,c){return{u:void 0===a?null:a,ub:void 0===b?!1:b,ka:!1,ba:c}}function Hd(a){return[a.u,a.ub,a.ba,a.ka,a.Qa]}function Id(a,b){var c=X(b[0],b[1],b[2]);c.ka=b[3];b[4]&&(c.cb=ud(a,c.Qa=b[4]));return c}l=Cd.prototype;
|
||||
l.ta=function(a,b,c,d){this.B=b;this.C=a;this.b=c;this.ea=a.v;(a=Tc(a,"messages"))&&Fd(this,a);Jd(this,function(a){a:{var b=d.B.g,c=a[0],e=a=0,k=b.length;if(c){a=Y(Z(d,c));if(-1===a){d.f("invalid address: "+c);break a}e=a>>>d.B.j;k=1}d.f("blockid physical blockaddr used size type");d.f("-------- --------- --------- ------ ------ ----");for(var c=-1,m=0;k--;){var p=b[e];p.type==c?m++||d.f("..."):(c=p.type,m=nc[c],p&&d.f(H(p.id,8)+" %"+H(e<<d.B.j,8)+" %"+H(p.u,8)+" "+I(p.Ga)+" "+
|
||||
I(p.size)+" "+m),c!=Hc&&(c=-1),m=0);a+=16384;e++}}});F(this)};
|
||||
l.da=function(a,b,c){var d=this;switch(b){case "debugInput":return this.aa=this.F[b]=c,c.onkeydown=function(a){var b;if(13==a.keyCode)b=c.value,c.value="",Yc(d,b,!0);else if(27==a.keyCode)c.value=b="";else if(38==a.keyCode?(b=null,d.w<d.v.length-1&&(b=d.v[++d.w])):40==a.keyCode&&(0<d.w?b=d.v[--d.w]:(b="",d.w=-1)),null!=b){var e=b.length;c.value=b;c.setSelectionRange(e,e)}null!=b&&a.preventDefault&&a.preventDefault()},!0;case "debugEnter":return this.F[b]=c,ub(c,function(){if(d.aa){var a=d.aa.value;
|
||||
d.aa.value="";Yc(d,a,!0);return!0}return!1}),!0;case "step":return this.F[b]=c,ub(c,function(a){var b=!1;La(d,!0)||(Ma(d,!0),b=ac(d,a?1:0,null),Ma(d,!1));return b}),!0}return!1};function Xc(a){if(a.aa){var b=0,c=0;window&&(b=window.scrollX,c=window.scrollY);a.aa.focus();window&&window.scrollTo(b,c)}}l.va=function(a,b){var c=0,d=Y(a);-1!==d&&(c=gc(this.B,d),b&&null!=a.u&&(a.u+=b||1));return c};l.Za=function(a,b,c){var d=Y(a);-1!==d&&(fc(this.B,d,b),c&&null!=a.u&&(a.u+=c||1),P(this.C,-1))};
|
||||
function Z(a,b,c){var d;c=(c?a.I:a.wa).u;var e,f;if(void 0!==b){b=xd(a,b);"%"==b.charAt(0)&&(e=!0,b=b.substr(1));d=b;var g,h;if(d.match(/^[a-z_][a-z0-9_]*$/i))for(d=d.toUpperCase(),c=0;c<a.H.length;c++){var k=a.H[c].R[d];if(null!=k){h=k.o;break}}null!=h&&(g=X(h));if(d=g)return d;0<=b.indexOf("0x")?f=16:0<=b.indexOf("0o")?f=8:0<=b.indexOf(".")&&(f=10);c=wd(a,b,void 0)}null!=c&&(d=X(c,e,f));return d}function Kd(a,b,c){c&&(c=c.match(/(['"])(.*?)\1/))&&(b.cb=ud(a,b.Qa=c[2]))}
|
||||
function Fd(a,b){a.A=a;a.V=a.fb=536870912;a.U=null;a.M=[];b=ud(a,b.replace("keys","key").replace("kbd","keyboard"),!1,"|");if(b.length)for(var c in M){var d;a:if(d=void 0,Array.prototype.indexOf)d=b.indexOf(c,d);else{d=d||0;0>d&&(d+=b.length);0>d&&(d=0);for(var e=b.length;d<e;d++)if(d in b&&b[d]===c)break a;d=-1}0<=d&&(a.V|=M[c],a.f(c+" messages enabled"))}}function Jd(a,b){for(var c in M)if(16==M[c]){a.na[c]=b;break}}l.jb=function(a){return Ld.indexOf(a.toUpperCase())};
|
||||
l.kb=function(a){var b;switch(a){case Md:b=this.b.g}return b};l.message=function(a,b){b&&(a+=" @"+W(this,X(this.b.sa).u));if(!this.U||a!=this.U)if(this.U=a,this.V&1073741824)this.M.push(a);else{var c;if(this.V&-2147483648&&this.b&&(c=this.b.i.J)||La(this,!0))this.T(),c&&(a+=" (cpu halted)");this.f(a);this.b&&(a=this.b,fd(a),a.O=0,a.C&&P(a.C,void 0))}};
|
||||
function Ed(a){var b;if(!jd(a))a.j&&a.j.length&&a.f("instruction history buffer freed"),a.P=0,a.j=[];else if(!a.j||!a.j.length){a.j=Array(Nd);for(b=0;b<a.j.length;b++)a.j[b]=X();a.P=0;a.f("instruction history buffer allocated")}}function Od(a,b,c){if(!Pd(a,c))return!1;$b(a.b,b);return!0}
|
||||
function ac(a,b,c,d){if(!Pd(a))return!1;var e="";null===c&&(e=(c=!a.ra||"tr"==a.ra)?"tr":"t");a.D=0;b||jd(a)&&kd(a,a.b.g,0);try{b=dd(a.b,b);var f=a.b.Ia(b);0<f&&(bc(a.b,f),a.D+=f,cc(a.b,f,!0),dc(a.b,f),a.fa++)}catch(g){"number"!=typeof g&&(a.D=0,Ia(a.b,g.stack||g.message))}!1!==d&&(a.ea&&a.ea.stop(),P(a.C,-1));Wc(a,c||!1,e);return 0<a.D}l.T=function(a){this.b&&this.b.T(a)};function Wc(a,b,c){a.xa&&(void 0===b&&(b=!0),c&&a.f(Qd+c),a.I=X(a.b.g),b&&1!=a.L?Rd(a):qe(a))}
|
||||
function Pd(a,b){var c;(c=!a.b||!Ka(a.b))||(c=a.b,c.i.S?c=!0:(c.f(c.toString()+" not powered"),c=!1),c=!c);return c||a.b.i.J?(b||a.f("cpu busy or unavailable, command ignored"),!1):!Ja(a.b)}l.la=function(a,b){return!b&&(this.reset(!0),a)?this.restore(a):!0};l.ca=function(a,b){b&&this.f(a?"suspending":"shutting down");return a?this.save():!0};l.reset=function(a){Ed(this);this.fa=0;this.U=null;this.D=0;this.I=X(this.b.g);this.i.J=!1;se(this);a||Wc(this)};
|
||||
l.save=function(){var a=new N(this);a.set(0,Hd(this.I));a.set(1,Hd(this.N));a.set(2,[this.v,this.O,this.V]);a.set(3,this.H);return a.data()};l.restore=function(a){var b=0;void 0!==a[2]&&(this.I=Id(this,a[b++]),this.N=Id(this,a[b++]),this.v=a[b][0],"string"==typeof this.v&&(this.v=[this.v]),this.O=a[b][1],this.V|=a[b][2]);a[3]&&(this.H=a[3]);return!0};l.start=function(a,b){this.L||this.f("running");this.i.J=!0;this.ya=a;this.za=b};
|
||||
l.stop=function(a,b){if(this.i.J){this.i.J=!1;this.D=b-this.za;if(!this.L){b="stopped";if(this.D){a-=this.ya;var c=0<a?Math.round(1E3*this.D/a):0;b+=" (";jd(this)&&(b+=this.fa+" instructions, ",this.fa=0);b+=this.D+" cycles, "+a+" ms, "+c+" hz)"}else Na(this,-2147483648)&&(b+=" (use the 't' command to execute blocked faults)");this.f(b)}Wc(this,!0);Xc(this);se(this,this.b.g);this.U=null}};function jd(a){return 1<a.g.length||!!a.ga}
|
||||
function kd(a,b,c){var d=-1;if(!c&&(d=a.b.pa(b),0==d&&a.b.sa==b)){b=a.b;var e=b.g;b.g=(e+1)%Ib;b=e}if(0<c&&(a.ga&&!--a.ga||Qc(a,b,1,a.g)))return!0;0<=c&&a.j.length&&(a.fa++,0>d&&(d=a.b.pa(b)),68719476735!=(d&65535)&&(c=a.j[a.P],c.u=b,c.ka=!1,c.ba=void 0,++a.P==a.j.length&&(a.P=0)));return!1}
|
||||
function Dd(a){var b,c;a.g=["bp"];if(a.K)for(b=1;b<a.K.length;b++){c=a.K[b];c=Y(c);var d=a.B;Pc(d.g[c>>>d.j],!1)}a.K=["br"];if(a.G)for(b=1;b<a.G.length;b++)c=a.G[b],c=Y(c),d=a.B,Pc(d.g[c>>>d.j],!0);a.G=["bw"];a.oa=0;a.ga=0}l.ua=function(a,b,c){var d=!0;c||te(this,a,b,!1,!0);if(a!=this.g){var e=Y(b);if(-1===e)this.f("invalid address: "+W(this,b.u)),d=!1;else{var f=this.B;f.g[e>>>f.j].ua(e&16383,a==this.G)}}d&&(a.push(b),c?b.ka=!0:(ue(this,a,a.length-1,"set"),Ed(this)));return d};
|
||||
function te(a,b,c,d,e){var f=!1;c=Y(c);for(var g=1;g<b.length;g++){var h=b[g];if(c==Y(h)&&(!d||h.ka)){f=!0;h.ka||e||ue(a,b,g,"cleared");b.splice(g,1);b!=a.g&&(d=a.B,Pc(d.g[c>>>d.j],b==a.G));h.ka||Ed(a);break}}return f}function ve(a,b){for(var c=1;c<b.length;c++)ue(a,b,c);return b.length-1}function ue(a,b,c,d){c=b[c];a.f(b[0]+" "+W(a,c.u)+(d?" "+d:c.Qa?' "'+c.Qa+'"':""))}
|
||||
function se(a,b){if(void 0!==b)Qc(a,b,1,a.g,!0),a.L=0;else for(b=1;b<a.g.length;b++){var c=a.g[b];if(c.ka){if(!te(a,a.g,c,!0))break;b=0}}}
|
||||
function Qc(a,b,c,d,e){var f=!1;if(!a.oa++)for(var g=1;!f&&g<d.length;g++){var h=d[g];if(!e||h.ka)for(var k=Y(h)&(d==a.g?65535:-1),m=0;m<c;m++)if(b+m==k){var p,f=!0;h.ka&&(te(a,d,h,!0),e=!0);if(p=h.cb){for(var f=!1,n=0;n<p.length;n++)if(!we(a,p[n],!0)){if(p[n].indexOf("if")){f=!0;break}for(var r=n+1;r<p.length&&p[r].indexOf("else");r++)n++;if(r==p.length){f=!0;break}}a.b.i.J||(f=!0)}if(f){e||ue(a,d,g,"hit");break}}}a.oa--;return f}
|
||||
function xe(a,b,c,d){var e=X(b.u),f=a.va(b,2),g,h;for(h in a.Aa)if(g=a.Aa[h][f&h])break;g||(g=ye);var k=g[0];0<=a.La.indexOf(k)&&(g=ye,k=g[0]);var m=h="",p=ze[k],n=g.length-1;k||n||(h=W(a,f));for(f=1;f<=n;f++)if(void 0!==g[f]){k="";if(!k||!k.length){h="INVALID";break}"string"!=typeof k&&(m=k[1],k=k[0]);0<h.length&&(h+=",");h+=k||"???"}g="";n=W(a,e.u)+":";if(-1!==e.u&&-1!==b.u){do if(g+=" "+W(a,a.va(e,2)),null==e.u)break;while(e.u!=b.u)}n+=hb(g,24);n+=hb(p,5);h&&(n+=" "+h);if(c||m)n=hb(n,60)+";"+(c||
|
||||
""),n=a.b.i.Ba?n+("cycles="+Zc(a.b).toString()+" cs="+H(a.b.ma)):n+(null!=d?"="+d.toString():""),m&&(";"!=n.slice(-1)&&(n+=" "),n+=m);return n}l.gb=function(a,b){return a[0]>b[0]?1:a[0]<b[0]?-1:0};function nd(a,b,c,d,e){var f=[],g;for(g in e){var h=e[g];"number"==typeof h&&(e[g]=h={o:h});var k=h.o,m=h.a;if(void 0!==k){var p=f,k=[k>>>0,g],n=jb(p,k,a.gb);0>n&&p.splice(-(n+1),0,k)}m&&(h.a=m.replace(/''/g,'"'))}a.H.push({ad:b,u:c,wb:d,R:e,eb:f})}
|
||||
function Ae(a,b,c){var d=[],e=Y(b)>>>0;for(b=0;b<a.H.length;b++){var f=a.H[b],g=f.u>>>0,h=f.wb;if(e>=g&&e<g+h){e=jb(f.eb,[e-g],a.gb);0<=e?Be(a,b,e,d):c&&(e=~e,Be(a,b,e-1,d),Be(a,b,e,d));break}}return d}function Be(a,b,c,d){var e={},f=a.H[b].eb,g=0,h=null;0<=c&&c<f.length&&(g=f[c][0],h=f[c][1]);h&&(e=a.H[b].R[h],h="."==h.charAt(0)?null:e.l||h);d.push(h);d.push(g);d.push(e.a);d.push(e.c)}
|
||||
function Ce(a,b){var c=b.match(/^\s*([A-Z_]?[A-Z0-9_]*)\s*(=?)\s*(.*)$/i);if(c){if(!c[1])return Bd(a)||a.f("no variables"),!0;if(!c[2])return Bd(a,c[1]);if(!c[3])return delete a.Z[c[1]],!0;b=wd(a,c[3]);return void 0!==b?(a.Z[c[1]]=b,!0):!1}a.f("invalid assignment:"+b);return!1}
|
||||
function De(a,b,c){var d=null;if(b=Z(a,b,!0)){var e=Ae(a,b,!0);if(e.length){var f,g;e[0]&&(g="",(f=b.u-e[1])&&(g=" + "+I(f)),f=e[0]+" ("+W(a,e[1])+")"+g,c&&a.f(f),d=f);4<e.length&&e[4]&&(g="",(f=e[5]-b.u)&&(g=" - "+I(f)),f=e[4]+" ("+W(a,e[5])+")"+g,c&&a.f(f),d||(d=f))}else c&&a.f("no symbols")}return d}
|
||||
function Rd(a,b){var c;if(b&&"?"==b[1])a.f("register commands:"),a.f("\tr\tdump registers"),a.f("\trm\tdump misc registers"),a.f("\trx [#]\tset flag or register x to [#]");else{var d=!1,e=a.b;null==c&&(c=!0);if(b&&1<b.length){var f=b[1];if("m"==f)d=!0;else{var g=f.indexOf("=");if(0<g)b=f.substr(g+1),f=f.substr(0,g);else if(2<b.length)b=b[2];else{a.f("missing value for "+b[1]);return}g=wd(a,b);if(void 0===g)return;switch(f.toUpperCase()){case "PC":e.g=g%Ib;a.I=X(e.g);break;default:a.f("unknown register: "+
|
||||
f);return}P(a.C);a.f("updated registers:")}}f=""+((Ld[Md]||"undefined")+" ");d&&(f+="\n");a.f(f);c&&(a.I=X(e.g),qe(a,W(a,a.I.u)))}}function Ee(a,b){b=ib(b);var c=b.match(/^(['"])(.*?)\1$/);c?1<c[2].length?a.f(c[2]):Ad(a,null,c[2].charCodeAt(0)):wd(a,b,!0)}
|
||||
function Fe(a,b,c){if("?"==c)a.f("trace commands:"),a.f("\tt [#]\ttrace # instructions"),a.f("\ttr [#]\ttrace # instructions with register updates"),a.f("\ttc [#]\ttrace # cycles"),a.f("note: bn [#] breaks after # instructions without updates");else{var d="t"!=b;c=yd(a,c,null,!0)||1;var e=0;"tc"==b&&(e=c,c=1);a.ra=b;tb(c,function(){return Ma(a,!0)&&ac(a,e,d,!1)},function(){a.ea&&a.ea.stop();P(a.C,-1);Ma(a,!1)})}}
|
||||
function qe(a,b,c,d){if(b=Z(a,b,!0)){void 0===d&&(d=1);var e=256;if(void 0!==c)if("l"==c.charAt(0))c=yd(a,c.substr(1)),null!=c&&(d=c);else{d=Z(a,c,!0);if(!d||d.u<b.u)return;e=d.u-b.u;if(256<e){a.f("range too large");return}d=-1}c=0;for(var f;0<e&&d--;){f=La(a,!1)||a.L?a.D:null;var g=null!=f?"cycles":null,h=Ae(a,b),k=b.u;if(h[0]&&d&&(!c&&d||0>h[0].indexOf("+"))){var m=h[0]+":";h[2]&&(m+=" "+h[2]);a.f(m)}h[3]&&(g=h[3],f=null);f=xe(a,b,g,f);a.f(f);a.I=b;e-=b.u-k;c++}}}
|
||||
function we(a,b,c){var d=!0;try{b.length&&"end"!=b?c||a.f(Qd+b):(a.O&&(a.f("ended assemble at "+W(a,a.N.u)),a.I=a.N,a.O=!1),b="");var e=b.charAt(0);if('"'==e||"'"==e)return!0;a.U=null;if(Ka(a)&&0<b.length){a.O&&(b="a "+W(a,a.N.u)+" "+b);var f=!1,g=b.replace(/ +/g," ").split(" ");g[0]=g[0].toLowerCase();if(g&&g.length)for(var h=g[0],k=h.charAt(0),m=1;m<h.length;m++){var p=h.charAt(m);if("?"==k||"r"==k||"a">p||"z"<p){g[0]=h.substr(m);g.unshift(h.substr(0,m));break}}switch(g[0].charAt(0)){case "a":var n=
|
||||
Z(a,g[1],!0);if(n)if(a.N=n,void 0===g[2])a.f("begin assemble at "+W(a,n.u)),a.O=!0,P(a.C);else{var r;a.f("not supported yet");r=[];if(r.length){for(var Q=0;Q<r.length;Q++)a.Za(n,r[Q],1);a.f(xe(a,a.N))}}break;case "b":a:{var wa=g[0],ya=g[1],ef=b;if("?"==ya)a.f("breakpoint commands:"),a.f("\tbp #\tset exec breakpoint"),a.f("\tbr #\tset read breakpoint"),a.f("\tbw #\tset write breakpoint"),a.f("\tbc #\tclear breakpoint (* to clear all)"),a.f("\tbl\tlist all breakpoints"),a.f("\tbn [#]\tbreak after # instruction(s)");
|
||||
else{var ia=wa.charAt(1);if("l"==ia){var pc;pc=0+ve(a,a.g);pc+=ve(a,a.K);(pc+=ve(a,a.G))||a.f("no breakpoints")}else if("n"==ia){var Sd=+ya||0;ya&&(a.ga=Sd);a.f("break after "+Sd+" instruction(s)")}else if(void 0===ya)a.f("missing breakpoint address");else{var E=X();if("*"!=ya&&(E=Z(a,ya,!0),!E))break a;"c"==ia?null==E.u?(Dd(a),a.f("all breakpoints cleared")):te(a,a.g,E)||te(a,a.K,E)||te(a,a.G,E)||a.f("breakpoint missing: "+W(a,E.u)):null!=E.u&&(Kd(a,E,ef),"p"==ia?a.ua(a.g,E):"r"==ia?a.ua(a.K,E):
|
||||
"w"==ia?a.ua(a.G,E):a.f("unknown breakpoint command: "+ia))}}}break;case "c":a.ja&&(a.ja.value="");break;case "d":a:{var za,Ua=g[0],R=g[1],ca=g[2],ff=g[3];if("?"==R){var Aa="";for(za in M)a.na[za]&&(Aa&&(Aa+=","),Aa+=za);Aa+=",state,symbols";a.f("dump memory commands:");a.f("\tdw [a] [n] dump n words at address a");a.f("\tds [a] [n] dump n words at address a as JSON");a.f("\tdh [p] [n] dump n instructions from history position p");Aa.length&&a.f("dump extension commands:\n\t"+Aa)}else if("state"==
|
||||
R){var qc=Ge(a.C,!0);"console"==ca?console.log(qc):(a.ja&&(a.ja.value=""),qc&&a.f(qc))}else if("symbols"==R)for(var rc=0;rc<a.H.length;rc++){var sc=a.H[rc],Ba;for(Ba in sc.R)if("."!=Ba.charAt(0)){var Td=sc.R[Ba].o;if(void 0!==Td){var Ud=sc.R[Ba].l;Ud&&(Ba=Ud);a.f(W(a,Td)+" "+Ba)}}}else{if("d"==Ua){for(za in M)if(g[1]==za){var Vd=a.na[za];Vd?(g.shift(),g.shift(),Vd(g)):a.f("no dump registered for "+R);break a}R||(Ua=a.Da||"dw")}else a.Da=Ua;if("dh"==Ua){var Wd=R,Xd=ca,Yd="",Zd=0,O=a.P,S=a.j;if(S.length){var K=
|
||||
+Wd||a.qa,Wa=+Xd||10;isNaN(K)?K=Wa:Yd="more ";K>S.length&&(a.f("note: only "+S.length+" available"),K=S.length);O-=K;0>O&&(null==S[S.length-1].u?(K=O+K,O=0):O+=S.length);var tc=[];"call"==Xd&&(Wa=1E5,tc=["CALL"]);for(void 0!==Wd&&a.f(K+" instructions earlier:");0<Wa&&O!=a.P;){var $d=S[O++];if(null==$d.u)break;var Xa=X($d.u),gf=K--,ae=xe(a,Xa,"history",gf);(!tc.length||0<=ae.indexOf(tc[0]))&&a.f(ae);Xa.Va&&(O+=Xa.Va,Wa-=Xa.Va,K-=Xa.Va);O>=S.length&&(O=0);a.qa=K;Zd++;Wa--}}Zd||(a.f("no "+Yd+"history available"),
|
||||
a.qa=void 0)}else{var ja=Z(a,R);if(ja){var ka=0,be="ds"==Ua;if(ca){if("l"==ca.charAt(0))ca=ca.substr(1)||ff,ka=yd(a,ca);else{var ce=Z(a,ca);ce&&(ka=ce.u-ja.u)}0>ka&&(ka=0);65536<ka&&(ka=65536)}var hf=a.ba;ja.ba&&(a.ba=ja.ba);for(var uc=ka||32,jf=(uc+4-1)/4|0||1,la="";jf--&&0<uc;){for(var ma="",vc="",R=W(a,ja.u),de=4;0<de--&&0<uc--;){var Ya=a.va(ja,1);be?(ma&&(ma+=","),ma+="0x"+H(Ya,9)):(ma+=W(a,Ya,4),ma+=" ");for(var kf=4;kf--;)var wc=Ya%256,vc=vc+(32<=wc&&128>wc?String.fromCharCode(wc):"."),Ya=Ya/
|
||||
256}la&&(la+="\n");la=be?la+(ma+","):la+(R+": "+ma+(de?"":" "+vc))}la&&a.f(la);a.wa=ja;a.ba=hf}}}}break;case "e":if("else"==g[0])break;var Za,ee,fe,ge,he=g[0],xc=g[1];"e"==he||"ew"==he?(Za=2,ee=65535,fe=a.va,ge=a.Za):xc=null;if(null==xc)a.f("edit memory commands:"),a.f("\tew [a] [...] edit words at address a");else{var Cb=Z(a,xc);if(Cb)for(var Db=2;Db<g.length;Db++){var $a=wd(a,g[Db]);if(void 0===$a){a.f("unrecognized value: "+g[Db]);break}$a&~ee&&a.f("warning: "+H($a)+" exceeds "+Za+"-byte value");
|
||||
a.f("changing "+W(a,Cb.u)+(Na(a,16)?"":" from "+W(a,fe.call(a,Cb),Za))+" to "+W(a,$a,Za));ge.call(a,Cb,$a,Za)}}break;case "g":a:{var ie=g[1],lf=b;if(void 0!==ie){var yc=Z(a,ie,!0);if(!yc)break a;Kd(a,yc,lf);a.ua(a.g,yc,!0)}Od(a,!0,c)}break;case "h":a.i.J?(c||a.f("halting"),a.T()):La(a,!0)||c||a.f("already halted");break;case "i":if("if"==g[0]){var zc;var ab=b.substr(2),ab=ib(ab);wd(a,ab)?(c||a.f("true: "+ab),zc=!0):(c||a.f("false: "+ab),zc=!1);zc||(d=!1);break}f=!0;break;case "k":var mf=g[0];if("?"==
|
||||
g[1])a.f("stack trace commands:"),a.f("\tk\tshow frame addresses"),a.f("\tks\tshow symbol information");else{var Ac=0,Bc=X(),bb=X();for(a.f("stack trace for "+W(a,bb.u));10>Ac;){for(var na=null,nf=256;65536>bb.u>>>0;){Bc.u=a.va(bb,2);if(null==bb.u||!nf--)break;if(!(Bc.u&1)){for(var of=a,Eb=Bc,je=null,cb=Eb.u,ke=cb,Cc=1;6>=Cc&&cb;Cc++){if(2<Cc){Eb.u=cb;var Fb=xe(of,Eb);if(0<=Fb.indexOf("JSR")){var le=Fb.indexOf(" ");if(cb+(Fb.indexOf(" ",le+1)-le-1)/2==ke){je=Fb;break}}}cb-=2}Eb.u=ke;if(na=je)break}}if(!na||
|
||||
null==na)break;var me=null;if("ks"==mf){var ne=na.match(/[0-9A-F]+$/);ne&&(me=De(a,ne[0]))}na=hb(na,50)+" ;"+(me||"stack="+W(a,bb.u));a.f(na);Ac++}Ac||a.f("no return addresses found")}break;case "l":if("ln"==g[0]){De(a,g[1],!0);break}f=!0;break;case "m":a:{var T,U=null,w=g[1];"?"==w&&(w=void 0);if(void 0!==w){var da=0;if("all"==w)da=1879046143,w=null;else if("on"==w)U=!0,w=null;else if("off"==w)U=!1,w=null;else{"keys"==w&&(w="key");"kbd"==w&&(w="keyboard");for(T in M)if(w==T){da=M[T];U=!!(a.V&da);
|
||||
break}if(!da){a.f("unknown message category: "+w);break a}}if(da)if("on"==g[2])a.V|=da,U=!0;else if("off"==g[2]&&(a.V&=~da,U=!1,1073741824==da)){for(var oe=1E3<=a.M.length?a.M.length-1E3:0;oe<a.M.length;)a.f(a.M[oe++]);a.M=[]}}var pf=0,db="";for(T in M)if(!w||w==T){var qf=!!(a.V&M[T]);if(null===U||U==qf)db&&(db+=","),++pf%10||(db+="\n\t"),"key"==T&&(T="keys"),db+=T}void 0===w&&a.f("message commands:\n\tm [category] [on|off]\tturn categories on/off");a.f((null!==U?U?"messages on: ":"messages off: ":
|
||||
"message categories:\n\t")+(db||"none"));Ed(a)}break;case "p":if("print"==g[0]){Ee(a,b.substr(5));break}var rf=g[0];if("?"==g[1])a.f("step commands:"),a.f("\tp\tstep over instruction"),a.f("\tpr\tstep over instruction with register update");else{var sf="pr"==rf?1:0;if(a.L)a.f("step in progress");else{var pe=X(a.b.g);a.va(pe);a.L?(a.ua(a.g,pe,!0),Od(a)||(a.C&&cd(a.C),a.L=0)):Fe(a,sf?"tr":"t")}}break;case "r":if("reset"==b){a.C&&a.C.reset();break}Rd(a,g);break;case "s":a:switch(g[1]){case "base":if(g[2]){var eb=
|
||||
+g[2];if(8==eb||10==eb||16==eb)a.ba=eb;else{a.f("invalid base: "+eb);break}}a.f("default base: "+a.ba);break;case "cs":var fb;void 0!==g[3]&&(fb=+g[3]);switch(g[2]){case "int":a.b.P=fb;break;case "start":a.b.na=fb;break;case "stop":a.b.Z=fb;break;default:a.f("unknown cs option");break a}void 0!==fb&&Vc(a.b);a.f("checksums "+(a.b.i.Ba?"enabled":"disabled"));break;case "sp":void 0!==g[2]&&($c(a.b,+g[2])||a.f("warning: using 1x multiplier, previous target not reached"));a.f("target speed: "+(a.b.M.toFixed(2)+
|
||||
"Mhz")+" ("+a.b.N+"x)");break;default:if(g[1]){a.f("unknown option: "+g[1]);break}case "?":a.f("debugger options:"),a.f("\tbase #\t\tset default base to #"),a.f("\tcs int #\tset checksum cycle interval to #"),a.f("\tcs start #\tset checksum cycle start count to #"),a.f("\tcs stop #\tset checksum cycle stop count to #"),a.f("\tsp #\t\tset speed multiplier to #")}break;case "t":Fe(a,g[0],g[1]);break;case "u":qe(a,g[1],g[2],8);break;case "v":if("var"==g[0]){Ce(a,b.substr(3))||(d=!1);break}if("ver"==
|
||||
g[0]){a.f((Hb||"PDP10")+" version 1.34.0 ("+a.b.ab+",RELEASE)");a.f(window?window.navigator.userAgent:"");break}f=!0;break;case "?":if(g[1]){Ee(a,b.substr(1));break}var Dc="commands:",Ec;for(Ec in He)Dc+="\n"+hb(Ec,9)+He[Ec];jd(a)||(Dc+="\nnote: history disabled if no exec breakpoints");a.f(Dc);break;default:f=!0}f&&(a.f("unknown command: "+b),d=!1)}}catch(re){a.f("debugger error: "+(re.stack||re.message)),d=!1}return d}
|
||||
function Yc(a,b,c){b=ud(a,b,c);for(var d in b)if(!we(a,b[+d]))return!1;return!0}
|
||||
var He={"?":"help/print","a [#]":"assemble","b [#]":"breakpoint",c:"clear output","d [#]":"dump memory","e [#]":"edit memory","g [#]":"go [to #]",h:"halt","if":"eval expression","int [#]":"request interrupt",k:"stack trace",ln:"list nearest symbol(s)",m:"messages",p:"step over",print:"print expression",r:"dump/set registers",reset:"reset machine",s:"set options","t [#]":"trace","u [#]":"unassemble","var":"assign variable",ver:"print version"},ze=[".WORD"],Md=0,Ld=["PC"],Gd={},ye=[0],Nd=1E3,Qd=">> ";
|
||||
L(function(){for(var a=D(document,C,"debugger"),b=0;b<a.length;b++){var c=a[b],d=A(c),d=new Cd(d);B(d,c)}});
|
||||
function Ie(a,b,c){u.call(this,"Computer",a,33554432);this.i.S=!1;this.O=null;Je(this,b);this.M=Tc(this,"autoPower",a,6);this.w=0;this.Z=+a.busWidth||+a.buswidth;this.K=this.G=this.L=null;this.I=this.U=!1;this.H=this.D=null;this.P=this.N=!1;this.aa=Tc(this,"url")||"";(Math.random()+.1).toString(36);this.j=Ke(this);if(this.b=z("CPU",this.id)){this.A=z("Debugger",this.id);this.B=new hc({id:this.Ka+".bus",busWidth:this.Z},this.b,this.A);var d,e=y(this.id);if((this.v=z("Panel",this.id))&&this.v.ja)for(b=
|
||||
0;b<e.length;b++)d=e[b],d.Y=this.v.Y,d.f=this.v.f,d.ja=this.v.ja;this.f(Hb+" v1.34.0\nCopyright \u00a9 2012-2017 Jeff Parsons <Jeff@pcjs.org>\nLicense: GPL version 3 or later <http://gnu.org/licenses/gpl.html>");for(b=0;b<e.length;b++)d=e[b],d.ta&&d.ta(this,this.B,this.b,this.A);b=null;d=Tc(this,"resume",a);void 0!==d&&(1<d.length?b=this.G=d:this.g=parseInt(d,10));var f;if(a=Tc(this,"state")||(f=!0,a.state))this.L=b=a,f||(this.I=!0,this.g=Le),this.g&&(this.H=new N(this,"1.34.0"),Me(this.H)?b=null:
|
||||
delete this.H);!b&&this.g&&(b=Ne(this))&&(this.I=!0);if(b){var g=this;J(b,null,!0,function(a,b,c){c?(g.G=null,g.I=!1,g.Y("Unable to load machine state from server (error "+c+(b?": "+ib(b):"")+")")):(g.K=b,g.U=!0);F(g)})}else F(this);this.F.power||(this.M=!0);!c&&this.M&&Oe(this,this.Fa)}else x("Unable to find CPU component")}q(Ie,u);function Je(a,b){if(!b){var c;if("object"==typeof resources&&(c=resources.parms))try{b=eval("("+c+")")}catch(d){x(d.message+" ("+c+")")}}a.O=b}
|
||||
function Tc(a,b,c,d){var e=b.toLowerCase(),e=rb(b)||rb(e);void 0===e&&a.O&&(e=a.O[b]);void 0===e&&c&&(e=c[b]);void 0===e&&"object"==typeof resources&&resources[b]&&(e=b);void 0===e&&(e=void 0);if("string"==typeof e&&d)switch(d){case 4:e=+e;isNaN(e)&&(e=0);break;case 6:e="true"==e}return e}function Oe(a,b,c){for(var d=y(a.id),e=0;e<=d.length;e++){var f=e<d.length?d[e]:a;if(!Ka(f)){Ka(f,function(){Oe(a,b,c)});return}}b.call(a,c)}
|
||||
function Pe(a,b){var c=new N(a,"1.34.0",Qe);if(Me(c)&&Re(c)){var d=c.get(Se),e=b?b.get(Se):"unknown";d!=e&&(a.Y("Machine state may be out-of-date\n("+d+" vs. "+e+")\nCheck your browser's local storage limits"),b||c.clear())}}l=Ie.prototype;
|
||||
l.Fa=function(a){void 0===a&&(a=this.g||(this.K?Te:Le));if(!this.w){this.w++;var b=!1,c=!1;this.N=!1;var d=this.H||new N(this,"1.34.0");if(a==Ue)b=!0;else if(a>Le){if(Me(d,this.K)){this.D=new N(this,"1.34.0",Ve);Me(this.D)&&(We(this,d),a=Xe,Ye(this.D));this.D.set(Se,kb());Ze(this.D);var e=this.g&&!this.I;if(a==Te||xa("Click OK to restore the previous "+Hb+" machine state, or CANCEL to reset the machine.")){if(c=Re(d)){var f=d.get("code"),g=d.get("data");f&&("ok"==f?Me(d,g):("error"==f&&"no machine state"!=
|
||||
g?(this.Y("Error: "+g),"unable to verify user"==g&&(pb($e,""),this.j=null)):this.f(f+": "+g),Ye(d),Me(d)?(c=Re(d),e=!0):c=!1))}e&&Pe(this,c?d:null)}else a==Xe&&d.clear()}else Pe(this);delete this.K;delete this.H}e=y(this.id);for(f=0;f<e.length;f++)g=e[f],g!==this&&g!=this.b&&(c=af(this,g,d,b,c));b=[d,a,c];a!=Ue?Oe(this,this.hb,b):this.hb(b)}};
|
||||
function af(a,b,c,d,e){if(!b.i.S){b.i.S=!0;var f=null;try{if(e&&((f=c.get(b.id))||(f=c.get(b.id.replace(/[a-z0-9]\./i,".")))),"string"===typeof f&&(f=null),!b.la(f,d)&&f&&(x("Unable to restore state for "+b.type),a.L&&!a.U?(c.clear(),a.g=Le,window&&window.location.reload()):a.N=!0,b.la(null),e=!1),!d&&b.$a){var g=b.$a.split("|");for(a=0;a<g.length;a++)b.status(g[a])}}catch(h){x("Error restoring state for "+b.type+" ("+h.message+")")}}return e}
|
||||
l.hb=function(a){var b=a[0],c=0>a[1];a=a[2];this.P=!0;this.i.S=!0;var d=this.F.power;d&&(d.textContent="Shutdown");this.b&&(af(this,this.b,b,c,a),P(this,-2),this.b.X());this.N&&(We(this,b),b.clear());!c&&this.D&&(this.D.clear(),delete this.D);this.w=0};
|
||||
function We(a,b){if(xa("There may be a problem with your "+Hb+" machine.\n\nTo help us diagnose it, click OK to send this "+Hb+" machine state to http://www.pcjs.org.")){var c=a.aa;a=a.j||"";b=b.toString();var d={};d.app=Hb;d.ver="1.34.0";d.url=c;d.user=a;d.type="bug";d.data=b;J("http://www.pcjs.org/api/v1/report",d,!0)}}
|
||||
function Ge(a,b,c){var d,e="none";if(a.w)return null;a.w--;var f=new N(a,"1.34.0"),g=new N(a,"1.34.0",Qe),h=kb();g.set(Se,h);f.set(Se,h);f.set(bf,"1.34.0");f.set(cf,window?window.location.href:null);f.set(df,window?window.navigator.userAgent:"");a.b&&a.b.ca&&(c&&(b&&(a.b.i.X=a.b.i.J),a.b.T()),d=a.b.ca(b,c),"object"===typeof d&&f.set(a.b.id,d),c&&(a.b.i.S=!1,!1===d&&(e=null)));for(var h=y(a.id),k=0;k<h.length;k++){var m=h[k];m.i.S&&(m.ca&&(d=m.ca(b,c),"object"===typeof d&&f.set(m.id,d)),c&&(m.i.S=
|
||||
!1,!1===d&&(e=null)))}e&&(c?(h=d=!1,b?(a.j&&tf(a,a.j,f.toString()),Ze(g)&&Ze(f)||(e=null,d=h=!0)):a.g&&(d=!0,h=a.g==uf),d&&f.clear(h)):e=f.toString());c&&(a.i.S=!1,b=a.F.power)&&(b.textContent="Power");a.w=0;return e}
|
||||
l.reset=function(){this.i.reset=!0;this.B&&this.B.reset&&(G(this,"Resetting "+this.B.type),this.B.reset());this.b&&this.b.reset&&(G(this,"Resetting "+this.b.type),this.b.reset());for(var a=y(this.id),b=0;b<a.length;b++){var c=a[b];c!==this&&c!==this.B&&c!==this.b&&c.reset&&(G(this,"Resetting "+c.type),c.reset())}this.i.reset=!1;P(this,-1)};l.start=function(a,b){for(var c=y(this.id),d=0;d<c.length;d++){var e=c[d];"CPU"!=e.type&&e!==this&&e.start&&e.start(a,b)}P(this,-1)};
|
||||
l.stop=function(a,b){for(var c=y(this.id),d=0;d<c.length;d++){var e=c[d];"CPU"!=e.type&&e!==this&&e.stop&&e.stop(a,b)}P(this,-1)};
|
||||
function P(a,b){if(a.b){var c=a.b,d=b||0,e=c.F.speed;e&&(0>=d||30<=(c.wa+=d))&&(e.textContent=c.i.J?c.L.toFixed(2)+"Mhz":"Stopped",c.wa=0)}if(a.v&&(a=a.v,b=b||0,a.K)){c=a.b.i.J;d=!!(a.b.j&8);if(0>=b||60<=(a.I+=b)){e=a.b.g;if(a.F.PC){var f=a.A&&a.A.ba||8,e=e||0,e=8==f?Pa(e,void 0):H(e,void 0);a.F.PC.textContent!=e&&(a.F.PC.textContent=e)}a.I=0}-1>b?a.j=a.b.g:0<b&&c&&!d&&(a.j=a.b.ea);Sb(a,a.j);Mb(a,a.w)}}
|
||||
l.da=function(a,b,c){var d=this;switch(b){case "power":return this.F[b]=c,c.onclick=function(){d.w||(d.i.S?Ge(d,!1,!0):Oe(d,d.Fa))},!0;case "reset":return this.F[b]=c,c.onclick=function(){if(d.i.S&&!d.w)if(d.g&&!d.G){var a=xa("Click OK to save changes to this "+Hb+" machine.\n\nWARNING: If you CANCEL, all disk changes will be discarded.");Ge(d,a,!0);!a&&d.L?window&&window.location.reload():d.Fa(Le)}else d.reset(),d.b&&!d.A&&d.b.X()},!0;case "save":if(Sa())c.parentNode.removeChild(c);else return this.F[b]=
|
||||
c,c.onclick=function(){var a=Ke(d,!0);if(a){var b=!!(d.g&&!d.G||d.L),c=Ge(d,b);b?tf(d,a,c):d.Y("Resume disabled, machine state not saved")}},!0}return!1};function Ke(a,b){var c=a.j;c||((c=ob($e),void 0!==c)?!c&&b&&(b=null,window&&(b=window.prompt("Saving machine states on the pcjs.org server is currently unsupported.\n\nIf you're running your own server, enter your user ID below.","")),c=b)&&((c=vf(a,c))||a.Y("The user ID is invalid.")):b&&a.Y("Browser local storage is not available"));return c}
|
||||
function vf(a,b){a.j=null;b=J(Ta()+"/api/v1/user?req=verify&user="+b);var c=b[1];if(!b[0]&&c)try{b=eval("("+c+")"),b.code&&"ok"==b.code&&(pb($e,b.data),a.j=b.data)}catch(d){x(d.message+" ("+c+")")}return a.j}function Ne(a){var b=null;a.j&&(b=Ta()+"/api/v1/user?req=load&user="+a.j+"&state="+wf(a,"1.34.0"));return b}
|
||||
function tf(a,b,c){if(c){var d={req:"store"};d.user=b;d.state=wf(a,"1.34.0");d.data=c;b=J(Ta()+"/api/v1/user",d);d=b[0];if(b[1]){if(d){var e=d.indexOf("\n");0<e&&(d=d.substr(0,e));d.indexOf("Error: ")||(d=d.substr(7))}d='{"code":'+b[1]+',"data":"'+d+'"}'}b=JSON.parse(d);b&&"ok"==b.code?a.Y("Machine state saved to server"):c&&(c=b&&b.data||"unable to save machine state",c="error"==b.code?"Error: "+c:"Error "+b.code+": "+c,a.Y(c),pb($e,""),a.j=null)}}
|
||||
function cd(a,b){if(a.ja){var c=0,d=0;!b&&window&&(c=window.scrollX,d=window.scrollY);a.ja.focus();!b&&window&&window.scrollTo(c,d)}}var Ve="failsafe",Qe="validate",Se="timestamp",bf="version",cf="url",df="browser",$e="user",Ue=-1,Le=0,Te=1,Xe=2,uf=3;L(function(){for(var a=D(document,C+"-machine"),b=0;b<a.length;b++)for(var c=a[b],d=A(c),c=D(c,C,"computer"),e=0;e<c.length;e++){var f=c[e],g=A(f),g=new Ie(g,d,!0);B(g,f);g.M&&Oe(g,g.Fa)}});
|
||||
wb.show.push(function(){for(var a=D(document,C,"computer"),b=0;b<a.length;b++){var c=A(a[b]);(c=z("Computer",c.id))&&c.P&&!c.i.S&&c.Fa(Ue)}});wb.exit.push(function(){for(var a=D(document,C,"computer"),b=0;b<a.length;b++){var c=A(a[b]);(c=z("Computer",c.id))&&c.i.S&&Ge(c,!(!c.g||c.G),!0)}});function N(a,b,c){this.id=a.id;this.A="";this.b={};this.B=this.C=!1;this.key=wf(a,b,c);Ye(this,a.rb)}l=N.prototype;l.set=function(a,b){try{this.b[a]=b}catch(c){}};l.get=function(a){return this.b[a]||null};
|
||||
l.data=function(){return this.b};function Me(a,b){return b?(a.A=b,a.B=!0,a.C=!1,!0):a.B?!0:mb()&&(b=ob(a.key))?(a.A=b,a.B=!0):!1}function Re(a){var b=!0;if(!a.C)try{a.b=JSON.parse(a.A),a.C=!0}catch(c){x(c.message||c),b=!1}return b}function Ze(a){var b=!0;if(mb()){var c=JSON.stringify(a.b);pb(a.key,c)||(x("Unable to store "+c.length+" bytes in browser local storage"),b=!1)}return b}l.toString=function(){return this.b?JSON.stringify(this.b):this.A};
|
||||
function Ye(a,b){a.A="";a.b={};a.B=a.C=!1;b&&a.set("parms",b)}l.clear=function(a){Ye(this);var b=[];try{for(var c=0,d=window.localStorage.length;c<d;c++)b.push(window.localStorage.key(c))}catch(e){}for(c=0;c<b.length;c++)if((d=b[c])&&(a||d.substr(0,this.key.length)==this.key)){try{window.localStorage.removeItem(d)}catch(e){}b.splice(c,1);c=0}};function wf(a,b,c){a=a.id;if(b){var d=b.indexOf(".");0<d&&(a+=".v"+b.substr(0,d))}c&&(a+="."+c);return a}var xf=0;
|
||||
function yf(a,b,c,d,e,f){e("Loading "+a+"...");J(a,null,!0,function(g,h,k){k?(h||(h="unable to load "+a+" ("+k+")"),f(h,null)):zf(h,a,b,c,d,e,f)})}
|
||||
function zf(a,b,c,d,e,f,g){function h(a,f){if(f)g(f,null);else{c&&(ta(c,b,a),(f=b)&&0>f.indexOf("/")&&"/"==window.location.pathname.slice(-1)&&(f=window.location.pathname+f),d?"}"==d.slice(-1)?(d=d.slice(0,-1),1<d.length&&(d+=",")):d='{state:"'+d+'",':d="{",d+='url:"'+f+'"}',"object"==typeof resources&&(f=null),a=a.replace(/(<machine[^>]*\sid=)(['"]).*?\2/,"$1$2"+c+"$2"+(d?" parms='"+d+"'":"")+(f?' url="'+f+'"':"")));e||(a=a.replace(/(<xsl:variable name="APPNAME">).*?(<\/xsl:variable>)/,"$1PDPjs$2"),
|
||||
a=a.replace(/(<xsl:variable name="APPCLASS">).*?(<\/xsl:variable>)/,"$1pdp11$2"));f=null;if("<"==a.charAt(0))try{e||(a=a.replace(/<!DOCTYPE(.|[\r\n])*]>\s*/g,"")),window.ActiveXObject||"ActiveXObject"in window?(f=new window.ActiveXObject("Microsoft.XMLDOM"),f.async=!1,f.loadXML(a)):f=(new window.DOMParser).parseFromString(a,"text/xml")}catch(p){f=null,a=p.message}else a="unrecognized XML: "+(255<a.length?a.substr(0,255)+"...":a);g(a,f)}}a?e?Af(a,f,h):h(a,null):g("no data"+(b?" for file: "+b:""),null)}
|
||||
function Af(a,b,c){var d;if(d=/<([a-z]+)\s+ref="(.*?)"(.*?)\/>/g.exec(a)){var e=d[2];b("Loading "+e+"...");J(e,null,!0,function(f,g,h){if(h||!g)c(a,"unable to resolve XML reference: "+d[0]+" ("+h+")");else{if(f=d[3])if(h=g.match(new RegExp("<"+d[1]+"[^>]*>"))){for(var k=h[0],m,p=/( [a-z]+=)(['"])(.*?)\2/g;m=p.exec(f);)k=0>k.indexOf(m[1])?k.replace(">",m[0]+">"):k.replace(new RegExp(m[1]+"(['\"])(.*?)\\1"),m[0]);h[0]!=k&&(g=g.replace(h[0],k))}else{c(a,"missing <"+d[1]+"> in "+e);return}g=g.replace(/<\?xml[^>]*>[\r\n]*/,
|
||||
"");a=a.replace(d[0],g);Af(a,b,c)}})}else c(a,null)}
|
||||
function Bf(a,b,c,d){function e(a){if(void 0===h){var b=g&&D(g,"machine-warning");h=b&&b[0]||g}h&&(h.innerHTML=Va(a))}function f(a){e("Error: "+a);k&&(--xf||zb(!0));k=!1}var g,h,k=!0;xf++;ua[a]={};try{if(g=document.getElementById(a)){var m;if("object"==typeof resources&&(m=resources.css)){var p=document.head||document.getElementsByTagName("head")[0],n=document.createElement("style");n.type="text/css";n.styleSheet?n.styleSheet.cssText=m:n.appendChild(document.createTextNode(m));p.appendChild(n)}c||
|
||||
(c="/versions/pdpjs/1.34.0/components.xsl");m=function(d,h){h?yf(c,null,null,!1,e,function(d,k){k?(ta(a,c,d),e("Processing "+b+"..."),window.ActiveXObject||"ActiveXObject"in window?(k=h.transformNode(k))?(g.outerHTML=k,--xf||zb(!0)):f("transformNodeToObject failed"):document.implementation&&document.implementation.createDocument?(d=new XSLTProcessor,d.importStylesheet(k),(k=d.transformToFragment(h,document))?g.parentNode?(g.parentNode.replaceChild(k,g),--xf||zb(!0)):f("invalid machine element: "+
|
||||
a):f("transformToFragment failed")):f("unable to transform XML: unsupported browser")):f(d)}):f(d)};"<"!=b.charAt(0)?yf(b,a,d,!0,e,m):zf(b,null,a,d,!1,e,m)}else f("missing machine element: "+a)}catch(r){f(r.message)}return k}window.embedPDP11=function(a,b,c,d){zb(!1);return Bf(a,b,c,d)};window.findMachineComponent=function(a,b){return z(b,a+".machine")};
|
||||
window.processMachineScript=function(a,b){var c=!1;a+=".machine";if("string"==typeof b&&!Ea[a]){for(var c=!0,d=Ea,e=a,f=b.length,g=[],h=[],k="",m=null,p=0;p<f;p++){var n=b[p];if('"'==n||"'"==n)m&&n!=m?k+=n:(m?m=null:m=n,k&&(h.push(k),k=""));else{if(!m){if("\r"==n||"\n"==n)n=";";if(" "==n||"\t"==n||";"==n){k&&(h.push(k),k="");";"==n&&h.length&&(g.push(h),h=[]);continue}}k+=n}}k&&h.push(k);h.length&&g.push(h);d[e]=g;Da(a)||(c=!1)}return c};window.enableEvents=zb;window.sendEvent=Bb;})();//# sourceMappingURL=/tmp/pdpjs/1.34.0/pdp10-dbg.map
|
||||
143
versions/pdpjs/1.34.0/pdp10.js
Normal file
143
versions/pdpjs/1.34.0/pdp10.js
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
(function(){/*
|
||||
http://pcjs.org/modules/shared/lib/diskapi.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/dumpapi.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/reportapi.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/userapi.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/keys.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/strlib.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/weblib.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/messages.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/device.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/rom.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/ram.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/computer.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/state.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/embed.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/defines.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/int36.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/component.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/usrlib.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/defines.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/panel.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/bus.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/memory.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/cpu.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/cpustate.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/serial.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/shared/lib/debugger.js (C) Jeff Parsons 2012-2017
|
||||
http://pcjs.org/modules/pdp10/lib/debugger.js (C) Jeff Parsons 2012-2017
|
||||
*/
|
||||
var k,aa="function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){if(c.get||c.set)throw new TypeError("ES3 does not support getters and setters.");a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)},q="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global?global:this;function ba(){ba=function(){};q.Symbol||(q.Symbol=ca)}var da=0;function ca(a){return"jscomp_symbol_"+(a||"")+da++}
|
||||
function ea(){ba();var a=q.Symbol.iterator;a||(a=q.Symbol.iterator=q.Symbol("iterator"));"function"!=typeof Array.prototype[a]&&aa(Array.prototype,a,{configurable:!0,writable:!0,value:function(){return fa(this)}});ea=function(){}}function fa(a){var b=0;return ga(function(){return b<a.length?{done:!1,value:a[b++]}:{done:!0}})}function ga(a){ea();a={next:a};a[q.Symbol.iterator]=function(){return this};return a}
|
||||
function r(a,b){function c(){}c.prototype=b.prototype;a.prototype=new c;a.prototype.constructor=a;for(var d in b)if(Object.defineProperties){var e=Object.getOwnPropertyDescriptor(b,d);e&&Object.defineProperty(a,d,e)}else a[d]=b[d]}function ha(a,b){if(b){var c=q;a=a.split(".");for(var d=0;d<a.length-1;d++){var e=a[d];e in c||(c[e]={});c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&aa(c,a,{configurable:!0,writable:!0,value:b})}}
|
||||
ha("Math.trunc",function(a){return a?a:function(a){a=Number(a);if(isNaN(a)||Infinity===a||-Infinity===a||!a)return a;var b=Math.floor(Math.abs(a));return 0>a?-b:b}});ha("Math.log2",function(a){return a?a:function(a){return Math.log(a)/Math.LN2}});
|
||||
var ia=Math.pow(2,36),ja=-Math.pow(2,35),t={Ab:0,Qa:1,Cb:2,Db:3,Eb:4,Fb:5,Gb:6,Hb:7,Fa:8,Ib:9,Ra:10,Jb:11,Kb:12,Sa:13,Lb:14,Mb:15,Nb:16,Ob:17,Pb:18,Qb:19,Rb:20,Sb:21,Tb:22,Ub:23,Vb:24,Wb:25,Xb:26," ":32,"!":33,'"':34,"#":35,$:36,"%":37,"&":38,"'":39,"(":40,")":41,"*":42,"+":43,",":44,"-":45,".":46,"/":47,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57,":":58,";":59,"<":60,"=":61,">":62,"?":63,"@":64,Ea:65,zb:66,Bb:67,Yb:68,E:69,Zb:70,$b:71,ac:72,bc:73,cc:74,dc:75,ec:76,fc:77,gc:78,hc:79,ic:80,Q:81,
|
||||
jc:82,kc:83,lc:84,mc:85,nc:86,oc:87,pc:88,qc:89,Wa:90,"[":91,"\\":92,"]":93,"^":94,_:95,"`":96,rc:97,sc:98,tc:99,d:100,e:101,uc:102,vc:103,wc:104,xc:105,yc:106,k:107,zc:108,Ac:109,n:110,Bc:111,p:112,q:113,r:114,Cc:115,t:116,Dc:117,Ec:118,Fc:119,x:120,y:121,z:122,"{":123,"|":124,"}":125,"~":126,Ta:127};
|
||||
function u(a,b){this.type=a;b||(b={id:"",name:""});this.id=b.id||"";this.name=b.name;this.Ia=b.comment;this.Va=b;this.exports={};this.i=this.bindings={};a=this.id.indexOf(".");0>a?this.ha=this.id:(this.ia=this.id.substr(0,a),this.ha=this.id.substr(a+1));this.b={ready:!1,ma:!1,za:!1,w:!1,error:!1};this.pa=null;this.b.error=!1;this.o=this.c=this.j=this.m=this.Y=null;v.push(this)}function ka(a,b,c){la[a]&&b&&(la[a][b]=c)}function w(){return Date.now()||+new Date}
|
||||
function x(a){window&&window.alert(a)}function ma(a){var b=!1;window&&(b=window.confirm(a));return b}function y(a){var b,c=[];a&&(a=0<(b=a.indexOf("."))?a.substr(0,b+1):"");for(b=0;b<v.length;b++){var d=v[b];a&&d.id.indexOf(a)||c.push(d)}return c}function na(a){if(void 0!==a){var b;for(b=0;b<v.length;b++)if(v[b].id===a)return v[b]}return null}
|
||||
function z(a,b){var c;if(void 0!==a){var d;b&&(b=0<(d=b.indexOf("."))?b.substr(0,d+1):"");for(d=0;d<v.length;d++)if(c)c==v[d]&&(c=null);else if(!(a!=v[d].type||b&&v[d].id.indexOf(b)))return v[d]}return null}function A(a){var b=null;if(a=a.getAttribute("data-value"))try{b=eval("("+a+")")}catch(c){x(c.message+" ("+a+")")}return b}
|
||||
function B(a,b){var c=C;b=D(b.parentNode,c+"-control");for(var d=0;d<b.length;d++)for(var e=b[d].childNodes,f=0;f<e.length;f++){var h=e[f];if(1===h.nodeType){var g=h.getAttribute("class");if(g)for(var l=g.split(" "),m=0;m<l.length;m++)switch(g=l[m],g){case c+"-binding":(g=A(h))&&g.binding&&a.S(g.type,g.binding,h,g.value),m=l.length}}}}
|
||||
function D(a,b,c){c&&(b+="-"+c+"-object");if(a.getElementsByClassName)return a.getElementsByClassName(b);var d;c=[];a=a.getElementsByTagName("*");var e=new RegExp("(^| )"+b+"( |$)");b=0;for(d=a.length;b<d;b++)e.test(a[b].className)&&c.push(a[b]);return c}
|
||||
function oa(a){for(var b=!0,c=pa[a];c&&c.length;){var d=c.splice(0,1)[0],e=d[0],f=null;0<=qa.indexOf(e)&&(f=function(){return function(){oa(a)}}());var h=ra[e];if(h)if(!f)b=h(d[1],d[2],d[3]);else{if(!h(f,d[1],d[2],d[3]))break}else{var b=!1,g=z(d[1],a);if(g)if(h=sa[e])b=h(g,d[2],d[3]);else{var l=g.exports;if(l&&(h=l[e]))if(b=!0,!f)b=h.call(g,d[2],d[3]);else if(!h.call(g,f,d[2],d[3]))break}}if(!b){x("Script error: "+e+(h?" failed":" unrecognized"));break}}c&&!c.length&&delete pa[a];return b}k=u.prototype;
|
||||
k.toString=function(){return this.name?this.name:this.id||this.type};
|
||||
k.S=function(a,b,c){switch(b){case "clear":return this.i[b]||(this.i[b]=c,c.onclick=function(a){return function(){a.i.print&&(a.i.print.value="")}}(this)),!0;case "print":return this.i[b]||(this.Y=this.i[b]=c,c.value="",this.J=function(a){return function(b,c){8192<a.value.length&&(a.value=a.value.substr(a.value.length-4096));a.value+=(void 0!==c?c+": ":"")+(b||"")+"\n";a.scrollTop=a.scrollHeight}}(c),this.C=function(a){this.J(a,this.ha)}),!0;default:return!1}};k.log=function(){};k.J=function(){};
|
||||
k.status=function(a){this.J(this.ha+": "+a)};k.C=function(a,b,c){c=c||this.type;b||x((c?c+": ":"")+a)};function ta(a,b){b&&(a.b.ready?b():a.pa=b);return a.b.ready}function E(a){if(!a.b.error&&(a.b.ready=!0,a.b.ready)){var b=a.pa;a.pa=null;b&&b()}}function ua(a,b){a.b.za?(a.b.ma=!1,a.b.za=!1):a.b.error?a.J(a.toString()+" error"):a.b.ma=b}k.T=function(){return this.b.w=!0};k.N=function(a,b){b&&(this.b.w=!1);return!0};
|
||||
window&&(window.PCjs||(window.PCjs={}),window.PCjs.Machines||(window.PCjs.Machines={}),window.PCjs.Components||(window.PCjs.Components=[]),window.PCjs.Commands||(window.PCjs.Commands={}));
|
||||
var la=window?window.PCjs.Machines:{},v=window?window.PCjs.Components:[],pa=window?window.PCjs.Commands:{},qa=["hold","sleep","wait"],ra={alert:function(a){x(a);return!0},sleep:function(a,b){setTimeout(a,+b);return!1}},sa={select:function(a,b,c){var d=!1;if(a=a.bindings[b])for(b=0;b<a.options.length;b++)if(a.options[b].textContent==c){a.selectedIndex!=b&&(a.selectedIndex=b);d=!0;break}return d}};
|
||||
Array.prototype.indexOf||(Array.prototype.indexOf=function(a,b){b=b||0;for(var c=this.length;b<c;b++)if(this[b]===a)return b;return-1});Array.isArray||(Array.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)});
|
||||
Function.prototype.bind||(Function.prototype.bind=function(a){function b(){return e.apply(this instanceof c&&a?this:a,d.concat(Array.prototype.slice.call(arguments)))}function c(){}if("function"!=typeof this)throw new TypeError("Function.prototype.bind: non-callable object");var d=Array.prototype.slice.call(arguments,1),e=this;c.prototype=this.prototype;b.prototype=new c;return b});
|
||||
function va(a,b){var c="";b?11<b&&(b=11):b=a&-16777216?11:a&-65536?8:6;if(null==a||isNaN(a))for(;0<b--;)c="?"+c;else for(;0<b--;)c=String.fromCharCode((a&7)+48)+c,a>>=3;return""+c}function F(a,b,c){var d="";b?8<b&&(b=8):b=a&-65536?8:4;if(null==a||isNaN(a))for(;0<b--;)d="?"+d;else for(;0<b--;){var e=a&15,e=e+(0<=e&&9>=e?48:55),d=String.fromCharCode(e)+d;a>>=4}return(c?"0x":"")+d}function wa(a){var b=a,c=a.lastIndexOf("/");0<=c&&(b=a.substr(c+1));c=b.indexOf("&");0<c&&(b=b.substr(0,c));return b}
|
||||
function xa(a){var b="",c=a.lastIndexOf(".");0<=c&&(b=a.substr(c+1).toLowerCase());return b}function ya(){var a=G();return-1!==a.indexOf("pcjs.org",a.length-8)}function za(a){return a.replace(/[&<>"']/g,function(a){return Aa[a]})}function Ba(a){return String.prototype.trim?a.trim():a.replace(/^\s+|\s+$/g,"")}var Aa={"&":"&","<":"<",">":">",'"':""","'":"'"};
|
||||
function Ca(){function a(a){return(10>a?"0":"")+a}var b=new Date;return b.getFullYear()+"-"+a(b.getMonth()+1)+"-"+a(b.getDate())+" "+a(b.getHours())+":"+a(b.getMinutes())+":"+a(b.getSeconds())}
|
||||
function H(a,b,c,d){c=void 0===c?!1:c;var e=0,f=null,h=null;if("object"==typeof resources&&(f=resources[a]))return d&&d(a,f,e),[f,e];if(c&&"function"==typeof resources)return resources(a,function(b,c){d&&d(a,b,c)}),h;var g=window.XMLHttpRequest?new window.XMLHttpRequest:new window.ActiveXObject("Microsoft.XMLHTTP");c&&(g.onreadystatechange=function(){4===g.readyState&&(f=g.responseText,200==g.status||!g.status&&f.length&&"file:"==(window?window.location.protocol:"file:")||(e=g.status||-1),d&&d(a,
|
||||
f,e))});if(b&&"object"==typeof b){var l="",m;for(m in b)b.hasOwnProperty(m)&&(l&&(l+="&"),l+=m+"="+encodeURIComponent(b[m]));l=l.replace(/%20/g,"+");g.open("POST",a,c);g.setRequestHeader("Content-type","application/x-www-form-urlencoded");g.send(l)}else g.open("GET",a,c),"bytes"==b&&g.overrideMimeType("text/plain; charset=x-user-defined"),g.send();c||(f=g.responseText,200!=g.status&&(e=g.status||-1),d&&d(a,f,e),h=[f,e]);return h}
|
||||
function Da(a,b){var c,d={D:null,I:null,R:null,P:null};if("["==b.charAt(0)||"{"==b.charAt(0))try{var e,f,h;if("<"==b.substr(0,1))throw Error(b);h=0>b.indexOf("0x")&&'["'!=b.substr(0,2)?JSON.parse(b.replace(/([a-z]+):/gm,'"$1":').replace(/\/\/[^\n]*/gm,"")):eval("("+b+")");d.R=h.load;d.P=h.exec;if(e=h.bytes)d.D=e;else if(e=h.words)for(d.D=Array(2*e.length),f=c=0;c<e.length;c++)d.D[f++]=e[c]&255,d.D[f++]=e[c]>>8&255;else if(e=h.data)for(d.D=Array(4*e.length),f=c=0;c<e.length;c++)d.D[f++]=e[c]&255,d.D[f++]=
|
||||
e[c]>>8&255,d.D[f++]=e[c]>>16&255,d.D[f++]=e[c]>>24&255;else d.D=h;d.I=h.symbols;d.D.length?1==d.D.length&&(x(d.D[0]),d=null):(x("Empty resource: "+a),d=null)}catch(g){x("Resource data error ("+a+"): "+g.message),d=null}else{e=[];b=b.replace(/\n/gm," ").replace(/ +$/,"").split(" ");for(c=0;c<b.length;c++){f=parseInt(b[c],16);if(isNaN(f)){x("Resource data error ("+a+"): invalid hex byte ("+b[c]+")");break}e.push(f&255)}c==b.length&&(d.D=e)}return d}
|
||||
function G(){return"http://"+(window?window.location.host:"www.pcjs.org")}function Ea(){if(null==Fa){var a=!1;if(window)try{window.localStorage.setItem("PCjs.localStorage","PCjs.localStorage"),a="PCjs.localStorage"==window.localStorage.getItem("PCjs.localStorage"),window.localStorage.removeItem("PCjs.localStorage")}catch(b){a=!1}Fa=a}return Fa}function Ga(a){var b;if(window)try{b=window.localStorage.getItem(a)}catch(c){}return b}
|
||||
function Ha(a,b){try{return window.localStorage.setItem(a,b),!0}catch(c){}return!1}function Ia(a){if(window){var b=window?window.navigator.userAgent:"";return"iOS"==a&&!!b.match(/(iPod|iPhone|iPad)/)&&!!b.match(/AppleWebKit/)||"MSIE"==a&&!!b.match(/(MSIE|Trident)/)||0<=b.indexOf(a)}return!1}
|
||||
function Ja(a){if(!Ka){var b,c={};if(window){b||(b=window.location.search.substr(1));for(var d,e=/\+/g,f=/([^&=]+)=?([^&]*)/g;d=f.exec(b);)c[decodeURIComponent(d[1].replace(e," "))]=decodeURIComponent(d[2].replace(e," "))}Ka=c}return Ka[a]}function La(a,b){if(window){var c=window[a];window[a]="function"!==typeof c?b:function(){c&&c();b()}}}function I(a){J.init.push(a)}
|
||||
function Ma(a){if(Na)try{for(var b=0;b<a.length;b++)a[b]()}catch(c){x(""+("An unexpected exception occurred:\n\n"+c.message+"\n\nPlease send this information to support@pcjs.org. Thanks."))}}function K(a){!Na&&a?(Na=!0,Oa&&Pa("init"),Ra&&Pa("show")):Na=a}function Pa(a){J[a]&&Ma(J[a])}var Ka=null,J={init:[],show:[],exit:[]},Oa=!1,Ra=!1,Na=!0,Fa=null;La("onload",function(){Oa=!0;Ma(J.init)});La("onpageshow",function(){Ra=!0;Ma(J.show)});La(Ia("Opera")||Ia("iOS")?"onunload":"onbeforeunload",function(){Ma(J.exit)});
|
||||
var C="pdp10",L="PDPjs",Sa=Math.pow(2,18),Ta=Math.pow(2,36),C="pdp10",L="PDPjs";
|
||||
function Ua(a,b){u.call(this,"Panel",a);this.A=this.B=0;this.O=b;this.f=this.v=this.g=this.h=0;this.F=this.H=-1;this.K=this.L=this.s=!1;this.M=Va;this.l={};this.a={START:[1,1,!0,!1,this.gb],STEP:[1,1,!1,!1,this.hb],ENABLE:[1,1,!1,!1,this.ab],CONT:[1,1,!0,!1,this.Za],DEP:[0,0,!0,!1,this.$a],EXAM:[1,1,!0,!1,this.bb],LOAD:[1,1,!0,!1,this.eb],TEST:[0,0,!0,!1,this.cb]};for(a=0;22>a;a++)this.a["S"+a]=[0,0,!1,!1,this.fb,a];this.o=this.c=this.j=this.m=null;this.exports={hold:this.Ya,toggle:this.sb,reset:this.ob,
|
||||
set:this.rb};E(this)}r(Ua,u);k=Ua.prototype;k.reset=function(a){this.stop();a&&M(this,this.h=0)};
|
||||
k.S=function(a,b,c,d){if(this.m&&this.m.S(a,b,c,d)||this.c&&this.c.S(a,b,c,d))return!0;switch(b){case "R0":case "R1":case "R2":case "R3":case "R4":case "R5":case "R6":case "R7":case "NF":case "ZF":case "VF":case "CF":case "PS":return this.i[b]=c,this.B++,!0;default:return"led"==a||"rled"==a?(this.i[b]=c,this.l[b]=d?1:0,this.B++,!0):"switch"==a?(void 0===this.a[b]&&(this.a[b]=[d?1:0,d?1:0]),this.i[b]=c,a=c.parentElement||c,a=a.parentElement||a,a.onmousedown=function(a,b){return function(){Wa(a,b)}}(this,
|
||||
b),a.onmouseup=a.onmouseout=function(a,b){return function(){N(a,b)}}(this,b),a.ontouchstart=function(a,b){return function(c){Wa(a,b);c.preventDefault()}}(this,b),a.ontouchend=function(a,b){return function(){N(a,b)}}(this,b),!0):u.prototype.S.call(this,a,b,c,d)}};k.U=function(a,b,c,d){this.m=a;this.j=b;this.c=c;this.o=d;Xa(this);Ya(this)};k.T=function(a,b){if(!b)if(this.O&&Za(),!a)this.reset(!0);else if(!this.restore(a))return!1;return!0};k.N=function(a){return a?this.save():!0};
|
||||
k.save=function(){var a=new O(this);a.set(0,[this.f,this.h,this.g]);return a.data()};k.restore=function(a){if(a=a[0])P(this,this.f=a[0]),M(this,this.h=a[1]),$a(this,a[2]);return!0};k.ob=function(){for(var a in this.a){var b=this.a[a];b[1]=b[0]}Ya(this);return!0};function ab(a,b,c){if(a=a.i[b])a.style.backgroundColor=c?"#ff0000":"#000000"}function Xa(a,b){for(var c in a.l)ab(a,c,null!=b?b:a.l[c])}
|
||||
function bb(a,b,c){if(a=a.i[b])a.style.marginTop=c?"0px":"20px",a.style.backgroundColor=c?"#00ff00":"#228B22"}function Ya(a){for(var b in a.a)bb(a,b,a.a[b][1])}k.Ya=function(a,b,c){if(Wa(this,b)){if(c){var d=this;setTimeout(function(){N(d,b);a&&a()},+c);return!1}N(this,b)}return!0};
|
||||
k.rb=function(a,b){if("SR"==a){a=b;b=8;var c;if(a){b||(b=10);var d=a.charAt(0),e=0<a.indexOf(",");e&&(a=a.replace(/,/g,""));"#"==d?(b=8,d=null):"$"==d&&(b=16,d=null);null==d?a=a.substr(1):("0"==d&&(d=a.charAt(1),"b"==d&&e&&(b=2,d=null),"o"==d?(b=8,d=null):"x"==d&&(b=16,d=null)),null==d?a=a.substr(2):(d=a.charAt(a.length-1).toLowerCase(),"y"==d?(b=2,d=null):"."==d?(b=10,d=null):"h"==d&&(b=16,d=null),null==d&&(a=a.substr(0,a.length-1))));var f,d=a;((e=b)&&10!=e?16==e?d.match(/^[0-9a-f]+$/i):8==e?d.match(/^[0-7]+$/):
|
||||
2==e&&d.match(/^[01]+$/):d.match(/^[0-9]+$/))&&!isNaN(f=parseInt(a,b))&&(c=f|0)}return $a(this,c)}return(c=this.a[a])?(c[1]=+b?1:0,bb(this,a,c[1]),!0):!1};k.sb=function(a){return Wa(this,a)?(N(this,a),!0):!1};function Wa(a,b){var c=a.a[b];return c?(bb(a,b,c[1]=1-c[1]),c[3]=!0,c[4]&&c[4].call(a,c[1],c[5]),b!=cb&&(a.K=b==db,a.L=b==eb),!0):!1}function N(a,b){var c=a.a[b];c&&(c[2]&&c[3]&&(bb(a,b,c[1]=c[0]),c[4]&&c[4].call(a,c[1],c[5])),c[3]=!1)}
|
||||
k.gb=function(a){a||this.c.b.u||(this.c.f=this.f%Sa,this.a[fb]&&this.a[fb][1]&&Q(this.c))};k.hb=function(){};k.ab=function(a){a||R(this.c)};k.Za=function(a){if(!a&&!this.c.b.u)if(this.a[fb]&&this.a[fb][1])Q(this.c);else{a=this.o;var b;if(b=a)a.b.ma&&(a.b.za=!0),b=!a.b.ma;if(b)ua(a,!0),a.f(0,null),ua(a,!1);else try{var c=this.c.ta(1);0<c&&(gb(this.c,c),hb(this.c,c,!0),ib(this.c,c))}catch(d){"number"!=typeof d&&(c=this.c,a=d.stack||d.message,c.b.error=!0,c.C(a))}this.stop();this.m&&S(this.m)}};
|
||||
k.$a=function(a){a&&!this.c.b.u&&(this.K&&jb(this),a=M(this,this.h=this.g),this.M==Va?kb(this.j,this.f,a):this.c.ca(this.f,a))};k.bb=function(a){if(!a&&!this.c.b.u){this.L&&jb(this);if(this.M==Va){a=this.j;var b=this.f;a=a.a[(b&a.g)>>>a.f].Ba(b&16383,b)}else a=this.c.Z(this.f);M(this,this.h=a)}};k.eb=function(a){a||this.c.b.u||P(this,this.g)};k.cb=function(a){a?(this.s=!0,Xa(this,!0)):(this.s=!1,Xa(this),$a(this,0))};k.fb=function(a,b){this.g=a?this.g|1<<b:this.g&~(1<<b)};
|
||||
function jb(a){var b=1,c=a.j.g;a.a[cb]&&a.a[cb][1]||(b=-b);P(a,a.f&~c|a.f+b&c)}function P(a,b){a.f=b&a.j.g;if(a.F!==a.f){a.F=a.f;b=a.F;for(var c=0;22>c;c++){var d=a,e="A"+c,f=b&1<<c;d.l[e]=f;d.s||ab(d,e,f)}}}function M(a,b){a.v=b%Ta;if(a.H!==a.v){a.H=a.v;b=a.H;for(var c=0;16>c;c++){var d=a,e="D"+c,f=b&1<<c;d.l[e]=f;d.s||ab(d,e,f)}}return a.v}function $a(a,b){a.g=b|0;for(b=0;22>b;b++)a.a["S"+b][1]=a.g&1<<b?1:0;Ya(a);return!0}k.stop=function(){P(this,this.c.f)};
|
||||
function Za(){for(var a=D(document,C,"panel"),b=0;b<a.length;b++){var c=a[b],d=A(c),e=na(d.id);e||(e=new Ua(d,!0));B(e,c)}}var Va=7,db="DEP",fb="ENABLE",eb="EXAM",cb="STEP";I(Za);function lb(a,b,c){u.call(this,"Bus",a);this.c=b;this.o=c;this.s=+a.busWidth||18;this.l=1<<this.s;this.g=this.l-1;this.f=Math.log2(16384);this.h=this.l/16384|0;this.a=[];a=new T;mb(a);this.a=Array(this.h);for(b=0;b<this.h;b++)this.a[b]=a;E(this)}r(lb,u);k=lb.prototype;k.reset=function(){};
|
||||
k.T=function(a,b){if(!b)if(!a)this.reset();else if(!this.restore(a))return!1;return!0};k.N=function(a){return a?this.save():!0};k.save=function(){var a=new O(this);a.set(0,nb(this));return a.data()};k.restore=function(a){a:{a=a[0];var b;for(b=0;b<a.length-1;b+=2){var c=a[b],d=a[b+1];if(d&&4096>d.length){for(var e=0,f=Array(4096),h=0;h<d.length-1;)for(var g=d[h++],l=d[h++];g--;)f[e++]=l;d=f}e=this.a[c];if(!e||!e.restore(d)){x("Unable to restore memory block "+c);a=!1;break a}}a=!0}return a};
|
||||
function ob(a,b,c,d){for(var e=b,f=c,h=e>>>a.f;0<f&&h<a.a.length;){var g=a.a[h],l=16384*h,m=16384-(e-l);m>f&&(m=f);if(g&&g.size){if(g.type==d){if(e+f<=g.X)return g.ra+=g.X-e,g.X=e,!0;if(e>=g.X+g.ra){m=g.size-(e-l);m>f&&(m=f);g.ra=e-g.X+m;e=l+16384;f-=m;h++;continue}}return pb(qb,e,f)}e=new T(0,e,m,16384,d);mb(e,g);a.a[h++]=e;e=l+16384;f-=m}return 0>=f?(a.status("Added "+(c>>10)+"Kb "+rb[d]+" at "+va(b)),!0):pb(sb,b,c)}function kb(a,b,c){a.a[(b&a.g)>>>a.f].sa(b&16383,c&65535,b)}
|
||||
function nb(a){for(var b=0,c=[],d=0;d<a.h;d++){var e=a.a[d];if(e.oa||e.Xa){c[b++]=d;var f=b++;if(e=e.save()){for(var h=0,g=0,l=[];h<e.length;){for(var m=e[h],p=h+1;p<e.length&&e[p]===m;)p++;l[g++]=p-h;l[g++]=m;h=p}l.length<e.length&&(e=l)}c[f]=e}}return c}function pb(a,b,c){x("Memory block error ("+a+": "+F(b)+","+F(c)+")");return!1}var qb=1,sb=2;function tb(a){u.call(this,"Device",a)}r(tb,u);k=tb.prototype;k.U=function(a,b,c,d){this.j=b;this.m=a;this.c=c;this.o=d;E(this)};
|
||||
k.T=function(a,b){if(!b)if(!a)this.reset();else if(!this.restore(a))return!1;return!0};k.N=function(a){return a?this.save():!0};k.reset=function(){};k.save=function(){return(new O(this)).data()};k.restore=function(){return!0};I(function(){for(var a=D(document,C,"device"),b=0;b<a.length;b++){var c,d=a[b];c=A(d);switch(c.type){case "default":c=new tb(c),B(c,d)}}});
|
||||
function T(a,b,c,d,e){this.id=ub+=2;this.X=b;this.ra=c;this.size=d||0;this.type=e||vb;this.m=e==wb;this.o=this.Oa;this.Z=this.Ba=this.Aa;this.a=this.Pa;this.ca=this.sa=this.Da;this.i=this.j=0;mb(this);this.oa=this.Xa=!1;if(this.size){a=this.c=Array(this.size);for(b=0;b<a.length;b++)a[b]=0;xb(this,yb)}else xb(this)}k=T.prototype;k.save=function(){return this.c};k.restore=function(a){return a&&this.size==a.length?(this.c=a,this.oa=!0):!1};function xb(a,b){b||(b=zb);Ab(a,b,void 0);Bb(a,b,void 0)}
|
||||
function Ab(a,b,c){c&&a.i||(a.Z=b[2]||a.Aa);if(c||void 0===c)a.o=b[0]||a.Oa,a.Ba=b[2]||a.Aa}function Bb(a,b,c){c&&a.j||(a.ca=!a.m&&b[3]||a.Da);if(c||void 0===c)a.a=b[1]||a.Pa,a.sa=b[3]||a.Da}function mb(a,b){a.i=a.j=0;b&&((a.i=b.i)&&Ab(a,Cb,!1),(a.j=b.j)&&Bb(a,Cb,!1))}k.Oa=function(){return 0};k.Pa=function(){};k.Aa=function(a,b){return this.Z(a,b)};k.Da=function(a,b,c){this.ca(a,b,c)};k.jb=function(a,b,c){c=this.c[c];return c=32>=a+b?c>>a&(1<<b)-1:Math.trunc(c/Math.pow(2,a))%Math.pow(2,b)};
|
||||
k.mb=function(a){return this.c[a]};k.vb=function(a,b,c,d){var e=this.c[d];if(32>=a+b)var f=(1<<b)-1<<a,e=e&~f|c<<a&f;else f=Math.pow(2,a),c%=Math.pow(2,b),a=e%Math.pow(2,a+b),e=e-a+c*f+a%f;this.c[d]!=e&&(this.c[d]=e,this.oa=!0)};k.xb=function(a,b){this.c[b]=a;this.oa=!0};k.ib=function(a,b,c,d){return this.o(a,b,c,d)};k.kb=function(a,b){return this.Ba(a,b)};k.ub=function(a,b,c,d,e){this.m||this.a(a,b,c,d,e)};k.wb=function(a,b,c){this.m||this.sa(a,b,c)};
|
||||
var vb=0,wb=2,rb=["NONE","RAM","ROM"],ub=0,zb=[],yb=[T.prototype.jb,T.prototype.vb,T.prototype.mb,T.prototype.xb],Cb=[T.prototype.ib,T.prototype.ub,T.prototype.kb,T.prototype.wb];
|
||||
function U(a,b){u.call(this,"CPU",a);b=+a.cycles||b;var c=+a.multiplier||1;this.ua=0;this.xa=b;this.L=c;this.va=Math.round(this.xa/1E4)/100;this.H=this.va*this.L;this.wa=this.fa=this.M=this.la=0;this.b.u=this.b.Ca=!1;this.b.G=a.autoStart;"string"==typeof this.b.G&&(this.b.G="true"==this.b.G);this.b.na=!1;this.ja=this.K=0;this.ka=+a.csStart;this.da=+a.csInterval;this.ea=+a.csStop;this.l=[];this.Ja=this.pb.bind(this);this.B=this.h=this.v=this.A=this.g=this.ba=this.s=this.O=this.ya=this.F=0;this.ga=
|
||||
null;E(this)}r(U,u);k=U.prototype;k.U=function(a,b,c,d){this.m=a;this.j=b;this.o=d;this.ga=a.g;for(a=0;a<Db.length;a++)(b=this.i[Db[a]])&&this.m.S(null,Db[a],b);E(this)};k.reset=function(){};k.save=function(){return null};k.restore=function(){return!1};
|
||||
k.T=function(a,b){var c=V(this.m,"autoStart");null!=c?this.b.G="true"==c?!0:"false"==c?!1:!!c:null==this.b.G&&(this.b.G=void 0===this.i.run);if(!b){if(a){Eb(this);if(!this.restore(a))return!1;Fb(this)}else this.reset();this.status("No debugger detected");this.b.G||this.J("CPU will not be auto-started "+(this.ga?"(click Run to start)":"(type 'go' to start)"))}return!0};k.N=function(a){return a?this.save():!0};k.G=function(){return this.b.u?!0:this.b.G?(Q(this),!0):!1};k.Ma=function(){return 0};
|
||||
function Fb(a){void 0===a.ka&&(a.ka=0);void 0===a.da&&(a.da=-1);void 0===a.ea&&(a.ea=-1);a.b.na=0<=a.ka&&0<a.da;a.b.na&&(a.ja=0,a.K=a.ka-a.B)}function ib(a,b){if(a.b.na){var c=!1;a.ja=a.ja+a.Ma()|0;a.K-=b;0>=a.K&&(a.K+=a.da,c=!0);0<=a.ea&&a.ea<=W(a)&&(a.da=a.ea=-1,Fb(a),R(a),c=!0);c&&a.J(W(a)+" cycles: checksum="+F(a.ja))}}
|
||||
k.S=function(a,b,c){var d=this;switch(b){case "power":case "reset":return this.i[b]=c,!0;case "run":return this.i[b]=c,c.onclick=function(){var a;if(a=d.m)if(a=d.m,a.b.w)a=!0;else{var b=null,c,g=y(a.id);for(c=0;c<g.length&&(b=g[c],b===a||b.b.ready);c++);if(c==g.length)for(c=0;c<g.length&&(b=g[c],b===a||b.b.w);c++);c==g.length&&(b=a);x("The "+b.type+" component ("+b.id+") is not "+(b.b.ready?"powered yet":"ready yet"+(b.pa?" (waiting for notification)":""))+".");a=!1}a&&(d.b.u?R(d):Q(d))},!0;case "speed":return this.i[b]=
|
||||
c,!0;case "setSpeed":return this.i[b]=c,c.onclick=function(){X(d,d.L<<1,!0)},c.textContent=this.H.toFixed(2)+"Mhz",!0}return!1};function hb(a,b,c){a.B+=b;c&&(a.v=a.A=0)}function Hb(a,b){var c=1;b&&1<a.L&&a.F&&(c=a.F/a.va);a.wa=Math.round(1E3/Ib);a.fa=Math.floor(a.xa/Ib*c);b||(a.M=a.fa);a.la=0}function W(a){return a.B+a.h+a.v-a.A}function Eb(a){a.F=0;a.ya=0;a.B=a.h=a.v=a.A=0;Fb(a);X(a,1)}
|
||||
function X(a,b,c){if(void 0!==b){.8>a.F/a.H&&(b=1);a.L=b;b=a.va*a.L;if(a.H!=b){a.H=b;b=a.H.toFixed(2)+"Mhz";var d=a.i.setSpeed;d&&(d.textContent=b);a.J("target speed: "+b)}c&&a.m&&(c=a.m,c.Y&&(d=b=0,window&&(b=window.scrollX,d=window.scrollY),c.Y.focus(),window&&window.scrollTo(b,d)))}hb(a,a.h);a.h=0;a.g=w();a.s=0;Hb(a)}function Jb(a){for(var b=[],c=0;c<a.l.length;c++)b.push(a.l[c][0]);return b}
|
||||
function gb(a,b){for(var c=a.l.length-1;0<=c;c--){var d=a.l[c];0>d[0]||(d[0]-=b,0>=d[0]&&(d[0]=-1,d[1]()))}}function Kb(a,b){var c=a.v-=a.A;a.A=0;b&&(a.v=0);return c}
|
||||
k.pb=function(){if(this.b.u){this.la>=this.xa&&Hb(this,!0);this.O=0;this.ba=w();if(this.s){var a=this.ba-this.s;a>this.wa&&(this.g+=a,this.g>this.ba&&(this.g=this.ba))}try{do{for(var b,c=this.b.na?1:this.fa,d=this.l.length-1;0<=d;d--){var e=this.l[d];0>e[0]||c>e[0]&&(c=e[0])}b=c;try{this.ta(b)}catch(f){if("number"!=typeof f)throw f;}b=Kb(this,!0);this.O+=b;this.h+=b;ib(this,b);gb(this,b);this.M-=b;if(0>=this.M){this.M+=this.fa;++this.ya>=Lb&&(this.m&&S(this.m,void 0),this.ya=0);break}}while(this.b.u)}catch(f){R(this);
|
||||
this.m&&this.m.stop(w(),W(this));a=f.stack||f.message;this.b.error=!0;this.C(a);return}if(this.b.u){a=setTimeout;b=this.Ja;this.s=w();c=this.wa;this.O&&(c=Math.round(c*this.O/this.fa));c-=this.s-this.ba;if(d=this.s-this.g)this.F=Math.round(this.h/(10*d))/100,864E5<=d&&(this.B=0,X(this));if(0>c||this.F<this.H)-1E3>c&&(this.g-=c),c=0;this.la+=this.O;this.s+=c;a(b,c)}}};
|
||||
function Q(a){var b;a.b.error?(a.J(a.toString()+" error"),b=!0):b=!1;if(!b)if(a.b.u)a.J(a.toString()+" busy");else{X(a);a.b.u=!0;a.b.Ca=!0;if(b=a.i.run)b.textContent="Halt";a.m&&a.m.start(a.g,W(a));a.o||a.status("Started");setTimeout(a.Ja,0)}}k.ta=function(){return 0};function R(a){var b=!1;if(a.b.u){Kb(a);hb(a,a.h);a.h=0;a.b.u=!1;if(b=a.i.run)b.textContent="Run";a.m&&a.m.stop(w(),W(a));b=!0;a.o||a.status("Stopped")}a.b.complete=void 0;return b}var Ib=30,Lb=15,Db=["power","reset"];
|
||||
function Mb(a){var b=+a.model||1001;U.call(this,a,6666667);this.Ua=b;this.Ha=+a.addrReset||0;this.W=null;this.Ga=[];this.b.complete=!1}r(Mb,U);k=Mb.prototype;k.reset=function(){this.status("Model "+this.Ua);this.b.u&&R(this);this.V=this.f=this.Ka=this.Ha;this.a=0;this.Z=this.lb;this.ca=this.yb;this.W=null;Eb(this);this.b.error=!1;U.prototype.reset.call(this)};k.Ma=function(){return 0};
|
||||
k.save=function(){var a=new O(this);a.set(0,[this.f,this.Ka,this.V,this.a]);a.set(1,[]);a.set(2,[this.B,this.L,this.b.G]);a.set(3,Nb(this));a.set(4,Jb(this));return a.data()};
|
||||
k.restore=function(a){var b;b=a[0];ea();ba();ea();var c=b[Symbol.iterator];b=c?c.call(b):fa(b);this.f=b.next().value;this.Ka=b.next().value;this.V=b.next().value;this.a=b.next().value;b=a[2];this.B=b[0];X(this,b[1]);this.b.G=b[2];b=a[3];for(c=b.length-1;0<=c;c--){var d;a:{for(d=0;d<this.Ga.length;d++){var e=this.Ga[d];if(e.tb===b[c]){d=e;break a}}d=null}d&&(d.next=this.W,this.W=d)}a=a[4];for(b=0;b<this.l.length&&b<a.length;b++)this.l[b][0]=a[b];return!0};
|
||||
function Nb(a){var b=[];for(a=a.W;a;)b.push(a.tb),a=a.next;return b}k.lb=function(a){var b=this.j;a=this.V=a;return b.a[(a&b.g)>>>b.f].Z(a&16383,a)};k.yb=function(a,b){var c=this.j;a=this.V=a;c.a[(a&c.g)>>>c.f].ca(a&16383,b,a)};
|
||||
k.ta=function(a){this.b.complete=!0;var b=a?this.b.Ca?0:1:-1;this.b.Ca=!1;this.v=this.A=a;this.a=this.a&-5|0;do{if(a=this.a)if(a=this.a&11)this.a&2?this.W||(this.a&=-3):this.a&1&&this.a++,a=!1;if(a){if(this.a&4&&this.o.h(this.f,b)){R(this);break}if(0>b)break}this.a&=15;a=this.f;this.Z(a);this.f=(a+1)%Sa}while(0<this.A);return this.b.complete?this.v-this.A:!1===this.b.complete?-1:0};I(function(){for(var a=D(document,C,"cpu"),b=0;b<a.length;b++){var c=a[b],d=A(c),d=new Mb(d);B(d,c)}});
|
||||
function Ob(a){u.call(this,"ROM",a);this.I=this.a=null;this.l=+a.addr;this.g=+a.size;this.f=a.alias;"string"==typeof this.f&&(this.f=eval(this.f));this.h=a.file;this.s=wa(this.h);if(this.h){a=this.h;var b=xa(this.s);"json"!=b&&"hex"!=b&&(a=G()+"/api/v1/dump?file="+this.h+"&format=bytes&decimal=true");var c=this;H(a,null,!0,function(a,b,f){f?(c.C("Unable to load ROM resource (error "+f+": "+a+")"),c.h=null):(ka(c.ia,a,b),(a=Da(a,b))?(c.a=a.D,c.I=a.I):c.h=null);Pb(c)})}}r(Ob,u);
|
||||
Ob.prototype.U=function(a,b,c,d){this.j=b;this.c=c;this.o=d;Pb(this)};Ob.prototype.T=function(){this.I&&(this.o&&this.o.a(this.id,this.l,this.g,this.I),delete this.I);return!0};Ob.prototype.N=function(){return!0};
|
||||
function Pb(a){if(!ta(a)){if(a.h){if(!a.a||!a.j)return;a.g||(a.g=a.a.length);if(a.a.length!=a.g){var b="ROM size ("+F(a.a.length,8,!0)+") does not match specified size ("+F(a.g,8,!0)+")";a.b.error=!0;a.C(b)}else{b=a.l;if(ob(a.j,b,a.g,wb)){var c;for(c=0;c<a.a.length;c++)kb(a.j,b+c,a.a[c]);b=!0}else b=!1;if(b){b=[];"number"==typeof a.f?b.push(a.f):null!=a.f&&a.f.length&&(b=a.f);for(c=0;c<b.length;c++){for(var d=a,e=b[c],f=d.j,h=d.g,g=[],l=d.l>>>f.f;0<h&&l<f.a.length;)g.push(f.a[l++]),h-=16384;f=d.j;
|
||||
d=d.g;h=0;for(e>>>=f.f;0<d&&e<f.a.length;){l=g[h++];if(!l)break;f.a[e++]=l;d-=16384}}delete a.a}}}E(a)}}I(function(){for(var a=D(document,C,"rom"),b=0;b<a.length;b++){var c=a[b],d=A(c),d=new Ob(d);B(d,c)}});
|
||||
function Y(a){u.call(this,"RAM",a);this.I=this.f=null;this.g=+a.addr;this.h=+a.size;this.R=a.load;this.P=a.exec;null!=this.R&&(this.R=+this.R);null!=this.P&&(this.P=+this.P);this.l=!1;this.a=a.file;this.s=wa(this.a);if(this.a){a=this.a;var b=xa(this.s);"json"!=b&&"hex"!=b&&(a=G()+"/api/v1/dump?file="+this.a+"&format=bytes&decimal=true");var c=this;H(a,null,!0,function(a,b,f){f?(c.C("Unable to load RAM resource (error "+f+": "+a+")"),c.a=null):(ka(c.ia,a,b),(a=Da(a,b))?(c.f=a.D,c.I=a.I,null==c.R&&
|
||||
(c.R=a.R),null==c.P&&(c.P=a.P)):c.a=null);Qb(c)})}}r(Y,u);Y.prototype.U=function(a,b,c,d){this.j=b;this.c=c;this.o=d;Qb(this)};Y.prototype.T=function(a,b){this.I&&(this.o&&this.o.a(this.id,this.g,this.h,this.I),delete this.I);b||this.reset();return!0};Y.prototype.N=function(){return!0};
|
||||
function Qb(a){if(a.j&&(!a.l&&a.h&&(ob(a.j,a.g,a.h,1)?a.l=!0:a.h=0),!ta(a))){if(!a.l)x("No RAM allocated");else if(a.a){if(!a.f||!a.j)return;Rb(a,a.f,a.R,a.P,a.g)?a.status('Loaded image "'+a.s+'"'):a.C('Error loading image "'+a.s+'"')}E(a)}}
|
||||
Y.prototype.reset=function(){if(this.l){for(var a=this.j,b=this.g,c=this.h,d=b&16383,b=b>>>a.f;0<c&&b<a.a.length;){var e=a.a[b],f=c,h=0,g,d=d||0,h=h||0;0>h&&h>=ja&&(h+=ia);h=Math.trunc(Math.abs(h))%ia;void 0===f&&(f=e.size);for(g=d;f--&&g<e.size;g++)e.sa(d,h,e.X+d);c-=16384;b++;d=0}this.f&&Rb(this,this.f,this.R,this.P,this.g,!this.o)}};
|
||||
function Rb(a,b,c,d,e,f){var h=!1,g=!1;if(null==c)for(var l=0;l<b.length-1;){var m=b[l]&255|(b[l+1]&255)<<8;if(m)if(m&255){if(1!=m)break;if(l+6>=b.length)break;for(var l=l+2,p=b[l++]&255|(b[l++]&255)<<8,n=b[l++]&255|(b[l++]&255)<<8,m=m+((p&255)+(p>>8)+(n&255)+(n>>8)),Qa=l,Gb=p-=6;0<p&&l<b.length;)m+=b[l++]&255,p--;if(p||l>=b.length)break;m+=b[l++]&255;if(m&255)break;if(Gb)for(;Gb--;)kb(a.j,n++,b[Qa++]);else n&1?h=!0:null==d&&(d=n);g=!0}else l++;else l+=2}if(!g&&(null==c&&(c=e),null!=c)){for(g=0;g<
|
||||
b.length;g++)kb(a.j,c+g,b[g]);g=!0}if(g){if(null==d||h)R(a.c),f=!1;null!=d&&(a=a.c,a.Ha=d,a.f=d%Sa,f?a.b.w?a.b.u||Q(a):a.b.G=!0:a.o&&a.b.w?R(a)||a.m.b.reset||(a.o.g(),S(a.m,-1)):!1===f&&R(a),!a.b.u&&a.ga&&a.ga.stop())}return g}I(function(){for(var a=D(document,C,"ram"),b=0;b<a.length;b++){var c=a[b],d=A(c),d=new Y(d);B(d,c)}});
|
||||
function Sb(a){u.call(this,"SerialPort",a);this.h=a.upperCase;"string"==typeof this.h&&(this.h="true"==this.h);this.g=!0;this.f=[];var b=a.binding;if("console"!=b){var c;a=Tb;b&&(void 0===c&&(c="Panel"),(c=z(c,this.id))&&(b=c.i[b])&&this.S(null,a,b))}this.a=this.l=null;this.exports={connect:this.Na,receiveData:this.qa,receiveStatus:this.nb,setConnection:this.qb}}r(Sb,u);k=Sb.prototype;
|
||||
k.S=function(a,b,c){var d=this;switch(b){case Tb:return this.i[b]=c,c.onkeydown=function(a){a=a||window.event;var b=0,c=a.keyCode;8==c?b=a.altKey?t.Fa:t.Ta:46==c?b=t.Fa:a.ctrlKey&&c>=t.Ea&&c<=t.Wa&&(b=c-(t.Ea-t.Qa));b&&(a.preventDefault&&a.preventDefault(),d.qa(b));return!0},c.onkeypress=function(a){a=a||window.event;if(!a.metaKey){var b=a.which||a.keyCode;a.altKey&&b==t.Sa&&(b=t.Ra);d.qa(b);a.preventDefault&&a.preventDefault()}return!0},c.onpaste=function(a){a.stopPropagation&&a.stopPropagation();
|
||||
a.preventDefault&&a.preventDefault();(a=a.clipboardData||window.clipboardData)&&d.qa(a.getData("Text"))},c.removeAttribute("readonly"),!0}return!1};k.U=function(a,b,c,d){this.m=a;this.j=b;this.c=c;this.o=d;E(this)};
|
||||
k.Na=function(a){if(!this.a){var b=V(this.m,"connection");if(b){var c=b.split("->");if(2==c.length){var d=Ba(c[0]);if(d!=this.ha)return;c=Ba(c[1]);if(this.a=na(c)){var e=this.a.exports;if(e){var f=e.connect;f&&f.call(this.a,this.g);if(this.l=e.receiveData){this.g=a;this.status("Connected "+this.ia+"."+d+" to "+c);return}}}}this.status("Unable to establish connection: "+b)}}};k.T=function(a,b){if(!b)if(this.Na(this.g),!a)this.reset();else if(!this.restore(a))return!1;return!0};
|
||||
k.N=function(a){return a?this.save():!0};k.reset=function(){};k.save=function(){var a=new O(this);a.set(0,[]);return a.data()};k.restore=function(){return!0};k.qa=function(a){if("number"==typeof a)this.f.push(a);else if("string"==typeof a)for(var b=0,c,d=0;d<a.length;d++){c=b;b=a.charCodeAt(d);if(10==b){if(13==c)continue;b=13}this.f.push(b)}else this.f=this.f.concat(a);return!0};k.nb=function(){};k.qb=function(a,b){return this.a?!1:(this.a=a,this.l=b,!0)};var Tb="buffer";
|
||||
I(function(){for(var a=D(document,C,"serial"),b=0;b<a.length;b++){var c=a[b],d=A(c),d=new Sb(d);B(d,c)}});
|
||||
function Ub(a,b,c){u.call(this,"Computer",a);this.b.w=!1;this.L=null;Vb(this,b);this.H=V(this,"autoPower",a,6);this.h=0;this.V=+a.busWidth||+a.buswidth;this.B=this.s=this.F=null;this.A=this.O=!1;this.v=this.l=null;this.M=this.K=!1;this.W=V(this,"url")||"";(Math.random()+.1).toString(36);this.f=Wb(this);if(this.c=z("CPU",this.id)){this.o=z("Debugger",this.id);this.j=new lb({id:this.ia+".bus",busWidth:this.V},this.c,this.o);var d,e=y(this.id);if((this.g=z("Panel",this.id))&&this.g.Y)for(b=0;b<e.length;b++)d=
|
||||
e[b],d.C=this.g.C,d.J=this.g.J,d.Y=this.g.Y;this.J(L+" v1.34.0\nCopyright \u00a9 2012-2017 Jeff Parsons <Jeff@pcjs.org>\nLicense: GPL version 3 or later <http://gnu.org/licenses/gpl.html>");for(b=0;b<e.length;b++)d=e[b],d.U&&d.U(this,this.j,this.c,this.o);b=null;d=V(this,"resume",a);void 0!==d&&(1<d.length?b=this.s=d:this.a=parseInt(d,10));var f;if(a=V(this,"state")||(f=!0,a.state))this.F=b=a,f||(this.A=!0,this.a=Xb),this.a&&(this.v=new O(this,"1.34.0"),Z(this.v)?b=null:delete this.v);!b&&this.a&&
|
||||
(b=Yb(this))&&(this.A=!0);if(b){var h=this;H(b,null,!0,function(a,b,c){c?(h.s=null,h.A=!1,h.C("Unable to load machine state from server (error "+c+(b?": "+Ba(b):"")+")")):(h.B=b,h.O=!0);E(h)})}else E(this);this.i.power||(this.H=!0);!c&&this.H&&Zb(this,this.aa)}else x("Unable to find CPU component")}r(Ub,u);function Vb(a,b){if(!b){var c;if("object"==typeof resources&&(c=resources.parms))try{b=eval("("+c+")")}catch(d){x(d.message+" ("+c+")")}}a.L=b}
|
||||
function V(a,b,c,d){var e=b.toLowerCase(),e=Ja(b)||Ja(e);void 0===e&&a.L&&(e=a.L[b]);void 0===e&&c&&(e=c[b]);void 0===e&&"object"==typeof resources&&resources[b]&&(e=b);void 0===e&&(e=void 0);if("string"==typeof e&&d)switch(d){case 4:e=+e;isNaN(e)&&(e=0);break;case 6:e="true"==e}return e}function Zb(a,b,c){for(var d=y(a.id),e=0;e<=d.length;e++){var f=e<d.length?d[e]:a;if(!ta(f)){ta(f,function(){Zb(a,b,c)});return}}b.call(a,c)}
|
||||
function $b(a,b){var c=new O(a,"1.34.0",ac);if(Z(c)&&bc(c)){var d=c.get(cc),e=b?b.get(cc):"unknown";d!=e&&(a.C("Machine state may be out-of-date\n("+d+" vs. "+e+")\nCheck your browser's local storage limits"),b||c.clear())}}k=Ub.prototype;
|
||||
k.aa=function(a){void 0===a&&(a=this.a||(this.B?dc:Xb));if(!this.h){this.h++;var b=!1,c=!1;this.K=!1;var d=this.v||new O(this,"1.34.0");if(a==ec)b=!0;else if(a>Xb){if(Z(d,this.B)){this.l=new O(this,"1.34.0",fc);Z(this.l)&&(gc(this,d),a=hc,ic(this.l));this.l.set(cc,Ca());jc(this.l);var e=this.a&&!this.A;if(a==dc||ma("Click OK to restore the previous "+L+" machine state, or CANCEL to reset the machine.")){if(c=bc(d)){var f=d.get("code"),h=d.get("data");f&&("ok"==f?Z(d,h):("error"==f&&"no machine state"!=
|
||||
h?(this.C("Error: "+h),"unable to verify user"==h&&(Ha(kc,""),this.f=null)):this.J(f+": "+h),ic(d),Z(d)?(c=bc(d),e=!0):c=!1))}e&&$b(this,c?d:null)}else a==hc&&d.clear()}else $b(this);delete this.B;delete this.v}e=y(this.id);for(f=0;f<e.length;f++)h=e[f],h!==this&&h!=this.c&&(c=lc(this,h,d,b,c));b=[d,a,c];a!=ec?Zb(this,this.La,b):this.La(b)}};
|
||||
function lc(a,b,c,d,e){if(!b.b.w){b.b.w=!0;var f=null;try{if(e&&((f=c.get(b.id))||(f=c.get(b.id.replace(/[a-z0-9]\./i,".")))),"string"===typeof f&&(f=null),!b.T(f,d)&&f&&(x("Unable to restore state for "+b.type),a.F&&!a.O?(c.clear(),a.a=Xb,window&&window.location.reload()):a.K=!0,b.T(null),e=!1),!d&&b.Ia){var h=b.Ia.split("|");for(a=0;a<h.length;a++)b.status(h[a])}}catch(g){x("Error restoring state for "+b.type+" ("+g.message+")")}}return e}
|
||||
k.La=function(a){var b=a[0],c=0>a[1];a=a[2];this.M=!0;this.b.w=!0;var d=this.i.power;d&&(d.textContent="Shutdown");this.c&&(lc(this,this.c,b,c,a),S(this,-2),this.c.G());this.K&&(gc(this,b),b.clear());!c&&this.l&&(this.l.clear(),delete this.l);this.h=0};
|
||||
function gc(a,b){if(ma("There may be a problem with your "+L+" machine.\n\nTo help us diagnose it, click OK to send this "+L+" machine state to http://www.pcjs.org.")){var c=a.W;a=a.f||"";b=b.toString();var d={};d.app=L;d.ver="1.34.0";d.url=c;d.user=a;d.type="bug";d.data=b;H("http://www.pcjs.org/api/v1/report",d,!0)}}
|
||||
function mc(a,b,c){var d,e="none";if(a.h)return null;a.h--;var f=new O(a,"1.34.0"),h=new O(a,"1.34.0",ac),g=Ca();h.set(cc,g);f.set(cc,g);f.set(nc,"1.34.0");f.set(oc,window?window.location.href:null);f.set(pc,window?window.navigator.userAgent:"");a.c&&a.c.N&&(c&&(b&&(a.c.b.G=a.c.b.u),R(a.c)),d=a.c.N(b,c),"object"===typeof d&&f.set(a.c.id,d),c&&(a.c.b.w=!1,!1===d&&(e=null)));for(var g=y(a.id),l=0;l<g.length;l++){var m=g[l];m.b.w&&(m.N&&(d=m.N(b,c),"object"===typeof d&&f.set(m.id,d)),c&&(m.b.w=!1,!1===
|
||||
d&&(e=null)))}e&&(c?(g=d=!1,b?(a.f&&qc(a,a.f,f.toString()),jc(h)&&jc(f)||(e=null,d=g=!0)):a.a&&(d=!0,g=a.a==rc),d&&f.clear(g)):e=f.toString());c&&(a.b.w=!1,b=a.i.power)&&(b.textContent="Power");a.h=0;return e}k.reset=function(){this.b.reset=!0;this.j&&this.j.reset&&this.j.reset();this.c&&this.c.reset&&this.c.reset();for(var a=y(this.id),b=0;b<a.length;b++){var c=a[b];c!==this&&c!==this.j&&c!==this.c&&c.reset&&c.reset()}this.b.reset=!1;S(this,-1)};
|
||||
k.start=function(a,b){for(var c=y(this.id),d=0;d<c.length;d++){var e=c[d];"CPU"!=e.type&&e!==this&&e.start&&e.start(a,b)}S(this,-1)};k.stop=function(a,b){for(var c=y(this.id),d=0;d<c.length;d++){var e=c[d];"CPU"!=e.type&&e!==this&&e.stop&&e.stop(a,b)}S(this,-1)};
|
||||
function S(a,b){if(a.c){var c=a.c,d=b||0,e=c.i.speed;e&&(0>=d||30<=(c.ua+=d))&&(e.textContent=c.b.u?c.F.toFixed(2)+"Mhz":"Stopped",c.ua=0)}if(a.g&&(a=a.g,b=b||0,a.B)){c=a.c.b.u;d=!!(a.c.a&8);if(0>=b||60<=(a.A+=b)){e=a.c.f;if(a.i.PC){var f=a.o&&a.o.l||8,e=e||0,e=8==f?va(e,void 0):F(e,void 0);a.i.PC.textContent!=e&&(a.i.PC.textContent=e)}a.A=0}-1>b?a.f=a.c.f:0<b&&c&&!d&&(a.f=a.c.V);P(a,a.f);M(a,a.h)}}
|
||||
k.S=function(a,b,c){var d=this;switch(b){case "power":return this.i[b]=c,c.onclick=function(){d.h||(d.b.w?mc(d,!1,!0):Zb(d,d.aa))},!0;case "reset":return this.i[b]=c,c.onclick=function(){if(d.b.w&&!d.h)if(d.a&&!d.s){var a=ma("Click OK to save changes to this "+L+" machine.\n\nWARNING: If you CANCEL, all disk changes will be discarded.");mc(d,a,!0);!a&&d.F?window&&window.location.reload():d.aa(Xb)}else d.reset(),d.c&&!d.o&&d.c.G()},!0;case "save":if(ya())c.parentNode.removeChild(c);else return this.i[b]=
|
||||
c,c.onclick=function(){var a=Wb(d,!0);if(a){var b=!!(d.a&&!d.s||d.F),c=mc(d,b);b?qc(d,a,c):d.C("Resume disabled, machine state not saved")}},!0}return!1};function Wb(a,b){var c=a.f;c||((c=Ga(kc),void 0!==c)?!c&&b&&(b=null,window&&(b=window.prompt("Saving machine states on the pcjs.org server is currently unsupported.\n\nIf you're running your own server, enter your user ID below.","")),c=b)&&((c=sc(a,c))||a.C("The user ID is invalid.")):b&&a.C("Browser local storage is not available"));return c}
|
||||
function sc(a,b){a.f=null;b=H(G()+"/api/v1/user?req=verify&user="+b);var c=b[1];if(!b[0]&&c)try{b=eval("("+c+")"),b.code&&"ok"==b.code&&(Ha(kc,b.data),a.f=b.data)}catch(d){x(d.message+" ("+c+")")}return a.f}function Yb(a){var b=null;a.f&&(b=G()+"/api/v1/user?req=load&user="+a.f+"&state="+tc(a,"1.34.0"));return b}
|
||||
function qc(a,b,c){if(c){var d={req:"store"};d.user=b;d.state=tc(a,"1.34.0");d.data=c;b=H(G()+"/api/v1/user",d);d=b[0];if(b[1]){if(d){var e=d.indexOf("\n");0<e&&(d=d.substr(0,e));d.indexOf("Error: ")||(d=d.substr(7))}d='{"code":'+b[1]+',"data":"'+d+'"}'}b=JSON.parse(d);b&&"ok"==b.code?a.C("Machine state saved to server"):c&&(c=b&&b.data||"unable to save machine state",c="error"==b.code?"Error: "+c:"Error "+b.code+": "+c,a.C(c),Ha(kc,""),a.f=null)}}
|
||||
var fc="failsafe",ac="validate",cc="timestamp",nc="version",oc="url",pc="browser",kc="user",ec=-1,Xb=0,dc=1,hc=2,rc=3;I(function(){for(var a=D(document,C+"-machine"),b=0;b<a.length;b++)for(var c=a[b],d=A(c),c=D(c,C,"computer"),e=0;e<c.length;e++){var f=c[e],h=A(f),h=new Ub(h,d,!0);B(h,f);h.H&&Zb(h,h.aa)}});J.show.push(function(){for(var a=D(document,C,"computer"),b=0;b<a.length;b++){var c=A(a[b]);(c=z("Computer",c.id))&&c.M&&!c.b.w&&c.aa(ec)}});
|
||||
J.exit.push(function(){for(var a=D(document,C,"computer"),b=0;b<a.length;b++){var c=A(a[b]);(c=z("Computer",c.id))&&c.b.w&&mc(c,!(!c.a||c.s),!0)}});function O(a,b,c){this.id=a.id;this.i="";this.c={};this.j=this.m=!1;this.key=tc(a,b,c);ic(this,a.Va)}k=O.prototype;k.set=function(a,b){try{this.c[a]=b}catch(c){}};k.get=function(a){return this.c[a]||null};k.data=function(){return this.c};function Z(a,b){return b?(a.i=b,a.j=!0,a.m=!1,!0):a.j?!0:Ea()&&(b=Ga(a.key))?(a.i=b,a.j=!0):!1}
|
||||
function bc(a){var b=!0;if(!a.m)try{a.c=JSON.parse(a.i),a.m=!0}catch(c){x(c.message||c),b=!1}return b}function jc(a){var b=!0;if(Ea()){var c=JSON.stringify(a.c);Ha(a.key,c)||(x("Unable to store "+c.length+" bytes in browser local storage"),b=!1)}return b}k.toString=function(){return this.c?JSON.stringify(this.c):this.i};function ic(a,b){a.i="";a.c={};a.j=a.m=!1;b&&a.set("parms",b)}
|
||||
k.clear=function(a){ic(this);var b=[];try{for(var c=0,d=window.localStorage.length;c<d;c++)b.push(window.localStorage.key(c))}catch(e){}for(c=0;c<b.length;c++)if((d=b[c])&&(a||d.substr(0,this.key.length)==this.key)){try{window.localStorage.removeItem(d)}catch(e){}b.splice(c,1);c=0}};function tc(a,b,c){a=a.id;if(b){var d=b.indexOf(".");0<d&&(a+=".v"+b.substr(0,d))}c&&(a+="."+c);return a}var uc=0;
|
||||
function vc(a,b,c,d,e,f){e("Loading "+a+"...");H(a,null,!0,function(h,g,l){l?(g||(g="unable to load "+a+" ("+l+")"),f(g,null)):wc(g,a,b,c,d,e,f)})}
|
||||
function wc(a,b,c,d,e,f,h){function g(a,f){if(f)h(f,null);else{c&&(ka(c,b,a),(f=b)&&0>f.indexOf("/")&&"/"==window.location.pathname.slice(-1)&&(f=window.location.pathname+f),d?"}"==d.slice(-1)?(d=d.slice(0,-1),1<d.length&&(d+=",")):d='{state:"'+d+'",':d="{",d+='url:"'+f+'"}',"object"==typeof resources&&(f=null),a=a.replace(/(<machine[^>]*\sid=)(['"]).*?\2/,"$1$2"+c+"$2"+(d?" parms='"+d+"'":"")+(f?' url="'+f+'"':"")));e||(a=a.replace(/(<xsl:variable name="APPNAME">).*?(<\/xsl:variable>)/,"$1PDPjs$2"),
|
||||
a=a.replace(/(<xsl:variable name="APPCLASS">).*?(<\/xsl:variable>)/,"$1pdp11$2"));f=null;if("<"==a.charAt(0))try{e||(a=a.replace(/<!DOCTYPE(.|[\r\n])*]>\s*/g,"")),window.ActiveXObject||"ActiveXObject"in window?(f=new window.ActiveXObject("Microsoft.XMLDOM"),f.async=!1,f.loadXML(a)):f=(new window.DOMParser).parseFromString(a,"text/xml")}catch(p){f=null,a=p.message}else a="unrecognized XML: "+(255<a.length?a.substr(0,255)+"...":a);h(a,f)}}a?e?xc(a,f,g):g(a,null):h("no data"+(b?" for file: "+b:""),null)}
|
||||
function xc(a,b,c){var d;if(d=/<([a-z]+)\s+ref="(.*?)"(.*?)\/>/g.exec(a)){var e=d[2];b("Loading "+e+"...");H(e,null,!0,function(f,h,g){if(g||!h)c(a,"unable to resolve XML reference: "+d[0]+" ("+g+")");else{if(f=d[3])if(g=h.match(new RegExp("<"+d[1]+"[^>]*>"))){for(var l=g[0],m,p=/( [a-z]+=)(['"])(.*?)\2/g;m=p.exec(f);)l=0>l.indexOf(m[1])?l.replace(">",m[0]+">"):l.replace(new RegExp(m[1]+"(['\"])(.*?)\\1"),m[0]);g[0]!=l&&(h=h.replace(g[0],l))}else{c(a,"missing <"+d[1]+"> in "+e);return}h=h.replace(/<\?xml[^>]*>[\r\n]*/,
|
||||
"");a=a.replace(d[0],h);xc(a,b,c)}})}else c(a,null)}
|
||||
function yc(a,b,c,d){function e(a){if(void 0===g){var b=h&&D(h,"machine-warning");g=b&&b[0]||h}g&&(g.innerHTML=za(a))}function f(a){e("Error: "+a);l&&(--uc||K(!0));l=!1}var h,g,l=!0;uc++;la[a]={};try{if(h=document.getElementById(a)){var m;if("object"==typeof resources&&(m=resources.css)){var p=document.head||document.getElementsByTagName("head")[0],n=document.createElement("style");n.type="text/css";n.styleSheet?n.styleSheet.cssText=m:n.appendChild(document.createTextNode(m));p.appendChild(n)}c||
|
||||
(c="/versions/pdpjs/1.34.0/components.xsl");m=function(d,g){g?vc(c,null,null,!1,e,function(d,l){l?(ka(a,c,d),e("Processing "+b+"..."),window.ActiveXObject||"ActiveXObject"in window?(l=g.transformNode(l))?(h.outerHTML=l,--uc||K(!0)):f("transformNodeToObject failed"):document.implementation&&document.implementation.createDocument?(d=new XSLTProcessor,d.importStylesheet(l),(l=d.transformToFragment(g,document))?h.parentNode?(h.parentNode.replaceChild(l,h),--uc||K(!0)):f("invalid machine element: "+a):
|
||||
f("transformToFragment failed")):f("unable to transform XML: unsupported browser")):f(d)}):f(d)};"<"!=b.charAt(0)?vc(b,a,d,!0,e,m):wc(b,null,a,d,!1,e,m)}else f("missing machine element: "+a)}catch(Qa){f(Qa.message)}return l}window.embedPDP11=function(a,b,c,d){K(!1);return yc(a,b,c,d)};window.findMachineComponent=function(a,b){return z(b,a+".machine")};
|
||||
window.processMachineScript=function(a,b){var c=!1;a+=".machine";if("string"==typeof b&&!pa[a]){for(var c=!0,d=pa,e=a,f=b.length,h=[],g=[],l="",m=null,p=0;p<f;p++){var n=b[p];if('"'==n||"'"==n)m&&n!=m?l+=n:(m?m=null:m=n,l&&(g.push(l),l=""));else{if(!m){if("\r"==n||"\n"==n)n=";";if(" "==n||"\t"==n||";"==n){l&&(g.push(l),l="");";"==n&&g.length&&(h.push(g),g=[]);continue}}l+=n}}l&&g.push(l);g.length&&h.push(g);d[e]=h;oa(a)||(c=!1)}return c};window.enableEvents=K;window.sendEvent=Pa;})();//# sourceMappingURL=/tmp/pdpjs/1.34.0/pdp10.map
|
||||
|
|
@ -292,7 +292,7 @@ case "^":d=f^e;break;case "|":d=f|e;break;case "&&":d=f&&e?1:0;break;case "||":d
|
|||
function zj(a,b,c){var d;if(b){b=Aj(a,b);for(var e=0,f=!1,g=b,k=[],l=[],m=b.split(/(\|\||&&|\||^|&|!=|==|>=|>>>|>>|>|<=|<<|<|-|\+|%|\/|\*)/);e<m.length;){var n=m[e++],q=n.length,n=Ya(n);if(!n){f=!0;break}n=Bj(a,n,null,!1===c);if(void 0===n){f=!0;c=!1;break}k.push(n);if(e==m.length)break;var n=m[e++],t=n.length;l.length&&Cj[n]<Cj[l[l.length-1]]&&yj(k,l,1);l.push(n);b=b.substr(q+t)}yj(k,l)&&1==k.length||(f=!0);f?c&&a.v("error parsing '"+g+"' at character "+(g.length-b.length)):(d=k.pop(),c&&Dj(a,null,
|
||||
d))}return d}function Aj(a,b){for(var c,d=a.ia?"(":"{",e=a.ia?")":"}",f=new RegExp(a.ia?"\\((.*?)\\)":"\\{(.*?)\\}");(c=b.match(f))&&!(0<=c[1].indexOf(d));){var g=zj(a,c[1]);b=b.replace(d+c[1]+e,null!=g?K(a,g):"undefined")}for(;(c=b.match(/\[(.*?)]/))&&!(0<=c[1].indexOf("["));)b=b.replace("["+c[1]+"]","unimplemented");for(a=b;b=a.match(/\$([a-z]+)/i);){c=null;switch(b[1].toLowerCase()){case "ops":c=0}if(null==c)break;a=a.replace(b[0],c.toString())}return a}
|
||||
function Bj(a,b,c,d){var e;null!=b?(e=a.qe(b),0<=e?e=a.re(e):(e=a.S[b],null==e&&(e=Ka(b,a.Ba))),null!=e||d||a.v("invalid "+(c?c:"value")+": "+b)):d||a.v("missing "+(c||"value"));return e}function Dj(a,b,c){var d,e=!1;if(void 0!==c){e=!0;d=c;var f=4,g="";if(!f||4<f)f=4;for(var k=0;k<f;k++)g&&(g=","+g),g=La(d&255,8)+g,d>>=8;d=F(c,0,!0)+" "+c+". "+E(c,0,!0)+" "+("0b"+g);32<=c&&127>c&&(d+=" '"+String.fromCharCode(c)+"'")}a.v((null!=b?b+": ":"")+d);return e}
|
||||
function Ej(a,b){if(b)return Dj(a,b,a.S[b]);var c=0;for(b in a.S)Dj(a,b,a.S[b]),c++;return 0<c}function K(a,b,c,d){switch(a.Ba){case 8:a=E(b,3*c-(2<c?1:0));break;case 10:a=b.toString();break;default:a=F(b,2*c)}d?d=a.replace(/^0+([0-9A-F]+)$/i,"$1"):d=a;return d}var Cj={"||":0,"&&":1,"|":2,"^":3,"&":4,"!=":5,"==":5,">=":6,">":6,"<=":6,"<":6,">>>":7,">>":7,"<<":7,"-":8,"+":8,"%":9,"/":9,"*":9};
|
||||
function Ej(a,b){if(b)return Dj(a,b,a.S[b]);var c=0;for(b in a.S)Dj(a,b,a.S[b]),c++;return 0<c}function K(a,b,c,d){c=void 0===c?0:c;switch(a.Ba){case 8:a=E(b,3*c);break;case 10:a=b.toString();break;default:a=F(b,2*c)}(void 0===d?0:d)?d=a.replace(/^0+([0-9A-F]+)$/i,"$1"):d=a;return d}var Cj={"||":0,"&&":1,"|":2,"^":3,"&":4,"!=":5,"==":5,">=":6,">":6,"<=":6,"<":6,">>>":7,">>":7,"<<":7,"-":8,"+":8,"%":9,"/":9,"*":9};
|
||||
function Fj(a){wj.call(this,a);this.Ga=!1;this.ia=!0;this.G=X();this.xa=X();this.N=X();this.B=[];this.g=this.L=this.C=[];Gj(this);this.P=this.Y=0;this.i=[];this.pa=void 0;Hj(this);this.F=this;this.ka={};this.ha=this.je=0;this.R=null;this.M=[];Ij(this,a.messages);this.ta=a.commands;this.Ja=Jj;this.W=[];this.I=0;this.Ka=this.qa=null;this.w=this.Ia=this.Ha=this.X=this.la=0;this.b=this.U=null;var b=this;window?void 0===window[w]&&(window[w]=function(a){return ke(b,a)}):void 0===global[w]&&(global[w]=
|
||||
function(a){return ke(b,a)})}p(Fj,wj);function Kj(a){a=a&&a.K;null==a&&(a=-1);return a}function X(a,b,c){return{K:void 0===a?null:a,Ma:void 0===b?!1:b,Va:!1,Ba:c}}h=Fj.prototype;h.Fd=function(a,b){a.K=b;a.Va=!1;a.Ba=void 0;return a};function Lj(a){return[a.K,a.Ma,a.Ba,a.Va,a.Pc]}function Mj(a,b){var c=X(b[0],b[1],b[2]);c.Va=b[3];b[4]&&(c.ge=xj(a,c.Pc=b[4]));return c}
|
||||
h.Aa=function(a,b,c,d){this.D=b;this.J=a;this.f=c;this.b=a.i;(a=fe(a,"messages"))&&Ij(this,a);1140>this.f.ka&&(this.W=this.W.concat(Nj));1145>this.f.ka&&(this.W=this.W.concat(Oj));qd(this,16,function(a){a:{var b=d.D.b,c=a[0],e=a=0,l=b.length;if(c){a=Kj(Pj(d,c));if(-1===a){d.v("invalid address: "+c);break a}e=a>>>d.D.g;l=1}d.v("blockid physical blockaddr used size type");d.v("-------- --------- --------- ------ ------ ----");for(var c=-1,m=0;l--;){var n=b[e];n.type==c?m++||d.v("..."):
|
||||
|
|
|
|||
Loading…
Reference in a new issue