1
0
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:
Paul Kimpel
2015-06-14 19:06:27 -07:00
parent 378ffb8dc1
commit f1fe18dab3
25 changed files with 666 additions and 192 deletions

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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
};
/**************************************/

View File

@@ -33,8 +33,8 @@
<li><a href="./webSite/HelpMenu.html">Help &amp; 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 &bull; Licensed under the MIT License
</div>
<div id=lastModDiv>Revised
2015-02-08
2015-06-10
</div>
</body>

View File

@@ -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);
});
}

View File

@@ -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() {});
});
}

View File

@@ -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() {});
});
}

View File

@@ -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);
});
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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 &bull; Licensed under the MIT License
</div>
<div id=lastModDiv>Revised
2014-09-26
2015-06-10
</div>
</body>

View File

@@ -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",

View File

@@ -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>

View File

@@ -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}

View File

@@ -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>&nbsp;
<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 &ndash; 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>

View File

@@ -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 "&amp;";
case "<":
return "&lt;";
case ">":
return "&gt;";
case "\"":
return "&quot;";
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);

View File

@@ -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);

View File

@@ -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 +

View File

@@ -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);
});
}
};

View File

@@ -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>

View File

@@ -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",

View File

@@ -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));

View File

@@ -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

View File

@@ -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();

View File

@@ -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;