mirror of
https://github.com/pkimpel/retro-b5500.git
synced 2026-02-12 03:07:30 +00:00
Initial integration of B5500SPOUnit with B5500IOUnit, et al.
This commit is contained in:
@@ -108,7 +108,7 @@ B5500CentralControl.unitIndex = [
|
||||
[null, 47,null, 46, 31, 45, 29, 44, 30, 43, 24, 42, 28, 41, 23, 40,
|
||||
17, 39, 20, 38, 19, 37,null, 36,null, 35,null, 34,null, 33, 22, 32]];
|
||||
|
||||
// The following object maps the unit mnemonics from B5500SystemConfiguration.Units
|
||||
// The following object maps the unit mnemonics from B5500SystemConfiguration.units
|
||||
// to the attributes needed to configure the CC unit[] array.
|
||||
|
||||
B5500CentralControl.unitSpecs = {
|
||||
@@ -814,7 +814,7 @@ B5500CentralControl.prototype.configureSystem = function() {
|
||||
function makeChange(cc, maskBit) {
|
||||
return function(ready) {
|
||||
cc.unitStatusMask = (ready ? cc.bitSet(cc.unitStatusMask, maskBit)
|
||||
: cc.bitReset(cc.unitStatusMask, maskBit);
|
||||
: cc.bitReset(cc.unitStatusMask, maskBit));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -865,18 +865,15 @@ B5500CentralControl.prototype.configureSystem = function() {
|
||||
}
|
||||
|
||||
// Configure the peripheral units
|
||||
for (mnem in cfg.Units) {
|
||||
if (cfg.Units[mnem]) {
|
||||
for (mnem in cfg.units) {
|
||||
if (cfg.units[mnem]) {
|
||||
specs = B5500CentralControl.unitSpecs[mnem];
|
||||
if (specs) {
|
||||
unitClass = specs.unitClass || B5500DummyUnit;
|
||||
if (unitClass) {
|
||||
u = new unitClass(mnem, specs.index, specs.designate,
|
||||
makeChange(this, specs.index), makeSignal(this, mnem);
|
||||
makeChange(this, specs.index), makeSignal(this, mnem));
|
||||
this.unit[specs.index] = u;
|
||||
u.mnemonic = mnem;
|
||||
u.index = specs.index;
|
||||
u.designate = specs.designate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,7 +549,7 @@ B5500IOUnit.prototype.finish = function () {
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500IOUnit.prototype.makeFinish(f) {
|
||||
B5500IOUnit.prototype.makeFinish = function(f) {
|
||||
/* Utility function to create a closure for I/O finish handlers */
|
||||
var that = this;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ var B5500SystemConfiguration = {
|
||||
IO3: false, // I/O Unit 3 available
|
||||
IO4: false, // I/O Unit 4 available
|
||||
|
||||
MemMod: [
|
||||
memMod: [
|
||||
true, // Memory module 0 available (4KW)
|
||||
true, // Memory module 1 available (4KW)
|
||||
false, // Memory module 2 available (4KW)
|
||||
@@ -37,7 +37,7 @@ var B5500SystemConfiguration = {
|
||||
false, // Memory module 6 available (4KW)
|
||||
false], // Memory module 7 available (4KW)
|
||||
|
||||
Units: {
|
||||
units: {
|
||||
SPO: true, // SPO keyboard/printer
|
||||
DKA: false, // Disk File Control A
|
||||
DKB: false, // Disk File Control B
|
||||
@@ -68,5 +68,5 @@ var B5500SystemConfiguration = {
|
||||
MTP: false, // Magnetic Tape Unit P
|
||||
MTR: false, // Magnetic Tape Unit R
|
||||
MTS: false, // Magnetic Tape Unit S
|
||||
MTT: false}; // Magnetic Tape Unit X
|
||||
MTT: false}, // Magnetic Tape Unit X
|
||||
};
|
||||
|
||||
@@ -7,83 +7,105 @@
|
||||
<meta http-equiv="Content-Style-Type" content="text/css">
|
||||
<link id=defaultStyleSheet rel=stylesheet type="text/css" href="B5500Console.css">
|
||||
|
||||
<script src="B5500SPOUnit.js"></script>
|
||||
|
||||
<script src="../emulator/B5500SystemConfiguration.js"></script>
|
||||
<script src="../emulator/B5500CentralControl.js"></script>
|
||||
<script src="../emulator/B5500Processor.js"></script>
|
||||
<script src="../emulator/B5500IOUnit.js"></script>
|
||||
|
||||
<script>
|
||||
var paPanel;
|
||||
var paState = 0;
|
||||
var paTimer = null;
|
||||
var pbState = 0;
|
||||
var pbTimer = null;
|
||||
|
||||
var PAStateChange = function() {
|
||||
var aNormal = document.getElementById("ANormalBtn");
|
||||
var aControl = document.getElementById("AControlBtn");
|
||||
var delay = Math.random();
|
||||
|
||||
if (paState || !pbState) { // PA will go to Normal State onlyl if PB is already in Normal State
|
||||
paState = 0;
|
||||
aNormal.className = "yellowButton";
|
||||
aControl.className = "yellowButton yellowLit";
|
||||
delay = Math.log(delay+1)*250;
|
||||
} else {
|
||||
paState = 1;
|
||||
aNormal.className = "yellowButton yellowLit";
|
||||
aControl.className = "yellowButton";
|
||||
delay = Math.log(2-delay)*delay*250;
|
||||
}
|
||||
paTimer = setTimeout(PAStateChange, delay);
|
||||
};
|
||||
|
||||
var PBStateChange = function() {
|
||||
var bNormal = document.getElementById("BNormalBtn");
|
||||
var delay = Math.random();
|
||||
|
||||
if (pbState) {
|
||||
pbState = 0;
|
||||
bNormal.className = "yellowButton";
|
||||
delay = Math.log(delay+1)*1000;
|
||||
} else {
|
||||
pbState = 1;
|
||||
bNormal.className = "yellowButton yellowLit";
|
||||
delay = Math.log(2-delay)*delay*delay*1000;
|
||||
}
|
||||
pbTimer = setTimeout(PBStateChange, delay);
|
||||
};
|
||||
|
||||
var PowerOnBtn_Click = function() {
|
||||
document.getElementById("PowerOnBtn").className = "whiteButton whiteLit";
|
||||
document.getElementById("AControlBtn").className = "yellowButton yellowLit";
|
||||
paState = pbState = 0;
|
||||
paTimer = setTimeout(PAStateChange, 3000);
|
||||
pbTimer = setTimeout(PBStateChange, 10000);
|
||||
if (!paPanel) {
|
||||
paPanel = window.open("B5500ProcessorPanel.html", "PAPanel", "resizable=yes,scrollbars=yes,width=1,height=1");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
var PowerOffBtn_Click = function() {
|
||||
paState = pbSate = 0;
|
||||
document.getElementById("PowerOnBtn").className = "whiteButton";
|
||||
document.getElementById("ANormalBtn").className = "yellowButton";
|
||||
document.getElementById("AControlBtn").className = "yellowButton";
|
||||
document.getElementById("BNormalBtn").className = "yellowButton";
|
||||
if (paPanel) {
|
||||
paPanel.close();
|
||||
paPanel = null;
|
||||
}
|
||||
if (paTimer) {
|
||||
clearTimeout(paTimer);
|
||||
paTimer = null;
|
||||
}
|
||||
if (pbTimer) {
|
||||
clearTimeout(pbTimer);
|
||||
pbTimer = null;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
var spoWin = window.open("B5500SPOUnit.html", "SPOWin", "scrollbars,resizable");
|
||||
var cc = new B5500CentralControl();
|
||||
var paPanel;
|
||||
var paState = 0;
|
||||
var paTimer = null;
|
||||
var pbState = 0;
|
||||
var pbTimer = null;
|
||||
|
||||
var PAStateChange = function() {
|
||||
var aNormal = document.getElementById("ANormalBtn");
|
||||
var aControl = document.getElementById("AControlBtn");
|
||||
var delay = Math.random();
|
||||
|
||||
if (paState || !pbState) { // PA will go to Normal State onlyl if PB is already in Normal State
|
||||
paState = 0;
|
||||
aNormal.className = "yellowButton";
|
||||
aControl.className = "yellowButton yellowLit";
|
||||
delay = Math.log(delay+1)*250;
|
||||
} else {
|
||||
paState = 1;
|
||||
aNormal.className = "yellowButton yellowLit";
|
||||
aControl.className = "yellowButton";
|
||||
delay = Math.log(2-delay)*delay*250;
|
||||
}
|
||||
paTimer = setTimeout(PAStateChange, delay);
|
||||
};
|
||||
|
||||
var PBStateChange = function() {
|
||||
var bNormal = document.getElementById("BNormalBtn");
|
||||
var delay = Math.random();
|
||||
|
||||
if (pbState) {
|
||||
pbState = 0;
|
||||
bNormal.className = "yellowButton";
|
||||
delay = Math.log(delay+1)*1000;
|
||||
} else {
|
||||
pbState = 1;
|
||||
bNormal.className = "yellowButton yellowLit";
|
||||
delay = Math.log(2-delay)*delay*delay*1000;
|
||||
}
|
||||
pbTimer = setTimeout(PBStateChange, delay);
|
||||
};
|
||||
|
||||
var PowerOnBtn_Click = function() {
|
||||
document.getElementById("PowerOnBtn").className = "whiteButton whiteLit";
|
||||
document.getElementById("AControlBtn").className = "yellowButton yellowLit";
|
||||
paState = pbState = 0;
|
||||
paTimer = setTimeout(PAStateChange, 3000);
|
||||
pbTimer = setTimeout(PBStateChange, 10000);
|
||||
cc.powerOn();
|
||||
/******
|
||||
if (!paPanel) {
|
||||
paPanel = window.open("B5500ProcessorPanel.html", "PAPanel", "resizable=yes,scrollbars=yes,width=1,height=1");
|
||||
}
|
||||
*****/
|
||||
return true;
|
||||
};
|
||||
|
||||
var PowerOffBtn_Click = function() {
|
||||
paState = pbSate = 0;
|
||||
document.getElementById("PowerOnBtn").className = "whiteButton";
|
||||
document.getElementById("ANormalBtn").className = "yellowButton";
|
||||
document.getElementById("AControlBtn").className = "yellowButton";
|
||||
document.getElementById("BNormalBtn").className = "yellowButton";
|
||||
cc.powerOff();
|
||||
/*****
|
||||
if (paPanel) {
|
||||
paPanel.close();
|
||||
paPanel = null;
|
||||
}
|
||||
*****/
|
||||
if (paTimer) {
|
||||
clearTimeout(paTimer);
|
||||
paTimer = null;
|
||||
}
|
||||
if (pbTimer) {
|
||||
clearTimeout(pbTimer);
|
||||
pbTimer = null;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/***** window.onload() outer block *****/
|
||||
|
||||
document.getElementById("PowerOnBtn").onclick = function() {
|
||||
return PowerOnBtn_Click();
|
||||
};
|
||||
|
||||
document.getElementById("PowerOffBtn").onclick = function() {
|
||||
return PowerOffBtn_Click();
|
||||
};
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
@@ -103,10 +125,8 @@ window.onload = function() {
|
||||
<button id=BNormalBtn class=yellowButton>B NORMAL</button>
|
||||
<button id=BControlBtn class=yellowButton>B CONTROL</button>
|
||||
|
||||
<button id=PowerOnBtn class=whiteButton
|
||||
onclick="return PowerOnBtn_Click()">POWER<br>ON</button>
|
||||
<button id=PowerOffBtn class=blackButton
|
||||
onclick="return PowerOffBtn_Click()">POWER OFF</button>
|
||||
<button id=PowerOnBtn class=whiteButton>POWER<br>ON</button>
|
||||
<button id=PowerOffBtn class=blackButton>POWER OFF</button>
|
||||
|
||||
<div id=BurroughsLogo>
|
||||
<img id=BurroughsLogoImage src="Burroughs-Logo-Neg.jpg">
|
||||
|
||||
@@ -45,54 +45,54 @@ B5500DummyUnit.prototype.clear = function() {
|
||||
B5500DummyUnit.prototype.read = function(finish, buffer, length, mode, control) {
|
||||
/* Initiates a read operation on the unit */
|
||||
|
||||
finish(0x04, 0);
|
||||
finish(0x04, 0); // report unit not ready
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500DummyUnit.prototype.space = function(finish, length, control) {
|
||||
/* Initiates a space operation on the unit */
|
||||
|
||||
finish(0x04, 0);
|
||||
finish(0x04, 0); // report unit not ready
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500DummyUnit.prototype.write = function(finish, buffer, length, mode, control) {
|
||||
/* Initiates a write operation on the unit */
|
||||
|
||||
finish(0x04, 0);
|
||||
finish(0x04, 0); // report unit not ready
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500DummyUnit.prototype.erase = function(finish, length) {
|
||||
/* Initiates an erase operation on the unit */
|
||||
|
||||
finish(0x04, 0);
|
||||
finish(0x04, 0); // report unit not ready
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500DummyUnit.prototype.rewind = function(finish) {
|
||||
/* Initiates a rewind operation on the unit */
|
||||
|
||||
finish(0x04, 0);
|
||||
finish(0x04, 0); // report unit not ready
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500DummyUnit.prototype.readCheck = function(finish, length) {
|
||||
/* Initiates a read check operation on the unit */
|
||||
|
||||
finish(0x04, 0);
|
||||
finish(0x04, 0); // report unit not ready
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500DummyUnit.prototype.readInterrogate = function(finish) {
|
||||
/* Initiates a read interrogate operation on the unit */
|
||||
|
||||
finish(0x04, 0);
|
||||
finish(0x04, 0); // report unit not ready
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500DummyUnit.prototype.writeInterrogate = function (finish) {
|
||||
/* Initiates a write interrogate operation on the unit */
|
||||
|
||||
finish(0x04, 0);
|
||||
finish(0x04, 0); // report unit not ready
|
||||
};
|
||||
|
||||
@@ -6,429 +6,6 @@
|
||||
<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="B5500SPOUnit.css">
|
||||
|
||||
<script>
|
||||
|
||||
window.onload = function() {
|
||||
const spoNotReady = 0;
|
||||
const spoLocal = 1;
|
||||
const spoRemote = 2;
|
||||
const spoInput = 3;
|
||||
const spoOutput = 4;
|
||||
|
||||
var $$ = function(e) {return document.getElementById(e)};
|
||||
var msgTank = [];
|
||||
var spoState = spoNotReady;
|
||||
var spoLocalRequested = false;
|
||||
var spoInputActive = false;
|
||||
var spoInputRequested = false;
|
||||
|
||||
var msgCtl = {
|
||||
buffer: null,
|
||||
length: 0,
|
||||
index: 0,
|
||||
col: 0,
|
||||
nextCharTime: 0,
|
||||
finished: null};
|
||||
|
||||
var keyFilter = [ // Filter keyCode values to valid B5500 ones
|
||||
0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, // 00-0F
|
||||
0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, // 10-1F
|
||||
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x3F,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, // 20-2F
|
||||
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, // 30-3F
|
||||
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, // 40-4F
|
||||
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x3F,0x5D,0x3F,0x3F, // 50-5F
|
||||
0x3F,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, // 60-6F
|
||||
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x3F]; // 70-7F
|
||||
|
||||
var hasClass = function(e, name) {
|
||||
/* returns true if element "e" has class "name" in its class list */
|
||||
var classes = e.className;
|
||||
|
||||
if (!e) {
|
||||
return false;
|
||||
} else if (classes == name) {
|
||||
return true;
|
||||
} else {
|
||||
return (classes.search("\\b" + name + "\\b") >= 0);
|
||||
}
|
||||
};
|
||||
|
||||
var addClass = function(e, name) {
|
||||
/* Adds a class "name" to the element "e"s class list */
|
||||
|
||||
if (!hasClass(e, name)) {
|
||||
e.className += (" " + name);
|
||||
}
|
||||
};
|
||||
|
||||
var removeClass = function(e, name) {
|
||||
/* Removes the class "name" from the element "e"s class list */
|
||||
|
||||
e.className = e.className.replace(new RegExp("\\b" + name + "\\b\\s*", "g"), "");
|
||||
};
|
||||
|
||||
var addFrameStyles = function(frame) {
|
||||
/* Appends the necessary styles for the <iframe> to its internal stylesheet */
|
||||
|
||||
frame.contentDocument.head.innerHTML += "<style>" +
|
||||
"BODY {background-color: #FFE} " +
|
||||
"PRE {margin: 0; font-size: 10pt; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace}" +
|
||||
"</style>";
|
||||
};
|
||||
|
||||
var appendEmptyLine = function(count) {
|
||||
/* Appends "count" <pre> elements to the <iframe>, creating an empty text node
|
||||
inside each new element */
|
||||
var body = $$("SPOUT").contentDocument.body;
|
||||
var line;
|
||||
var x;
|
||||
|
||||
for (x=1; x<=count; x++) {
|
||||
line = document.createElement("pre");
|
||||
line.appendChild(document.createTextNode(""));
|
||||
body.appendChild(line);
|
||||
line.scrollIntoView();
|
||||
}
|
||||
};
|
||||
|
||||
var accept = function() {
|
||||
var inputBtn = $$("SPOInputRequestBtn");
|
||||
|
||||
spoState = spoInput;
|
||||
addClass(inputBtn, "yellowLit");
|
||||
|
||||
msgCtl.buffer = new Uint8Array(80);
|
||||
msgCtl.length = 0;
|
||||
msgCtl.index = 0;
|
||||
msgCtl.col = 0;
|
||||
msgCtl.nextCharTime = 0;
|
||||
msgCtl.finished = printFinished;
|
||||
msgTank.push(msgCtl.buffer);
|
||||
};
|
||||
|
||||
var backspaceChar = function() {
|
||||
/* Handles backspace for SPO input */
|
||||
var line = $$("SPOUT").contentDocument.body.lastChild.lastChild;
|
||||
|
||||
if (msgCtl.length > 0) {
|
||||
msgCtl.length--;
|
||||
msgCtl.index--;
|
||||
line.nodeValue = line.nodeValue.substring(0, msgCtl.length);
|
||||
if (msgCtl.col > 0) {
|
||||
msgCtl.col--;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var echoChar = function(c) {
|
||||
/* Echoes the character code "c" to the SPO printer. Used by keyboard input */
|
||||
var body = $$("SPOUT").contentDocument.body;
|
||||
var line = body.lastChild.lastChild;
|
||||
|
||||
if (c == 8) {
|
||||
if (line.nodeValue.length > 0) {
|
||||
line.nodeValue = line.nodeValue.substring(-1);
|
||||
}
|
||||
} else if (c == 13) {
|
||||
appendEmptyLine(1);
|
||||
} else if (line.nodeValue.length < 72) {
|
||||
line.nodeValue += String.fromCharCode(c);
|
||||
} else {
|
||||
line.nodeValue = line.nodeValue.substring(0,71) + String.fromCharCode(c);
|
||||
}
|
||||
};
|
||||
|
||||
var printChar = function() {
|
||||
/* Prints one character to the SPO. If more characters remain to be printed,
|
||||
schedules itself 100 ms later to print the next one, otherwise calls finished().
|
||||
If the column counter exceeds 72, a CR/LF are output. A CR/LF are also output
|
||||
at the end of the message */
|
||||
var body = $$("SPOUT").contentDocument.body;
|
||||
var c;
|
||||
var nextTime = msgCtl.nextCharTime + 100;
|
||||
var delay = nextTime - new Date().getTime();
|
||||
var line = body.lastChild.lastChild;
|
||||
|
||||
msgCtl.nextCharTime = nextTime;
|
||||
if (msgCtl.col < 72) { // print the character
|
||||
if (msgCtl.index < msgCtl.length) {
|
||||
c = String.fromCharCode(msgCtl.buffer[msgCtl.index]);
|
||||
line.nodeValue += c;
|
||||
msgCtl.index++;
|
||||
msgCtl.col++;
|
||||
setTimeout(printChar, delay);
|
||||
} else { // set up for the final CR/LF
|
||||
msgCtl.col = 72;
|
||||
setTimeout(printChar, delay);
|
||||
}
|
||||
} else if (msgCtl.col == 72) { // delay to fake the output of a carriage-return
|
||||
msgCtl.col++;
|
||||
setTimeout(printChar, delay);
|
||||
} else { // actually output the CR/LF
|
||||
appendEmptyLine(1);
|
||||
if (msgCtl.index < msgCtl.length) {
|
||||
msgCtl.col = 0; // more characters to print after the CR/LF
|
||||
setTimeout(printChar, delay);
|
||||
} else { // message text is exhausted
|
||||
msgCtl.finished();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var print = function(buffer, length, finished) {
|
||||
/* Prints the contents of the "buffer" for "length" characters */
|
||||
var body = $$("SPOUT").contentDocument.body;
|
||||
var count = body.childNodes.length;
|
||||
|
||||
spoState = spoOutput;
|
||||
while (count-- > 500) {
|
||||
body.removeChild(body.firstChild);
|
||||
}
|
||||
|
||||
msgCtl.buffer = buffer;
|
||||
msgCtl.length = length;
|
||||
msgCtl.index = 0;
|
||||
msgCtl.col = 0;
|
||||
msgCtl.nextCharTime = new Date().getTime();
|
||||
msgCtl.finished = finished;
|
||||
printChar();
|
||||
};
|
||||
|
||||
var printFinished = function() {
|
||||
/* Called to report that all printing to the SPO is complete */
|
||||
|
||||
if (msgTank.length > 1) {
|
||||
msgTank = msgTank.slice(1);
|
||||
print(msgTank[0], msgTank[0].length, printFinished);
|
||||
} else {
|
||||
spoState = spoRemote;
|
||||
msgTank = [];
|
||||
if (spoLocalRequested) {
|
||||
spoLocalRequested = false;
|
||||
setRemote(false);
|
||||
} else if (spoInputRequested) {
|
||||
spoInputRequested = false;
|
||||
accept();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var setReady = function(ready) {
|
||||
/* Sets the ready status of the SPO based on the truth of "ready" */
|
||||
var readyBtn = $$("SPOReadyBtn");
|
||||
|
||||
if (ready && spoState == spoNotReady) {
|
||||
addClass(readyBtn, "yellowLit");
|
||||
spoState = spoLocal;
|
||||
setRemote(true);
|
||||
} else if (!ready && spoState != spoNotReady) {
|
||||
spoState = spoNotReady;
|
||||
removeClass(readyBtn, "yellowLit");
|
||||
}
|
||||
};
|
||||
|
||||
var setRemote = function(remote) {
|
||||
/* Sets the remote status of the SPO based on the truth of "remote" */
|
||||
var localBtn = $$("SPOLocalBtn");
|
||||
var remoteBtn = $$("SPORemoteBtn");
|
||||
|
||||
if (remote && spoState == spoLocal) {
|
||||
spoState = spoRemote;
|
||||
addClass(remoteBtn, "yellowLit");
|
||||
removeClass(localBtn, "yellowLit");
|
||||
} else if (!remote && spoState == spoRemote) {
|
||||
spoState = spoLocal;
|
||||
spoInputRequested = false;
|
||||
addClass(localBtn, "yellowLit");
|
||||
removeClass(remoteBtn, "yellowLit");
|
||||
}
|
||||
};
|
||||
|
||||
var initiateInput = function(ev) {
|
||||
/* Handles a successful Input Request event and enables the keyboard */
|
||||
|
||||
if (spoState == spoRemote) {
|
||||
accept();
|
||||
} else if (spoState == spoOutput) {
|
||||
inputRequested = true;
|
||||
}
|
||||
};
|
||||
|
||||
var terminateInput = function(ev) {
|
||||
/* Handles the End of Message event */
|
||||
var text;
|
||||
|
||||
if (spoState == spoInput) {
|
||||
if (spoLocalRequested) {
|
||||
setRemote(false);
|
||||
} else {
|
||||
spoState = spoRemote;
|
||||
}
|
||||
removeClass($$("SPOInputRequestBtn"), "yellowLit");
|
||||
text = String.fromCharCode.apply(null, msgCtl.buffer.subarray(0, msgCtl.length));
|
||||
printChar();
|
||||
printText("YOU ENTERED: " + text);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
var cancelInput = function(ev) {
|
||||
/* Handles the Error message event */
|
||||
|
||||
if (spoState = spoInput) {
|
||||
if (spoLocalRequested) {
|
||||
setRemote(false);
|
||||
} else {
|
||||
spoState = spoRemote;
|
||||
}
|
||||
removeClass($$("SPOInputRequestBtn"), "yellowLit");
|
||||
printChar();
|
||||
printText("**ERROR");
|
||||
}
|
||||
};
|
||||
|
||||
var printText = function(msg) {
|
||||
/* Utility function to convert a string to a Typed Array buffer and queue
|
||||
it for printing */
|
||||
var buf = new Uint8Array(msg.length);
|
||||
var length = msg.length;
|
||||
var x;
|
||||
|
||||
for (x=0; x<length; x++) {
|
||||
buf[x] = msg.charCodeAt(x);
|
||||
}
|
||||
|
||||
msgTank.push(buf);
|
||||
if (msgTank.length <= 1) {
|
||||
print(buf, length, printFinished);
|
||||
}
|
||||
};
|
||||
|
||||
var doTests = function() {
|
||||
|
||||
printText("*** B5500 SPO TEST ***");
|
||||
printText(" ");
|
||||
printText("WHAT HATH PASADENA WROUGHT?");
|
||||
printText("");
|
||||
/*****
|
||||
printText("123456789.123456789.123456789.123456789.123456789.123456789.123456789.1");
|
||||
printText("123456789.123456789.123456789.123456789.123456789.123456789.123456789.12");
|
||||
printText("123456789.123456789.123456789.123456789.123456789.123456789.123456789.123");
|
||||
printText("");
|
||||
printText(" 10 20 30 40 50 60 70 80 90 100");
|
||||
printText("123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.");
|
||||
printText("~");
|
||||
printText("END");
|
||||
*****/
|
||||
}
|
||||
|
||||
/***** window.onload() outer block *****/
|
||||
|
||||
window.resizeBy($$("SPODiv").scrollWidth-document.body.scrollWidth,
|
||||
$$("SPODiv").scrollHeight-document.body.scrollHeight);
|
||||
window.moveTo((screen.availWidth-window.outerWidth)/2, (screen.availHeight-window.outerHeight)/2);
|
||||
|
||||
$$("SPORemoteBtn").onclick = function() {
|
||||
setRemote(true);
|
||||
};
|
||||
|
||||
$$("SPOPowerBtn").onclick = function() {
|
||||
alert("Don't DO that");
|
||||
};
|
||||
|
||||
$$("SPOLocalBtn").onclick = function() {
|
||||
spoInputRequested = false;
|
||||
if (msgTank.length > 0) {
|
||||
spoLocalRequested = true;
|
||||
} else {
|
||||
setRemote(false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$$("SPOInputRequestBtn").onclick = initiateInput;
|
||||
|
||||
$$("SPOErrorBtn").onclick = cancelInput;
|
||||
|
||||
$$("SPOEndOfMessageBtn").onclick = terminateInput;
|
||||
|
||||
window.onkeypress = function(ev) {
|
||||
var c = ev.charCode;
|
||||
var index = msgCtl.length;
|
||||
var nextTime;
|
||||
var result = false;
|
||||
var stamp = new Date().getTime();
|
||||
|
||||
if (msgCtl.nextCharTime > stamp) {
|
||||
nextTime = msgCtl.nextCharTime + 100;
|
||||
} else {
|
||||
nextTime = stamp + 100;
|
||||
}
|
||||
msgCtl.nextCharTime = nextTime;
|
||||
if (spoState == spoInput) {
|
||||
if (c >= 32 && c <= 126) {
|
||||
msgCtl.buffer[index] = c = keyFilter[c & 0x7F];
|
||||
if (msgCtl.length < 72) {
|
||||
msgCtl.col++;
|
||||
msgCtl.length++;
|
||||
msgCtl.index++;
|
||||
}
|
||||
setTimeout(function() {echoChar(c)}, nextTime-stamp);
|
||||
}
|
||||
} else if (spoState == spoLocal) {
|
||||
if (c >= 32 && c <= 126) {
|
||||
c = keyFilter[c & 0x7F];
|
||||
setTimeout(function() {echoChar(c)}, nextTime-stamp);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
window.onkeydown = function(ev) {
|
||||
var c = ev.keyCode;
|
||||
var result = false;
|
||||
|
||||
if (spoState == spoRemote) {
|
||||
if (c == 27) {
|
||||
initiateInput(ev);
|
||||
}
|
||||
} else if (spoState == spoInput) {
|
||||
switch (c) {
|
||||
case 27: // ESC
|
||||
cancelInput(ev);
|
||||
break;
|
||||
case 8: // Backspace
|
||||
backspaceChar();
|
||||
break;
|
||||
case 13: // Enter
|
||||
case 126: // "~" (B5500 left arrow/group mark)
|
||||
terminateInput(ev);
|
||||
break;
|
||||
default:
|
||||
result = true;
|
||||
}
|
||||
} else if (spoState == spoLocal) {
|
||||
switch (c) {
|
||||
case 8: // Backspace
|
||||
case 13: // Enter
|
||||
echoChar(c);
|
||||
break;
|
||||
default:
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
addFrameStyles($$("SPOUT"));
|
||||
appendEmptyLine(32);
|
||||
setReady(true);
|
||||
setRemote(true);
|
||||
|
||||
doTests();
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
540
webUI/B5500SPOUnit.js
Normal file
540
webUI/B5500SPOUnit.js
Normal file
@@ -0,0 +1,540 @@
|
||||
/***********************************************************************
|
||||
* retro-b5500/emulator B5500SPOUnit.js
|
||||
************************************************************************
|
||||
* Copyright (c) 2012, Nigel Williams and Paul Kimpel.
|
||||
* Licensed under the MIT License, see
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
************************************************************************
|
||||
* B5500 SPO Peripheral Unit module.
|
||||
*
|
||||
* Defines a SPO peripheral unit type that implements the Supervisory
|
||||
* Print Out device on the operator's console.
|
||||
*
|
||||
************************************************************************
|
||||
* 2012-12-22 P.Kimpel
|
||||
* Original version, from B5500DummyUnit.js.
|
||||
***********************************************************************/
|
||||
"use strict";
|
||||
|
||||
/**************************************/
|
||||
function B5500SPOUnit(mnemonic, index, designate, statusChange, signal) {
|
||||
/* Constructor for the SPOUnit object */
|
||||
var that = this;
|
||||
|
||||
this.maxScrollLines = 500; // Maximum amount of printer scrollback
|
||||
this.charPeriod = 100; // Printer speed, milliseconds per character
|
||||
|
||||
this.mnemonic = mnemonic; // Unit mnemonic
|
||||
this.index = index; // Ready-mask bit number
|
||||
this.designate = designate; // IOD unit designate number
|
||||
this.statusChange = statusChange; // external function to call for ready-status change
|
||||
this.signal = signal; // external function to call for special signals (e.g,. SPO input request)
|
||||
this.finish = null; // external function to call for I/O completion
|
||||
|
||||
this.clear();
|
||||
|
||||
this.backspaceChar.that = this; // Store object context these functions
|
||||
this.printChar.that = this;
|
||||
this.writeChar.that = this;
|
||||
|
||||
this.window = window.open("B5500SPOUnit.html", "SPOWin", "scrollbars,resizable,width=600,height=500");
|
||||
|
||||
this.window.onload = function() {
|
||||
/* Initializes the SPO window and user interface */
|
||||
var x;
|
||||
|
||||
that.doc = that.window.document;
|
||||
that.paper = that.$$("SPOUT").contentDocument.body;
|
||||
that.$$("SPOUT").contentDocument.head.innerHTML += "<style>" +
|
||||
"BODY {background-color: #FFE} " +
|
||||
"PRE {margin: 0; font-size: 10pt; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace}" +
|
||||
"</style>";
|
||||
|
||||
window.resizeBy(that.$$("SPODiv").scrollWidth-document.body.scrollWidth,
|
||||
that.$$("SPODiv").scrollHeight-document.body.scrollHeight);
|
||||
window.moveTo((screen.availWidth-window.outerWidth)/2, (screen.availHeight-window.outerHeight)/2);
|
||||
|
||||
that.$$("SPORemoteBtn").onclick = function() {
|
||||
that.setRemote();
|
||||
};
|
||||
|
||||
that.$$("SPOPowerBtn").onclick = function() {
|
||||
that.window.alert("Don't DO that");
|
||||
};
|
||||
|
||||
that.$$("SPOLocalBtn").onclick = function() {
|
||||
that.setLocal();
|
||||
};
|
||||
|
||||
that.$$("SPOInputRequestBtn").onclick = function() {
|
||||
if (that.spoState == that.spoRemote) {
|
||||
that.signal();
|
||||
} else if (that.spoState == that.spoOutput) {
|
||||
that.signal();
|
||||
}
|
||||
};
|
||||
|
||||
that.$$("SPOErrorBtn").onclick = function() {
|
||||
that.cancelInput();
|
||||
};
|
||||
|
||||
that.$$("SPOEndOfMessageBtn").onclick = function() {
|
||||
that.terminateInput();
|
||||
};
|
||||
|
||||
window.onkeypress = function(ev) {
|
||||
that.keyPress(ev);
|
||||
};
|
||||
|
||||
window.onkeydown = function(ev) {
|
||||
that.keyDown(ev);
|
||||
};
|
||||
|
||||
for (x=0; x<32; x++) {
|
||||
that.appendEmptyLine();
|
||||
}
|
||||
that.setReady();
|
||||
that.printText("retro-B5500 Emulator Version 0.01", function() {
|
||||
that.setRemote();
|
||||
that.appendEmptyLine();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// this.spoState enumerations
|
||||
B5500SPOUnit.prototype.spoNotReady = 0;
|
||||
B5500SPOUnit.prototype.spoLocal = 1;
|
||||
B5500SPOUnit.prototype.spoRemote = 2;
|
||||
B5500SPOUnit.prototype.spoInput = 3;
|
||||
B5500SPOUnit.prototype.spoOutput = 4;
|
||||
|
||||
B5500SPOUnit.prototype.keyFilter = [ // Filter keyCode values to valid B5500 ones
|
||||
0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, // 00-0F
|
||||
0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, // 10-1F
|
||||
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x3F,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, // 20-2F
|
||||
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, // 30-3F
|
||||
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, // 40-4F
|
||||
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x3F,0x5D,0x3F,0x3F, // 50-5F
|
||||
0x3F,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, // 60-6F
|
||||
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x3F]; // 70-7F
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.$$ = function(e) {
|
||||
return this.doc.getElementById(e)};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.clear = function() {
|
||||
/* Initializes (and if necessary, creates) the SPO unit state */
|
||||
|
||||
this.ready = false; // ready status
|
||||
this.busy = false; // busy status
|
||||
this.activeIOUnit = 0; // I/O unit currently using this device
|
||||
this.spoState = this.spoNotReady; // Current state of SPO interface
|
||||
this.spoLocalRequested = false; // LOCAL button pressed while active
|
||||
this.errorMask = 0; // error mask for finish()
|
||||
|
||||
this.buffer = null;
|
||||
this.bufLength = 0;
|
||||
this.bufIndex = 0;
|
||||
this.printCol = 0;
|
||||
this.nextCharTime = 0;
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.hasClass = function(e, name) {
|
||||
/* returns true if element "e" has class "name" in its class list */
|
||||
var classes = e.className;
|
||||
|
||||
if (!e) {
|
||||
return false;
|
||||
} else if (classes == name) {
|
||||
return true;
|
||||
} else {
|
||||
return (classes.search("\\b" + name + "\\b") >= 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.addClass = function(e, name) {
|
||||
/* Adds a class "name" to the element "e"s class list */
|
||||
|
||||
if (!this.hasClass(e, name)) {
|
||||
e.className += (" " + name);
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.removeClass = function(e, name) {
|
||||
/* Removes the class "name" from the element "e"s class list */
|
||||
|
||||
e.className = e.className.replace(new RegExp("\\b" + name + "\\b\\s*", "g"), "");
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.setNotReady = function() {
|
||||
/* Sets the status of the SPO to Not Ready */
|
||||
var readyBtn = this.$$("SPOReadyBtn");
|
||||
|
||||
if (this.spoState == this.spoLocal) {
|
||||
this.spoState = this.spoNotReady;
|
||||
this.removeClass(readyBtn, "yellowLit");
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.setReady = function() {
|
||||
/* Sets the status of the SPO to Ready */
|
||||
var readyBtn = this.$$("SPOReadyBtn");
|
||||
|
||||
if (this.spoState == this.spoNotReady) {
|
||||
this.addClass(readyBtn, "yellowLit");
|
||||
this.spoState = this.spoLocal;
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.setLocal = function() {
|
||||
/* Sets the status of the SPO to Local */
|
||||
var localBtn = this.$$("SPOLocalBtn");
|
||||
var remoteBtn = this.$$("SPORemoteBtn");
|
||||
|
||||
if (this.spoState == this.spoRemote) {
|
||||
this.spoState = this.spoLocal;
|
||||
this.addClass(localBtn, "yellowLit");
|
||||
this.removeClass(remoteBtn, "yellowLit");
|
||||
|
||||
// Set up to echo characters from the keyboard
|
||||
this.buffer = null;
|
||||
this.bufLength = 0;
|
||||
this.bufIndex = 0;
|
||||
this.printCol = 0;
|
||||
this.nextCharTime = new Date().getTime();
|
||||
this.finish = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.setRemote = function() {
|
||||
/* Sets the status of the SPO to Remote */
|
||||
var localBtn = this.$$("SPOLocalBtn");
|
||||
var remoteBtn = this.$$("SPORemoteBtn");
|
||||
|
||||
if (this.spoState == this.spoLocal) {
|
||||
this.spoState = this.spoRemote;
|
||||
this.addClass(remoteBtn, "yellowLit");
|
||||
this.removeClass(localBtn, "yellowLit");
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.appendEmptyLine = function() {
|
||||
/* Removes excess lines already printed, then appends a new <pre> element
|
||||
to the <iframe>, creating an empty text node inside the new element */
|
||||
var count = this.paper.childNodes.length;
|
||||
var line = document.createElement("pre");
|
||||
|
||||
while (count-- > this.maxScrollLines) {
|
||||
this.paper.removeChild(this.paper.firstChild);
|
||||
}
|
||||
line.appendChild(document.createTextNode(""));
|
||||
this.paper.appendChild(line);
|
||||
line.scrollIntoView();
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.backspaceChar = function backspaceChar() {
|
||||
/* Handles backspace for SPO input */
|
||||
var that = backspaceChar.that;
|
||||
var line = this.paper.lastChild.lastChild;
|
||||
|
||||
if (this.bufLength > 0) {
|
||||
this.bufIndex--;
|
||||
}
|
||||
if (this.printCol > 0) {
|
||||
this.printCol--;
|
||||
}
|
||||
if (line.nodeValue.length > 0) {
|
||||
line.nodeValue = line.nodeValue.substring(-1);
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.printChar = function printChar(c) {
|
||||
/* Echoes the character code "c" to the SPO printer */
|
||||
var that = printChar.that;
|
||||
var line = this.paper.lastChild.lastChild;
|
||||
|
||||
if (line.nodeValue.length < 72) {
|
||||
line.nodeValue += String.fromCharCode(c);
|
||||
} else {
|
||||
line.nodeValue = line.nodeValue.substring(0, 71) + String.fromCharCode(c);
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.writeChar = function() {
|
||||
/* Outputs one character from the buffer to the SPO. If more characters remain
|
||||
to be printed, schedules itself 100 ms later to print the next one, otherwise
|
||||
calls finished(). If the column counter exceeds 72, a CR/LF pair is output.
|
||||
A CR/LF pair is also output at the end of the message. Note the use of the local
|
||||
function property "that" (initialized in the constructor), which supplies the
|
||||
necessary SPOUnit object context across setTimeout() calls */
|
||||
var that = writeChar.that; // retrieve our object context
|
||||
var nextTime = that.nextCharTime + that.charPeriod;
|
||||
var delay = nextTime - new Date().getTime();
|
||||
|
||||
that.nextCharTime = nextTime;
|
||||
if (that.printCol < 72) { // print the character
|
||||
if (that.bufIndex < that.bufLength) {
|
||||
that.printChar(that.buffer[that.bufIndex]);
|
||||
that.bufIndex++;
|
||||
that.printCol++;
|
||||
setTimeout(that.writeChar, delay);
|
||||
} else { // set up for the final CR/LF
|
||||
that.printCol = 72;
|
||||
setTimeout(that.writeChar, delay);
|
||||
}
|
||||
} else if (that.printCol == 72) { // delay to fake the output of a new-line
|
||||
that.printCol++;
|
||||
setTimeout(that.writeChar, delay);
|
||||
} else { // actually output the CR/LF
|
||||
that.appendEmptyLine();
|
||||
if (that.bufIndex < that.bufLength) {
|
||||
that.printCol = 0; // more characters to print after the CR/LF
|
||||
setTimeout(that.writeChar, delay);
|
||||
} else { // message text is exhausted
|
||||
if (that.spoLocalRequested) {
|
||||
that.setLocal();
|
||||
} else {
|
||||
that.spoState = that.spoRemote;
|
||||
}
|
||||
that.finish(that.errorMask, that.bufLength); // report finish with any errors
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.terminateInput = function() {
|
||||
/* Handles the End of Message event. Turns off then Input Request lamp, then
|
||||
calls writeChar(), which will find bufIndex==bufLength, output a new-line,
|
||||
set the state to Remote, and call finish() for us. Slick, eh? */
|
||||
|
||||
if (this.spoState == this.spoInput) {
|
||||
this.removeClass(this.$$("SPOInputRequestBtn"), "yellowLit");
|
||||
this.bufLength = this.bufIndex;
|
||||
this.nextCharTime = new Date().getTime();
|
||||
this.writeChar();
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.cancelInput = function() {
|
||||
/* Handles the Error message event. This is identical to terminateInput(),
|
||||
but it also sets a parity error so the input message will be rejected */
|
||||
|
||||
if (this.spoState = this.spoInput) {
|
||||
this.removeClass(this.$$("SPOInputRequestBtn"), "yellowLit");
|
||||
this.errorMask |= 0x10; // set parity/error-button bit
|
||||
this.bufLength = this.bufIndex;
|
||||
this.nextCharTime = new Date().getTime();
|
||||
this.writeChar();
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.keyPress = function(ev) {
|
||||
/* Handles keyboard character events. Depending on the state of the unit,
|
||||
either buffers the character for transmission to the I/O Unit, simply echos
|
||||
it to the printer, or ignores it altogether */
|
||||
var c = ev.charCode;
|
||||
var index = this.bufLength;
|
||||
var nextTime;
|
||||
var result = true;
|
||||
var stamp = new Date().getTime();
|
||||
|
||||
if (this.nextCharTime > stamp) {
|
||||
nextTime = this.nextCharTime + this.charPeriod;
|
||||
} else {
|
||||
nextTime = stamp + this.charPeriod;
|
||||
}
|
||||
this.nextCharTime = nextTime;
|
||||
if (this.spoState == this.spoInput) {
|
||||
if (c >= 32 && c <= 126) {
|
||||
this.buffer[this.bufIndex++] = c = keyFilter[c];
|
||||
if (this.printCol < 72) {
|
||||
this.printCol++;
|
||||
}
|
||||
setTimeout(function() {this.printChar(c)}, nextTime-stamp);
|
||||
}
|
||||
} else if (this.spoState == this.spoLocal) {
|
||||
if (c >= 32 && c <= 126) {
|
||||
c = keyFilter[c];
|
||||
setTimeout(function() {this.printChar(c)}, nextTime-stamp);
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.keyDown = function(ev) {
|
||||
/* Handles key-down events to capture ESC, BS, and Enter keystrokes */
|
||||
var c = ev.keyCode;
|
||||
var result = true;
|
||||
|
||||
if (this.spoState == this.spoRemote) {
|
||||
if (c == 27) {
|
||||
if (that.spoState == that.spoRemote) {
|
||||
that.signal();
|
||||
} else if (that.spoState == that.spoOutput) {
|
||||
that.signal();
|
||||
}
|
||||
result = false;
|
||||
}
|
||||
} else if (this.spoState == this.spoInput) {
|
||||
switch (c) {
|
||||
case 27: // ESC
|
||||
this.cancelInput();
|
||||
result = false;
|
||||
break;
|
||||
case 8: // Backspace
|
||||
this.backspaceChar();
|
||||
result = false;
|
||||
break;
|
||||
case 13: // Enter
|
||||
case 126: // "~" (B5500 left arrow/group mark)
|
||||
this.terminateInput();
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
} else if (this.spoState == this.spoLocal) {
|
||||
switch (c) {
|
||||
case 8: // Backspace
|
||||
case 13: // Enter
|
||||
this.backspaceChar();
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.printText = function(msg, finish) {
|
||||
/* Utility function to convert a string to a Typed Array buffer and queue
|
||||
it for printing. This is intended only for printing an initialization message
|
||||
in Local state */
|
||||
var buf = new Uint8Array(msg.length);
|
||||
var length = msg.length;
|
||||
var x;
|
||||
|
||||
for (x=0; x<length; x++) {
|
||||
buf[x] = msg.charCodeAt(x);
|
||||
}
|
||||
this.buffer = buf;
|
||||
this.bufLength = length;
|
||||
this.bufIndex = 0;
|
||||
this.printCol = 0;
|
||||
this.nextCharTime = new Date().getTime();
|
||||
this.finish = finish;
|
||||
this.writeChar(); // start the printing process
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.read = function(finish, buffer, length, mode, control) {
|
||||
/* Initiates a read operation on the unit */
|
||||
|
||||
this.errorMask = 0;
|
||||
switch (this.spoState) {
|
||||
case this.spoRemote:
|
||||
this.spoState = this.spoInput;
|
||||
this.addClass(inputBtn, "yellowLit");
|
||||
this.buffer = buffer;
|
||||
this.bufLength = length;
|
||||
this.bufIndex = 0;
|
||||
this.printCol = 0;
|
||||
this.nextCharTime = new Date().getTime();
|
||||
this.finish = finish;
|
||||
break;
|
||||
case this.spoOutput:
|
||||
case this.spoInput:
|
||||
finish(0x01, 0); // report unit busy (should never happen)
|
||||
break;
|
||||
default:
|
||||
finish(0x04, 0); // report unit not ready
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.space = function(finish, length, control) {
|
||||
/* Initiates a space operation on the unit */
|
||||
|
||||
finish(0x04, 0); // report unit not ready
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.write = function(finish, buffer, length, mode, control) {
|
||||
/* Initiates a write operation on the unit */
|
||||
|
||||
this.errorMask = 0;
|
||||
switch (this.spoState) {
|
||||
case this.spoRemote:
|
||||
this.spoState = this.spoOutput;
|
||||
this.buffer = buffer;
|
||||
this.bufLength = length;
|
||||
this.bufIndex = 0;
|
||||
this.printCol = 0;
|
||||
this.nextCharTime = new Date().getTime();
|
||||
this.finish = finish;
|
||||
this.writeChar(); // start the printing process
|
||||
break;
|
||||
case this.spoOutput:
|
||||
case this.spoInput:
|
||||
finish(0x01, 0); // report unit busy (should never happen)
|
||||
break;
|
||||
default:
|
||||
finish(0x04, 0); // report unit not ready
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.erase = function(finish, length) {
|
||||
/* Initiates an erase operation on the unit */
|
||||
|
||||
finish(0x04, 0); // report unit not ready
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.rewind = function(finish) {
|
||||
/* Initiates a rewind operation on the unit */
|
||||
|
||||
finish(0x04, 0); // report unit not ready
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.readCheck = function(finish, length) {
|
||||
/* Initiates a read check operation on the unit */
|
||||
|
||||
finish(0x04, 0); // report unit not ready
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.readInterrogate = function(finish) {
|
||||
/* Initiates a read interrogate operation on the unit */
|
||||
|
||||
finish(0x04, 0); // report unit not ready
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.writeInterrogate = function (finish) {
|
||||
/* Initiates a write interrogate operation on the unit */
|
||||
|
||||
finish(0x04, 0); // report unit not ready
|
||||
};
|
||||
145
webUI/prototypes/B5500SPOPrototype.css
Normal file
145
webUI/prototypes/B5500SPOPrototype.css
Normal file
@@ -0,0 +1,145 @@
|
||||
/***********************************************************************
|
||||
* retro-b5500/emulator B5500SPOPrototype.css
|
||||
************************************************************************
|
||||
* Copyright (c) 2012, Nigel Williams and Paul Kimpel.
|
||||
* Licensed under the MIT License, see http://www.opensource.org/licenses/mit-license.php
|
||||
************************************************************************
|
||||
* B5500 emulator SPO web interface style sheet.
|
||||
************************************************************************
|
||||
* 2012-12-16 P.Kimpel
|
||||
* Original version, from B5500ConsoleUnit.css.
|
||||
***********************************************************************/
|
||||
|
||||
BODY {
|
||||
position: relative;
|
||||
background-color: black;
|
||||
margin: 4px}
|
||||
|
||||
PRE {
|
||||
font-family: Lucida Sans Typewriter, Courier New, Courier, monospace;
|
||||
font-size: 10pt;
|
||||
margin: 0}
|
||||
|
||||
|
||||
DIV#SPODiv {
|
||||
position: relative;
|
||||
width: 800px;
|
||||
background-color: #FDA;
|
||||
border-radius: 32px;
|
||||
padding: 2em;
|
||||
text-align: left}
|
||||
|
||||
IFRAME#SPOUT {
|
||||
width: 620px;
|
||||
height: 475px;
|
||||
border: 2px solid black;
|
||||
background-color: #FFE;
|
||||
font-family: Lucida Sans Typewriter, Courier New, Courier, monospace;
|
||||
font-size: 10pt}
|
||||
|
||||
DIV#SPOControlsDiv {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: 136px;
|
||||
left: 690px;
|
||||
top: 32px}
|
||||
|
||||
IMG#TeletypeLogo {
|
||||
left: auto;
|
||||
right: auto}
|
||||
|
||||
BUTTON.whiteButton {
|
||||
position: absolute;
|
||||
background-color: #CCC;
|
||||
color: black;
|
||||
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
width: 60px;
|
||||
height: 40px;
|
||||
border: 1px solid #DDD;
|
||||
border-radius: 4px}
|
||||
|
||||
BUTTON.blackButton {
|
||||
position: absolute;
|
||||
background-color: black;
|
||||
color: white;
|
||||
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
width: 60px;
|
||||
height: 40px;
|
||||
border: 1px solid #DDD;
|
||||
border-radius: 4px}
|
||||
|
||||
BUTTON.yellowButton {
|
||||
position: absolute;
|
||||
background-color: #990;
|
||||
color: black;
|
||||
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
width: 60px;
|
||||
height: 40px;
|
||||
border: 1px solid #DDD;
|
||||
border-radius: 4px}
|
||||
|
||||
BUTTON.whiteLit {
|
||||
background-color: white}
|
||||
|
||||
BUTTON.yellowLit {
|
||||
background-color: #FF0}
|
||||
|
||||
BUTTON.blackBorder {
|
||||
border: 1px solid black}
|
||||
|
||||
BUTTON#SPOReadyBtn {
|
||||
top: 187px;
|
||||
left: 0px}
|
||||
|
||||
BUTTON#SPOPowerBtn {
|
||||
top: 187px;
|
||||
right: 0px}
|
||||
|
||||
BUTTON#SPORemoteBtn {
|
||||
top: 243px;
|
||||
left: 0px}
|
||||
|
||||
BUTTON#SPOLocalBtn {
|
||||
top: 243px;
|
||||
right: 0px}
|
||||
|
||||
BUTTON#SPOInputRequestBtn {
|
||||
top: 299px;
|
||||
left: 0px}
|
||||
|
||||
BUTTON#SPOEndOfMessageBtn {
|
||||
top: 299px;
|
||||
right: 0px}
|
||||
|
||||
BUTTON#SPOBlank1Btn {
|
||||
top: 355px;
|
||||
left: 0px}
|
||||
|
||||
BUTTON#SPOErrorBtn {
|
||||
top: 355px;
|
||||
right: 0px}
|
||||
|
||||
BUTTON#SPOBlank2Btn {
|
||||
top: 411px;
|
||||
left: 0px}
|
||||
|
||||
BUTTON#SPOBlank3Btn {
|
||||
top: 411px;
|
||||
right: 0px}
|
||||
|
||||
.center {
|
||||
text-align: center}
|
||||
|
||||
.data {
|
||||
font-family: Courier New, Courier, monospace;
|
||||
text-align: left}
|
||||
|
||||
.number {
|
||||
font-family: Courier New, Courier, monospace;
|
||||
text-align: right}
|
||||
454
webUI/prototypes/B5500SPOPrototype.html
Normal file
454
webUI/prototypes/B5500SPOPrototype.html
Normal file
@@ -0,0 +1,454 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>B5500 Emulator SPO Unit Prototype</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
<meta name="Author" content="Nigel Williams & Paul Kimpel">
|
||||
<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="B5500SPOPrototype.css">
|
||||
|
||||
<script>
|
||||
|
||||
window.onload = function() {
|
||||
const spoNotReady = 0;
|
||||
const spoLocal = 1;
|
||||
const spoRemote = 2;
|
||||
const spoInput = 3;
|
||||
const spoOutput = 4;
|
||||
|
||||
var $$ = function(e) {return document.getElementById(e)};
|
||||
var msgTank = [];
|
||||
var spoState = spoNotReady;
|
||||
var spoLocalRequested = false;
|
||||
var spoInputActive = false;
|
||||
var spoInputRequested = false;
|
||||
|
||||
var msgCtl = {
|
||||
buffer: null,
|
||||
length: 0,
|
||||
index: 0,
|
||||
col: 0,
|
||||
nextCharTime: 0,
|
||||
finished: null};
|
||||
|
||||
var keyFilter = [ // Filter keyCode values to valid B5500 ones
|
||||
0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, // 00-0F
|
||||
0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, // 10-1F
|
||||
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x3F,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, // 20-2F
|
||||
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, // 30-3F
|
||||
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, // 40-4F
|
||||
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x3F,0x5D,0x3F,0x3F, // 50-5F
|
||||
0x3F,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, // 60-6F
|
||||
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x3F]; // 70-7F
|
||||
|
||||
var hasClass = function(e, name) {
|
||||
/* returns true if element "e" has class "name" in its class list */
|
||||
var classes = e.className;
|
||||
|
||||
if (!e) {
|
||||
return false;
|
||||
} else if (classes == name) {
|
||||
return true;
|
||||
} else {
|
||||
return (classes.search("\\b" + name + "\\b") >= 0);
|
||||
}
|
||||
};
|
||||
|
||||
var addClass = function(e, name) {
|
||||
/* Adds a class "name" to the element "e"s class list */
|
||||
|
||||
if (!hasClass(e, name)) {
|
||||
e.className += (" " + name);
|
||||
}
|
||||
};
|
||||
|
||||
var removeClass = function(e, name) {
|
||||
/* Removes the class "name" from the element "e"s class list */
|
||||
|
||||
e.className = e.className.replace(new RegExp("\\b" + name + "\\b\\s*", "g"), "");
|
||||
};
|
||||
|
||||
var addFrameStyles = function(frame) {
|
||||
/* Appends the necessary styles for the <iframe> to its internal stylesheet */
|
||||
|
||||
frame.contentDocument.head.innerHTML += "<style>" +
|
||||
"BODY {background-color: #FFE} " +
|
||||
"PRE {margin: 0; font-size: 10pt; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace}" +
|
||||
"</style>";
|
||||
};
|
||||
|
||||
var appendEmptyLine = function(count) {
|
||||
/* Appends "count" <pre> elements to the <iframe>, creating an empty text node
|
||||
inside each new element */
|
||||
var body = $$("SPOUT").contentDocument.body;
|
||||
var line;
|
||||
var x;
|
||||
|
||||
for (x=1; x<=count; x++) {
|
||||
line = document.createElement("pre");
|
||||
line.appendChild(document.createTextNode(""));
|
||||
body.appendChild(line);
|
||||
line.scrollIntoView();
|
||||
}
|
||||
};
|
||||
|
||||
var accept = function() {
|
||||
var inputBtn = $$("SPOInputRequestBtn");
|
||||
|
||||
spoState = spoInput;
|
||||
addClass(inputBtn, "yellowLit");
|
||||
|
||||
msgCtl.buffer = new Uint8Array(80);
|
||||
msgCtl.length = 0;
|
||||
msgCtl.index = 0;
|
||||
msgCtl.col = 0;
|
||||
msgCtl.nextCharTime = 0;
|
||||
msgCtl.finished = printFinished;
|
||||
msgTank.push(msgCtl.buffer);
|
||||
};
|
||||
|
||||
var backspaceChar = function() {
|
||||
/* Handles backspace for SPO input */
|
||||
var line = $$("SPOUT").contentDocument.body.lastChild.lastChild;
|
||||
|
||||
if (msgCtl.length > 0) {
|
||||
msgCtl.length--;
|
||||
msgCtl.index--;
|
||||
line.nodeValue = line.nodeValue.substring(0, msgCtl.length);
|
||||
if (msgCtl.col > 0) {
|
||||
msgCtl.col--;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var echoChar = function(c) {
|
||||
/* Echoes the character code "c" to the SPO printer. Used by keyboard input */
|
||||
var body = $$("SPOUT").contentDocument.body;
|
||||
var line = body.lastChild.lastChild;
|
||||
|
||||
if (c == 8) {
|
||||
if (line.nodeValue.length > 0) {
|
||||
line.nodeValue = line.nodeValue.substring(-1);
|
||||
}
|
||||
} else if (c == 13) {
|
||||
appendEmptyLine(1);
|
||||
} else if (line.nodeValue.length < 72) {
|
||||
line.nodeValue += String.fromCharCode(c);
|
||||
} else {
|
||||
line.nodeValue = line.nodeValue.substring(0,71) + String.fromCharCode(c);
|
||||
}
|
||||
};
|
||||
|
||||
var printChar = function() {
|
||||
/* Prints one character to the SPO. If more characters remain to be printed,
|
||||
schedules itself 100 ms later to print the next one, otherwise calls finished().
|
||||
If the column counter exceeds 72, a CR/LF are output. A CR/LF are also output
|
||||
at the end of the message */
|
||||
var body = $$("SPOUT").contentDocument.body;
|
||||
var c;
|
||||
var nextTime = msgCtl.nextCharTime + 100;
|
||||
var delay = nextTime - new Date().getTime();
|
||||
var line = body.lastChild.lastChild;
|
||||
|
||||
msgCtl.nextCharTime = nextTime;
|
||||
if (msgCtl.col < 72) { // print the character
|
||||
if (msgCtl.index < msgCtl.length) {
|
||||
c = String.fromCharCode(msgCtl.buffer[msgCtl.index]);
|
||||
line.nodeValue += c;
|
||||
msgCtl.index++;
|
||||
msgCtl.col++;
|
||||
setTimeout(printChar, delay);
|
||||
} else { // set up for the final CR/LF
|
||||
msgCtl.col = 72;
|
||||
setTimeout(printChar, delay);
|
||||
}
|
||||
} else if (msgCtl.col == 72) { // delay to fake the output of a carriage-return
|
||||
msgCtl.col++;
|
||||
setTimeout(printChar, delay);
|
||||
} else { // actually output the CR/LF
|
||||
appendEmptyLine(1);
|
||||
if (msgCtl.index < msgCtl.length) {
|
||||
msgCtl.col = 0; // more characters to print after the CR/LF
|
||||
setTimeout(printChar, delay);
|
||||
} else { // message text is exhausted
|
||||
msgCtl.finished();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var print = function(buffer, length, finished) {
|
||||
/* Prints the contents of the "buffer" for "length" characters */
|
||||
var body = $$("SPOUT").contentDocument.body;
|
||||
var count = body.childNodes.length;
|
||||
|
||||
spoState = spoOutput;
|
||||
while (count-- > 500) {
|
||||
body.removeChild(body.firstChild);
|
||||
}
|
||||
|
||||
msgCtl.buffer = buffer;
|
||||
msgCtl.length = length;
|
||||
msgCtl.index = 0;
|
||||
msgCtl.col = 0;
|
||||
msgCtl.nextCharTime = new Date().getTime();
|
||||
msgCtl.finished = finished;
|
||||
printChar();
|
||||
};
|
||||
|
||||
var printFinished = function() {
|
||||
/* Called to report that all printing to the SPO is complete */
|
||||
|
||||
if (msgTank.length > 1) {
|
||||
msgTank = msgTank.slice(1);
|
||||
print(msgTank[0], msgTank[0].length, printFinished);
|
||||
} else {
|
||||
spoState = spoRemote;
|
||||
msgTank = [];
|
||||
if (spoLocalRequested) {
|
||||
spoLocalRequested = false;
|
||||
setRemote(false);
|
||||
} else if (spoInputRequested) {
|
||||
spoInputRequested = false;
|
||||
accept();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var setReady = function(ready) {
|
||||
/* Sets the ready status of the SPO based on the truth of "ready" */
|
||||
var readyBtn = $$("SPOReadyBtn");
|
||||
|
||||
if (ready && spoState == spoNotReady) {
|
||||
addClass(readyBtn, "yellowLit");
|
||||
spoState = spoLocal;
|
||||
setRemote(true);
|
||||
} else if (!ready && spoState != spoNotReady) {
|
||||
spoState = spoNotReady;
|
||||
removeClass(readyBtn, "yellowLit");
|
||||
}
|
||||
};
|
||||
|
||||
var setRemote = function(remote) {
|
||||
/* Sets the remote status of the SPO based on the truth of "remote" */
|
||||
var localBtn = $$("SPOLocalBtn");
|
||||
var remoteBtn = $$("SPORemoteBtn");
|
||||
|
||||
if (remote && spoState == spoLocal) {
|
||||
spoState = spoRemote;
|
||||
addClass(remoteBtn, "yellowLit");
|
||||
removeClass(localBtn, "yellowLit");
|
||||
} else if (!remote && spoState == spoRemote) {
|
||||
spoState = spoLocal;
|
||||
spoInputRequested = false;
|
||||
addClass(localBtn, "yellowLit");
|
||||
removeClass(remoteBtn, "yellowLit");
|
||||
}
|
||||
};
|
||||
|
||||
var initiateInput = function(ev) {
|
||||
/* Handles a successful Input Request event and enables the keyboard */
|
||||
|
||||
if (spoState == spoRemote) {
|
||||
accept();
|
||||
} else if (spoState == spoOutput) {
|
||||
inputRequested = true;
|
||||
}
|
||||
};
|
||||
|
||||
var terminateInput = function(ev) {
|
||||
/* Handles the End of Message event */
|
||||
var text;
|
||||
|
||||
if (spoState == spoInput) {
|
||||
if (spoLocalRequested) {
|
||||
setRemote(false);
|
||||
} else {
|
||||
spoState = spoRemote;
|
||||
}
|
||||
removeClass($$("SPOInputRequestBtn"), "yellowLit");
|
||||
text = String.fromCharCode.apply(null, msgCtl.buffer.subarray(0, msgCtl.length));
|
||||
printChar();
|
||||
printText("YOU ENTERED: " + text);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
var cancelInput = function(ev) {
|
||||
/* Handles the Error message event */
|
||||
|
||||
if (spoState = spoInput) {
|
||||
if (spoLocalRequested) {
|
||||
setRemote(false);
|
||||
} else {
|
||||
spoState = spoRemote;
|
||||
}
|
||||
removeClass($$("SPOInputRequestBtn"), "yellowLit");
|
||||
printChar();
|
||||
printText("**ERROR");
|
||||
}
|
||||
};
|
||||
|
||||
var printText = function(msg) {
|
||||
/* Utility function to convert a string to a Typed Array buffer and queue
|
||||
it for printing */
|
||||
var buf = new Uint8Array(msg.length);
|
||||
var length = msg.length;
|
||||
var x;
|
||||
|
||||
for (x=0; x<length; x++) {
|
||||
buf[x] = msg.charCodeAt(x);
|
||||
}
|
||||
|
||||
msgTank.push(buf);
|
||||
if (msgTank.length <= 1) {
|
||||
print(buf, length, printFinished);
|
||||
}
|
||||
};
|
||||
|
||||
var doTests = function() {
|
||||
|
||||
printText("*** B5500 SPO TEST ***");
|
||||
printText(" ");
|
||||
printText("WHAT HATH PASADENA WROUGHT?");
|
||||
printText("");
|
||||
/*****
|
||||
printText("123456789.123456789.123456789.123456789.123456789.123456789.123456789.1");
|
||||
printText("123456789.123456789.123456789.123456789.123456789.123456789.123456789.12");
|
||||
printText("123456789.123456789.123456789.123456789.123456789.123456789.123456789.123");
|
||||
printText("");
|
||||
printText(" 10 20 30 40 50 60 70 80 90 100");
|
||||
printText("123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.");
|
||||
printText("~");
|
||||
printText("END");
|
||||
*****/
|
||||
}
|
||||
|
||||
/***** window.onload() outer block *****/
|
||||
|
||||
window.resizeBy($$("SPODiv").scrollWidth-document.body.scrollWidth,
|
||||
$$("SPODiv").scrollHeight-document.body.scrollHeight);
|
||||
window.moveTo((screen.availWidth-window.outerWidth)/2, (screen.availHeight-window.outerHeight)/2);
|
||||
|
||||
$$("SPORemoteBtn").onclick = function() {
|
||||
setRemote(true);
|
||||
};
|
||||
|
||||
$$("SPOPowerBtn").onclick = function() {
|
||||
alert("Don't DO that");
|
||||
};
|
||||
|
||||
$$("SPOLocalBtn").onclick = function() {
|
||||
spoInputRequested = false;
|
||||
if (msgTank.length > 0) {
|
||||
spoLocalRequested = true;
|
||||
} else {
|
||||
setRemote(false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$$("SPOInputRequestBtn").onclick = initiateInput;
|
||||
|
||||
$$("SPOErrorBtn").onclick = cancelInput;
|
||||
|
||||
$$("SPOEndOfMessageBtn").onclick = terminateInput;
|
||||
|
||||
window.onkeypress = function(ev) {
|
||||
var c = ev.charCode;
|
||||
var index = msgCtl.length;
|
||||
var nextTime;
|
||||
var result = false;
|
||||
var stamp = new Date().getTime();
|
||||
|
||||
if (msgCtl.nextCharTime > stamp) {
|
||||
nextTime = msgCtl.nextCharTime + 100;
|
||||
} else {
|
||||
nextTime = stamp + 100;
|
||||
}
|
||||
msgCtl.nextCharTime = nextTime;
|
||||
if (spoState == spoInput) {
|
||||
if (c >= 32 && c <= 126) {
|
||||
msgCtl.buffer[index] = c = keyFilter[c & 0x7F];
|
||||
if (msgCtl.length < 72) {
|
||||
msgCtl.col++;
|
||||
msgCtl.length++;
|
||||
msgCtl.index++;
|
||||
}
|
||||
setTimeout(function() {echoChar(c)}, nextTime-stamp);
|
||||
}
|
||||
} else if (spoState == spoLocal) {
|
||||
if (c >= 32 && c <= 126) {
|
||||
c = keyFilter[c & 0x7F];
|
||||
setTimeout(function() {echoChar(c)}, nextTime-stamp);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
window.onkeydown = function(ev) {
|
||||
var c = ev.keyCode;
|
||||
var result = false;
|
||||
|
||||
if (spoState == spoRemote) {
|
||||
if (c == 27) {
|
||||
initiateInput(ev);
|
||||
}
|
||||
} else if (spoState == spoInput) {
|
||||
switch (c) {
|
||||
case 27: // ESC
|
||||
cancelInput(ev);
|
||||
break;
|
||||
case 8: // Backspace
|
||||
backspaceChar();
|
||||
break;
|
||||
case 13: // Enter
|
||||
case 126: // "~" (B5500 left arrow/group mark)
|
||||
terminateInput(ev);
|
||||
break;
|
||||
default:
|
||||
result = true;
|
||||
}
|
||||
} else if (spoState == spoLocal) {
|
||||
switch (c) {
|
||||
case 8: // Backspace
|
||||
case 13: // Enter
|
||||
echoChar(c);
|
||||
break;
|
||||
default:
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
addFrameStyles($$("SPOUT"));
|
||||
appendEmptyLine(32);
|
||||
setReady(true);
|
||||
setRemote(true);
|
||||
|
||||
doTests();
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id=SPODiv>
|
||||
<iframe id=SPOUT scrolling=auto></iframe>
|
||||
<div id=SPOControlsDiv>
|
||||
<img id=TeletypeLogo src="TeletypeLogo.gif">
|
||||
<button id=SPOReadyBtn class="yellowButton blackBorder">READY</button>
|
||||
<button id=SPOPowerBtn class="blackButton blackBorder">POWER</button>
|
||||
<button id=SPORemoteBtn class="yellowButton blackBorder">REMOTE</button>
|
||||
<button id=SPOLocalBtn class="yellowButton blackBorder">LOCAL</button>
|
||||
<button id=SPOInputRequestBtn class="yellowButton blackBorder">INPUT REQUEST</button>
|
||||
<button id=SPOEndOfMessageBtn class="yellowButton blackBorder">END OF MESSAGE</button>
|
||||
<button id=SPOBlank1Btn class="yellowButton blackBorder"></button>
|
||||
<button id=SPOErrorBtn class="yellowButton blackBorder">ERROR</button>
|
||||
<button id=SPOBlank2Btn class="yellowButton blackBorder"></button>
|
||||
<button id=SPOBlank3Btn class="yellowButton blackBorder"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user