mirror of
https://github.com/pkimpel/retro-b5500.git
synced 2026-04-14 08:59:13 +00:00
Commit release 1.02:
1. Move project from Google Code to GitHub (https://github.com/pkimpel/retro-b5500/). Update links and help pages; convert wiki pages to GitHub's MarkDown format. 2. Implement emulator-hosted memory dump to a tape image that can be saved and input into the B5500 DUMP/ANALYZE utility for analysis. Activated by clicking the NOT READY button on the Console. 3. Fix bad assignments to Processor X register in arithmetic ops (affected only SyllableDebugger script). 4. Remove IndexedDB.openDatabase() version parameter so the B5500ColdLoader and tools/ scripts will work in non-Firefox browsers. 5. Add a "?db" query string parameter to the tools/scripts so these scripts can open disk subsystems other than B5500DiskUnit. 6. Correct pre-allocated file locations and ESU card in tools/COLDSTART-XIII.card. 7. Implement new double-click mechanism to copy and clear the contents of card punch, datacom terminal, and line-printer output areas to a temporary window for subsequent copying or saving. 8. Correct handling of Ctrl-B (break), Ctrl-D (disconnect request), Ctrl-E (WRU), Ctrl-L (clear input buffer), and Ctrl-Q (alternate end-of-message) in B5500DatacomUnit. 9. Implement reporting of Model IB (slow, bulk) disk in B5500DiskUnit readInterrogate. 10. Implement detection of browser IndexedDB quota-exceeded errors in B5500DiskUnit (primarily to handle the fixed 2GB limit for off-line storage in Firefox). 11. Correct problem when line printer exhausted paper and FORM FEED triple-click did not clear the condition. 12. Eliminate BOT marker sensed in result for tape drive Write Interrogate operation -- Mark XIII and XV MCPs treat this as an error and will not purge blank tapes because of it. 13. Fix double-click of SPO INPUT REQUEST button either sending a duplicate interrupt to the system or the second click moving focus from the SPO input box. 14. Further tuning of delay-deviation adjustment mechanism in B5500SetCallback.js. 15. Reinstate ability of SPO to wrap long outputs to additional lines (apparently lost with new SPO input mechanism in 1.00). 16. Commit preliminary COOLSTART-XIII.card and MCPTAPEDISK-XIII.card decks.
This commit is contained in:
37
README.md
37
README.md
@@ -1,8 +1,35 @@
|
||||
# retro-b5500
|
||||
Automatically exported from code.google.com/p/retro-b5500
|
||||
The Burroughs B5500 was an innovative computer system. Released first as the B5000 in 1962 and then, with minor improvements and a new disk subsystem, re-released as the B5500 in 1964, its design was a radical departure from other commercial systems of the day. Many of the concepts that it embodied were being worked on and implemented by others around the same time, but it is difficult to think of another system that pulled so many concepts together and made them work so well in a commercially-successful product:
|
||||
|
||||
This project is presently an experimental export from Google Code while we evaluate GitHub as a new home for the retro-b5500 emulator project.
|
||||
* Multi-programming (multiple tasks sharing the same processor)
|
||||
* Multi-processing (multiple physical processors sharing common memory and I/O)
|
||||
* Automatic memory address relocation
|
||||
* Automatic memory segment overlay (what we now call virtual memory)
|
||||
* Variable-length memory segments
|
||||
* Hardware bounds checking
|
||||
* Stack- and descriptor-oriented instruction set
|
||||
* Unified integer/floating-point numeric format
|
||||
* Management by a sophisticated operating system, the Master Control Program, or **MCP**
|
||||
* Designed for and programmed exclusively in higher-level languages
|
||||
|
||||
The project remains hosted at https://code.google.com/p/retro-b5500.
|
||||
|
||||
We recommend that you not clone or fork from this repo during this evaluation period.
|
||||
The B5500 was the foundation for the Burroughs B6000/7000/A Series, which are still produced and sold today as Unisys ClearPath MCP systems.
|
||||
|
||||
The main goal of this project is creation of a web browser-based emulator for the B5500. A second goal is reconstruction of the source and object code for the system.
|
||||
|
||||
A complete software release (Mark XIII, 1971) is presently available from the hosting site below under liberal terms of a Unisys educational/hobbyist license.
|
||||
|
||||
The contents of this project are licensed under the [MIT License](http://www.opensource.org/licenses/mit-license.php).
|
||||
|
||||
| Related Sites | URL |
|
||||
| ------------- | ----- |
|
||||
| Getting Started | http://www.phkimpel.us/B5500/webSite/HelpMenu.html |
|
||||
| Project Blog | http://retro-b5500.blogspot.com/ |
|
||||
| Emulator hosting site | http://www.phkimpel.us/B5500/ |
|
||||
| Burroughs Mark XIII Software Release | http://www.phkimpel.us/B5500/webSite/SoftwareRequest.html |
|
||||
| B5500 at retroComputingTasmania | http://www.retrocomputingtasmania.com/home/projects/burroughs-b5500 |
|
||||
| Documents at bitsavers.org | http://bitsavers.org/pdf/burroughs/B5000_5500_5700/ |
|
||||
| Release Downloads | https://drive.google.com/folderview?id=0BxqKm7v4xBswM29qUkxPTkVfYzg&usp=sharing |
|
||||
| Web/email Forum | http://groups.google.com/group/retro-b5500 |
|
||||
|
||||
|
||||
This project was originally hosted on Google Code at https://code.google.com/p/retro-b5500 and moved to GitHub in June 2015.
|
||||
|
||||
@@ -61,7 +61,7 @@ function B5500CentralControl(global) {
|
||||
/**************************************/
|
||||
|
||||
/* Global constants */
|
||||
B5500CentralControl.version = "1.01";
|
||||
B5500CentralControl.version = "1.02";
|
||||
|
||||
B5500CentralControl.memReadCycles = 2; // assume 2 µs memory read cycle time (the other option was 3 µs)
|
||||
B5500CentralControl.memWriteCycles = 4; // assume 4 µs memory write cycle time (the other option was 6 µs)
|
||||
@@ -896,6 +896,7 @@ B5500CentralControl.prototype.runTest = function runTest(runAddr) {
|
||||
this.P1.start();
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500CentralControl.prototype.dumpSystemState = function dumpSystemState(caption, writer) {
|
||||
/* Generates a dump of the processor states and all of memory
|
||||
"caption is an identifying string that is output in the heading line.
|
||||
@@ -961,7 +962,7 @@ B5500CentralControl.prototype.dumpSystemState = function dumpSystemState(caption
|
||||
}
|
||||
}
|
||||
|
||||
function convertWordtoANSI(value) {
|
||||
function convertWordToANSI(value) {
|
||||
/* Converts the "value" as a B5500 word to an eight character string and returns it */
|
||||
var c; // current character
|
||||
var s = ""; // working string value
|
||||
@@ -1020,7 +1021,7 @@ B5500CentralControl.prototype.dumpSystemState = function dumpSystemState(caption
|
||||
bic += "????????";
|
||||
} else {
|
||||
line += " " + padOctal(accessor.word, 16);
|
||||
bic += convertWordtoANSI(accessor.word);
|
||||
bic += convertWordToANSI(accessor.word);
|
||||
}
|
||||
} // for x
|
||||
|
||||
@@ -1040,6 +1041,102 @@ B5500CentralControl.prototype.dumpSystemState = function dumpSystemState(caption
|
||||
writer(-1, null);
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500CentralControl.prototype.dumpSystemTape = function dumpSystemTape(caption, writer) {
|
||||
/* Generates a dump of the processor states and all of memory for generation of a tape image.
|
||||
"caption is an identifying string that is output in the heading line.
|
||||
"writer" is a function that is called to output lines of text to the outside
|
||||
world. It takes two parameters:
|
||||
"phase" is a numeric code indicating the type of line being output:
|
||||
0 = initialization (text parameter not valid)
|
||||
32 = core memory: control word plus 512 memory words translated to ANSI
|
||||
-1 = end of dump (text parameter is caption)
|
||||
"text" is the line of text to be output.
|
||||
*/
|
||||
var addr;
|
||||
var bic;
|
||||
var dupCount = 0;
|
||||
var lastLine = "";
|
||||
var line;
|
||||
var lineAddr;
|
||||
var mod;
|
||||
var x;
|
||||
|
||||
var accessor = { // Memory access control block
|
||||
requestorID: "C", // Memory requestor ID
|
||||
addr: 0, // Memory address
|
||||
word: 0, // 48-bit data word
|
||||
MAIL: 0, // Truthy if attempt to access @000-@777 in normal state
|
||||
MPED: 0, // Truthy if memory parity error
|
||||
MAED: 0 // Truthy if memory address/inhibit error
|
||||
};
|
||||
|
||||
var BICtoANSI = [
|
||||
"0", "1", "2", "3", "4", "5", "6", "7",
|
||||
"8", "9", "#", "@", "?", ":", ">", "}",
|
||||
"+", "A", "B", "C", "D", "E", "F", "G",
|
||||
"H", "I", ".", "[", "&", "(", "<", "~",
|
||||
"|", "J", "K", "L", "M", "N", "O", "P",
|
||||
"Q", "R", "$", "*", "-", ")", ";", "{",
|
||||
" ", "/", "S", "T", "U", "V", "W", "X",
|
||||
"Y", "Z", ",", "%", "!", "=", "]", "\""];
|
||||
|
||||
function convertWordToANSI(value) {
|
||||
/* Converts the "value" as a B5500 word to an eight character string and returns it */
|
||||
var c; // current character
|
||||
var s = ""; // working string value
|
||||
var w = value; // working word value
|
||||
var x; // character counter
|
||||
|
||||
for (x=0; x<8; ++x) {
|
||||
c = w % 64;
|
||||
w = (w-c)/64;
|
||||
s = BICtoANSI[c] + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
writer(0, null);
|
||||
|
||||
// Dump all of memory
|
||||
for (mod=0; mod<0x8000; mod+=0x1000) {
|
||||
accessor.addr = mod;
|
||||
this.fetch(accessor);
|
||||
if (accessor.MAED) { // invalid address
|
||||
bic = convertWordToANSI(0x200000000000 + mod);
|
||||
for (addr=0; addr<512; ++addr) {
|
||||
bic += "00000000";
|
||||
}
|
||||
writer(32, bic);
|
||||
} else {
|
||||
for (addr=0; addr<0x1000; addr+=512) {
|
||||
lineAddr = mod+addr;
|
||||
bic = convertWordToANSI(lineAddr);
|
||||
for (x=0; x<512; ++x) {
|
||||
accessor.addr = lineAddr+x;
|
||||
this.fetch(accessor);
|
||||
if (accessor.MPED) {
|
||||
bic += "????????";
|
||||
} else {
|
||||
bic += convertWordToANSI(accessor.word);
|
||||
}
|
||||
} // for x
|
||||
|
||||
writer(32, bic);
|
||||
} // for addr
|
||||
}
|
||||
} // for mod
|
||||
|
||||
bic = caption.toUpperCase();
|
||||
while (bic.length < 150) {
|
||||
bic += " ";
|
||||
}
|
||||
while (bic.length < 160) {
|
||||
bic += " ";
|
||||
}
|
||||
writer(-1, bic);
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500CentralControl.prototype.configureSystem = function configureSystem(cfg) {
|
||||
/* Establishes the hardware module configuration from the system configuration
|
||||
@@ -1126,6 +1223,7 @@ B5500CentralControl.prototype.configureSystem = function configureSystem(cfg) {
|
||||
}
|
||||
|
||||
// Configure the peripheral units
|
||||
this.unitStatusMask = 0;
|
||||
for (mnem in cfg.units) {
|
||||
if (cfg.units[mnem].enabled) {
|
||||
specs = B5500CentralControl.unitSpecs[mnem];
|
||||
@@ -1173,6 +1271,7 @@ B5500CentralControl.prototype.powerOff = function powerOff() {
|
||||
for (x=0; x<this.unit.length; ++x) {
|
||||
if (this.unit[x]) {
|
||||
this.unit[x].shutDown();
|
||||
this.unit[x] = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1190,6 +1289,7 @@ B5500CentralControl.prototype.powerOff = function powerOff() {
|
||||
}
|
||||
|
||||
this.clear();
|
||||
this.unitStatusMask = 0;
|
||||
this.poweredUp = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1965,8 +1965,8 @@ B5500Processor.prototype.singlePrecisionMultiply = function singlePrecisionMulti
|
||||
|
||||
this.B = (sb*128 + eb)*0x8000000000 + mb; // Final Answer
|
||||
}
|
||||
this.X = xx; // for display purposes only
|
||||
}
|
||||
this.X = xx; // for display purposes only
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
@@ -2075,8 +2075,8 @@ B5500Processor.prototype.singlePrecisionDivide = function singlePrecisionDivide(
|
||||
}
|
||||
|
||||
this.B = (sb*128 + eb)*0x8000000000 + xx; // Final Answer
|
||||
this.X = xx; // for display purposes only
|
||||
}
|
||||
this.X = xx; // for display purposes only
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
@@ -2173,8 +2173,8 @@ B5500Processor.prototype.integerDivide = function integerDivide() {
|
||||
this.A = 0; // required by specs
|
||||
this.B = (sb*128 + eb)*0x8000000000 + xx; // Final Answer
|
||||
}
|
||||
this.X = xx; // for display purposes only
|
||||
}
|
||||
this.X = xx; // for display purposes only
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
@@ -2281,8 +2281,8 @@ B5500Processor.prototype.remainderDivide = function remainderDivide() {
|
||||
this.A = 0; // required by specs
|
||||
this.B = (sb*128 + eb)*0x8000000000 + mb; // Final Answer
|
||||
}
|
||||
this.X = xx; // for display purposes only
|
||||
}
|
||||
this.X = xx; // for display purposes only
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
<li><a href="./webSite/HelpMenu.html">Help & Getting Started</a>
|
||||
<br>A menu of information resources to assist you in setting up and operating the emulator.
|
||||
|
||||
<li><a href="https://code.google.com/p/retro-b5500/">Open Source Project</a>
|
||||
<br>Source code, documentation, and other developer resources for the retro-B5500 emulator project at Google Code.
|
||||
<li><a href="https://github.com/pkimpel/retro-b5500/">Open Source Project</a>
|
||||
<br>Source code, documentation, and other developer resources for the retro-B5500 emulator project at GitHub.
|
||||
|
||||
<li><a href="http://retro-b5500.blogspot.com/">Project Blog</a>
|
||||
<br>The retro-B5500 project blog.
|
||||
@@ -62,7 +62,7 @@
|
||||
Copyright (c) 2013, Nigel Williams and Paul Kimpel • Licensed under the MIT License
|
||||
</div>
|
||||
<div id=lastModDiv>Revised
|
||||
2015-02-08
|
||||
2015-06-10
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
************************************************************************
|
||||
* 2013-07-27 P.Kimpel
|
||||
* Original version, from B5500DiskDirList.html.
|
||||
* 2015-04-17 P.Kimpel
|
||||
* Add "db=" URL parameter.
|
||||
***********************************************************************/
|
||||
"use strict";
|
||||
|
||||
@@ -34,7 +36,6 @@ if (!window.indexedDB) { // for Safari, mostly
|
||||
window.addEventListener("load", function() {
|
||||
var configName = "CONFIG"; // database configuration store name
|
||||
var dbName = "B5500DiskUnit"; // IDB database name
|
||||
var dbVersion; // current IDB database version (leave undefined)
|
||||
var directoryTop; // start of directory area
|
||||
var directoryEnd; // end of directory area
|
||||
var euPrefix = "EU"; // prefix for EU object store names
|
||||
@@ -509,15 +510,15 @@ window.addEventListener("load", function() {
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function openDatabase(name, version, successor) {
|
||||
/* Attempts to open the disk subsystem database for the specified "name"
|
||||
and "version". Stores the IDB database object in "disk" if successful, or
|
||||
stores null if unsuccessful. Also gets directoryTop from seg 0 */
|
||||
function openDatabase(name, successor) {
|
||||
/* Attempts to open the disk subsystem database for the specified "name".
|
||||
Stores the IDB database object in "disk" if successful, or stores null
|
||||
if unsuccessful. Also gets directoryTop from seg 0 */
|
||||
var block = new Array(30);
|
||||
var db = null;
|
||||
var req;
|
||||
|
||||
req = window.indexedDB.open(name, version);
|
||||
req = window.indexedDB.open(name); // open current version
|
||||
|
||||
req.onerror = function(ev) {
|
||||
alert("Cannot open disk database: " + ev.target.error);
|
||||
@@ -544,6 +545,32 @@ window.addEventListener("load", function() {
|
||||
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function getDBName(defaultName) {
|
||||
/* Parses the URL query string for a "db=name" parameter. If "db" is
|
||||
found, returns the corresponding name; if not found, returns "defaultName" */
|
||||
var args;
|
||||
var i;
|
||||
var name;
|
||||
var search = location.search.substring(1); // drop the "?"
|
||||
var value = defaultName;
|
||||
var x;
|
||||
|
||||
args = search.split("&");
|
||||
for (x=args.length-1; x>=0; --x) {
|
||||
i = args[x].indexOf("=");
|
||||
if (i > 0 ) {
|
||||
name = decodeURIComponent(args[x].substring(0, i));
|
||||
if (name.toLowerCase() == "db") {
|
||||
value = decodeURIComponent(args[x].substring(i+1));
|
||||
break; // out of for loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function checkBrowser() {
|
||||
/* Checks whether this browser can support the necessary stuff */
|
||||
@@ -569,7 +596,8 @@ window.addEventListener("load", function() {
|
||||
/********** Start of window.onload() **********/
|
||||
|
||||
if (!checkBrowser()) {
|
||||
openDatabase(dbName, dbVersion, function() {
|
||||
dbName = getDBName(dbName);
|
||||
openDatabase(dbName, function() {
|
||||
directorySearch("TAPE", "COMPARE", fixIt);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
************************************************************************
|
||||
* 2013-04-16 P.Kimpel
|
||||
* Original version, from B5500ColdLoader.html.
|
||||
* 2015-04-17 P.Kimpel
|
||||
* Add "db=" URL parameter.
|
||||
***********************************************************************/
|
||||
"use strict";
|
||||
|
||||
@@ -32,7 +34,6 @@ if (!window.indexedDB) { // for Safari, mostly
|
||||
window.addEventListener("load", function() {
|
||||
var configName = "CONFIG"; // database configuration store name
|
||||
var dbName = "B5500DiskUnit"; // IDB database name
|
||||
var dbVersion; // current IDB database version (leave undefined)
|
||||
var directoryTop; // start of directory area
|
||||
var directoryEnd; // end of directory area
|
||||
var euPrefix = "EU"; // prefix for EU object store names
|
||||
@@ -505,15 +506,15 @@ window.addEventListener("load", function() {
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function openDatabase(name, version, successor) {
|
||||
/* Attempts to open the disk subsystem database for the specified "name"
|
||||
and "version". Stores the IDB database object in "disk" if successful, or
|
||||
stores null if unsuccessful. Also gets directoryTop from seg 0 */
|
||||
function openDatabase(name, successor) {
|
||||
/* Attempts to open the disk subsystem database for the specified "name".
|
||||
Stores the IDB database object in "disk" if successful, or stores null
|
||||
if unsuccessful. Also gets directoryTop from seg 0 */
|
||||
var block = new Array(30);
|
||||
var db = null;
|
||||
var req;
|
||||
|
||||
req = window.indexedDB.open(name, version);
|
||||
req = window.indexedDB.open(name); // open current version
|
||||
|
||||
req.onerror = function(ev) {
|
||||
alert("Cannot open disk database: " + ev.target.error);
|
||||
@@ -540,6 +541,32 @@ window.addEventListener("load", function() {
|
||||
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function getDBName(defaultName) {
|
||||
/* Parses the URL query string for a "db=name" parameter. If "db" is
|
||||
found, returns the corresponding name; if not found, returns "defaultName" */
|
||||
var args;
|
||||
var i;
|
||||
var name;
|
||||
var search = location.search.substring(1); // drop the "?"
|
||||
var value = defaultName;
|
||||
var x;
|
||||
|
||||
args = search.split("&");
|
||||
for (x=args.length-1; x>=0; --x) {
|
||||
i = args[x].indexOf("=");
|
||||
if (i > 0 ) {
|
||||
name = decodeURIComponent(args[x].substring(0, i));
|
||||
if (name.toLowerCase() == "db") {
|
||||
value = decodeURIComponent(args[x].substring(i+1));
|
||||
break; // out of for loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function checkBrowser() {
|
||||
/* Checks whether this browser can support the necessary stuff */
|
||||
@@ -565,7 +592,8 @@ window.addEventListener("load", function() {
|
||||
/********** Start of window.onload() **********/
|
||||
|
||||
if (!checkBrowser()) {
|
||||
openDatabase(dbName, dbVersion, function() {
|
||||
dbName = getDBName(dbName);
|
||||
openDatabase(dbName, function() {
|
||||
directoryList(function() {});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
************************************************************************
|
||||
* 2013-04-17 P.Kimpel
|
||||
* Original version, from B5500DiskDirList.html.
|
||||
* 2015-04-17 P.Kimpel
|
||||
* Add "db=" URL parameter.
|
||||
***********************************************************************/
|
||||
"use strict";
|
||||
|
||||
@@ -33,7 +35,6 @@ if (!window.indexedDB) { // for Safari, mostly
|
||||
window.addEventListener("load", function() {
|
||||
var configName = "CONFIG"; // database configuration store name
|
||||
var dbName = "B5500DiskUnit"; // IDB database name
|
||||
var dbVersion; // current IDB database version (leave undefined)
|
||||
var directoryTop; // start of directory area
|
||||
var directoryEnd; // end of directory area
|
||||
var euPrefix = "EU"; // prefix for EU object store names
|
||||
@@ -653,15 +654,15 @@ window.addEventListener("load", function() {
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function openDatabase(name, version, successor) {
|
||||
/* Attempts to open the disk subsystem database for the specified "name"
|
||||
and "version". Stores the IDB database object in "disk" if successful, or
|
||||
stores null if unsuccessful. Also gets directoryTop from seg 0 */
|
||||
function openDatabase(name, successor) {
|
||||
/* Attempts to open the disk subsystem database for the specified "name".
|
||||
Stores the IDB database object in "disk" if successful, or stores null
|
||||
if unsuccessful. Also gets directoryTop from seg 0 */
|
||||
var block = new Array(30);
|
||||
var db = null;
|
||||
var req;
|
||||
|
||||
req = window.indexedDB.open(name, version);
|
||||
req = window.indexedDB.open(name); // open current version
|
||||
|
||||
req.onerror = function(ev) {
|
||||
alert("Cannot open disk database: " + ev.target.error);
|
||||
@@ -688,6 +689,32 @@ window.addEventListener("load", function() {
|
||||
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function getDBName(defaultName) {
|
||||
/* Parses the URL query string for a "db=name" parameter. If "db" is
|
||||
found, returns the corresponding name; if not found, returns "defaultName" */
|
||||
var args;
|
||||
var i;
|
||||
var name;
|
||||
var search = location.search.substring(1); // drop the "?"
|
||||
var value = defaultName;
|
||||
var x;
|
||||
|
||||
args = search.split("&");
|
||||
for (x=args.length-1; x>=0; --x) {
|
||||
i = args[x].indexOf("=");
|
||||
if (i > 0 ) {
|
||||
name = decodeURIComponent(args[x].substring(0, i));
|
||||
if (name.toLowerCase() == "db") {
|
||||
value = decodeURIComponent(args[x].substring(i+1));
|
||||
break; // out of for loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function checkBrowser() {
|
||||
/* Checks whether this browser can support the necessary stuff */
|
||||
@@ -713,7 +740,8 @@ window.addEventListener("load", function() {
|
||||
/********** Start of window.onload() **********/
|
||||
|
||||
if (!checkBrowser()) {
|
||||
openDatabase(dbName, dbVersion, function() {
|
||||
dbName = getDBName(dbName);
|
||||
openDatabase(dbName, function() {
|
||||
directoryList(function() {});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
************************************************************************
|
||||
* 2013-07-27 P.Kimpel
|
||||
* Original version, from B5500DiskDirList.html.
|
||||
* 2015-04-17 P.Kimpel
|
||||
* Add "db=" URL parameter.
|
||||
***********************************************************************/
|
||||
"use strict";
|
||||
|
||||
@@ -34,7 +36,6 @@ if (!window.indexedDB) { // for Safari, mostly
|
||||
window.addEventListener("load", function() {
|
||||
var configName = "CONFIG"; // database configuration store name
|
||||
var dbName = "B5500DiskUnit"; // IDB database name
|
||||
var dbVersion; // current IDB database version (leave undefined)
|
||||
var directoryTop; // start of directory area
|
||||
var directoryEnd; // end of directory area
|
||||
var euPrefix = "EU"; // prefix for EU object store names
|
||||
@@ -495,15 +496,15 @@ window.addEventListener("load", function() {
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function openDatabase(name, version, successor) {
|
||||
/* Attempts to open the disk subsystem database for the specified "name"
|
||||
and "version". Stores the IDB database object in "disk" if successful, or
|
||||
stores null if unsuccessful. Also gets directoryTop from seg 0 */
|
||||
function openDatabase(name, successor) {
|
||||
/* Attempts to open the disk subsystem database for the specified "name".
|
||||
Stores the IDB database object in "disk" if successful, or stores null
|
||||
if unsuccessful. Also gets directoryTop from seg 0 */
|
||||
var block = new Array(30);
|
||||
var db = null;
|
||||
var req;
|
||||
|
||||
req = window.indexedDB.open(name, version);
|
||||
req = window.indexedDB.open(name); // open current version
|
||||
|
||||
req.onerror = function(ev) {
|
||||
alert("Cannot open disk database: " + ev.target.error);
|
||||
@@ -530,6 +531,32 @@ window.addEventListener("load", function() {
|
||||
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function getDBName(defaultName) {
|
||||
/* Parses the URL query string for a "db=name" parameter. If "db" is
|
||||
found, returns the corresponding name; if not found, returns "defaultName" */
|
||||
var args;
|
||||
var i;
|
||||
var name;
|
||||
var search = location.search.substring(1); // drop the "?"
|
||||
var value = defaultName;
|
||||
var x;
|
||||
|
||||
args = search.split("&");
|
||||
for (x=args.length-1; x>=0; --x) {
|
||||
i = args[x].indexOf("=");
|
||||
if (i > 0 ) {
|
||||
name = decodeURIComponent(args[x].substring(0, i));
|
||||
if (name.toLowerCase() == "db") {
|
||||
value = decodeURIComponent(args[x].substring(i+1));
|
||||
break; // out of for loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function checkBrowser() {
|
||||
/* Checks whether this browser can support the necessary stuff */
|
||||
@@ -555,7 +582,8 @@ window.addEventListener("load", function() {
|
||||
/********** Start of window.onload() **********/
|
||||
|
||||
if (!checkBrowser()) {
|
||||
openDatabase(dbName, dbVersion, function() {
|
||||
dbName = getDBName(dbName);
|
||||
openDatabase(dbName, function() {
|
||||
directorySearch("SYSTEM", "LOG", fixIt);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
<head>
|
||||
<title>B5500 Emulator Fix StorageNames</title>
|
||||
<meta name="Author" content="Nigel Williams & Paul Kimpel">
|
||||
<!-- 2014-08-30 Original version -->
|
||||
<!--
|
||||
2014-08-30 Original version
|
||||
2015-04-17 Add "db=" URL parameter
|
||||
-->
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="Content-Script-Type" content="text/javascript">
|
||||
<meta http-equiv="Content-Style-Type" content="text/css">
|
||||
@@ -14,6 +17,36 @@ var storageName = "B5500DiskUnit";
|
||||
window.addEventListener("load", function(ev) {
|
||||
var req;
|
||||
|
||||
/**************************************/
|
||||
function getDBName(defaultName) {
|
||||
/* Parses the URL query string for a "db=name" parameter. If "db" is
|
||||
found, returns the corresponding name; if not found, returns "defaultName" */
|
||||
var args;
|
||||
var i;
|
||||
var name;
|
||||
var search = location.search.substring(1); // drop the "?"
|
||||
var value = defaultName;
|
||||
var x;
|
||||
|
||||
args = search.split("&");
|
||||
for (x=args.length-1; x>=0; --x) {
|
||||
i = args[x].indexOf("=");
|
||||
if (i > 0 ) {
|
||||
name = decodeURIComponent(args[x].substring(0, i));
|
||||
if (name.toLowerCase() == "db") {
|
||||
value = decodeURIComponent(args[x].substring(i+1));
|
||||
break; // out of for loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/***************************************/
|
||||
|
||||
storageName = getDBName(storageName);
|
||||
|
||||
req = window.indexedDB.open(configName);
|
||||
|
||||
req.onerror = function(ev) {
|
||||
|
||||
@@ -90,8 +90,8 @@
|
||||
00000000=000000#04*)H/}VY6044A4("000000:2*14"$"50R004J0O4J70100S0|JI000001000011
|
||||
DRCTRYTP 2000 % START OF DIRECTORY AREA
|
||||
DIRECT 3604 % END OF DIRECTORY AREA
|
||||
ESU 3 % MAX NUMBER OF EUS
|
||||
DATE 09/01/84 % CURRENT SYSTEM DATE
|
||||
ESU 2 % MAX NUMBER OF EUS
|
||||
DATE 05/10/85 % CURRENT SYSTEM DATE
|
||||
SYSTEMS = 1 % MUST BE 1 (FOR NOW)
|
||||
FENCE = 16384 % USED BY TSMCP ONLY
|
||||
FILE DIRCTRY/DISK, 1|1600, 999 % DIRECTORY ENTRY FOR DIRECTORY
|
||||
@@ -101,13 +101,13 @@ FILE SYSTEM/LOG, 1|5000, 999 % RESERVE SPACE FOR LOG
|
||||
FILE MCP/DISK, 1|1500, 999 % RESERVE SPACE FOR MCP CODE
|
||||
8610
|
||||
FILE DMPAREA/DISK, 1|100, 999 % RESERVE SPACE FOR MEM DUMP
|
||||
10108
|
||||
10110
|
||||
FILE RESERVE/DISK, 1|2000, 999 % RESERVE FOR NO-USER-DISK EVENT
|
||||
10208
|
||||
TYPE BOJ % PRINT BOJ MESSAGES
|
||||
TYPE EOJ % PRINT EOJ MESSAGES
|
||||
10210
|
||||
TYPE BOJ % PRINT BOJ MESSAGES
|
||||
TYPE EOJ % PRINT EOJ MESSAGES
|
||||
TYPE OPEN % PRINT FILE OPEN MESSAGES
|
||||
USE TERMNATE % REMOVE TASKS FROM MEMORY AT EOJ (NORMALLY SET)
|
||||
USE TERMNATE % REMOVE TASKS FROM MEMORY AT EOJ (NORMALLY SET)
|
||||
TYPE TIME % REQUIRE TIME TO BE ENTERED AT SPO AFTER HALT/LOAD
|
||||
USE ONEBREAK % WRITE ALL BREAKOUTS TO A SINGLE TAPE
|
||||
USE AUTOPRNT % PRINT BACKUP (SPOOLED) FILES BY DEFAULT
|
||||
@@ -243,4 +243,4 @@ STOP
|
||||
,FORTRAN/DISK -
|
||||
,COBOL/DISK -
|
||||
,LOGOUT/DISK
|
||||
?END
|
||||
?END
|
||||
@@ -21,37 +21,37 @@
|
||||
<h2>Main Links</h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="http://code.google.com/p/retro-b5500/wiki/WebUIGettingStarted">Getting Started</a>
|
||||
<li><a href="https://github.com/pkimpel/retro-b5500/wiki/WebUIGettingStarted">Getting Started</a>
|
||||
<br>Describes the resources you will need to set up and run the emulator, along with instructions for downloading the necessary files and doing the setup.
|
||||
|
||||
<li><a href="http://code.google.com/p/retro-b5500/wiki/WebUIConfiguringTheSystem">Configuring the System</a>
|
||||
<li><a href="https://github.com/pkimpel/retro-b5500/wiki/WebUIConfiguringTheSystem">Configuring the System</a>
|
||||
<br>Describes how to configure system components and disk subsystems for the emulator.
|
||||
|
||||
<li><a href="http://code.google.com/p/retro-b5500/wiki/WebUIRunningTheEmulator">Running the Emulator</a>
|
||||
<li><a href="https://github.com/pkimpel/retro-b5500/wiki/WebUIRunningTheEmulator">Running the Emulator</a>
|
||||
<br>Describes how to halt/load (boot) the MCP operating system in the emulator using a web browser. Also describes some basic MCP operational tasks.
|
||||
|
||||
<li><a href="http://code.google.com/p/retro-b5500/wiki/WebUIUsingTheConsole">Using the B5500 Console</a>
|
||||
<li><a href="https://github.com/pkimpel/retro-b5500/wiki/WebUIUsingTheConsole">Using the B5500 Console</a>
|
||||
<br>Describes the controls and lights on the B5500 console display.
|
||||
|
||||
<li><a href="http://code.google.com/p/retro-b5500/wiki/WebUIUsingTheSPO">Using the SPO</a>
|
||||
<li><a href="https://github.com/pkimpel/retro-b5500/wiki/WebUIUsingTheSPO">Using the SPO</a>
|
||||
<br>Describes how to operate the supervisory keyboard/printer within the browser-based emulator. Also gives a brief overview of MCP commands used with the SPO.
|
||||
|
||||
<li><a href="http://code.google.com/p/retro-b5500/wiki/WebUIUsingTheCardReader">Using the Card Reader</a>
|
||||
<li><a href="https://github.com/pkimpel/retro-b5500/wiki/WebUIUsingTheCardReader">Using the Card Reader</a>
|
||||
<br>Describes how to operate the card reader device within the browser-based emulator.
|
||||
|
||||
<li><a href="http://code.google.com/p/retro-b5500/wiki/WebUIUsingTheLinePrinter">Using the Line Printer</a>
|
||||
<li><a href="https://github.com/pkimpel/retro-b5500/wiki/WebUIUsingTheLinePrinter">Using the Line Printer</a>
|
||||
<br>Describes how to operate the line printer within the browser-based emulator.
|
||||
|
||||
<li><a href="http://code.google.com/p/retro-b5500/wiki/WebUIUsingTheCardPunch">Using the Card Punch</a>
|
||||
<li><a href="https://github.com/pkimpel/retro-b5500/wiki/WebUIUsingTheCardPunch">Using the Card Punch</a>
|
||||
<br>Describes how to operate the card punch device within the browser-based emulator.
|
||||
|
||||
<li><a href="http://code.google.com/p/retro-b5500/wiki/WebUIUsingTheMagTapeDrive">Using the Magnetic Tape Drive</a>
|
||||
<li><a href="https://github.com/pkimpel/retro-b5500/wiki/WebUIUsingTheMagTapeDrive">Using the Magnetic Tape Drive</a>
|
||||
<br>Describes how to operate magnetic tape drives within the browser-based emulator.
|
||||
|
||||
<li><a href="http://code.google.com/p/retro-b5500/wiki/WebUIUsingDatacom">Using the Datacom Terminal</a>
|
||||
<li><a href="https://github.com/pkimpel/retro-b5500/wiki/WebUIUsingDatacom">Using the Datacom Terminal</a>
|
||||
<br>Describes how to operate the data-communications terminal within the browser-based emulator.
|
||||
|
||||
<li><a href="http://code.google.com/p/retro-b5500/wiki/WebUIHowToSetUpCANDE">Setting Up TSMCP and CANDE</a>
|
||||
<li><a href="https://github.com/pkimpel/retro-b5500/wiki/WebUIHowToSetUpCANDE">Setting Up TSMCP and CANDE</a>
|
||||
<br>Describes how to set up and initiate the Timesharing MCP and CANDE user interface within the browser-based emulator.
|
||||
|
||||
</ul>
|
||||
@@ -63,7 +63,7 @@
|
||||
Copyright (c) 2013, Nigel Williams and Paul Kimpel • Licensed under the MIT License
|
||||
</div>
|
||||
<div id=lastModDiv>Revised
|
||||
2014-09-26
|
||||
2015-06-10
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -71,6 +71,40 @@ B5500CardPunch.prototype.clear = function clear() {
|
||||
this.stacker2Count = 0; // cards in stacker #2
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500CardPunch.prototype.emptyStacker = function emptyStacker(stacker) {
|
||||
/* Empties the stacker of all text lines */
|
||||
|
||||
while (stacker.firstChild) {
|
||||
stacker.removeChild(stacker.firstChild);
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500CardPunch.prototype.copyStacker = function copyStacker(ev) {
|
||||
/* Copies the text contents of a "stacker" area of the device, opens a new
|
||||
temporary window, and pastes that text into the window so it can be copied
|
||||
or saved by the user */
|
||||
var stacker = ev.target;
|
||||
var text = stacker.textContent;
|
||||
var title = "B5500 " + this.mnemonic + " Stacker Snapshot";
|
||||
var win = window.open("./B5500FramePaper.html", this.mnemonic + "-Snapshot",
|
||||
"scrollbars,resizable,width=500,height=500");
|
||||
|
||||
win.moveTo((screen.availWidth-win.outerWidth)/2, (screen.availHeight-win.outerHeight)/2);
|
||||
win.addEventListener("load", function() {
|
||||
var doc;
|
||||
|
||||
doc = win.document;
|
||||
doc.title = title;
|
||||
doc.getElementById("Paper").textContent = text;
|
||||
});
|
||||
|
||||
this.emptyStacker(stacker);
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500CardPunch.prototype.setPunchReady = function setPunchReady(ready) {
|
||||
/* Controls the ready-state of the card punch */
|
||||
@@ -86,14 +120,10 @@ B5500CardPunch.prototype.setPunchReady = function setPunchReady(ready) {
|
||||
this.stacker1Count = this.stacker2Count = 0;
|
||||
this.$$("CPStacker1Bar").value = 0;
|
||||
B5500Util.removeClass(this.$$("CPStacker1Full"), "annunciatorLit");
|
||||
while (this.stacker1.firstChild) {
|
||||
this.stacker1.removeChild(this.stacker1.firstChild);
|
||||
}
|
||||
this.emptyStacker(stacker1);
|
||||
this.$$("CPStacker2Bar").value = 0;
|
||||
B5500Util.removeClass(this.$$("CPStacker2Full"), "annunciatorLit");
|
||||
while (this.stacker2.firstChild) {
|
||||
this.stacker2.removeChild(this.stacker2.firstChild);
|
||||
}
|
||||
this.emptyStacker(stacker2);
|
||||
}
|
||||
}
|
||||
this.armRunout(false);
|
||||
@@ -214,6 +244,10 @@ B5500CardPunch.prototype.punchOnload = function punchOnload() {
|
||||
|
||||
this.window.addEventListener("beforeunload",
|
||||
B5500CardPunch.prototype.beforeUnload, false);
|
||||
this.stacker1.addEventListener("dblclick",
|
||||
B5500CentralControl.bindMethod(this, B5500CardPunch.prototype.copyStacker));
|
||||
this.stacker2.addEventListener("dblclick",
|
||||
B5500CentralControl.bindMethod(this, B5500CardPunch.prototype.copyStacker));
|
||||
this.$$("CPStartBtn").addEventListener("click",
|
||||
B5500CentralControl.bindMethod(this, B5500CardPunch.prototype.CPStartBtn_onclick), false);
|
||||
this.$$("CPStopBtn").addEventListener("click",
|
||||
|
||||
@@ -72,7 +72,6 @@ if (!window.indexedDB) { // for Safari, mostly
|
||||
window.addEventListener("load", function() {
|
||||
var configName = "CONFIG"; // database configuration store name
|
||||
var dbName = "B5500DiskUnit"; // IDB database name
|
||||
var dbVersion = 1; // current IDB database version
|
||||
var directoryTop = 2000; // start of directory area
|
||||
var directoryEnd = 3008; // end of directory area
|
||||
var euSize = 200000; // model I size (5 Storage Units: 6MW or 48MC)
|
||||
@@ -1989,13 +1988,13 @@ window.addEventListener("load", function() {
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function openDatabase(name, version) {
|
||||
/* Attempts to open the disk subsystem database for the specified "name"
|
||||
and "version". Stores the IDB database object in "db" if successful, or
|
||||
stores null if unsuccessful */
|
||||
function openDatabase(name) {
|
||||
/* Attempts to open the disk subsystem database for the specified "name".
|
||||
Stores the IDB database object in "db" if successful, or stores null
|
||||
if unsuccessful */
|
||||
var req;
|
||||
|
||||
req = window.indexedDB.open(name, version);
|
||||
req = window.indexedDB.open(name);
|
||||
|
||||
req.onerror = function(ev) {
|
||||
alert("Cannot open retro-B5500 Disk Subsystem database:\n" + ev.target.error);
|
||||
@@ -2103,7 +2102,7 @@ window.addEventListener("load", function() {
|
||||
deleteDiskDatabase(dbName);
|
||||
});
|
||||
$$("LoadBtn").addEventListener("click", loadFromTape);
|
||||
openDatabase(dbName, dbVersion);
|
||||
openDatabase(dbName);
|
||||
}
|
||||
}, false);
|
||||
</script>
|
||||
|
||||
@@ -74,8 +74,9 @@ H1 {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0}
|
||||
#PageFooter {
|
||||
position: absolute;
|
||||
font-size: 75%;
|
||||
bottom: 4px;
|
||||
text-align: center;
|
||||
width: 100%}
|
||||
margin-top: 2em;
|
||||
margin-left: calc(50% - 3in);
|
||||
margin-right: calc(50% - 3in);
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
text-align: center}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
* Split off Javascript code into a separate script.
|
||||
* 2015-01-24 P.Kimpel
|
||||
* Strip down to new, minimal home page sans console panel.
|
||||
* 2015-06-10 P.Kimpel
|
||||
* Change project links from Google Code to GitHub.
|
||||
***********************************************************************/
|
||||
-->
|
||||
<meta name="Author" content="Nigel Williams & Paul Kimpel">
|
||||
@@ -63,13 +65,13 @@
|
||||
<hr>
|
||||
<table id=InfoTable>
|
||||
<tr>
|
||||
<td><a href="https://code.google.com/p/retro-b5500/" target="_blank">
|
||||
<td><a href="https://github.com/pkimpel/retro-b5500/" target="_blank">
|
||||
Open-Source Project</a>
|
||||
<td id=StatusMsg>
|
||||
<td class=rj><a href="http://retro-b5500.blogspot.com/" target="_blank">
|
||||
Project Blog</a>
|
||||
<tr>
|
||||
<td><a href="http://code.google.com/p/retro-b5500/wiki/WebUIGettingStarted" target="_blank">
|
||||
<td><a href="https://github.com/pkimpel/retro-b5500/wiki/WebUIGettingStarted" target="_blank">
|
||||
Getting Started Wiki</a>
|
||||
<td>
|
||||
<td class=rj><a href="http://www.phkimpel.us/B5500/" target="_blank">
|
||||
@@ -87,6 +89,10 @@
|
||||
<button id=StartUpNoPowerBtn title="Start the emulator in a power-off state">
|
||||
Start – Powered Off
|
||||
</button>
|
||||
|
||||
<div id=PageFooter>
|
||||
<i>(Caution: Closing this window, minimizing it, or placing the page on a non-active tab may cause the emulator to run very slowly)</i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table id=CallbackTable>
|
||||
@@ -101,9 +107,5 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div id=PageFooter>
|
||||
<i>(Note: Closing or minimizing this window may cause the emulator to run very slowly)</i>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -296,14 +296,14 @@ B5500ConsolePanel.prototype.dumpState = function dumpState(caption) {
|
||||
/* Call-back function for cc.dumpSystemState */
|
||||
|
||||
switch (phase) {
|
||||
case 0:
|
||||
case 0: // Initialization and heading line
|
||||
lastPhase = phase;
|
||||
doc.writeln(escapeHTML(text));
|
||||
doc.writeln("User Agent: " + navigator.userAgent);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
case 1: // Processor 1 state
|
||||
case 2: // Processor 2 state
|
||||
if (phase == lastPhase) {
|
||||
doc.writeln(escapeHTML(text));
|
||||
} else {
|
||||
@@ -314,7 +314,7 @@ B5500ConsolePanel.prototype.dumpState = function dumpState(caption) {
|
||||
}
|
||||
break;
|
||||
|
||||
case 32:
|
||||
case 32: // Memory lines
|
||||
if (phase != lastPhase) {
|
||||
lastPhase = phase;
|
||||
doc.writeln();
|
||||
@@ -323,7 +323,7 @@ B5500ConsolePanel.prototype.dumpState = function dumpState(caption) {
|
||||
doc.writeln(escapeHTML(text));
|
||||
break;
|
||||
|
||||
case -1:
|
||||
case -1: // Termination
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
@@ -341,6 +341,72 @@ B5500ConsolePanel.prototype.dumpState = function dumpState(caption) {
|
||||
win.focus();
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
B5500ConsolePanel.prototype.dumpTape = function dumpTape(caption) {
|
||||
/* Generates a dump of all of memory to a MEMORY/DUMP tape image */
|
||||
var doc;
|
||||
var win = window.open("", "", "location=no,resizable,scrollbars,status");
|
||||
var x;
|
||||
|
||||
var htmlMatch = /[<>&"]/g; // regular expression for escaping HTML text
|
||||
var tapeLabel = " LABEL 0MEMORY 0DUMP00100175001019936500000000000000000000000000000000000000000";
|
||||
|
||||
function htmlFilter(c) {
|
||||
/* Used to escape HTML-sensitive characters in a string */
|
||||
switch (c) {
|
||||
case "&":
|
||||
return "&";
|
||||
case "<":
|
||||
return "<";
|
||||
case ">":
|
||||
return ">";
|
||||
case "\"":
|
||||
return """;
|
||||
default:
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHTML(text) {
|
||||
/* Returns "text" as escaped HTML */
|
||||
|
||||
return text.replace(htmlMatch, htmlFilter);
|
||||
}
|
||||
|
||||
function writer(phase, text) {
|
||||
/* Call-back function for cc.dumpSystemTape */
|
||||
|
||||
switch (phase) {
|
||||
case 0: // Initialization, write tape label
|
||||
doc.writeln(tapeLabel);
|
||||
doc.writeln("}"); // tape mark
|
||||
break;
|
||||
|
||||
case 32: // Dump data
|
||||
doc.writeln(escapeHTML(text));
|
||||
break;
|
||||
|
||||
case -1: // Termination, write tape label
|
||||
doc.writeln(text);
|
||||
doc.writeln("}"); // tape mark
|
||||
doc.writeln(tapeLabel);
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
doc = win.document;
|
||||
doc.open();
|
||||
doc.writeln("<html><head><title>retro-B5500 Console Tape Dump</title>");
|
||||
doc.writeln("</head><body>");
|
||||
doc.write("<pre>");
|
||||
|
||||
this.cc.dumpSystemTape(caption, writer);
|
||||
|
||||
doc.writeln("</pre></body></html>")
|
||||
doc.close();
|
||||
win.focus();
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
B5500ConsolePanel.prototype.displayCallbackState = function displayCallbackState() {
|
||||
/* Builds a table of outstanding callback state */
|
||||
@@ -757,6 +823,10 @@ B5500ConsolePanel.prototype.consoleOnload = function consoleOnload(ev) {
|
||||
B5500CentralControl.bindMethod(this, function(ev) {
|
||||
this.dumpState("Memory-Check Button");
|
||||
}));
|
||||
this.$$("NotReadyBtn").addEventListener("click",
|
||||
B5500CentralControl.bindMethod(this, function(ev) {
|
||||
this.dumpTape("Not-Ready Button");
|
||||
}));
|
||||
|
||||
this.aControl = this.$$("AControlBtn");
|
||||
this.aNormal = this.$$("ANormalBtn");
|
||||
@@ -767,7 +837,8 @@ B5500ConsolePanel.prototype.consoleOnload = function consoleOnload(ev) {
|
||||
this.buildLightMaps();
|
||||
|
||||
this.cc = new B5500CentralControl(this.global);
|
||||
this.global.B5500DumpState = this.dumpState;
|
||||
this.global.B5500DumpState = this.dumpState; // for use by Processor
|
||||
this.global.B5500DumpState = this.dumpTape; // for use by Processor
|
||||
this.window.resizeTo(this.doc.documentElement.scrollWidth + this.window.outerWidth - this.window.innerWidth + 2, // kludge +2, dunno why
|
||||
this.doc.documentElement.scrollHeight + this.window.outerHeight - this.window.innerHeight);
|
||||
this.window.moveTo(screen.availWidth - this.window.outerWidth, 0);
|
||||
|
||||
@@ -273,7 +273,11 @@ B5500DatacomUnit.prototype.outputChar = function outputChar() {
|
||||
var nextTime;
|
||||
var stamp;
|
||||
|
||||
if (this.bufIndex < this.bufLength) {
|
||||
if (this.bufIndex >= this.bufLength) {
|
||||
this.interrupt = true;
|
||||
this.setState(this.fullBuffer ? this.bufWriteReady : this.bufIdle);
|
||||
this.signal();
|
||||
} else {
|
||||
stamp = performance.now();
|
||||
nextTime = (this.nextCharTime < stamp ? stamp : this.nextCharTime) + this.charPeriod;
|
||||
delay = nextTime - stamp;
|
||||
@@ -306,10 +310,6 @@ B5500DatacomUnit.prototype.outputChar = function outputChar() {
|
||||
break;
|
||||
}
|
||||
this.showBufferIndex();
|
||||
} else {
|
||||
this.interrupt = true;
|
||||
this.setState(this.fullBuffer ? this.bufWriteReady : this.bufIdle);
|
||||
this.signal();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -370,7 +370,7 @@ B5500DatacomUnit.prototype.keyAction = function keyAction(ev, c) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
break;
|
||||
case 0x21: // ! EOT, disconnect
|
||||
case 0x21: // !, EOT, send disconnect request
|
||||
this.buffer[this.bufIndex++] = 0x7D; // } greater-or-equal code
|
||||
this.interrupt = true;
|
||||
this.abnormal = true;
|
||||
@@ -400,6 +400,7 @@ B5500DatacomUnit.prototype.keyAction = function keyAction(ev, c) {
|
||||
case 0x0C: // Ctrl-L, FF, clear input buffer
|
||||
if (this.bufState == this.bufInputBusy) {
|
||||
this.bufIndex = this.bufLength = 0;
|
||||
this.setState(this.bufIdle);
|
||||
}
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
@@ -450,27 +451,7 @@ B5500DatacomUnit.prototype.keyPress = function keyPress(ev) {
|
||||
var c = ev.charCode;
|
||||
|
||||
if (ev.ctrlKey) {
|
||||
switch(c) {
|
||||
case 0x42:
|
||||
case 0x62:
|
||||
c = 0x02; // Ctrl-B: force STX, break
|
||||
break;
|
||||
case 0x45:
|
||||
case 0x65:
|
||||
c = 0x05; // Ctrl-E:force ENQ, WRU
|
||||
break;
|
||||
case 0x4C:
|
||||
case 0x6C:
|
||||
c = 0x0C; // Ctrl-L: force FF, clear input buffer
|
||||
break;
|
||||
case 0x51:
|
||||
case 0x71:
|
||||
c = 0x7E; // Ctrl-Q: DC1, X-ON to ~ (GM) for end-of-message
|
||||
break;
|
||||
default:
|
||||
c = 0; // not something we want
|
||||
break;
|
||||
}
|
||||
c = 0; // not something we want
|
||||
}
|
||||
|
||||
this.keyAction(ev, c);
|
||||
@@ -489,6 +470,31 @@ B5500DatacomUnit.prototype.keyDown = function keyDown(ev) {
|
||||
case 0x0D: // Enter: force ~ (GM) for end-of-message
|
||||
this.keyAction(ev, 0x7E);
|
||||
break;
|
||||
case 0x42:
|
||||
if (ev.ctrlKey) {
|
||||
this.keyAction(ev, 0x02); // Ctrl-B: force STX, break
|
||||
}
|
||||
break;
|
||||
case 0x44:
|
||||
if (ev.ctrlKey) {
|
||||
this.keyAction(ev, 0x21); // Ctrl-D: force EOT, disconnect request
|
||||
}
|
||||
break;
|
||||
case 0x45:
|
||||
if (ev.ctrlKey) {
|
||||
this.keyAction(ev, 0x05); // Ctrl-E:force ENQ, WRU
|
||||
}
|
||||
break;
|
||||
case 0x4C:
|
||||
if (ev.ctrlKey) {
|
||||
this.keyAction(ev, 0x0C); // Ctrl-L: force FF, clear input buffer
|
||||
}
|
||||
break;
|
||||
case 0x51:
|
||||
if (ev.ctrlKey) {
|
||||
this.keyAction(ev, 0x7E); // Ctrl-Q: DC1, X-ON to ~ (GM) for end-of-message
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -505,10 +511,10 @@ B5500DatacomUnit.prototype.termConnectBtnClick = function termConnectBtnClick(ev
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.copyPaper = function copyPaper(ev) {
|
||||
B5500DatacomUnit.prototype.copyPaper = function copyPaper(ev) {
|
||||
/* Copies the text contents of the "paper" area of the SPO, opens a new
|
||||
temporary window, and pastes that text into the window so it can be copied
|
||||
or saved */
|
||||
or saved by the user */
|
||||
var text = ev.target.textContent;
|
||||
var title = "B5500 " + this.mnemonic + " Text Snapshot";
|
||||
var win = window.open("./B5500FramePaper.html", this.mnemonic + "-Snapshot",
|
||||
@@ -566,7 +572,7 @@ B5500DatacomUnit.prototype.datacomOnload = function datacomOnload() {
|
||||
this.$$("TermOut").addEventListener("keypress",
|
||||
B5500CentralControl.bindMethod(this, B5500DatacomUnit.prototype.keyPress), false);
|
||||
this.paper.addEventListener("dblclick",
|
||||
B5500CentralControl.bindMethod(this, B5500SPOUnit.prototype.copyPaper), false);
|
||||
B5500CentralControl.bindMethod(this, B5500DatacomUnit.prototype.copyPaper), false);
|
||||
this.$$("TermConnectBtn").addEventListener("click",
|
||||
B5500CentralControl.bindMethod(this, B5500DatacomUnit.prototype.termConnectBtnClick), false);
|
||||
|
||||
|
||||
@@ -463,7 +463,7 @@ B5500DiskStorageConfig.prototype.normalizeStorageConfig = function normalizeStor
|
||||
|
||||
if (newConfig.configLevel != this.dbConfigLevel) {
|
||||
this.alertWin.alert("ERROR: Cannot normalize existing CONFIG\nlevel " +
|
||||
newConfig.configLevel + " to current level " + that.dbConfigLevel);
|
||||
newConfig.configLevel + " to current level " + this.dbConfigLevel);
|
||||
}
|
||||
|
||||
return newConfig;
|
||||
@@ -695,6 +695,7 @@ B5500DiskStorageConfig.prototype.saveStorageDialog = function saveStorageDialog(
|
||||
/**************************************/
|
||||
B5500DiskStorageConfig.prototype.deleteStorageDialog = function deleteStorageDialog(storageName) {
|
||||
/* Initiates deletion of the currently-selected system configuration */
|
||||
var that = this;
|
||||
|
||||
function deleteFailed(ev) {
|
||||
that.alertWin.alert("Deletion of database \"" + storageName +
|
||||
|
||||
@@ -134,8 +134,8 @@ function B5500DiskUnit(mnemonic, index, designate, statusChange, signal, options
|
||||
this.initiateStamp = 0; // timestamp of last initiation (set by IOUnit)
|
||||
this.config = null; // copy of CONFIG store contents
|
||||
this.db = null; // the IDB database object
|
||||
this.euBase = // base EU number for this DFCU
|
||||
(mnemonic=="DKB" && !options.DFX ? 10 : 0);
|
||||
this.euPrefix = // prefix for EU object store names
|
||||
(mnemonic=="DKA" || options.DFX ? "EU" : "EU1");
|
||||
|
||||
this.stdFinish = B5500CentralControl.bindMethod(this, B5500DiskUnit.prototype.stdFinish);
|
||||
|
||||
@@ -143,7 +143,6 @@ function B5500DiskUnit(mnemonic, index, designate, statusChange, signal, options
|
||||
this.openDatabase(); // attempt to open the IDB database
|
||||
}
|
||||
|
||||
B5500DiskUnit.prototype.euPrefix = "EU"; // prefix for EU object store names
|
||||
B5500DiskUnit.prototype.charXferRate = 96; // avg. transfer rate [characters/ms = KC/sec]
|
||||
B5500DiskUnit.prototype.modelILatency = 40; // Model-I disk max rotational latency [ms]
|
||||
B5500DiskUnit.prototype.modelIBLatency = 80; // Model-IB disk max rotational latency [ms]
|
||||
@@ -203,7 +202,7 @@ B5500DiskUnit.prototype.loadStorageConfig = function loadStorageConfig(storageCo
|
||||
var name;
|
||||
|
||||
for (name in config) { // for each property in the config
|
||||
if (name.search(euRex) == 0) { // filter name for "EUn" or "EUnn"
|
||||
if (name.search(euRex) == 0) { // filter name for "EUn" or "EU1n"
|
||||
eu = config[name];
|
||||
eu.maxLatency = (eu.slow ? this.modelIBLatency : this.modelILatency);
|
||||
eu.charXferRate = this.charXferRate;
|
||||
@@ -278,7 +277,7 @@ B5500DiskUnit.prototype.read = function read(finish, buffer, length, mode, contr
|
||||
this.finish = finish; // for global error handler
|
||||
var segs = Math.floor((length+239)/240);
|
||||
var segAddr = control % 1000000; // starting seg address
|
||||
var euNumber = (control % 10000000 - segAddr)/1000000 + this.euBase;
|
||||
var euNumber = (control % 10000000 - segAddr)/1000000;
|
||||
var euName = this.euPrefix + euNumber;
|
||||
var endAddr = segAddr+segs-1; // ending seg address
|
||||
|
||||
@@ -367,14 +366,16 @@ B5500DiskUnit.prototype.write = function write(finish, buffer, length, mode, con
|
||||
this.finish = finish; // for global error handler
|
||||
var segs = Math.floor((length+239)/240);
|
||||
var segAddr = control % 1000000; // starting seg address
|
||||
var euNumber = (control % 10000000 - segAddr)/1000000 + this.euBase;
|
||||
var euNumber = (control % 10000000 - segAddr)/1000000;
|
||||
var euName = this.euPrefix + euNumber;
|
||||
var endAddr = segAddr+segs-1; // ending seg address
|
||||
|
||||
eu = this.config[euName];
|
||||
if (!eu) { // EU does not exist
|
||||
console.log(euName + " does not exist");
|
||||
this.stdFinish(0x20, 0); // set D27F for EU not ready
|
||||
} else if (segAddr < 0) {
|
||||
console.log(euName + " invalid starting addr");
|
||||
this.stdFinish(0x20, 0); // set D27F for invalid starting seg address
|
||||
} else {
|
||||
if (endAddr >= eu.size) { // if read is past end of disk
|
||||
@@ -392,6 +393,14 @@ B5500DiskUnit.prototype.write = function write(finish, buffer, length, mode, con
|
||||
} else {
|
||||
// Do the write
|
||||
txn = this.db.transaction(euName, "readwrite")
|
||||
txn.onerror = function writeTxnOnError(ev) {
|
||||
console.log(euName + " write txn onerror", ev);
|
||||
that.stdFinish(0x20, 0);
|
||||
};
|
||||
txn.onabort = function writeTxnOnAbort(ev) {
|
||||
console.log(euName + " write txn onabort", ev);
|
||||
that.stdFinish(0x20, 0);
|
||||
};
|
||||
txn.oncomplete = function writeComplete(ev) {
|
||||
that.timer = setCallback(that.mnemonic, that, finishTime - performance.now(),
|
||||
function writeTimeout() {
|
||||
@@ -440,7 +449,7 @@ B5500DiskUnit.prototype.readCheck = function readCheck(finish, length, control)
|
||||
this.finish = finish; // for global error handler
|
||||
var segs = Math.floor((length+239)/240);
|
||||
var segAddr = control % 1000000; // starting seg address
|
||||
var euNumber = (control % 10000000 - segAddr)/1000000 + this.euBase;
|
||||
var euNumber = (control % 10000000 - segAddr)/1000000;
|
||||
var euName = this.euPrefix + euNumber;
|
||||
var endAddr = segAddr+segs-1; // ending seg address
|
||||
|
||||
@@ -501,7 +510,7 @@ B5500DiskUnit.prototype.readInterrogate = function readInterrogate(finish, contr
|
||||
the address */
|
||||
var eu; // EU characteristics object
|
||||
var segAddr = control % 1000000; // starting seg address
|
||||
var euNumber = (control % 10000000 - segAddr)/1000000 + this.euBase;
|
||||
var euNumber = (control % 10000000 - segAddr)/1000000;
|
||||
var euName = this.euPrefix + euNumber;
|
||||
|
||||
this.finish = finish; // for global error handler
|
||||
@@ -511,11 +520,13 @@ B5500DiskUnit.prototype.readInterrogate = function readInterrogate(finish, contr
|
||||
} else {
|
||||
if (segAddr < 0 || segAddr >= eu.size) { // if read is past end of disk
|
||||
this.errorMask |= 0x20; // set D27F for invalid seg address
|
||||
} else if (eu.slow) {
|
||||
this.errorMask |= 0x10; // set D28F (lockout bit) to indicate Mod IB (slow) disk
|
||||
}
|
||||
this.timer = setCallback(this.mnemonic, this,
|
||||
Math.random()*eu.maxLatency + this.initiateStamp - performance.now(),
|
||||
function readInterrogateTimeout() {
|
||||
this.stdFinish(0, length);
|
||||
this.stdFinish(0, 0);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -533,7 +544,7 @@ B5500DiskUnit.prototype.writeInterrogate = function writeInterrogate(finish, con
|
||||
|
||||
var eu; // EU characteristics object
|
||||
var segAddr = control % 1000000; // starting seg address
|
||||
var euNumber = (control % 10000000 - segAddr)/1000000 + this.euBase;
|
||||
var euNumber = (control % 10000000 - segAddr)/1000000;
|
||||
var euName = this.euPrefix + euNumber;
|
||||
|
||||
this.finish = finish; // for global error handler
|
||||
@@ -547,7 +558,7 @@ B5500DiskUnit.prototype.writeInterrogate = function writeInterrogate(finish, con
|
||||
this.timer = setCallback(this.mnemonic, this,
|
||||
Math.random()*eu.maxLatency + this.initiateStamp - performance.now(),
|
||||
function writeInterrogateTimeout() {
|
||||
this.stdFinish(0, length);
|
||||
this.stdFinish(0, 0);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<meta http-equiv="Content-Script-Type" content="text/javascript">
|
||||
<meta http-equiv="Content-Style-Type" content="text/css">
|
||||
<link id=defaultStyleSheet rel=stylesheet type="text/css" href="B5500Common.css">
|
||||
<link id=cardPrinterStyleSheet rel=stylesheet type="text/css" href="B5500LinePrinter.css">
|
||||
<link id=printerStyleSheet rel=stylesheet type="text/css" href="B5500LinePrinter.css">
|
||||
</head>
|
||||
|
||||
<body id=printerBody class=deviceBody>
|
||||
|
||||
@@ -83,7 +83,6 @@ B5500LinePrinter.prototype.clear = function clear() {
|
||||
B5500LinePrinter.prototype.setPrinterReady = function setPrinterReady(ready) {
|
||||
/* Controls the ready-state of the line printer */
|
||||
|
||||
this.formFeedCount = 0;
|
||||
if (ready && !this.ready) {
|
||||
this.statusChange(1);
|
||||
B5500Util.addClass(this.$$("LPStartBtn"), "greenLit")
|
||||
@@ -102,15 +101,43 @@ B5500LinePrinter.prototype.ripPaper = function ripPaper(ev) {
|
||||
/* Handles an event to clear the "paper" from the printer */
|
||||
|
||||
this.formFeedCount = 0;
|
||||
if (this.window.confirm("Do you want to clear the \"paper\" from the printer?")) {
|
||||
B5500Util.removeClass(this.$$("LPEndOfPaperBtn"), "whiteLit");
|
||||
this.paperMeter.value = this.paperLeft = this.maxPaperLines;
|
||||
while (this.paper.firstChild) {
|
||||
this.paper.removeChild(this.paper.firstChild);
|
||||
}
|
||||
B5500Util.removeClass(this.$$("LPEndOfPaperBtn"), "whiteLit");
|
||||
this.paperMeter.value = this.paperLeft = this.maxPaperLines;
|
||||
while (this.paper.firstChild) {
|
||||
this.paper.removeChild(this.paper.firstChild);
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500LinePrinter.prototype.copyPaper = function copyPaper(ev) {
|
||||
/* Copies the text contents of the "paper" area of the device, opens a new
|
||||
temporary window, and pastes that text into the window so it can be copied
|
||||
or saved by the user */
|
||||
var barGroup = this.paper.firstChild;
|
||||
var text = "";
|
||||
var title = "B5500 " + this.mnemonic + " Paper Snapshot";
|
||||
var win = window.open("./B5500FramePaper.html", this.mnemonic + "-Snapshot",
|
||||
"scrollbars,resizable,width=500,height=500");
|
||||
|
||||
while (barGroup) {
|
||||
text += barGroup.textContent + "\n";
|
||||
barGroup = barGroup.nextSibling;
|
||||
}
|
||||
|
||||
win.moveTo((screen.availWidth-win.outerWidth)/2, (screen.availHeight-win.outerHeight)/2);
|
||||
win.addEventListener("load", function() {
|
||||
var doc;
|
||||
|
||||
doc = win.document;
|
||||
doc.title = title;
|
||||
doc.getElementById("Paper").textContent = text;
|
||||
});
|
||||
|
||||
this.ripPaper();
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500LinePrinter.prototype.appendLine = function appendLine(text) {
|
||||
/* Appends one line, with a trailing new-line character, to the current
|
||||
@@ -147,7 +174,7 @@ B5500LinePrinter.prototype.appendLine = function appendLine(text) {
|
||||
/**************************************/
|
||||
B5500LinePrinter.prototype.printLine = function printLine(text, control) {
|
||||
/* Prints one line to the "paper", handling carriage control and greenbar
|
||||
group completion. For now, SPACE 0 (overprintng) is treated as single-spacing */
|
||||
group completion. For now, SPACE 0 (overprinting) is treated as single-spacing */
|
||||
var lines = 1;
|
||||
|
||||
this.appendLine(text || "\xA0");
|
||||
@@ -238,7 +265,7 @@ B5500LinePrinter.prototype.LPStopBtn_onclick = function LPStopBtn_onclick(ev) {
|
||||
|
||||
/**************************************/
|
||||
B5500LinePrinter.prototype.LPSpaceBtn_onclick = function LPSpaceBtn_onclick(ev) {
|
||||
/* Handle the click event for the Skip To Heading button */
|
||||
/* Handle the click event for the Space button */
|
||||
|
||||
if (!this.ready) {
|
||||
this.formFeedCount = 0;
|
||||
@@ -255,7 +282,9 @@ B5500LinePrinter.prototype.LPFormFeedBtn_onclick = function LPFormFeedBtn_onclic
|
||||
this.printLine("", -1);
|
||||
this.endOfPaper.scrollIntoView();
|
||||
if (++this.formFeedCount >= 3) {
|
||||
this.ripPaper();
|
||||
if (this.window.confirm("Do you want to clear the \"paper\" from the printer?")) {
|
||||
this.ripPaper();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -266,7 +295,7 @@ B5500LinePrinter.prototype.LPEndOfPaperBtn_onclick = function LPEndOfPaperBtn_on
|
||||
and end-of-paper condition, this will make the printer ready, but it will
|
||||
still be in an EOP condition. The next time a print line is received, the
|
||||
EOP condition will force it not-ready again. You can print only one line
|
||||
at a time (presumably to the end of the current page. The EOP condition can
|
||||
at a time (presumably to the end of the current page). The EOP condition can
|
||||
be cleared by clicking Skip To Heading three times to "rip" the paper */
|
||||
|
||||
if (this.paperLeft <= 0 && !this.ready) {
|
||||
@@ -327,6 +356,8 @@ B5500LinePrinter.prototype.printerOnload = function printerOnload() {
|
||||
|
||||
this.window.addEventListener("beforeunload",
|
||||
B5500LinePrinter.prototype.beforeUnload, false);
|
||||
this.paper.addEventListener("dblclick",
|
||||
B5500CentralControl.bindMethod(this, B5500LinePrinter.prototype.copyPaper));
|
||||
this.$$("LPEndOfPaperBtn").addEventListener("click",
|
||||
B5500CentralControl.bindMethod(this, B5500LinePrinter.prototype.LPEndOfPaperBtn_onclick), false);
|
||||
this.$$("LPFormFeedBtn").addEventListener("click",
|
||||
|
||||
@@ -66,7 +66,9 @@ B5500MagTapeDrive.prototype.tapeRemote = 2;
|
||||
|
||||
B5500MagTapeDrive.prototype.density = 800;
|
||||
// 800 bits/inch
|
||||
B5500MagTapeDrive.prototype.charsPerSec = 72000;
|
||||
B5500MagTapeDrive.prototype.tapeSpeed = 90;
|
||||
// tape motion speed [inches/sec]
|
||||
B5500MagTapeDrive.prototype.charsPerSec = B5500MagTapeDrive.prototype.tapeSpeed*B5500MagTapeDrive.prototype.density;
|
||||
// B425, 90 inches/sec @ 800 bits/inch
|
||||
B5500MagTapeDrive.prototype.gapLength = 0.75;
|
||||
// inter-block blank tape gap [inches]
|
||||
@@ -74,8 +76,6 @@ B5500MagTapeDrive.prototype.startStopTime = 0.0045 + 0.0042;
|
||||
// tape start+stop time [sec]
|
||||
B5500MagTapeDrive.prototype.rewindSpeed = 320;
|
||||
// rewind speed [inches/sec]
|
||||
B5500MagTapeDrive.prototype.tapeSpeed = B5500MagTapeDrive.prototype.charsPerSec/B5500MagTapeDrive.prototype.density;
|
||||
// tape motion speed [inches/sec]
|
||||
B5500MagTapeDrive.prototype.maxTapeLength = 2410*12;
|
||||
// max tape length on reel [inches]
|
||||
B5500MagTapeDrive.prototype.postEOTLength = 20*12;
|
||||
@@ -159,6 +159,7 @@ B5500MagTapeDrive.prototype.clear = function clear() {
|
||||
this.reelAngle = 0; // current rotation angle of reel image [degrees]
|
||||
this.tapeInches = 0; // number of inches currently up-tape
|
||||
this.writeRing = false; // true if write ring is present and tape is writable
|
||||
this.botSensed = false; // true if BOT marker sensed during reverse tape motion
|
||||
this.atBOT = true; // true if tape at BOT
|
||||
this.atEOT = false; // true if tape at EOT
|
||||
|
||||
@@ -245,15 +246,16 @@ B5500MagTapeDrive.prototype.setAtBOT = function setAtBOT(atBOT) {
|
||||
|
||||
if (atBOT ^ this.atBOT) {
|
||||
this.atBOT = atBOT;
|
||||
if (atBOT) {
|
||||
if (!atBOT) {
|
||||
this.botSensed = false;
|
||||
B5500Util.removeClass(this.$$("MTAtBOTLight"), "annunciatorLit");
|
||||
} else {
|
||||
this.imgIndex = 0;
|
||||
this.tapeInches = 0;
|
||||
B5500Util.addClass(this.$$("MTAtBOTLight"), "annunciator");
|
||||
this.reelBar.value = this.imgMaxInches;
|
||||
this.reelIcon.style.transform = "rotate(0deg)";
|
||||
this.reelIcon.style["-webkit-transform"] = "rotate(0deg)"; // temp for Chrome
|
||||
} else {
|
||||
B5500Util.removeClass(this.$$("MTAtBOTLight"), "annunciatorLit");
|
||||
this.reelIcon.style.transform = "none";
|
||||
this.reelIcon.style["-webkit-transform"] = "none"; // temp for Chrome
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -264,11 +266,11 @@ B5500MagTapeDrive.prototype.setAtEOT = function setAtEOT(atEOT) {
|
||||
|
||||
if (atEOT ^ this.atEOT) {
|
||||
this.atEOT = atEOT;
|
||||
if (atEOT) {
|
||||
if (!atEOT) {
|
||||
B5500Util.removeClass(this.$$("MTAtEOTLight"), "annunciatorLit");
|
||||
} else {
|
||||
B5500Util.addClass(this.$$("MTAtEOTLight"), "annunciatorLit");
|
||||
this.reelBar.value = 0;
|
||||
} else {
|
||||
B5500Util.removeClass(this.$$("MTAtEOTLight"), "annunciatorLit");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -413,6 +415,7 @@ B5500MagTapeDrive.prototype.loadTape = function loadTape() {
|
||||
mt.reelBar.value = mt.imgMaxInches;
|
||||
mt.setAtEOT(false);
|
||||
mt.setAtBOT(true);
|
||||
mt.botSensed = false;
|
||||
mt.tapeState = mt.tapeLocal; // setTapeRemote() requires it not be unloaded
|
||||
mt.setTapeRemote(false);
|
||||
mt.reelIcon.style.visibility = "visible";
|
||||
@@ -471,15 +474,18 @@ B5500MagTapeDrive.prototype.loadTape = function loadTape() {
|
||||
|
||||
function blankLoader() {
|
||||
/* Loads a blank tape image into the drive */
|
||||
var x;
|
||||
|
||||
writeRing = true;
|
||||
eotInches = tapeInches;
|
||||
tapeInches += mt.postEOTLength;
|
||||
mt.image = new Uint8Array(new ArrayBuffer(tapeInches*mt.density));
|
||||
mt.image[0] = 0x81; // put a little noise on the tape to avoid blank-tape timeouts
|
||||
mt.image[1] = 0x03;
|
||||
mt.image[2] = 0x8F;
|
||||
mt.imgTopIndex = 3;
|
||||
for (x=1; x<80; ++x) {
|
||||
mt.image[x] = 0x40;
|
||||
}
|
||||
mt.image[80] = 0x8F;
|
||||
mt.imgTopIndex = 81;
|
||||
finishLoad();
|
||||
}
|
||||
|
||||
@@ -741,16 +747,16 @@ B5500MagTapeDrive.prototype.tapeRewind = function tapeRewind(makeReady) {
|
||||
if (interval <= 0) {
|
||||
interval = this.spinUpdateInterval/2;
|
||||
}
|
||||
if (this.tapeInches > 0) {
|
||||
if (this.tapeInches <= 0) {
|
||||
this.setAtBOT(true);
|
||||
this.botSensed = true;
|
||||
this.timer = setCallback(this.mnemonic, this, 2000, rewindFinish);
|
||||
} else {
|
||||
inches = interval/1000*this.rewindSpeed;
|
||||
this.tapeInches -= inches;
|
||||
lastStamp = stamp;
|
||||
this.timer = setCallback(this.mnemonic, this, this.spinUpdateInterval, rewindDelay);
|
||||
this.spinReel(-inches);
|
||||
} else {
|
||||
this.setAtBOT(true);
|
||||
this.timer = setCallback(this.mnemonic, this, 2000, rewindFinish);
|
||||
this.spinReel(6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,10 +828,8 @@ B5500MagTapeDrive.prototype.buildErrorMask = function buildErrorMask(chars) {
|
||||
var mask = this.errorMask & 0x01FC7FFF; // clear out the char count bits
|
||||
|
||||
mask |= (chars & 0x07) << 15;
|
||||
if (this.atBOT) {
|
||||
if (this.botSensed) {
|
||||
mask |= 0x80000; // tape at BOT
|
||||
} else if (this.atEOT) {
|
||||
mask |= 0x40020; // tape at EOT
|
||||
}
|
||||
this.errorMask = mask;
|
||||
return mask;
|
||||
@@ -894,7 +898,11 @@ B5500MagTapeDrive.prototype.bcdSpaceBackward = function bcdSpaceBackward(checkEO
|
||||
|
||||
if (imgIndex <= 0) {
|
||||
this.setAtBOT(true);
|
||||
this.errorMask |= 0x100000; // set blank-tape bit
|
||||
if (this.botSensed) {
|
||||
this.errorMask |= 0x100000; // set blank-tape bit
|
||||
} else {
|
||||
this.botSensed = true;
|
||||
}
|
||||
} else {
|
||||
if (this.atEOT) {
|
||||
this.setAtEOT(false);
|
||||
@@ -1028,7 +1036,11 @@ B5500MagTapeDrive.prototype.bcdReadBackward = function bcdReadBackward(oddParity
|
||||
|
||||
if (imgIndex <= 0) {
|
||||
this.setAtBOT(true);
|
||||
this.errorMask |= 0x100000; // set blank-tape bit
|
||||
if (this.botSensed) {
|
||||
this.errorMask |= 0x100000; // set blank-tape bit
|
||||
} else {
|
||||
this.botSensed = true;
|
||||
}
|
||||
} else {
|
||||
if (this.atEOT) {
|
||||
this.setAtEOT(false);
|
||||
@@ -1196,7 +1208,7 @@ B5500MagTapeDrive.prototype.read = function read(finish, buffer, length, mode, c
|
||||
count = this.bcdReadBackward(mode);
|
||||
residue = 7 - count % 8;
|
||||
imgCount -= this.imgIndex;
|
||||
inches = -imgCount/this.density - this.gapLength;
|
||||
inches = (imgCount > 0 ? -imgCount/this.density - this.gapLength : 0);
|
||||
this.tapeInches += inches;
|
||||
if (this.atEOT && this.tapeInches < this.imgEOTInches) {
|
||||
this.setAtEOT(false);
|
||||
@@ -1205,10 +1217,11 @@ B5500MagTapeDrive.prototype.read = function read(finish, buffer, length, mode, c
|
||||
count = this.bcdReadForward(mode);
|
||||
residue = count % 8;
|
||||
imgCount = this.imgIndex - imgCount;
|
||||
inches = imgCount/this.density + this.gapLength;
|
||||
inches = (imgCount > 0 ? imgCount/this.density + this.gapLength : 0);
|
||||
this.tapeInches += inches;
|
||||
if (!this.atEOT && this.tapeInches > this.imgEOTInches) {
|
||||
this.setAtEOT(true);
|
||||
this.errorMask |= 0x40020; // tape at EOT
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1249,7 +1262,7 @@ B5500MagTapeDrive.prototype.space = function space(finish, length, control) {
|
||||
if (control) {
|
||||
this.bcdSpaceBackward(true);
|
||||
imgCount -= this.imgIndex;
|
||||
inches = -imgCount/this.density - this.gapLength;
|
||||
inches = (imgCount > 0 ? -imgCount/this.density - this.gapLength : 0);
|
||||
this.tapeInches += inches;
|
||||
if (this.atEOT && this.tapeInches < this.imgEOTInches) {
|
||||
this.setAtEOT(false);
|
||||
@@ -1257,10 +1270,11 @@ B5500MagTapeDrive.prototype.space = function space(finish, length, control) {
|
||||
} else {
|
||||
this.bcdSpaceForward(true);
|
||||
imgCount = this.imgIndex - imgCount;
|
||||
inches = imgCount/this.density + this.gapLength;
|
||||
inches = (imgCount > 0 ? imgCount/this.density + this.gapLength : 0);
|
||||
this.tapeInches += inches;
|
||||
if (!this.atEOT && this.tapeInches > this.imgEOTInches) {
|
||||
this.setAtEOT(true);
|
||||
this.errorMask |= 0x40020; // tape at EOT
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1307,6 +1321,7 @@ B5500MagTapeDrive.prototype.write = function write(finish, buffer, length, mode,
|
||||
this.tapeInches += inches;
|
||||
if (!this.atEOT && this.tapeInches > this.imgEOTInches) {
|
||||
this.setAtEOT(true);
|
||||
this.errorMask |= 0x40020; // tape at EOT
|
||||
}
|
||||
|
||||
this.imgWritten = true;
|
||||
@@ -1349,6 +1364,7 @@ B5500MagTapeDrive.prototype.erase = function erase(finish, length) {
|
||||
this.tapeInches += inches;
|
||||
if (!this.atEOT && this.tapeInches > this.imgEOTInches) {
|
||||
this.setAtEOT(true);
|
||||
this.errorMask |= 0x40020; // tape at EOT
|
||||
}
|
||||
|
||||
this.imgWritten = true;
|
||||
@@ -1410,11 +1426,19 @@ B5500MagTapeDrive.prototype.writeInterrogate = function writeInterrogate(finish,
|
||||
} else if (!this.ready) {
|
||||
finish(0x04, 0); // report unit not ready
|
||||
} else {
|
||||
if (this.writeRing) {
|
||||
this.buildErrorMask(0);
|
||||
} else {
|
||||
this.errorMask |= 0x50; // RD bits 26 & 28 => no write ring, don't return Mod III bits
|
||||
if (!this.writeRing) {
|
||||
this.errorMask |= 0x50; // RD bits 26 & 28 => no write ring
|
||||
}
|
||||
this.buildErrorMask(0);
|
||||
|
||||
/* For some reason the MCP does not like the BOT status being reported in
|
||||
the result descriptor for a write interrogate. The I/O Control Unit flows
|
||||
clearly show the Mod-III I/O Unit bits being set, but the MCP error mask
|
||||
causes them to be seen as fatal errors. For now, we'll unconditionally
|
||||
eliminate the BOT result bit in the error mask, although that just doesn't
|
||||
seem right */
|
||||
this.errorMask &= 0xFF7FFFF;
|
||||
|
||||
finish(this.errorMask, 0);
|
||||
}
|
||||
//console.log(this.mnemonic + " writeInterrogate: c=" + control + ", mask=" + this.errorMask.toString(8));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
CACHE MANIFEST
|
||||
# retro-B5500 emulator 1.01, 2015-02-08 17:30
|
||||
# retro-B5500 emulator 1.02, 2015-06-14 16:30
|
||||
|
||||
CACHE:
|
||||
../emulator/B5500CentralControl.js
|
||||
|
||||
@@ -88,6 +88,7 @@ B5500SPOUnit.prototype.clear = function clear() {
|
||||
this.nextCharTime = 0;
|
||||
|
||||
this.spoState = this.spoLocal; // Current state of SPO interface
|
||||
this.spoInputRequested = false; // INPUT REQUEST button pressed
|
||||
this.spoLocalRequested = false; // LOCAL button pressed while active
|
||||
};
|
||||
|
||||
@@ -96,6 +97,7 @@ B5500SPOUnit.prototype.setLocal = function setLocal() {
|
||||
/* Sets the status of the SPO to Local and enables the input element */
|
||||
|
||||
this.spoLocalRequested = false;
|
||||
this.spoInputRequested = false;
|
||||
this.spoState = this.spoLocal;
|
||||
this.endOfPaper.scrollIntoView();
|
||||
B5500Util.addClass(this.$$("SPOLocalBtn"), "yellowLit");
|
||||
@@ -134,6 +136,7 @@ B5500SPOUnit.prototype.setRemote = function setRemote() {
|
||||
if (this.spoState == this.spoLocal) {
|
||||
this.spoState = this.spoRemote;
|
||||
this.spoLocalRequested = false;
|
||||
this.spoInputRequested = false;
|
||||
B5500Util.addClass(this.$$("SPORemoteBtn"), "yellowLit");
|
||||
B5500Util.removeClass(this.$$("SPOLocalBtn"), "yellowLit");
|
||||
B5500Util.removeClass(this.inputBox, "visible");
|
||||
@@ -212,7 +215,8 @@ B5500SPOUnit.prototype.printChar = function printChar(c) {
|
||||
line += s;
|
||||
++this.printCol;
|
||||
} else {
|
||||
line = line.substring(0, 71) + s;
|
||||
line = s;
|
||||
this.appendEmptyLine();
|
||||
}
|
||||
this.paper.lastChild.nodeValue = line;
|
||||
};
|
||||
@@ -259,9 +263,19 @@ B5500SPOUnit.prototype.requestInput = function requestInput() {
|
||||
/* Handles the request for keyboard input, from either the Input Request
|
||||
button or the ESC key */
|
||||
|
||||
if (this.spoState == this.spoRemote || this.spoState == this.spoOutput) {
|
||||
B5500Util.addClass(this.$$("SPOInputRequestBtn"), "yellowLit");
|
||||
this.signal();
|
||||
switch (this.spoState) {
|
||||
case this.spoRemote:
|
||||
case this.spoOutput:
|
||||
if (!this.spoInputRequested) {
|
||||
this.spoInputRequested = true;
|
||||
B5500Util.addClass(this.$$("SPOInputRequestBtn"), "yellowLit");
|
||||
this.signal();
|
||||
}
|
||||
break;
|
||||
case this.spoInput:
|
||||
// the second click moved focus out of the SPO input control
|
||||
this.inputBox.focus();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -400,7 +414,7 @@ B5500SPOUnit.prototype.keyDown = function keyDown(ev) {
|
||||
B5500SPOUnit.prototype.copyPaper = function copyPaper(ev) {
|
||||
/* Copies the text contents of the "paper" area of the SPO, opens a new
|
||||
temporary window, and pastes that text into the window so it can be copied
|
||||
or saved */
|
||||
or saved by the user */
|
||||
var text = ev.target.textContent;
|
||||
var title = "B5500 " + this.mnemonic + " Text Snapshot";
|
||||
var win = window.open("./B5500FramePaper.html", this.mnemonic + "-Snapshot",
|
||||
@@ -501,8 +515,8 @@ B5500SPOUnit.prototype.spoOnload = function spoOnload() {
|
||||
|
||||
this.printText("retro-B5500 Emulator Version " + B5500CentralControl.version,
|
||||
B5500CentralControl.bindMethod(this, function initFinish() {
|
||||
//window.open("", "B5500Console").focus();
|
||||
this.window.focus();
|
||||
window.open("", "B5500Console").focus();
|
||||
this.setRemote();
|
||||
this.appendEmptyLine("\xA0");
|
||||
this.endOfPaper.scrollIntoView();
|
||||
@@ -521,6 +535,7 @@ B5500SPOUnit.prototype.read = function read(finish, buffer, length, mode, contro
|
||||
switch (this.spoState) {
|
||||
case this.spoRemote:
|
||||
this.spoState = this.spoInput;
|
||||
this.spoInputRequested = false;
|
||||
B5500Util.addClass(this.$$("SPOReadyBtn"), "yellowLit");
|
||||
B5500Util.removeClass(this.$$("SPOInputRequestBtn"), "yellowLit");
|
||||
this.endOfPaper.scrollIntoView();
|
||||
|
||||
@@ -84,8 +84,8 @@
|
||||
|
||||
(function (global) {
|
||||
/* Define a closure for the setCallback() mechanism */
|
||||
var delayAlpha = 0.99; // exponential-moving-average decay factor
|
||||
var delayDev = {NUL: 0}; // hash of average delay time deviations by category
|
||||
var delayAlpha = 0.25; // delay deviation decay factor
|
||||
var delayDev = {NUL: 0}; // hash of delay time deviations by category
|
||||
var minTimeout = 4; // minimum setTimeout() threshold, milliseconds
|
||||
var nextTokenNr = 1; // next setCallback token return value
|
||||
var pendingCallbacks = {}; // hash of pending callbacks, indexed by token as a string
|
||||
@@ -107,8 +107,7 @@
|
||||
delete pendingCallbacks[tokenName];
|
||||
category = thisCallback.category;
|
||||
if (category) {
|
||||
delayDev[category] = (delayDev[category] || 0)*delayAlpha +
|
||||
(endStamp - thisCallback.startStamp - thisCallback.delay)*(1.0-delayAlpha);
|
||||
delayDev[category] += endStamp - thisCallback.startStamp - thisCallback.delay;
|
||||
}
|
||||
try {
|
||||
thisCallback.fcn.call(thisCallback.context, thisCallback.arg);
|
||||
@@ -152,6 +151,7 @@
|
||||
setTimeout mechanism will be used */
|
||||
var categoryName = (category || "NUL").toString();
|
||||
var delay = callbackDelay || 0;
|
||||
var delayBias;
|
||||
var thisCallback;
|
||||
var token = nextTokenNr++;
|
||||
var tokenName = token.toString();
|
||||
@@ -175,7 +175,14 @@
|
||||
pendingCallbacks[tokenName] = thisCallback;
|
||||
|
||||
// Decide whether to do a time wait or just a yield.
|
||||
delay -= (delayDev[categoryName] || 0); // bias by the current avg. deviation
|
||||
if (categoryName in delayDev) {
|
||||
delayBias = delayDev[categoryName]*delayAlpha;
|
||||
delayDev[categoryName] -= delayBias;
|
||||
delay -= delayBias;
|
||||
} else {
|
||||
delayDev[categoryName] = 0; // got a new one
|
||||
}
|
||||
|
||||
if (delay < minTimeout) {
|
||||
thisCallback.isTimeout = false;
|
||||
thisCallback.cancelToken = 0;
|
||||
|
||||
Reference in New Issue
Block a user