mirror of
https://github.com/pkimpel/retro-b5500.git
synced 2026-02-26 09:03:37 +00:00
1. Release emulator version 0.20.
2. Fully implement Double Precision Add/Subtract (DLA/DLS), Multiply (DLM), and Divide (DLD) syllables. 3. Replace standard setTimeout() by redesigned setCallback() mechanism throughout the emulator for scheduling timing delays and other callbacks on the Javascript thread. Delete obsolete setImmediate() mechanism. 4. Replace "new Date().getTime()" by "performance.now()" calls for greater timer precision. 5. Minor tweaks to Single Precision arithmetic operators. 6. Replace Javascript postfix operators by prefix operators wherever feasible (e.g., x++ becomes ++x). 8. Attempt to correct character translation and keyboard filtering in DatacomUnit for CANDE. 9. Minor changes to button colors and illumination behavior for I/O devices and Console. 10. Suppress I/O device classes in B5500SyllableDebugger by default (uncomment in source to enable). . Drop support for webkitIndexedDB and mozIndexedDB (for now). . Configure four tape drives (MTA-MTD) by default.
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
* Licensed under the MIT License,
|
||||
* see http://www.opensource.org/licenses/mit-license.php
|
||||
************************************************************************
|
||||
* B5500 Central Control module.
|
||||
* B5500 Emulator Central Control module.
|
||||
************************************************************************
|
||||
* 2012-06-03 P.Kimpel
|
||||
* Original version, from thin air.
|
||||
@@ -53,15 +53,15 @@ function B5500CentralControl(global) {
|
||||
this.cardLoadSelect = 0; // 0=> load from disk/drum; 1=> load from cards
|
||||
|
||||
this.nextTimeStamp = 0; // Next actual Date.getTime() for timer tick
|
||||
this.timer = null; // Reference to the RTC setCallback id.
|
||||
this.timer = 0; // RTC setCallback id.
|
||||
|
||||
this.clear(); // Create and initialize the Central Control state
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
/* Global constants */
|
||||
|
||||
B5500CentralControl.version = "0.19";
|
||||
/* Global constants */
|
||||
B5500CentralControl.version = "0.20";
|
||||
|
||||
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)
|
||||
@@ -153,8 +153,8 @@ B5500CentralControl.unitSpecs = {
|
||||
/**************************************/
|
||||
B5500CentralControl.bindMethod = function bindMethod(f, context) {
|
||||
/* Returns a new function that binds the function "f" to the object "context".
|
||||
Note that this is a constructor property function, NOT an instance method of
|
||||
the CC object */
|
||||
Note that this is a static constructor property function, NOT an instance
|
||||
method of the CC object */
|
||||
|
||||
return function bindMethodAnon() {f.apply(context, arguments)};
|
||||
};
|
||||
@@ -166,7 +166,7 @@ B5500CentralControl.prototype.clear = function clear() {
|
||||
|
||||
if (this.timer) {
|
||||
clearCallback(this.timer);
|
||||
this.timer = null;
|
||||
this.timer = 0;
|
||||
}
|
||||
|
||||
this.IAR = 0; // Interrupt address register
|
||||
@@ -212,18 +212,18 @@ B5500CentralControl.prototype.clear = function clear() {
|
||||
this.unitBusyLatch = 0; // Peripheral unit latched status (reset by console UI)
|
||||
this.unitBusyMask = 0; // Peripheral unit busy-status bitmask
|
||||
|
||||
if (this.PA) {
|
||||
this.PA.clear();
|
||||
}
|
||||
if (this.PB) {
|
||||
this.PB.clear();
|
||||
}
|
||||
this.P1 = (this.PB1L ? this.PB : this.PA);
|
||||
this.P2 = (this.PB1L ? this.PA : this.PB);
|
||||
if (!this.P2) {
|
||||
this.P2BF = 1; // mark non-existent P2 as busy
|
||||
this.ccLatch |= 0x10;
|
||||
}
|
||||
if (this.PA) {
|
||||
this.PA.clear();
|
||||
}
|
||||
if (this.PB) {
|
||||
this.PB.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
@@ -562,11 +562,9 @@ B5500CentralControl.prototype.clearInterrupt = function clearInterrupt() {
|
||||
/**************************************/
|
||||
B5500CentralControl.prototype.tock = function tock() {
|
||||
/* Handles the 1/60th second real-time clock tick */
|
||||
var interval; // milliseconds to next tick
|
||||
var thisTime = new Date().getTime();
|
||||
|
||||
if (this.TM < 63) {
|
||||
this.TM++;
|
||||
++this.TM;
|
||||
} else {
|
||||
this.TM = 0;
|
||||
if (!this.inhCCI03F) {
|
||||
@@ -574,14 +572,13 @@ B5500CentralControl.prototype.tock = function tock() {
|
||||
this.signalInterrupt();
|
||||
}
|
||||
}
|
||||
interval = (this.nextTimeStamp += B5500CentralControl.rtcTick) - thisTime;
|
||||
this.timer = setCallback(tock, this, interval);
|
||||
this.nextTimeStamp += B5500CentralControl.rtcTick;
|
||||
this.timer = setCallback(this.mnemonic, this, this.nextTimeStamp - performance.now(), this.tock);
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500CentralControl.prototype.readTimer = function readTimer() {
|
||||
/* Returns the value of the 1/60th second timer */
|
||||
var thisTime = new Date().getTime();
|
||||
|
||||
return this.CCI03F*64 + this.TM;
|
||||
};
|
||||
@@ -599,7 +596,7 @@ B5500CentralControl.prototype.haltP2 = function haltP2() {
|
||||
if (this.P2.scheduler) {
|
||||
clearCallback(this.P2.scheduler);
|
||||
}
|
||||
this.P2.scheduler = setCallback(this.P2.schedule, this.P2, 0);
|
||||
this.P2.scheduler = setCallback(this.P2.mnemonic, this.P2, 0, this.P2.schedule);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -726,7 +723,7 @@ B5500CentralControl.prototype.halt = function halt() {
|
||||
|
||||
if (this.timer) {
|
||||
clearCallback(this.timer);
|
||||
this.timer = null;
|
||||
this.timer = 0;
|
||||
}
|
||||
|
||||
if (this.PA && this.PA.busy) {
|
||||
@@ -793,9 +790,9 @@ B5500CentralControl.prototype.load = function load(dontStart) {
|
||||
} else if (this.testUnitBusy(22)) { // SPO is busy
|
||||
result = 3;
|
||||
} else { // ready to rock 'n roll
|
||||
this.nextTimeStamp = new Date().getTime();
|
||||
this.nextTimeStamp = performance.now();
|
||||
this.tock();
|
||||
this.LOFF = 1;
|
||||
this.LOFF = 1; // set the Load FF
|
||||
if (this.IO1 && this.IO1.REMF && !this.AD1F) {
|
||||
this.AD1F = 1;
|
||||
this.iouMask |= 0x1;
|
||||
@@ -870,8 +867,8 @@ B5500CentralControl.prototype.loadTest = function loadTest(buf, loadAddr) {
|
||||
// Store any partial word that may be left
|
||||
while (bytes > 0) {
|
||||
word += data.getUint8(x, false)*power;
|
||||
x++;
|
||||
bytes--;
|
||||
++x;
|
||||
--bytes;
|
||||
power /= 0x100;
|
||||
}
|
||||
store.call(this, addr, word);
|
||||
@@ -932,11 +929,11 @@ B5500CentralControl.prototype.dumpSystemState = function dumpSystemState(caption
|
||||
" ", "/", "S", "T", "U", "V", "W", "X",
|
||||
"Y", "Z", ",", "%", "!", "=", "]", "\""];
|
||||
|
||||
function padLeft(text, minLength, char) {
|
||||
/* Pads "text" on the left to a total length of "minLength" with "char" */
|
||||
function padLeft(text, minLength, c) {
|
||||
/* Pads "text" on the left to a total length of "minLength" with "c" */
|
||||
var s = text.toString();
|
||||
var len = s.length;
|
||||
var pad = char || " ";
|
||||
var pad = c || " ";
|
||||
|
||||
while (len++ < minLength) {
|
||||
s = pad + s;
|
||||
@@ -963,7 +960,7 @@ B5500CentralControl.prototype.dumpSystemState = function dumpSystemState(caption
|
||||
var w = value; // working word value
|
||||
var x; // character counter
|
||||
|
||||
for (x=0; x<8; x++) {
|
||||
for (x=0; x<8; ++x) {
|
||||
c = w % 64;
|
||||
w = (w-c)/64;
|
||||
s = BICtoANSI[c] + s;
|
||||
@@ -1004,7 +1001,7 @@ B5500CentralControl.prototype.dumpSystemState = function dumpSystemState(caption
|
||||
lineAddr = mod+addr;
|
||||
line = " ";
|
||||
bic = " ";
|
||||
for (x=0; x<4; x++) {
|
||||
for (x=0; x<4; ++x) {
|
||||
accessor.addr = lineAddr+x;
|
||||
this.fetch(accessor);
|
||||
if (accessor.MPED) {
|
||||
@@ -1020,7 +1017,7 @@ B5500CentralControl.prototype.dumpSystemState = function dumpSystemState(caption
|
||||
} // for x
|
||||
|
||||
if (line == lastLine && lineAddr < 0x7FFC) {
|
||||
dupCount++;
|
||||
++dupCount;
|
||||
} else {
|
||||
if (dupCount > 0) {
|
||||
writer(32, "..... ................ for " + dupCount*4 + " words");
|
||||
@@ -1116,7 +1113,7 @@ B5500CentralControl.prototype.configureSystem = function configureSystem() {
|
||||
if (cfg.IO4) {this.IO4 = new B5500IOUnit("4", this)}
|
||||
|
||||
// Configure memory
|
||||
for (x=0; x<8; x++) {
|
||||
for (x=0; x<8; ++x) {
|
||||
if (cfg.memMod[x]) {
|
||||
this.addressSpace[x] = new ArrayBuffer(32768); // 4K B5500 words @ 8 bytes each
|
||||
this.memMod[x] = new Float64Array(this.addressSpace[x]);
|
||||
@@ -1162,11 +1159,11 @@ B5500CentralControl.prototype.powerOff = function powerOff() {
|
||||
|
||||
if (this.timer) {
|
||||
clearCallback(this.timer);
|
||||
this.timer = null;
|
||||
this.timer = 0;
|
||||
}
|
||||
|
||||
// Shut down the peripheral devices
|
||||
for (x=0; x<this.unit.length; x++) {
|
||||
for (x=0; x<this.unit.length; ++x) {
|
||||
if (this.unit[x]) {
|
||||
this.unit[x].shutDown();
|
||||
}
|
||||
@@ -1180,7 +1177,7 @@ B5500CentralControl.prototype.powerOff = function powerOff() {
|
||||
this.IO2 = null;
|
||||
this.IO3 = null;
|
||||
this.IO4 = null;
|
||||
for (x=0; x<8; x++) {
|
||||
for (x=0; x<8; ++x) {
|
||||
this.memMod[x] = null;
|
||||
this.addressSpace[x] = null;
|
||||
}
|
||||
@@ -1191,6 +1188,6 @@ B5500CentralControl.prototype.powerOff = function powerOff() {
|
||||
if (this.poweredUp) {
|
||||
this.halt();
|
||||
// Wait a little while for I/Os, etc., to finish
|
||||
setCallback(shutDown, this, 500);
|
||||
setCallback(this.mnemonic, this, 500, shutDown);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* Licensed under the MIT License, see
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
************************************************************************
|
||||
* B5500 Input/Output Unit module.
|
||||
* B5500 Emulator Input/Output Unit module.
|
||||
*
|
||||
* Instance variables in all caps generally refer to register or flip-flop (FF)
|
||||
* entities in the processor hardware. See the Burroughs B5500 Reference Manual
|
||||
@@ -46,7 +46,7 @@ function B5500IOUnit(ioUnitID, cc) {
|
||||
this.mnemonic = "IO" + ioUnitID; // Unit mnemonic
|
||||
this.cc = cc; // Reference back to Central Control module
|
||||
|
||||
this.forkHandle = null; // Reference to current setCallback id
|
||||
this.forkHandle = 0; // Current setCallback token
|
||||
this.accessor = { // Memory access control block
|
||||
requestorID: ioUnitID, // Memory requestor ID
|
||||
addr: 0, // Memory address
|
||||
@@ -62,7 +62,6 @@ function B5500IOUnit(ioUnitID, cc) {
|
||||
this.buffer = new Uint8Array(this.bufferArea);
|
||||
|
||||
// Establish contexts for asynchronously-called methods
|
||||
this.boundForkIO = B5500CentralControl.bindMethod(this.forkIO, this);
|
||||
this.boundFinishBusy = this.makeFinish(this.finishBusy);
|
||||
this.boundFinishDatacomRead = this.makeFinish(this.finishDatacomRead);
|
||||
this.boundFinishDatacomWrite = this.makeFinish(this.finishDatacomWrite);
|
||||
@@ -277,21 +276,21 @@ B5500IOUnit.prototype.fetchBuffer = function fetchBuffer(mode, words) {
|
||||
if (words <= 0) {
|
||||
done = true;
|
||||
} else {
|
||||
words--;
|
||||
--words;
|
||||
if (overflow) {
|
||||
this.AOFF = 1; // for display only
|
||||
this.D26F = 1; // address overflow: set invalid address error
|
||||
done = true;
|
||||
} else if (!this.fetch(addr)) { // fetch the next word from memory
|
||||
w = this.W; // fill the buffer with this word's characters
|
||||
for (s=0; s<8; s++) {
|
||||
for (s=0; s<8; ++s) {
|
||||
c = (w - (w %= 0x40000000000))/0x40000000000;
|
||||
buf[count++] = table[c];
|
||||
w *= 64; // shift word left 6 bits
|
||||
} // for s
|
||||
}
|
||||
if (addr < 0x7FFF) {
|
||||
addr++;
|
||||
++addr;
|
||||
} else {
|
||||
overflow = true;
|
||||
}
|
||||
@@ -328,14 +327,14 @@ B5500IOUnit.prototype.fetchBufferWithGM = function fetchBufferWithGM(mode, words
|
||||
if (words <= 0) {
|
||||
done = true;
|
||||
} else {
|
||||
words--;
|
||||
--words;
|
||||
if (overflow) {
|
||||
this.AOFF = 1; // for display only
|
||||
this.D26F = 1; // address overflow: set invalid address error
|
||||
done = true;
|
||||
} else if (!this.fetch(addr)) { // fetch the next word from memory
|
||||
w = this.W; // fill the buffer with this word's characters
|
||||
for (s=0; s<8; s++) {
|
||||
for (s=0; s<8; ++s) {
|
||||
c = (w - (w %= 0x40000000000))/0x40000000000;
|
||||
if (c == 0x1F) {
|
||||
done = true; // group-mark detected
|
||||
@@ -347,7 +346,7 @@ B5500IOUnit.prototype.fetchBufferWithGM = function fetchBufferWithGM(mode, words
|
||||
} // for s
|
||||
}
|
||||
if (addr < 0x7FFF) {
|
||||
addr++;
|
||||
++addr;
|
||||
} else {
|
||||
overflow = true;
|
||||
}
|
||||
@@ -399,7 +398,7 @@ B5500IOUnit.prototype.storeBuffer = function storeBuffer(chars, offset, mode, wo
|
||||
this.store(addr); // store the word in memory
|
||||
}
|
||||
if (addr < 0x7FFF) {
|
||||
addr++;
|
||||
++addr;
|
||||
} else {
|
||||
overflow = true;
|
||||
}
|
||||
@@ -421,9 +420,9 @@ B5500IOUnit.prototype.storeBuffer = function storeBuffer(chars, offset, mode, wo
|
||||
} else {
|
||||
this.store(addr); // store the word in memory
|
||||
}
|
||||
words--;
|
||||
--words;
|
||||
if (addr < 0x7FFF) {
|
||||
addr++;
|
||||
++addr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,7 +474,7 @@ B5500IOUnit.prototype.storeBufferWithGM = function storeBufferWithGM(chars, offs
|
||||
this.store(addr); // store the word in memory
|
||||
}
|
||||
if (addr < 0x7FFF) {
|
||||
addr++;
|
||||
++addr;
|
||||
} else {
|
||||
overflow = true;
|
||||
}
|
||||
@@ -489,8 +488,8 @@ B5500IOUnit.prototype.storeBufferWithGM = function storeBufferWithGM(chars, offs
|
||||
} // while !done
|
||||
|
||||
w += 0x1F*power; // set group mark in register
|
||||
s++;
|
||||
count++;
|
||||
++s;
|
||||
++count;
|
||||
|
||||
if (s > 0 && words > 0) { // partial word left to be stored
|
||||
this.W = w;
|
||||
@@ -501,9 +500,9 @@ B5500IOUnit.prototype.storeBufferWithGM = function storeBufferWithGM(chars, offs
|
||||
} else {
|
||||
this.store(addr); // store the word in memory
|
||||
}
|
||||
words--;
|
||||
--words;
|
||||
if (addr < 0x7FFF) {
|
||||
addr++;
|
||||
++addr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,7 +555,7 @@ B5500IOUnit.prototype.storeBufferBackward = function storeBufferBackward(chars,
|
||||
this.store(addr); // store the word in memory
|
||||
}
|
||||
if (addr > 0) {
|
||||
addr--;
|
||||
--addr;
|
||||
} else {
|
||||
overflow = true;
|
||||
}
|
||||
@@ -582,9 +581,9 @@ B5500IOUnit.prototype.storeBufferBackward = function storeBufferBackward(chars,
|
||||
} else {
|
||||
this.store(addr); // store the word in memory
|
||||
}
|
||||
words--;
|
||||
--words;
|
||||
if (addr > 0) {
|
||||
addr--;
|
||||
--addr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -637,7 +636,7 @@ B5500IOUnit.prototype.storeBufferBackwardWithGM = function storeBufferBackwardWi
|
||||
this.store(addr); // store the word in memory
|
||||
}
|
||||
if (addr > 0) {
|
||||
addr--;
|
||||
--addr;
|
||||
} else {
|
||||
overflow = true;
|
||||
}
|
||||
@@ -651,8 +650,8 @@ B5500IOUnit.prototype.storeBufferBackwardWithGM = function storeBufferBackwardWi
|
||||
} // while !done
|
||||
|
||||
w += 0x1F*power; // set group mark in register
|
||||
s++;
|
||||
count++;
|
||||
++s;
|
||||
++count;
|
||||
|
||||
if (s > 0 && words > 0) { // partial word left to be stored
|
||||
this.W = w;
|
||||
@@ -663,9 +662,9 @@ B5500IOUnit.prototype.storeBufferBackwardWithGM = function storeBufferBackwardWi
|
||||
} else {
|
||||
this.store(addr); // store the word in memory
|
||||
}
|
||||
words--;
|
||||
--words;
|
||||
if (addr > 0) {
|
||||
addr--;
|
||||
--addr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -804,7 +803,7 @@ B5500IOUnit.prototype.finishDatacomWrite = function finishDatacomWrite(errorMask
|
||||
var tuBuf = (errorMask%0x10000000000 - errorMask%0x40000000)/0x40000000; // get TU/buf #
|
||||
|
||||
if (!(bufExhausted || (this.DwordCount & 0x10))) {
|
||||
length++; // account for the terminating Group Mark
|
||||
++length; // account for the terminating Group Mark
|
||||
}
|
||||
this.Daddress += (length+7) >>> 3;
|
||||
|
||||
@@ -878,9 +877,9 @@ B5500IOUnit.prototype.initiateDiskIO = function initiateDiskIO(u) {
|
||||
if (this.fetch(this.Daddress)) { // fetch the segment address from first buffer word
|
||||
this.finish();
|
||||
} else {
|
||||
this.Daddress++; // bump memory address past the seg address word
|
||||
++this.Daddress; // bump memory address past the seg address word
|
||||
w = this.W; // convert address word to binary
|
||||
for (x=0; x<7; x++) { // 7 decimal digits: 1 for EU, 6 for EU-relative address
|
||||
for (x=0; x<7; ++x) { // 7 decimal digits: 1 for EU, 6 for EU-relative address
|
||||
c = w % 0x40; // get low-order six bits of word
|
||||
segAddr += (c % 0x10)*p; // isolate the numeric portion and accumulate
|
||||
w = (w-c)/0x40; // shift word right six bits
|
||||
@@ -1069,7 +1068,7 @@ B5500IOUnit.prototype.forkIO = function forkIO() {
|
||||
var u; // peripheral unit object
|
||||
var x; // temp number variable
|
||||
|
||||
this.forkHandle = null; // clear the setCallback() handle
|
||||
this.forkHandle = 0; // clear the setCallback() handle
|
||||
|
||||
x = this.D; // explode the D-register into its fields
|
||||
this.Dunit = (x%0x200000000000 - x%0x10000000000)/0x10000000000; // [3:5]
|
||||
@@ -1184,7 +1183,7 @@ B5500IOUnit.prototype.initiate = function initiate() {
|
||||
environment, all of the Javascript action occurs on one thread, so this allows us to
|
||||
multiplex what normally would be asynchronous operations on that thread */
|
||||
|
||||
this.initiateStamp = new Date().getTime();
|
||||
this.initiateStamp = performance.now();
|
||||
this.clearD();
|
||||
this.AOFF = 0;
|
||||
this.EXNF = 0;
|
||||
@@ -1199,7 +1198,7 @@ B5500IOUnit.prototype.initiate = function initiate() {
|
||||
} else {
|
||||
this.D31F = 0; // reset the IOD-fetch error condition
|
||||
this.D = this.W;
|
||||
this.forkHandle = setCallback(this.boundForkIO, this, 0);
|
||||
this.forkHandle = setCallback(this.mnemonic, this, 0, this.forkIO);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1232,7 +1231,7 @@ B5500IOUnit.prototype.initiateLoad = function initiateLoad(cardLoadSelect, loadC
|
||||
loadComplete();
|
||||
}
|
||||
|
||||
this.initiateStamp = new Date().getTime();
|
||||
this.initiateStamp = performance.now();
|
||||
this.clearD();
|
||||
if (cardLoadSelect) {
|
||||
this.D = 0x0A0004800010; // unit 10, read, binary mode, addr @00020
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,9 +54,9 @@ var B5500SystemConfiguration = {
|
||||
DRA: false, // Drum/Auxmem A
|
||||
DRB: false, // Drum/Auxmem B
|
||||
MTA: true, // Magnetic Tape Unit A
|
||||
MTB: false, // Magnetic Tape Unit B
|
||||
MTC: false, // Magnetic Tape Unit C
|
||||
MTD: false, // Magnetic Tape Unit D
|
||||
MTB: true, // Magnetic Tape Unit B
|
||||
MTC: true, // Magnetic Tape Unit C
|
||||
MTD: true, // Magnetic Tape Unit D
|
||||
MTE: false, // Magnetic Tape Unit E
|
||||
MTF: false, // Magnetic Tape Unit F
|
||||
MTH: false, // Magnetic Tape Unit H
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -66,9 +66,11 @@ BUTTON.redButton {
|
||||
border-radius: 4px}
|
||||
|
||||
BUTTON.greenLit {
|
||||
background-color: green}
|
||||
color: black;
|
||||
background-color: #0F0}
|
||||
|
||||
BUTTON.redLit {
|
||||
color: black;
|
||||
background-color: #F00}
|
||||
|
||||
#CPNotReadyLight {
|
||||
|
||||
@@ -26,7 +26,7 @@ function B5500CardPunch(mnemonic, unitIndex, designate, statusChange, signal) {
|
||||
this.statusChange = statusChange; // external function to call for ready-status change
|
||||
this.signal = signal; // external function to call for special signals (not used here)
|
||||
|
||||
this.timer = null; // setTimeout() token
|
||||
this.timer = 0; // setCallback() token
|
||||
this.initiateStamp = 0; // timestamp of last initiation (set by IOUnit)
|
||||
|
||||
this.clear();
|
||||
@@ -108,6 +108,7 @@ B5500CardPunch.prototype.setPunchReady = function setPunchReady(ready) {
|
||||
|
||||
if (ready && !this.ready) {
|
||||
this.statusChange(1);
|
||||
this.addClass(this.$$("CPStartBtn"), "greenLit")
|
||||
this.removeClass(this.$$("CPNotReadyLight"), "redLit");
|
||||
this.ready = true;
|
||||
if (this.runoutArmed) {
|
||||
@@ -128,6 +129,7 @@ B5500CardPunch.prototype.setPunchReady = function setPunchReady(ready) {
|
||||
}
|
||||
} else if (!ready && this.ready) {
|
||||
this.statusChange(0);
|
||||
this.removeClass(this.$$("CPStartBtn"), "greenLit")
|
||||
this.addClass(this.$$("CPNotReadyLight"), "redLit");
|
||||
this.ready = false;
|
||||
}
|
||||
@@ -268,7 +270,6 @@ B5500CardPunch.prototype.space = function space(finish, length, control) {
|
||||
B5500CardPunch.prototype.write = function write(finish, buffer, length, mode, control) {
|
||||
/* Initiates a write operation on the unit */
|
||||
var text;
|
||||
var that = this;
|
||||
|
||||
this.errorMask = 0;
|
||||
this.busy = true;
|
||||
@@ -290,10 +291,12 @@ B5500CardPunch.prototype.write = function write(finish, buffer, length, mode, co
|
||||
}
|
||||
}
|
||||
|
||||
this.timer = setTimeout(function writeDelay() {
|
||||
that.busy = false;
|
||||
finish(that.errorMask, length);
|
||||
}, 60000/this.cardsPerMinute + this.initiateStamp - new Date().getTime());
|
||||
this.timer = setCallback(this.mnemonic, this,
|
||||
60000/this.cardsPerMinute + this.initiateStamp - performance.now(),
|
||||
function writeDelay() {
|
||||
this.busy = false;
|
||||
finish(this.errorMask, length);
|
||||
});
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
@@ -336,7 +339,7 @@ B5500CardPunch.prototype.shutDown = function shutDown() {
|
||||
/* Shuts down the device */
|
||||
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
clearCallback(this.timer);
|
||||
}
|
||||
this.window.removeEventListener("beforeunload", this.beforeUnload, false);
|
||||
this.window.close();
|
||||
|
||||
@@ -59,9 +59,11 @@ BUTTON.redButton {
|
||||
border-radius: 4px}
|
||||
|
||||
BUTTON.greenLit {
|
||||
color: black;
|
||||
background-color: #0F0}
|
||||
|
||||
BUTTON.redLit {
|
||||
color: black;
|
||||
background-color: #F00}
|
||||
|
||||
#CRNotReadyLight {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
function B5500CardReader(mnemonic, unitIndex, designate, statusChange, signal) {
|
||||
/* Constructor for the CardReader object */
|
||||
var that = this;
|
||||
var x = (mnemonic == "CRA" ? 0 : 30);
|
||||
var x = (mnemonic == "CRA" ? 0 : 1)*110;
|
||||
|
||||
this.mnemonic = mnemonic; // Unit mnemonic
|
||||
this.unitIndex = unitIndex; // Ready-mask bit number
|
||||
@@ -27,7 +27,7 @@ function B5500CardReader(mnemonic, unitIndex, designate, statusChange, signal) {
|
||||
this.statusChange = statusChange; // external function to call for ready-status change
|
||||
this.signal = signal; // external function to call for special signals (not used here)
|
||||
|
||||
this.timer = null; // setTimeout() token
|
||||
this.timer = 0; // setCallback() token
|
||||
this.initiateStamp = 0; // timestamp of last initiation (set by IOUnit)
|
||||
|
||||
this.clear();
|
||||
@@ -39,7 +39,7 @@ function B5500CardReader(mnemonic, unitIndex, designate, statusChange, signal) {
|
||||
}
|
||||
this.doc = null;
|
||||
this.window = window.open("../webUI/B5500CardReader.html", mnemonic,
|
||||
"scrollbars=no,resizable,width=560,height=160,left="+x+",top="+x);
|
||||
"scrollbars=no,resizable,width=560,height=160,left=0,top="+x);
|
||||
this.window.addEventListener("load", function windowLoad() {
|
||||
that.readerOnload();
|
||||
}, false);
|
||||
@@ -122,9 +122,11 @@ B5500CardReader.prototype.setReaderReady = function setReaderReady(ready) {
|
||||
this.ready = ready;
|
||||
if (ready) {
|
||||
this.statusChange(1);
|
||||
this.addClass(this.$$("CRStartBtn"), "greenLit")
|
||||
this.removeClass(this.$$("CRNotReadyLight"), "redLit");
|
||||
} else {
|
||||
this.statusChange(0);
|
||||
this.removeClass(this.$$("CRStartBtn"), "greenLit")
|
||||
this.addClass(this.$$("CRNotReadyLight"), "redLit");
|
||||
}
|
||||
};
|
||||
@@ -369,7 +371,6 @@ B5500CardReader.prototype.read = function read(finish, buffer, length, mode, con
|
||||
buffer is empty and EOF is armed, returns EOF; otherwise if not ready,
|
||||
returns Not Ready */
|
||||
var card;
|
||||
var that = this;
|
||||
|
||||
this.errorMask = 0;
|
||||
if (this.busy) {
|
||||
@@ -389,10 +390,12 @@ B5500CardReader.prototype.read = function read(finish, buffer, length, mode, con
|
||||
card = this.readCardBinary(buffer, length);
|
||||
}
|
||||
|
||||
this.timer = setTimeout(function readDelay() {
|
||||
that.busy = false;
|
||||
finish(that.errorMask, length);
|
||||
}, 60000/this.cardsPerMinute + this.initiateStamp - new Date().getTime());
|
||||
this.timer = setCallback(this.mnemonic, this,
|
||||
60000/this.cardsPerMinute + this.initiateStamp - performance.now(),
|
||||
function readDelay() {
|
||||
this.busy = false;
|
||||
finish(this.errorMask, length);
|
||||
});
|
||||
|
||||
if (this.bufIndex < this.bufLength) {
|
||||
this.progressBar.value = this.bufLength-this.bufIndex;
|
||||
@@ -467,7 +470,7 @@ B5500CardReader.prototype.shutDown = function shutDown() {
|
||||
/* Shuts down the device */
|
||||
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
clearCallback(this.timer);
|
||||
}
|
||||
this.window.removeEventListener("beforeunload", this.beforeUnload, false);
|
||||
this.window.close();
|
||||
|
||||
@@ -77,7 +77,7 @@ BUTTON.whiteButton {
|
||||
BUTTON.greenButton {
|
||||
position: absolute;
|
||||
background-color: #060;
|
||||
color: black;
|
||||
color: white;
|
||||
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
|
||||
font-size: 8pt;
|
||||
font-weight: normal;
|
||||
@@ -129,9 +129,11 @@ BUTTON.blackLit {
|
||||
color: #FFF}
|
||||
|
||||
BUTTON.greenLit {
|
||||
color: black;
|
||||
background-color: #0F0}
|
||||
|
||||
BUTTON.redLit {
|
||||
color: black;
|
||||
background-color: #F00}
|
||||
|
||||
BUTTON.yellowLit {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<meta http-equiv="Content-Style-Type" content="text/css">
|
||||
<link id=defaultStyleSheet rel=stylesheet type="text/css" href="B5500Console.css">
|
||||
|
||||
<script src="./B5500SetCallback.js"></script>
|
||||
<script src="./B5500SetCallback.js"></script> <!-- must be first -->
|
||||
|
||||
<script src="./B5500DummyUnit.js"></script>
|
||||
<script src="./B5500SPOUnit.js"></script>
|
||||
@@ -26,17 +26,12 @@
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
if (!window.indexedDB) { // for Safari, mostly
|
||||
window.indexedDB = window.webkitIndexedDB || window.mozIndexedDB;
|
||||
}
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
var aControl;
|
||||
var aNormal;
|
||||
var bControl;
|
||||
var bNormal;
|
||||
var boundBlinkenlicht;
|
||||
var cc = new B5500CentralControl(this);
|
||||
var cc = new B5500CentralControl(window);
|
||||
var ccLatches = [0, 0, 0];
|
||||
var ccLightsMap = new Array(6);
|
||||
var elapsedAverage = 0;
|
||||
@@ -49,6 +44,7 @@ window.addEventListener("load", function() {
|
||||
var lastPAControlRate = -1;
|
||||
var lastPBNormalRate = -1;
|
||||
var lastPBControlRate = -1;
|
||||
var perf = performance; // it's faster if locally cached
|
||||
var perLightsMap = new Array(48);
|
||||
var procDelay;
|
||||
var procSlack;
|
||||
@@ -56,7 +52,7 @@ window.addEventListener("load", function() {
|
||||
var slackAlpha = 0.990; // decay factor for exponential weighted avg.
|
||||
var slackAverage = 0; // average P1 slack time
|
||||
var slackLast = 0; // last P1 total slack time
|
||||
var timer;
|
||||
var timer = 0; // timing cookie
|
||||
var timerInterval = 50; // milliseconds
|
||||
|
||||
function $$(id) {
|
||||
@@ -85,7 +81,6 @@ window.addEventListener("load", function() {
|
||||
$$("LoadBtn").disabled = false;
|
||||
$$("HaltBtn").disabled = true;
|
||||
$$("MemoryCheckBtn").disabled = false;
|
||||
window.focus();
|
||||
if (showAnnunciators) {
|
||||
$$("CentralControl").style.visibility = "visible";
|
||||
}
|
||||
@@ -111,8 +106,8 @@ window.addEventListener("load", function() {
|
||||
$$("HaltBtn").disabled = true;
|
||||
$$("MemoryCheckBtn").disabled = true;
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
clearInterval(timer);
|
||||
timer = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -123,8 +118,8 @@ window.addEventListener("load", function() {
|
||||
$$("HaltBtn").disabled = true;
|
||||
$$("LoadBtn").disabled = false;
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
clearInterval(timer);
|
||||
timer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +135,7 @@ window.addEventListener("load", function() {
|
||||
$$("LoadBtn").disabled = true;
|
||||
elapsedLast = 0;
|
||||
slackLast = slackAverage = 0;
|
||||
boundBlinkenlicht();
|
||||
timer = setInterval(dasBlinkenlicht, timerInterval);
|
||||
break;
|
||||
case 1:
|
||||
alert("P1 busy or not available");
|
||||
@@ -176,25 +171,25 @@ window.addEventListener("load", function() {
|
||||
|
||||
var htmlMatch = /[<>&"]/g; // regular expression for escaping HTML text
|
||||
|
||||
function htmlFilter(c) {
|
||||
/* Used to escape HTML-sensitive characters in a string */
|
||||
switch (c) {
|
||||
case "&":
|
||||
return "&";
|
||||
case "<":
|
||||
return "<";
|
||||
case ">":
|
||||
return ">";
|
||||
case "\"":
|
||||
return """;
|
||||
default:
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHTML(text) {
|
||||
/* Returns "text" as escaped HTML */
|
||||
|
||||
function htmlFilter(char) {
|
||||
/* Used to escape HTML-sensitive characters in a string */
|
||||
switch (char) {
|
||||
case "&":
|
||||
return "&";
|
||||
case "<":
|
||||
return "<";
|
||||
case ">":
|
||||
return ">";
|
||||
case "\"":
|
||||
return """;
|
||||
default:
|
||||
return char;
|
||||
}
|
||||
}
|
||||
|
||||
return text.replace(htmlMatch, htmlFilter);
|
||||
}
|
||||
|
||||
@@ -205,6 +200,7 @@ window.addEventListener("load", function() {
|
||||
case 0:
|
||||
lastPhase = phase;
|
||||
doc.writeln(escapeHTML(text));
|
||||
doc.writeln("User Agent: " + navigator.userAgent);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
@@ -307,11 +303,6 @@ window.addEventListener("load", function() {
|
||||
unitBusyChange = lastUnitBusyMask ^ unitBusyMask;
|
||||
lastUnitBusyMask = unitBusyMask;
|
||||
|
||||
/********** for P2 debugging **********
|
||||
$$("PABZ").className = (cc.PA && cc.PA.busy ? "busy" : "");
|
||||
$$("PBBZ").className = (cc.PB && cc.PB.busy ? "busy" : "");
|
||||
**********/
|
||||
|
||||
x = 0;
|
||||
while (ccChange) {
|
||||
if (ccChange & 0x01) {
|
||||
@@ -345,14 +336,11 @@ window.addEventListener("load", function() {
|
||||
|
||||
function dasBlinkenlicht() {
|
||||
var cycles;
|
||||
var et;
|
||||
var pa = cc.PA;
|
||||
var pb = cc.PB;
|
||||
var p1 = cc.P1;
|
||||
var stamp = new Date().getTime();
|
||||
var stateRate;
|
||||
|
||||
timer = setTimeout(boundBlinkenlicht, timerInterval);
|
||||
cycles = p1.normalCycles+p1.controlCycles;
|
||||
|
||||
if (pa) {
|
||||
@@ -363,7 +351,7 @@ window.addEventListener("load", function() {
|
||||
aNormal.className = "yellowButton";
|
||||
}
|
||||
} else {
|
||||
stateRate = Math.round(pa.normalCycles/cycles*6 + 0.49);
|
||||
stateRate = Math.round(pa.normalCycles/cycles*6 + 0.25);
|
||||
if (stateRate != lastPANormalRate) {
|
||||
lastPANormalRate = stateRate;
|
||||
switch (stateRate) {
|
||||
@@ -391,7 +379,7 @@ window.addEventListener("load", function() {
|
||||
}
|
||||
}
|
||||
|
||||
stateRate = Math.round(pa.controlCycles/cycles*6 + 0.49);
|
||||
stateRate = Math.round(pa.controlCycles/cycles*6 + 0.25);
|
||||
if (stateRate != lastPAControlRate) {
|
||||
lastPAControlRate = stateRate;
|
||||
switch (stateRate) {
|
||||
@@ -431,7 +419,7 @@ window.addEventListener("load", function() {
|
||||
lastPBControlRate = -1;
|
||||
}
|
||||
} else {
|
||||
stateRate = Math.round(pb.normalCycles/cycles*6 + 0.49);
|
||||
stateRate = Math.round(pb.normalCycles/cycles*6 + 0.25);
|
||||
if (stateRate != lastPBNormalRate) {
|
||||
lastPBNormalRate = stateRate;
|
||||
switch (stateRate) {
|
||||
@@ -459,7 +447,7 @@ window.addEventListener("load", function() {
|
||||
}
|
||||
}
|
||||
|
||||
stateRate = Math.round(pb.controlCycles/cycles*6 + 0.49);
|
||||
stateRate = Math.round(pb.controlCycles/cycles*6 + 0.25);
|
||||
if (stateRate != lastPBControlRate) {
|
||||
lastPBControlRate = stateRate;
|
||||
switch (stateRate) {
|
||||
@@ -491,18 +479,8 @@ window.addEventListener("load", function() {
|
||||
}
|
||||
}
|
||||
|
||||
et = p1.procTime;
|
||||
while (et < 0) {
|
||||
et += stamp;
|
||||
}
|
||||
/**************
|
||||
elapsedAverage = (et-elapsedLast)*(1-slackAlpha) + elapsedAverage*slackAlpha;
|
||||
slackAverage = (p1.procSlack-slackLast)*(1-slackAlpha) + slackAverage*slackAlpha;
|
||||
elapsedLast = et;
|
||||
slackLast = p1.procSlack;
|
||||
**************/
|
||||
procDelay.innerHTML = p1.delayDeltaAvg.toFixed(1);
|
||||
procSlack.innerHTML = (p1.procSlackAvg/p1.procRunAvg*100).toFixed(1) + "%";
|
||||
procSlack.innerHTML = (p1.procSlackAvg/p1.procRunAvg*100).toFixed(1);
|
||||
|
||||
if (showAnnunciators) {
|
||||
displayCentralControl();
|
||||
@@ -545,6 +523,7 @@ window.addEventListener("load", function() {
|
||||
if (!window.FileReader) {missing += ", FileReader"}
|
||||
if (!window.FileList) {missing += ", FileList"}
|
||||
if (!window.postMessage) {missing += ", window.postMessage"}
|
||||
if (!(window.performance && "now" in performance)) {missing += ", performance.now"}
|
||||
|
||||
if (missing.length == 0) {
|
||||
return false;
|
||||
@@ -576,7 +555,7 @@ window.addEventListener("load", function() {
|
||||
$$("RetroVersion").style.color = (B5500SystemConfiguration.PB ? "yellow" : "white");
|
||||
});
|
||||
$$("MemoryCheckBtn").addEventListener("click", function(ev) {
|
||||
dumpState("Memory Check Button");
|
||||
dumpState("Memory-Check Button");
|
||||
});
|
||||
|
||||
aControl = $$("AControlBtn");
|
||||
@@ -585,8 +564,9 @@ window.addEventListener("load", function() {
|
||||
bNormal = $$("BNormalBtn");
|
||||
procDelay = $$("procDelay");
|
||||
procSlack = $$("procSlack");
|
||||
boundBlinkenlicht = bindMethod(dasBlinkenlicht, this);
|
||||
buildLightMaps();
|
||||
|
||||
window.dumpState = dumpState;
|
||||
}
|
||||
}, false);
|
||||
</script>
|
||||
@@ -612,7 +592,7 @@ window.addEventListener("load", function() {
|
||||
|
||||
<div id=BurroughsLogo>
|
||||
<img id=BurroughsLogoImage src="Burroughs-Logo-Neg.jpg" alt="Burroughs logo"
|
||||
title="Click to toggle the white annunciator lights">
|
||||
title="Click to toggle display of the white annunciator lights">
|
||||
</div>
|
||||
<div id=RetroVersion title="retro-B5500 emulator version">
|
||||
?.??
|
||||
@@ -625,7 +605,7 @@ window.addEventListener("load", function() {
|
||||
<table id=CentralControl style="visibility:hidden">
|
||||
<colgroup>
|
||||
<col span=31 class=AnnunciatorCol>
|
||||
<col span=2>
|
||||
<col span=3>
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr id=CCInterruptRow>
|
||||
@@ -653,6 +633,7 @@ window.addEventListener("load", function() {
|
||||
<td id=PABZ>PABZ <!-- Processor A busy *** debug *** -->
|
||||
<td id=PBBZ>PBBZ <!-- Processor B busy *** debug *** -->
|
||||
<td id=procSlack>
|
||||
<td>%
|
||||
<td class=statLabel title="Percentage of time Processor A is throttling its performance">P1 Slack
|
||||
<tr id=CCPeripheralRow>
|
||||
<td id=DRA>DRA <!-- 31 -->
|
||||
@@ -687,6 +668,7 @@ window.addEventListener("load", function() {
|
||||
<td id=MTS>MTS <!-- 33 -->
|
||||
<td id=MTT>MTT <!-- 32 -->
|
||||
<td id=procDelay>
|
||||
<td>ms
|
||||
<td class=statLabel title="Average excess throttling delay for Processor A (ms)">P1 Delay
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -39,8 +39,8 @@ function B5500DatacomUnit(mnemonic, unitIndex, designate, statusChange, signal)
|
||||
|
||||
this.buffer = new ArrayBuffer(448); // adapter buffer storage
|
||||
this.initiateStamp = 0; // timestamp of last initiation (set by IOUnit)
|
||||
this.inTimer = null; // input setCallback() token
|
||||
this.outTimer = null; // output setCallback() token
|
||||
this.inTimer = 0; // input setCallback() token
|
||||
this.outTimer = 0; // output setCallback() token
|
||||
|
||||
this.clear();
|
||||
|
||||
@@ -69,12 +69,12 @@ B5500DatacomUnit.prototype.bufWriteReady = 5;
|
||||
B5500DatacomUnit.prototype.keyFilter = [ // Filter keyCode values to valid BCL ones
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 00-0F
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 10-1F
|
||||
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x00,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, // 20-2F
|
||||
0x20,0x7D,0x22,0x23,0x24,0x25,0x26,0x7B,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, // 20-2F
|
||||
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x00,0x3D,0x00,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,0x00,0x5D,0x00,0x00, // 50-5F
|
||||
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x7C,0x5D,0x21,0x7E, // 50-5F
|
||||
0x00,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,0x7B,0x7C,0x7D,0x7E,0x00]; // 70-7F
|
||||
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x00,0x00,0x00,0x7E,0x00]; // 70-7F
|
||||
|
||||
/**************************************/
|
||||
B5500DatacomUnit.prototype.$$ = function $$(e) {
|
||||
@@ -303,31 +303,34 @@ B5500DatacomUnit.prototype.outputChar = function outputChar() {
|
||||
var stamp;
|
||||
|
||||
if (this.bufIndex < this.bufLength) {
|
||||
stamp = new Date().getTime();
|
||||
stamp = performance.now();
|
||||
nextTime = (this.nextCharTime < stamp ? stamp : this.nextCharTime) + this.charPeriod;
|
||||
delay = nextTime - stamp;
|
||||
this.nextCharTime = nextTime;
|
||||
|
||||
c = this.buffer[this.bufIndex++];
|
||||
switch (c) {
|
||||
case 0x7B: // { less-or-equal, output CR
|
||||
this.printCol = 0;
|
||||
this.outTimer = setCallback(this.outputChar, this, delay);
|
||||
break;
|
||||
case 0x21: // ! not-equal, output LF
|
||||
this.appendEmptyLine();
|
||||
this.outTimer = setCallback(this.outputChar, this, delay);
|
||||
this.outTimer = setCallback(this.mnemonic, this, delay, this.outputChar);
|
||||
break;
|
||||
case 0x3C: // < less-than, output RO (DEL)
|
||||
case 0x3E: // > greater-than, output X-ON (DC1)
|
||||
this.outTimer = setCallback(this.outputChar, this, delay);
|
||||
this.outTimer = setCallback(this.mnemonic, this, delay, this.outputChar);
|
||||
break; // do nothing, just delay
|
||||
case 0x7B: // { less-or-equal, output CR
|
||||
this.printCol = 0;
|
||||
this.outTimer = setCallback(this.mnemonic, this, delay, this.outputChar);
|
||||
break;
|
||||
case 0x7D: // } greater-or-equal, disconnect
|
||||
this.termDisconnect();
|
||||
break;
|
||||
case 0x7E: // ~ left-arrow, end-of-message (should never happen)
|
||||
this.bufIndex = this.bufLength;
|
||||
this.outTimer = setCallback(this.mnemonic, this, 0, this.outputChar);
|
||||
default:
|
||||
this.printChar(c);
|
||||
this.outTimer = setCallback(this.outputChar, this, delay);
|
||||
this.outTimer = setCallback(this.mnemonic, this, delay, this.outputChar);
|
||||
break;
|
||||
}
|
||||
this.showBufferIndex();
|
||||
@@ -352,16 +355,16 @@ B5500DatacomUnit.prototype.keyPress = function keyPress(ev) {
|
||||
/* Handles keyboard character events. Depending on the state of the buffer,
|
||||
either buffers the character for transmission to the I/O Unit, echos
|
||||
it to the printer, or ignores it altogether */
|
||||
var c = ev.charCode;
|
||||
var delay;
|
||||
var index = this.bufLength;
|
||||
var nextTime;
|
||||
var stamp;
|
||||
var b; // translated character
|
||||
var c = ev.charCode; // input character, ASCII
|
||||
var delay; // inter-character delay, ms
|
||||
var nextTime; // next character output time, ms
|
||||
var stamp; // current timestamp, ms
|
||||
|
||||
//this.$$("CharCode").innerHTML = c.toString() + ":0x" + c.toString(16);
|
||||
|
||||
if (this.connected) {
|
||||
stamp = new Date().getTime();
|
||||
stamp = performance.now();
|
||||
if (this.bufState == this.bufIdle) {
|
||||
this.bufIndex = this.bufLength = 0;
|
||||
this.nextCharTime = stamp;
|
||||
@@ -406,15 +409,16 @@ B5500DatacomUnit.prototype.keyPress = function keyPress(ev) {
|
||||
if (this.bufState == this.bufReadReady && this.fullBuffer) {
|
||||
this.interrupt = true;
|
||||
this.setState(this.bufInputBusy);
|
||||
setCallback(this.signal, this, delay); // buffer overflow
|
||||
setCallback(this.mnemonic, this, delay, this.signal); // buffer overflow
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
} else if (this.bufState == this.bufInputBusy || this.bufState == this.bufIdle) {
|
||||
switch (c) {
|
||||
case 0x7E: // ~ left-arrow (Group Mark), end of message
|
||||
this.inTimer = setCallback(this.printChar, this, delay, c);
|
||||
case 0x5F: // _ underscore (TTY left-arrow), end of message
|
||||
this.inTimer = setCallback(this.mnemonic, this, delay, this.printChar, c);
|
||||
this.nextCharTime = this.charPeriod + nextTime;
|
||||
setCallback(this.terminateInput, this, this.charPeriod+delay);
|
||||
setCallback(this.mnemonic, this, this.charPeriod+delay, this.terminateInput);
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
break;
|
||||
@@ -422,7 +426,7 @@ B5500DatacomUnit.prototype.keyPress = function keyPress(ev) {
|
||||
if (this.bufIndex > 0) {
|
||||
this.bufIndex--;
|
||||
}
|
||||
this.inTimer = setCallback(this.printChar, this, delay, c);
|
||||
this.inTimer = setCallback(this.mnemonic, this, delay, this.printChar, c);
|
||||
this.nextCharTime = nextTime;
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
@@ -432,8 +436,8 @@ B5500DatacomUnit.prototype.keyPress = function keyPress(ev) {
|
||||
this.interrupt = true;
|
||||
this.abnomal = true;
|
||||
this.setState(this.bufReadReady);
|
||||
setCallback(this.signal, this, delay);
|
||||
this.inTimer = setCallback(this.printChar, this, delay, c);
|
||||
setCallback(this.mnemonic, this, delay, this.signal);
|
||||
this.inTimer = setCallback(this.mnemonic, this, delay, this.printChar, c);
|
||||
this.nextCharTime = nextTime;
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
@@ -449,7 +453,7 @@ B5500DatacomUnit.prototype.keyPress = function keyPress(ev) {
|
||||
this.interrupt = true;
|
||||
this.abnormal = true;
|
||||
this.setState(this.bufWriteReady);
|
||||
setCallback(this.signal, this, delay);
|
||||
setCallback(this.mnemonic, this, delay, this.signal);
|
||||
}
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
@@ -466,10 +470,13 @@ B5500DatacomUnit.prototype.keyPress = function keyPress(ev) {
|
||||
this.setState(this.bufState); // just to turn on the annunciator
|
||||
// no break
|
||||
default:
|
||||
c = this.keyFilter[c];
|
||||
if (c) { // if it's a character we will accept
|
||||
this.buffer[this.bufIndex++] = c;
|
||||
this.inTimer = setCallback(this.printChar, this, delay, c);
|
||||
b = this.keyFilter[c];
|
||||
if (b) { // if it's a character we will accept
|
||||
this.buffer[this.bufIndex++] = b;
|
||||
if (c >= 0x61 && c <= 0x7A) {
|
||||
c -= 32; // up-case echoed letters
|
||||
}
|
||||
this.inTimer = setCallback(this.mnemonic, this, delay, this.printChar, c);
|
||||
this.nextCharTime = nextTime;
|
||||
if (this.bufIndex < this.bufferSize) {
|
||||
this.setState(this.bufInputBusy);
|
||||
@@ -477,7 +484,7 @@ B5500DatacomUnit.prototype.keyPress = function keyPress(ev) {
|
||||
this.interrupt = true;
|
||||
this.fullBuffer = true;
|
||||
this.setState(this.bufReadReady);
|
||||
setCallback(this.signal, this, this.charPeriod+delay); // full buffer, no GM detected
|
||||
setCallback(this.mnemonic, this, this.charPeriod+delay, this.signal); // full buffer, no GM detected
|
||||
}
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
@@ -489,7 +496,7 @@ B5500DatacomUnit.prototype.keyPress = function keyPress(ev) {
|
||||
this.interrupt = true;
|
||||
this.abnormal = true;
|
||||
this.setState(this.bufReadReady);
|
||||
setCallback(this.signal, this, delay);
|
||||
setCallback(this.mnemonic, this, delay, this.signal);
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
@@ -538,7 +545,7 @@ B5500DatacomUnit.prototype.datacomOnload = function datacomOnload() {
|
||||
"</style>";
|
||||
|
||||
this.window.focus();
|
||||
this.nextCharTime = new Date().getTime();
|
||||
this.nextCharTime = performance.now();
|
||||
|
||||
this.window.addEventListener("beforeunload", this.beforeUnload, false);
|
||||
|
||||
@@ -784,10 +791,10 @@ B5500DatacomUnit.prototype.shutDown = function shutDown() {
|
||||
/* Shuts down the device */
|
||||
|
||||
if (this.inTimer) {
|
||||
clearTimeout(this.inTimer);
|
||||
clearCallback(this.inTimer);
|
||||
}
|
||||
if (this.outTimer) {
|
||||
clearTimeout(this.outTimer);
|
||||
clearCallback(this.outTimer);
|
||||
}
|
||||
this.window.removeEventListener("beforeunload", this.beforeUnload, false);
|
||||
this.window.close();
|
||||
|
||||
@@ -78,7 +78,7 @@ function B5500DiskUnit(mnemonic, index, designate, statusChange, signal) {
|
||||
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.timer = null; // setTimeout() token
|
||||
this.timer = 0; // setCallback() token
|
||||
this.initiateStamp = 0; // timestamp of last initiation (set by IOUnit)
|
||||
|
||||
this.clear();
|
||||
@@ -145,8 +145,8 @@ B5500DiskUnit.prototype.openDatabase = function openDataBase() {
|
||||
var db = null;
|
||||
var that = this;
|
||||
|
||||
that.statusChange(0); // initially force DFCU status to not ready
|
||||
req = indexedDB.open(that.dbName, that.dbVersion);
|
||||
this.statusChange(0); // initially force DFCU status to not ready
|
||||
req = indexedDB.open(this.dbName, this.dbVersion);
|
||||
|
||||
req.onerror = function idbOpenOnerror(ev) {
|
||||
alert("Cannot open " + that.mnemonic + " database: " + ev.target.error);
|
||||
@@ -220,10 +220,11 @@ B5500DiskUnit.prototype.read = function read(finish, buffer, length, mode, contr
|
||||
req = this.disk.transaction(euName).objectStore(euName).get(segAddr);
|
||||
req.onsuccess = function singleReadOnsuccess(ev) {
|
||||
that.copySegment(ev.target.result, buffer, 0);
|
||||
this.timer = setTimeout(function singleReadTimeout() {
|
||||
finish(that.errorMask, length);
|
||||
that.errorMask = 0;
|
||||
}, finishTime - new Date().getTime());
|
||||
this.timer = setCallback(this.mnemonic, this, finishTime - performance.now(),
|
||||
function singleReadTimeout() {
|
||||
finish(this.errorMask, length);
|
||||
this.errorMask = 0;
|
||||
});
|
||||
}
|
||||
} else { // A multi-segment read
|
||||
range = IDBKeyRange.bound(segAddr, endAddr);
|
||||
@@ -252,10 +253,11 @@ B5500DiskUnit.prototype.read = function read(finish, buffer, length, mode, contr
|
||||
bx += 240;
|
||||
segAddr++;
|
||||
}
|
||||
this.timer = setTimeout(function rangeReadTimeout() {
|
||||
finish(that.errorMask, length);
|
||||
that.errorMask = 0;
|
||||
}, finishTime - new Date().getTime());
|
||||
this.timer = setCallback(this.mnemonic, this, finishTime - performance.now(),
|
||||
function rangeReadTimeout() {
|
||||
finish(this.errorMask, length);
|
||||
this.errorMask = 0;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -278,7 +280,6 @@ B5500DiskUnit.prototype.write = function write(finish, buffer, length, mode, con
|
||||
var euSize; // max seg size for EU
|
||||
var finishTime; // predicted time of I/O completion, ms
|
||||
var req; // IDB request object
|
||||
var that = this; // local object context
|
||||
var txn; // IDB transaction object
|
||||
|
||||
this.finish = finish; // for global error handler
|
||||
@@ -314,10 +315,11 @@ B5500DiskUnit.prototype.write = function write(finish, buffer, length, mode, con
|
||||
} else {
|
||||
txn = this.disk.transaction(euName, "readwrite")
|
||||
txn.oncomplete = function writeComplete(ev) {
|
||||
this.timer = setTimeout(function writeTimeout() {
|
||||
finish(that.errorMask, length);
|
||||
that.errorMask = 0;
|
||||
}, finishTime - new Date().getTime());
|
||||
this.timer = setCallback(this.mnemonic, this, finishTime - performance.now(),
|
||||
function writeTimeout() {
|
||||
finish(this.errorMask, length);
|
||||
this.errorMask = 0;
|
||||
});
|
||||
};
|
||||
eu = txn.objectStore(euName);
|
||||
for (; segAddr<=endAddr; segAddr++) {
|
||||
@@ -352,7 +354,6 @@ B5500DiskUnit.prototype.readCheck = function readCheck(finish, length, control)
|
||||
var finishTime; // predicted time of I/O completion, ms
|
||||
var range; // key range for multi-segment read
|
||||
var req; // IDB request object
|
||||
var that = this; // local object context
|
||||
var txn; // IDB transaction object
|
||||
|
||||
this.finish = finish; // for global error handler
|
||||
@@ -389,7 +390,7 @@ B5500DiskUnit.prototype.readCheck = function readCheck(finish, length, control)
|
||||
} else { // A multi-segment read
|
||||
range = IDBKeyRange.bound(segAddr, endAddr);
|
||||
txn = this.disk.transaction(euName);
|
||||
finish(that.errorMask, length); // post I/O complete now -- DFCU will signal when check finished
|
||||
finish(this.errorMask, length); // post I/O complete now -- DFCU will signal when check finished
|
||||
|
||||
req = txn.objectStore(euName).openCursor(range);
|
||||
req.onsuccess = function readCheckOnsuccess(ev) {
|
||||
@@ -398,10 +399,11 @@ B5500DiskUnit.prototype.readCheck = function readCheck(finish, length, control)
|
||||
if (cursor) { // found a segment at some address in range
|
||||
cursor.continue();
|
||||
} else { // at end of range
|
||||
this.timer = setTimeout(function readCheckTimeout() {
|
||||
that.signal();
|
||||
// DO NOT clear the error mask
|
||||
}, finishTime - new Date().getTime());
|
||||
this.timer = setCallback(this.mnemonic, this, finishTime - performance.now(),
|
||||
function readCheckTimeout() {
|
||||
this.signal();
|
||||
// DO NOT clear the error mask
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -419,7 +421,6 @@ B5500DiskUnit.prototype.readInterrogate = function readInterrogate(finish, contr
|
||||
var segAddr = control % 1000000; // starting seg address
|
||||
var euNumber = (control % 10000000 - segAddr)/1000000;
|
||||
var euName = this.euPrefix + euNumber;
|
||||
var that = this;
|
||||
|
||||
this.finish = finish; // for global error handler
|
||||
euSize = this.config[euName];
|
||||
@@ -430,10 +431,12 @@ B5500DiskUnit.prototype.readInterrogate = function readInterrogate(finish, contr
|
||||
if (segAddr < 0 || segAddr >= euSize) { // if read is past end of disk
|
||||
this.errorMask |= 0x20; // set D27F for invalid seg address
|
||||
}
|
||||
this.timer = setTimeout(function readInterrogateTimeout() {
|
||||
finish(that.errorMask, length);
|
||||
that.errorMask = 0;
|
||||
}, Math.random()*this.maxLatency*1000 + this.initiateStamp - new Date().getTime());
|
||||
this.timer = setCallback(this.mnemonic, this,
|
||||
Math.random()*this.maxLatency*1000 + this.initiateStamp - performance.now(),
|
||||
function readInterrogateTimeout() {
|
||||
finish(this.errorMask, length);
|
||||
this.errorMask = 0;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -452,7 +455,6 @@ B5500DiskUnit.prototype.writeInterrogate = function writeInterrogate(finish, con
|
||||
var segAddr = control % 1000000; // starting seg address
|
||||
var euNumber = (control % 10000000 - segAddr)/1000000;
|
||||
var euName = this.euPrefix + euNumber;
|
||||
var that = this;
|
||||
|
||||
this.finish = finish; // for global error handler
|
||||
euSize = this.config[euName];
|
||||
@@ -463,10 +465,12 @@ B5500DiskUnit.prototype.writeInterrogate = function writeInterrogate(finish, con
|
||||
if (segAddr < 0 || segAddr >= euSize) { // if read is past end of disk
|
||||
this.errorMask |= 0x20; // set D27F for invalid seg address
|
||||
}
|
||||
this.timer = setTimeout(function writeInterrogateTimeout() {
|
||||
finish(that.errorMask, length);
|
||||
that.errorMask = 0;
|
||||
}, Math.random()*this.maxLatency*1000 + this.initiateStamp - new Date().getTime());
|
||||
this.timer = setCallback(this.mnemonic, this,
|
||||
Math.random()*this.maxLatency*1000 + this.initiateStamp - performance.now(),
|
||||
function writeInterrogateTimeout() {
|
||||
finish(this.errorMask, length);
|
||||
this.errorMask = 0;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -475,7 +479,7 @@ B5500DiskUnit.prototype.shutDown = function shutDown() {
|
||||
/* Shuts down the device */
|
||||
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
clearCallback(this.timer);
|
||||
}
|
||||
// this device has no window to close
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ function B5500DummyPrinter(mnemonic, unitIndex, designate, statusChange, signal)
|
||||
this.statusChange = statusChange; // external function to call for ready-status change
|
||||
this.signal = signal; // external function to call for special signals (e.g,. Printer Finished)
|
||||
|
||||
this.timer = null; // setTimeout() token
|
||||
this.timer = 0; // setCallback() token
|
||||
this.initiateStamp = 0; // timestamp of last initiation (set by IOUnit)
|
||||
|
||||
this.clear();
|
||||
@@ -163,8 +163,9 @@ B5500DummyPrinter.prototype.write = function write(finish, buffer, length, mode,
|
||||
}
|
||||
}
|
||||
|
||||
this.timer = setTimeout(this.signal,
|
||||
60000/this.linesPerMinute + this.initiateStamp - new Date().getTime());
|
||||
this.timer = setCallback(this.mnemonic, this,
|
||||
60000/this.linesPerMinute + this.initiateStamp - performance.now(),
|
||||
this.signal);
|
||||
finish(this.errorMask, 0);
|
||||
this.endOfPaper.scrollIntoView();
|
||||
};
|
||||
@@ -219,7 +220,7 @@ B5500DummyPrinter.prototype.shutDown = function shutDown() {
|
||||
/* Shuts down the device */
|
||||
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
clearCallback(this.timer);
|
||||
}
|
||||
this.window.removeEventListener("beforeunload", this.beforeUnload, false);
|
||||
this.window.close();
|
||||
|
||||
@@ -28,7 +28,7 @@ function B5500DummyUnit(mnemonic, index, designate, statusChange, signal) {
|
||||
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.timer = null; // setTimeout() token
|
||||
this.timer = 0; // setCallback() token
|
||||
this.initiateStamp = 0; // timestamp of last initiation (set by IOUnit)
|
||||
|
||||
this.clear();
|
||||
@@ -107,7 +107,7 @@ B5500DummyUnit.prototype.shutDown = function shutDown() {
|
||||
/* Shuts down the device */
|
||||
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
clearCallback(this.timer);
|
||||
}
|
||||
// this device has no window to close
|
||||
};
|
||||
|
||||
@@ -62,6 +62,7 @@ BUTTON.yellowLit {
|
||||
background-color: #FF0}
|
||||
|
||||
BUTTON.redLit {
|
||||
color: black;
|
||||
background-color: #F00}
|
||||
|
||||
SPAN.annunciator {
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
function B5500MagTapeDrive(mnemonic, unitIndex, designate, statusChange, signal) {
|
||||
/* Constructor for the MagTapeDrive object */
|
||||
var that = this;
|
||||
var x = ((mnemonic.charCodeAt(2) - "A".charCodeAt(0) + 1)*30);
|
||||
var x = ((mnemonic.charCodeAt(2) - "A".charCodeAt(0))*30);
|
||||
|
||||
this.mnemonic = mnemonic; // Unit mnemonic
|
||||
this.unitIndex = unitIndex; // Ready-mask bit number
|
||||
@@ -39,7 +39,7 @@ function B5500MagTapeDrive(mnemonic, unitIndex, designate, statusChange, signal)
|
||||
this.statusChange = statusChange; // external function to call for ready-status change
|
||||
this.signal = signal; // external function to call for special signals (not used here)
|
||||
|
||||
this.timer = null; // setCallback() token
|
||||
this.timer = 0; // setCallback() token
|
||||
this.initiateStamp = 0; // timestamp of last initiation (set by IOUnit)
|
||||
|
||||
this.clear();
|
||||
@@ -51,7 +51,7 @@ function B5500MagTapeDrive(mnemonic, unitIndex, designate, statusChange, signal)
|
||||
}
|
||||
this.doc = null;
|
||||
this.window = window.open("../webUI/B5500MagTapeDrive.html", mnemonic,
|
||||
"scrollbars=no,resizable,width=560,height=120,left="+x+",top="+x);
|
||||
"scrollbars=no,resizable,width=560,height=120,left=280,top="+x);
|
||||
this.window.addEventListener("load", function windowLoad() {
|
||||
that.tapeDriveOnLoad();
|
||||
}, false);
|
||||
@@ -75,6 +75,8 @@ 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;
|
||||
@@ -83,9 +85,9 @@ B5500MagTapeDrive.prototype.maxBlankFrames = 9*12*B5500MagTapeDrive.prototype.de
|
||||
// max blank tape length, 9 feet [frames]
|
||||
B5500MagTapeDrive.prototype.bcdTapeMark = 0x8F;
|
||||
// .bcd image EOF code
|
||||
B5500MagTapeDrive.prototype.reelCircumference = 10*3.14159;
|
||||
B5500MagTapeDrive.prototype.reelCircumference = 10*Math.PI;
|
||||
// max circumference of tape [inches]
|
||||
B5500MagTapeDrive.prototype.maxSpinAngle = 37;
|
||||
B5500MagTapeDrive.prototype.maxSpinAngle = 33;
|
||||
// max angle to rotate reel image [degrees]
|
||||
|
||||
B5500MagTapeDrive.prototype.bcdXlateInOdd = [ // Translate odd parity BIC to ASCII
|
||||
@@ -153,7 +155,7 @@ B5500MagTapeDrive.prototype.clear = function clear() {
|
||||
this.imgWritten = false; // tape image has been modified (implies writable)
|
||||
|
||||
this.tapeState = this.tapeUnloaded; // tape drive state
|
||||
this.angle = 0; // current rotation angle of reel image [degrees]
|
||||
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.atBOT = true; // true if tape at BOT
|
||||
@@ -197,19 +199,19 @@ B5500MagTapeDrive.prototype.removeClass = function removeClass(e, name) {
|
||||
/**************************************/
|
||||
B5500MagTapeDrive.prototype.spinReel = function spinReel(inches) {
|
||||
/* Rotates the reel image icon an appropriate amount based on the number of
|
||||
inches of tape movement. The rotation is limited to 33 degrees in either
|
||||
direction so that movement remains apparent to the viewer */
|
||||
inches of tape movement. The rotation is limited to this.maxSpinAngle degrees
|
||||
in either direction so that movement remains apparent to the viewer */
|
||||
var circumference = this.reelCircumference*(1 - this.tapeInches/this.maxTapeLength/2);
|
||||
var angle = inches/circumference*360;
|
||||
var degrees = inches/circumference*360;
|
||||
|
||||
if (angle >= this.maxSpinAngle) {
|
||||
angle = this.maxSpinAngle;
|
||||
} else if (angle < -this.maxSpinAngle) {
|
||||
angle = -this.maxSpinAngle;
|
||||
if (degrees >= this.maxSpinAngle) {
|
||||
degrees = this.maxSpinAngle;
|
||||
} else if (degrees < -this.maxSpinAngle) {
|
||||
degrees = -this.maxSpinAngle;
|
||||
}
|
||||
|
||||
this.angle = (this.angle + angle)%360;
|
||||
this.reelIcon.style.transform = "rotate(" + this.angle.toFixed(0) + "deg)";
|
||||
this.reelAngle = (this.reelAngle + degrees)%360;
|
||||
this.reelIcon.style.transform = "rotate(" + this.reelAngle.toFixed(0) + "deg)";
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
@@ -273,7 +275,7 @@ B5500MagTapeDrive.prototype.setTapeUnloaded = function setTapeUnloaded() {
|
||||
this.reelIcon.style.visibility = "hidden";
|
||||
if (this.timer) {
|
||||
clearCallback(this.timer);
|
||||
this.timer = null;
|
||||
this.timer = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -628,25 +630,24 @@ B5500MagTapeDrive.prototype.unloadTape = function unloadTape() {
|
||||
var text = doc.getElementById("TapeText");
|
||||
var x = 0; // image data index
|
||||
|
||||
while (text.firstChild) {
|
||||
while (text.firstChild) { // delete the please-wait message
|
||||
text.removeChild(text.firstChild);
|
||||
}
|
||||
|
||||
c = image[x];
|
||||
do {
|
||||
c &= 0x7F;
|
||||
c &= 0x7F; // clear the start-of-block bit
|
||||
table = (mt.bcdXlateInEven[c] < 0xFF ? mt.bcdXlateInEven : mt.bcdXlateInOdd);
|
||||
bufIndex = 0;
|
||||
do {
|
||||
if (bufIndex >= bufLength) {
|
||||
if (bufIndex >= bufLength) { // ASCII block size exceeded
|
||||
text.appendChild(doc.createTextNode(String.fromCharCode.apply(null, buf.subarray(0, bufIndex))));
|
||||
bufIndex = 0;
|
||||
}
|
||||
if (c > 0) {
|
||||
if (c > 0) { // drop any unrecorded tape frames
|
||||
buf[bufIndex++] = table[c];
|
||||
}
|
||||
x++;
|
||||
if (x < imgLength) {
|
||||
if (++x < imgLength) {
|
||||
c = image[x];
|
||||
} else {
|
||||
break;
|
||||
@@ -666,7 +667,7 @@ B5500MagTapeDrive.prototype.unloadTape = function unloadTape() {
|
||||
doc.write("<html><head></head><body><pre id=TapeText>Converting... please wait...</pre></body></html>");
|
||||
doc.close();
|
||||
doc.title = "B5500 " + this.mnemonic + " Unload Tape";
|
||||
setCallback(unloadDriver, this, 20); // give the message time to display
|
||||
setCallback(this.mnemonic, this, 50, unloadDriver); // give the message time to display
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
@@ -675,11 +676,11 @@ B5500MagTapeDrive.prototype.tapeRewind = function tapeRewind(makeReady) {
|
||||
of time depending on how far up-tape we are. If makeReady is true [valid only when
|
||||
called from this.rewind()], then readies the unit again when the rewind is complete */
|
||||
var inches;
|
||||
var lastStamp = new Date().getTime();
|
||||
var updateInterval = 30; // ms
|
||||
var lastStamp = performance.now();
|
||||
var updateInterval = 15; // ms
|
||||
|
||||
function rewindFinish() {
|
||||
this.timer = null;
|
||||
this.timer = 0;
|
||||
this.busy = false;
|
||||
this.removeClass(this.$$("MTRewindingLight"), "whiteLit");
|
||||
if (makeReady && this.tapeState == this.tapeRemote) {
|
||||
@@ -689,7 +690,7 @@ B5500MagTapeDrive.prototype.tapeRewind = function tapeRewind(makeReady) {
|
||||
}
|
||||
|
||||
function rewindDelay() {
|
||||
var stamp = new Date().getTime();
|
||||
var stamp = performance.now();
|
||||
var interval = stamp - lastStamp;
|
||||
|
||||
if (interval <= 0) {
|
||||
@@ -698,20 +699,20 @@ B5500MagTapeDrive.prototype.tapeRewind = function tapeRewind(makeReady) {
|
||||
if (this.tapeInches > 0) {
|
||||
inches = interval/1000*this.rewindSpeed;
|
||||
this.tapeInches -= inches;
|
||||
lastStamp = stamp;
|
||||
this.timer = setCallback(this.mnemonic, this, updateInterval, rewindDelay);
|
||||
this.spinReel(-inches);
|
||||
this.progressBar.value = this.imgMaxInches - this.tapeInches;
|
||||
lastStamp = stamp;
|
||||
this.timer = setCallback(rewindDelay, this, updateInterval);
|
||||
} else {
|
||||
this.spinReel(6);
|
||||
this.setAtBOT(true);
|
||||
this.timer = setCallback(rewindFinish, this, 2000);
|
||||
this.timer = setCallback(this.mnemonic, this, 2000, rewindFinish);
|
||||
this.spinReel(6);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.timer) {
|
||||
clearCallback(this.timer);
|
||||
this.timer = null;
|
||||
this.timer = 0;
|
||||
}
|
||||
if (this.tapeState != this.tapeUnloaded && !this.atBOT) {
|
||||
this.busy = true;
|
||||
@@ -719,7 +720,7 @@ B5500MagTapeDrive.prototype.tapeRewind = function tapeRewind(makeReady) {
|
||||
this.statusChange(0);
|
||||
this.setAtEOT(false);
|
||||
this.addClass(this.$$("MTRewindingLight"), "whiteLit");
|
||||
this.timer = setCallback(rewindDelay, this, 1000);
|
||||
this.timer = setCallback(this.mnemonic, this, 1000, rewindDelay);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1178,11 +1179,12 @@ B5500MagTapeDrive.prototype.read = function read(finish, buffer, length, mode, c
|
||||
}
|
||||
|
||||
this.buildErrorMask(residue);
|
||||
this.timer = setCallback(function readDelay() {
|
||||
this.busy = false;
|
||||
finish(this.errorMask, count);
|
||||
}, this, (imgCount/this.charsPerSec + this.startStopTime)*1000 +
|
||||
this.initiateStamp - new Date().getTime());
|
||||
this.timer = setCallback(this.mnemonic, this,
|
||||
(imgCount/this.charsPerSec + this.startStopTime)*1000 + this.initiateStamp - performance.now(),
|
||||
function readDelay() {
|
||||
this.busy = false;
|
||||
finish(this.errorMask, count);
|
||||
});
|
||||
|
||||
this.spinReel(inches);
|
||||
if (this.tapeInches < this.imgMaxInches) {
|
||||
@@ -1235,11 +1237,12 @@ B5500MagTapeDrive.prototype.space = function space(finish, length, control) {
|
||||
}
|
||||
|
||||
this.buildErrorMask(0);
|
||||
this.timer = setCallback(function readDelay() {
|
||||
this.busy = false;
|
||||
finish(this.errorMask, 0);
|
||||
}, this, (imgCount/this.charsPerSec + this.startStopTime)*1000 +
|
||||
this.initiateStamp - new Date().getTime());
|
||||
this.timer = setCallback(this.mnemonic, this,
|
||||
(imgCount/this.charsPerSec + this.startStopTime)*1000 + this.initiateStamp - performance.now(),
|
||||
function readDelay() {
|
||||
this.busy = false;
|
||||
finish(this.errorMask, 0);
|
||||
});
|
||||
|
||||
this.spinReel(inches);
|
||||
if (this.tapeInches < this.imgMaxInches) {
|
||||
@@ -1287,11 +1290,12 @@ B5500MagTapeDrive.prototype.write = function write(finish, buffer, length, mode,
|
||||
|
||||
this.imgWritten = true;
|
||||
this.buildErrorMask(residue);
|
||||
this.timer = setCallback(function writeDelay() {
|
||||
this.busy = false;
|
||||
finish(this.errorMask, count);
|
||||
}, this, (imgCount/this.charsPerSec + this.startStopTime)*1000 +
|
||||
this.initiateStamp - new Date().getTime());
|
||||
this.timer = setCallback(this.mnemonic, this,
|
||||
(imgCount/this.charsPerSec + this.startStopTime)*1000 + this.initiateStamp - performance.now(),
|
||||
function writeDelay() {
|
||||
this.busy = false;
|
||||
finish(this.errorMask, count);
|
||||
});
|
||||
|
||||
this.spinReel(inches);
|
||||
if (this.tapeInches < this.imgMaxInches) {
|
||||
@@ -1334,11 +1338,12 @@ B5500MagTapeDrive.prototype.erase = function erase(finish, length) {
|
||||
|
||||
this.imgWritten = true;
|
||||
this.buildErrorMask(0);
|
||||
this.timer = setCallback(function eraseDelay() {
|
||||
this.busy = false;
|
||||
finish(this.errorMask, 0);
|
||||
}, this, (length/this.charsPerSec + this.startStopTime)*1000 +
|
||||
this.initiateStamp - new Date().getTime());
|
||||
this.timer = setCallback(this.mnemonic, this,
|
||||
(length/this.charsPerSec + this.startStopTime)*1000 + this.initiateStamp - performance.now(),
|
||||
function eraseDelay() {
|
||||
this.busy = false;
|
||||
finish(this.errorMask, 0);
|
||||
});
|
||||
|
||||
this.spinReel(inches);
|
||||
if (this.tapeInches < this.imgMaxInches) {
|
||||
|
||||
@@ -57,7 +57,7 @@ IMG#TeletypeLogo {
|
||||
BUTTON.greenButton {
|
||||
position: absolute;
|
||||
background-color: #060;
|
||||
color: black;
|
||||
color: white;
|
||||
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
|
||||
font-size: 8pt;
|
||||
font-weight: normal;
|
||||
@@ -79,6 +79,7 @@ BUTTON.yellowButton {
|
||||
border-radius: 4px}
|
||||
|
||||
BUTTON.greenLit {
|
||||
color: black;
|
||||
background-color: #0F0}
|
||||
|
||||
BUTTON.yellowLit {
|
||||
|
||||
@@ -30,8 +30,8 @@ function B5500SPOUnit(mnemonic, unitIndex, designate, statusChange, signal) {
|
||||
this.signal = signal; // external function to call for special signals (e.g,. SPO input request)
|
||||
|
||||
this.initiateStamp = 0; // timestamp of last initiation (set by IOUnit)
|
||||
this.inTimer = null; // input setCallback() token
|
||||
this.outTimer = null; // output setCallback() token
|
||||
this.inTimer = 0; // input setCallback() token
|
||||
this.outTimer = 0; // output setCallback() token
|
||||
|
||||
this.clear();
|
||||
|
||||
@@ -134,7 +134,7 @@ B5500SPOUnit.prototype.setLocal = function setLocal() {
|
||||
this.buffer = null;
|
||||
this.bufLength = 0;
|
||||
this.bufIndex = 0;
|
||||
this.nextCharTime = new Date().getTime();
|
||||
this.nextCharTime = performance.now();
|
||||
this.finish = null;
|
||||
};
|
||||
|
||||
@@ -207,25 +207,25 @@ B5500SPOUnit.prototype.outputChar = function outputChar() {
|
||||
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 */
|
||||
var nextTime = this.nextCharTime + this.charPeriod;
|
||||
var delay = nextTime - new Date().getTime();
|
||||
var delay = nextTime - performance.now();
|
||||
|
||||
this.nextCharTime = nextTime;
|
||||
if (this.printCol < 72) { // print the character
|
||||
if (this.bufIndex < this.bufLength) {
|
||||
this.printChar(this.buffer[this.bufIndex]);
|
||||
this.bufIndex++;
|
||||
this.outTimer = setCallback(this.outputChar, this, delay);
|
||||
this.outTimer = setCallback(this.mnemonic, this, delay, this.outputChar);
|
||||
} else { // set up for the final CR/LF
|
||||
this.printCol = 72;
|
||||
this.outTimer = setCallback(this.outputChar, this, delay);
|
||||
this.outTimer = setCallback(this.mnemonic, this, delay, this.outputChar);
|
||||
}
|
||||
} else if (this.printCol == 72) { // delay to fake the output of a carriage-return
|
||||
this.printCol++;
|
||||
this.outTimer = setCallback(this.outputChar, this, delay+this.charPeriod);
|
||||
this.outTimer = setCallback(this.mnemonic, this, delay+this.charPeriod, this.outputChar);
|
||||
} else { // actually output the CR/LF
|
||||
this.appendEmptyLine();
|
||||
if (this.bufIndex < this.bufLength) {
|
||||
this.outTimer = setCallback(this.outputChar, this, delay);
|
||||
this.outTimer = setCallback(this.mnemonic, this, delay, this.outputChar);
|
||||
} else { // message text is exhausted
|
||||
this.finish(this.errorMask, this.bufLength); // report finish with any errors
|
||||
if (this.spoLocalRequested) {
|
||||
@@ -246,7 +246,7 @@ B5500SPOUnit.prototype.terminateInput = function terminateInput() {
|
||||
if (this.spoState == this.spoInput) {
|
||||
this.removeClass(this.$$("SPOReadyBtn"), "yellowLit");
|
||||
this.bufLength = this.bufIndex;
|
||||
this.nextCharTime = new Date().getTime();
|
||||
this.nextCharTime = performance.now();
|
||||
this.outputChar();
|
||||
}
|
||||
};
|
||||
@@ -271,7 +271,7 @@ B5500SPOUnit.prototype.keyPress = function keyPress(ev) {
|
||||
var delay;
|
||||
var index = this.bufLength;
|
||||
var nextTime;
|
||||
var stamp = new Date().getTime();
|
||||
var stamp = performance.now();
|
||||
|
||||
nextTime = (this.nextCharTime > stamp ? this.nextCharTime : stamp) + this.charPeriod;
|
||||
delay = nextTime - stamp;
|
||||
@@ -279,13 +279,13 @@ B5500SPOUnit.prototype.keyPress = function keyPress(ev) {
|
||||
if (this.spoState == this.spoInput) {
|
||||
if (c >= 32 && c < 126) {
|
||||
this.buffer[this.bufIndex++] = c = this.keyFilter[c];
|
||||
this.inTimer = setCallback(this.printChar, this, delay, c);
|
||||
this.inTimer = setCallback(this.mnemonic, this, delay, this.printChar, c);
|
||||
this.nextCharTime = nextTime;
|
||||
ev.preventDefault();
|
||||
}
|
||||
if (c == 126) { // "~" (B5500 group-mark)
|
||||
c = this.keyFilter[c];
|
||||
this.inTimer = setCallback(this.printChar, this, delay, c);
|
||||
this.inTimer = setCallback(this.mnemonic, this, delay, this.printChar, c);
|
||||
this.nextCharTime = nextTime + this.charPeriod;
|
||||
this.terminateInput();
|
||||
ev.preventDefault();
|
||||
@@ -293,7 +293,7 @@ B5500SPOUnit.prototype.keyPress = function keyPress(ev) {
|
||||
} else if (this.spoState == this.spoLocal) {
|
||||
if (c >= 32 && c <= 126) {
|
||||
c = this.keyFilter[c];
|
||||
this.inTimer = setCallback(this.printChar, this, delay, c);
|
||||
this.inTimer = setCallback(this.mnemonic, this, delay, this.printChar, c);
|
||||
this.nextCharTime = nextTime;
|
||||
ev.preventDefault();
|
||||
}
|
||||
@@ -306,7 +306,7 @@ B5500SPOUnit.prototype.keyDown = function keyDown(ev) {
|
||||
var c = ev.keyCode;
|
||||
var delay;
|
||||
var nextTime;
|
||||
var stamp = new Date().getTime();
|
||||
var stamp = performance.now();
|
||||
|
||||
nextTime = (this.nextCharTime > stamp ? this.nextCharTime : stamp) + this.charPeriod;
|
||||
delay = nextTime - stamp;
|
||||
@@ -330,7 +330,7 @@ B5500SPOUnit.prototype.keyDown = function keyDown(ev) {
|
||||
switch (this.spoState) {
|
||||
case this.spoInput:
|
||||
case this.spoLocal:
|
||||
this.inTimer = setCallback(this.backspaceChar, this, delay);
|
||||
this.inTimer = setCallback(this.mnemonic, this, delay, this.backspaceChar);
|
||||
this.nextCharTime = nextTime;
|
||||
ev.preventDefault();
|
||||
break;
|
||||
@@ -344,7 +344,7 @@ B5500SPOUnit.prototype.keyDown = function keyDown(ev) {
|
||||
ev.preventDefault();
|
||||
break;
|
||||
case this.spoLocal:
|
||||
this.inTimer = setCallback(this.appendEmptyLine, this, delay+this.charPeriod);
|
||||
this.inTimer = setCallback(this.mnemonic, this, this.charPeriod+delay, this.appendEmptyLine);
|
||||
this.nextCharTime = nextTime;
|
||||
ev.preventDefault();
|
||||
break;
|
||||
@@ -369,7 +369,7 @@ B5500SPOUnit.prototype.printText = function printText(msg, finish) {
|
||||
this.bufLength = length;
|
||||
this.bufIndex = 0;
|
||||
this.printCol = 0;
|
||||
this.nextCharTime = new Date().getTime();
|
||||
this.nextCharTime = performance.now();
|
||||
this.finish = finish;
|
||||
this.outputChar(); // start the printing process
|
||||
};
|
||||
@@ -456,7 +456,7 @@ B5500SPOUnit.prototype.read = function read(finish, buffer, length, mode, contro
|
||||
this.buffer = buffer;
|
||||
this.bufLength = length;
|
||||
this.bufIndex = 0;
|
||||
this.nextCharTime = new Date().getTime();
|
||||
this.nextCharTime = performance.now();
|
||||
this.finish = finish;
|
||||
this.window.focus();
|
||||
break;
|
||||
@@ -543,10 +543,10 @@ B5500SPOUnit.prototype.shutDown = function shutDown() {
|
||||
/* Shuts down the device */
|
||||
|
||||
if (this.inTimer) {
|
||||
clearTimeout(this.inTimer);
|
||||
clearCallback(this.inTimer);
|
||||
}
|
||||
if (this.outTimer) {
|
||||
clearTimeout(this.outTimer);
|
||||
clearCallback(this.outTimer);
|
||||
}
|
||||
this.window.removeEventListener("beforeunload", this.beforeUnload, false);
|
||||
this.window.close();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* Licensed under the MIT License, see
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
************************************************************************
|
||||
* B5500 universal function call-back module.
|
||||
* B5500 emulator universal function call-back module.
|
||||
*
|
||||
* Implements a combination setTimeout() and setImmediate() facility for the
|
||||
* B5500 emulator web-based user interface. setCallback() is used the same way
|
||||
@@ -19,24 +19,30 @@
|
||||
* about 15ms, unless you are running Google Chrome. This module will use
|
||||
* setTimeout() if the requested delay time is above a certain threshold, and
|
||||
* a setImmediate()-like mechanism (based on window.postMessage) if the requested
|
||||
* delay is above that threshold.
|
||||
* delay is below that threshold.
|
||||
*
|
||||
* Even though this mechanism may execute the call-back function sooner than the
|
||||
* requested delay specifies, the timing and throttling mechanisms in the
|
||||
* emulator will correct for that in subsequent delay cycles. We are going for
|
||||
* good average behavior, and quick call-backs are better than consistently
|
||||
* too-long callbacks in this environment, so that I/Os can be initiated and
|
||||
* their finish detected in finer-grained time increments.
|
||||
* To help compensate for the fact that the call-back function may be called
|
||||
* sooner than requested, and that due to either other activity or browser
|
||||
* limitations the delay may be longer than requested, the timing behavior of
|
||||
* setCallback() may be divided into "categories." For each category, a separate
|
||||
* record is kept of the exponential-moving-average difference between the
|
||||
* requested delay and the actual delay. This difference is used to adjust the
|
||||
* requested delay on subsequent calls in an attempt to smooth out the differences.
|
||||
* We are going for good average behavior here, and quick call-backs are better
|
||||
* than consistently too-long callbacks in this environment, so that I/Os can be
|
||||
* initiated and their finish detected in finer-grained time increments.
|
||||
*
|
||||
* The SetCallback mechanism defines two functions, which become members of the
|
||||
* global (window) object:
|
||||
*
|
||||
* cookie = setCallback(fcn, context, delay, args...)
|
||||
* cookie = setCallback(category, context, delay, fcn[, arg])
|
||||
*
|
||||
* Requests that the function "fcn" be called after "delay" milliseconds.
|
||||
* The function will be called as a method of "context", passing the
|
||||
* list of arguments "args...". The call-back "fcn" may be called
|
||||
* earlier or later than the specified delay. setCallBack returns a
|
||||
* The function will be called as a method of "context", passing a
|
||||
* single optional argument "arg". The call-back "fcn" may be called
|
||||
* earlier or later than the specified delay. The string "category" (which
|
||||
* may be empty, null, or undefined) defines the category under which the
|
||||
* average delay difference will be maintained. setCallBack returns a
|
||||
* numeric token identifying the call-back event, which can be used
|
||||
* with clearCallback(). Note that passing a string in lieu of a function
|
||||
* object is not permitted.
|
||||
@@ -51,34 +57,54 @@
|
||||
* David Baron's setZeroTimeout() implemenmentation described in his blog
|
||||
* at http://dbaron.org/log/20100309-faster-timeouts.
|
||||
*
|
||||
* Stole a little of their code, too.
|
||||
* I stole a little of their code, too.
|
||||
*
|
||||
************************************************************************
|
||||
* 2013-08-04 P.Kimpel
|
||||
* Original version, cloned from B5500DiskUnit.js.
|
||||
* 2014-04-05 P.Kimpel
|
||||
* Change calling sequence to add "category" parameter; reorder setCallback
|
||||
* parameters into a more reasonable sequence; implement call-back pooling.
|
||||
***********************************************************************/
|
||||
"use strict";
|
||||
|
||||
(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 minTimeout = 4; // minimum setTimeout() threshold, milliseconds
|
||||
var nextCookieNr = 1; // next setCallback cookie return value
|
||||
var pendingCallbacks = {}; // hash of pending callbacks, indexed by cookie as a string
|
||||
var secretPrefix = "com.google.code.p.retro-b5500.webUI." + new Date().getTime().toString(16);
|
||||
var perf = global.performance; // cached window.performance object
|
||||
var pool = []; // pool of reusable callback objects
|
||||
var poolLength = 0; // length of active entries in pool
|
||||
var secretPrefix = "com.google.code.p.retro-b5500.webUI." + Date.now().toString(16);
|
||||
|
||||
/**************************************/
|
||||
function activateCallback(cookieName) {
|
||||
function activateCallback(cookie) {
|
||||
/* Activates a callback after its delay period has expired */
|
||||
var category;
|
||||
var cookieName = cookie.toString();
|
||||
var endStamp = perf.now();
|
||||
var thisCallback;
|
||||
|
||||
if (cookieName in pendingCallbacks) {
|
||||
thisCallback = pendingCallbacks[cookieName];
|
||||
thisCallback = pendingCallbacks[cookieName];
|
||||
if (thisCallback) {
|
||||
delete pendingCallbacks[cookieName];
|
||||
try {
|
||||
thisCallback.fcn.apply(thisCallback.context, thisCallback.args);
|
||||
} catch (err) {
|
||||
console.log("B5500SetCallback.activateCallback: " + err);
|
||||
category = thisCallback.category;
|
||||
if (category) {
|
||||
delayDev[category] = (delayDev[category] || 0) +
|
||||
(endStamp - thisCallback.startStamp - thisCallback.delay)*(1.0-delayAlpha);
|
||||
}
|
||||
try {
|
||||
thisCallback.fcn.call(thisCallback.context, thisCallback.arg);
|
||||
} catch (err) {
|
||||
console.log("B5500SetCallback.activateCallback: " + err.name + ", " + err.message);
|
||||
}
|
||||
|
||||
thisCallback.context = null;
|
||||
thisCallback.fcn = null;
|
||||
pool[poolLength++] = thisCallback;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,45 +114,100 @@
|
||||
var cookieName = cookie.toString();
|
||||
var thisCallback;
|
||||
|
||||
if (cookieName in pendingCallbacks) {
|
||||
thisCallback = pendingCallbacks[cookieName];
|
||||
thisCallback = pendingCallbacks[cookieName];
|
||||
if (thisCallback) {
|
||||
delete pendingCallbacks[cookieName];
|
||||
if (thisCallback.cancelToken) {
|
||||
if (thisCallback.type == 2) {
|
||||
if (thisCallback.isTimeout) {
|
||||
if (thisCallback.cancelToken) {
|
||||
global.clearTimeout(thisCallback.cancelToken);
|
||||
}
|
||||
}
|
||||
|
||||
thisCallback.context = null;
|
||||
thisCallback.fcn = null;
|
||||
pool[poolLength++] = thisCallback;
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function setCallback(fcn, context, callbackDelay, args) {
|
||||
function _setCallback_Old(fcn, context, callbackDelay, arg) {
|
||||
/* Sets up and schedules a callback for function "fcn", called with context
|
||||
"context", after a delay of "delay" ms. Any "args" will be passed to "fcn".
|
||||
If the delay is less than "minTimeout", a setImmediate-like mechanism based on
|
||||
window.postsMessage() will be used; otherwise the environment's standard
|
||||
"context", after a delay of "delay" ms. An optional "arg" value will be passed
|
||||
to "fcn". If the delay is less than "minTimeout", a setImmediate-like mechanism
|
||||
based on window.postsMessage() will be used; otherwise the environment's standard
|
||||
setTimeout mechanism will be used */
|
||||
var delay = callbackDelay || 0;
|
||||
var cookie = nextCookieNr++;
|
||||
var cookieName = cookie.toString();
|
||||
var thisCallback = {
|
||||
args: null,
|
||||
fcn: fcn,
|
||||
context: context || this,
|
||||
delay: delay,
|
||||
};
|
||||
var thisCallback;
|
||||
|
||||
pendingCallbacks[cookieName] = thisCallback;
|
||||
if (arguments.length > 3) {
|
||||
thisCallback.args = Array.prototype.slice.call(arguments, 3);
|
||||
if (poolLength > 0) {
|
||||
thisCallback = pool[--poolLength];
|
||||
pool[poolLength] = null;
|
||||
} else {
|
||||
thisCallback = {};
|
||||
}
|
||||
|
||||
thisCallback.startStamp = perf.now();
|
||||
thisCallback.category = "NUL";
|
||||
thisCallback.context = context || this;
|
||||
thisCallback.delay = delay;
|
||||
thisCallback.fcn = fcn;
|
||||
thisCallback.arg = arg;
|
||||
pendingCallbacks[cookieName] = thisCallback;
|
||||
|
||||
if (delay < minTimeout) {
|
||||
thisCallback.type = 1;
|
||||
thisCallback.isTimeout = false;
|
||||
global.postMessage(secretPrefix + cookieName, "*");
|
||||
thisCallback.cancelToken = 0;
|
||||
} else {
|
||||
thisCallback.type = 2;
|
||||
thisCallback.cancelToken = global.setTimeout(activateCallback, delay, cookieName);
|
||||
thisCallback.isTimeout = true;
|
||||
thisCallback.cancelToken = global.setTimeout(activateCallback, delay, cookie);
|
||||
}
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function setCallback(category, context, callbackDelay, fcn, arg) {
|
||||
/* Sets up and schedules a callback for function "fcn", called with context
|
||||
"context", after a delay of "delay" ms. An optional "arg" value will be passed
|
||||
to "fcn". If the delay is less than "minTimeout", a setImmediate-like mechanism
|
||||
based on window.postsMessage() will be used; otherwise the environment's standard
|
||||
setTimeout mechanism will be used */
|
||||
var categoryName = (category || "NUL").toString();
|
||||
var cookie = nextCookieNr++;
|
||||
var cookieName = cookie.toString();
|
||||
var delay = callbackDelay || 0;
|
||||
var thisCallback;
|
||||
|
||||
// Allocate a call-back object from the pool.
|
||||
if (poolLength <= 0) {
|
||||
thisCallback = {};
|
||||
} else {
|
||||
thisCallback = pool[--poolLength];
|
||||
pool[poolLength] = null;
|
||||
}
|
||||
|
||||
// Fill in the object and tank it in pendingCallbacks.
|
||||
thisCallback.startStamp = perf.now();
|
||||
thisCallback.category = categoryName;
|
||||
thisCallback.context = context || this;
|
||||
thisCallback.delay = (delay < 0 ? 0 : delay);
|
||||
thisCallback.fcn = fcn;
|
||||
thisCallback.arg = arg;
|
||||
|
||||
pendingCallbacks[cookieName] = thisCallback;
|
||||
|
||||
// Decide whether to do a time wait or just a yield.
|
||||
if (delay >= minTimeout) {
|
||||
thisCallback.isTimeout = true;
|
||||
thisCallback.cancelToken = global.setTimeout(activateCallback,
|
||||
delay - (delayDev[categoryName] || 0), cookie);
|
||||
} else {
|
||||
thisCallback.isTimeout = false;
|
||||
global.postMessage(secretPrefix + cookieName, "*");
|
||||
thisCallback.cancelToken = 0;
|
||||
}
|
||||
|
||||
return cookie;
|
||||
@@ -135,16 +216,14 @@
|
||||
/**************************************/
|
||||
function onMessage(ev) {
|
||||
/* Handler for the global.onmessage event. Activates the callback */
|
||||
var cookieName;
|
||||
var payload;
|
||||
|
||||
if (ev.source === global) {
|
||||
// if (ev.source === global) {
|
||||
payload = ev.data.toString();
|
||||
if (payload.substring(0, secretPrefix.length) === secretPrefix) {
|
||||
cookieName = payload.substring(secretPrefix.length);
|
||||
activateCallback(cookieName);
|
||||
activateCallback(payload.substring(secretPrefix.length));
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
/********** Outer block of anonymous closure **********/
|
||||
|
||||
@@ -17,13 +17,14 @@ BODY {
|
||||
margin: 4px}
|
||||
|
||||
DIV#LogoDiv {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
background-color: #666;
|
||||
border-radius: 0.5em;
|
||||
width: 198px;
|
||||
height: 104px;
|
||||
right: 0;
|
||||
top: 0}
|
||||
height: 100px;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
z-index: -1}
|
||||
|
||||
DIV#BurroughsLogo {
|
||||
background-color: black;
|
||||
@@ -42,7 +43,7 @@ DIV#B5500Logo {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
position: absolute;
|
||||
top: 56px;
|
||||
bottom: 16px;
|
||||
right: 16px}
|
||||
|
||||
IMG#BurroughsLogoImage {
|
||||
@@ -88,10 +89,13 @@ TABLE.border>THEAD>TR>TH, TABLE.border>TBODY>TR>TD {
|
||||
border: 1px solid #CCCCCC}
|
||||
|
||||
TABLE#RegisterBank1 {
|
||||
background-color: white;
|
||||
position: relative}
|
||||
|
||||
TABLE#RegisterBank2 {
|
||||
background-color: white;
|
||||
position: absolute;
|
||||
z-index: +1;
|
||||
right: 0;
|
||||
top: 120px}
|
||||
|
||||
|
||||
@@ -10,13 +10,19 @@
|
||||
<script src="./B5500SetCallback.js"></script>
|
||||
|
||||
<script src="./B5500DummyUnit.js"></script>
|
||||
|
||||
<!-- Uncomment the following elements to enable I/O devices in the debugger.
|
||||
To halt/load the MCP, you will need at least the SPO and Disk. -->
|
||||
|
||||
<!--
|
||||
<script src="./B5500SPOUnit.js"></script>
|
||||
<script src="./B5500DiskUnit.js"></script>
|
||||
<script src="./B5500CardReader.js"></script>
|
||||
<script src="./B5500CardPunch.js"></script>
|
||||
<script src="./B5500DummyPrinter.js"></script>
|
||||
<script src="./B5500CardPunch.js"></script>
|
||||
<script src="./B5500DatacomUnit.js"></script>
|
||||
<script src="./B5500MagTapeDrive.js"></script>
|
||||
-->
|
||||
|
||||
<script src="../emulator/B5500SystemConfiguration.js"></script>
|
||||
<script src="../emulator/B5500CentralControl.js"></script>
|
||||
@@ -26,10 +32,6 @@
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
if (!window.indexedDB) { // for Safari, mostly
|
||||
window.indexedDB = window.webkitIndexedDB || window.mozIndexedDB;
|
||||
}
|
||||
|
||||
var runningCycles = 1000; // Number of instructions per run-mode interval
|
||||
|
||||
var cc;
|
||||
@@ -270,9 +272,9 @@ function setText(id, text) {
|
||||
function escapeHTML(text) {
|
||||
/* Returns "text" as escaped HTML */
|
||||
|
||||
function htmlFilter(char) {
|
||||
function htmlFilter(c) {
|
||||
/* Used to escape HTML-sensitive characters in a string */
|
||||
switch (char) {
|
||||
switch (c) {
|
||||
case "&":
|
||||
return "&";
|
||||
case "<":
|
||||
@@ -282,18 +284,18 @@ function escapeHTML(text) {
|
||||
case "\"":
|
||||
return """;
|
||||
default:
|
||||
return char;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
return text.replace(htmlMatch, htmlFilter);
|
||||
}
|
||||
|
||||
function padLeft(text, minLength, char) {
|
||||
/* Pads "text" on the left to a total length of "minLength" with "char" */
|
||||
function padLeft(text, minLength, c) {
|
||||
/* Pads "text" on the left to a total length of "minLength" with "c" */
|
||||
var s = text.toString();
|
||||
var len = s.length;
|
||||
var pad = char || " ";
|
||||
var pad = c || " ";
|
||||
|
||||
while (len++ < minLength) {
|
||||
s = pad + s;
|
||||
@@ -976,7 +978,7 @@ function runIt(ev) {
|
||||
} while (runSilently && stopAddress && px.C != stopAddress);
|
||||
|
||||
if (stopAddress && px.C != stopAddress) {
|
||||
setCallback(syllabicate, this, 0);
|
||||
setCallback(null, this, 0, syllabicate);
|
||||
if (!runSilently) {
|
||||
displaySystemState();
|
||||
}
|
||||
@@ -1101,7 +1103,7 @@ function fileLoader_onLoad(ev) {
|
||||
words + " words, last addr = @" + (addr+words-1).toString(8));
|
||||
} catch (e) {
|
||||
words = 0;
|
||||
alert("File load failed: " + e.toString());
|
||||
alert("File load failed: " + e.name + ", " + e.message);
|
||||
}
|
||||
if (words > 0) {
|
||||
px.preset(0x10); // execute from address @20
|
||||
@@ -1395,7 +1397,8 @@ window.onload = function() {
|
||||
<div id=BurroughsLogo>
|
||||
<img id=BurroughsLogoImage src="Burroughs-Logo-Neg.jpg">
|
||||
</div>
|
||||
<div id=B5500Logo>B 5500
|
||||
<div id=B5500Logo>
|
||||
<img id=RetroLogoImage src="retro-B5500-Logo.png" alt="retro-B5500 logo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
54
webUI/prototypes/B5500MagTapeAnimate.html
Normal file
54
webUI/prototypes/B5500MagTapeAnimate.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>B5500 Emulator Magnetic Tape Drive Animation</title>
|
||||
<meta name="Author" content="Nigel Williams & Paul Kimpel">
|
||||
<!-- 2014-04-06 Original version, cloned from B5500MagTapeDrive.html -->
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
<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="B5500MagTapeDrive.css">
|
||||
|
||||
<style>
|
||||
IMG#MTReel {
|
||||
visibility: visible;
|
||||
animation-name: spinReel;
|
||||
animation-duration: 1.5s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate}
|
||||
|
||||
@keyframes spinReel {
|
||||
from {transform: rotate(0)}
|
||||
to {transform: rotate(1030deg)}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id=MTDiv>
|
||||
<!--
|
||||
<button id=MTUnloadBtn class="blackButton">UNLOAD</button>
|
||||
<button id=MTLoadBtn class="blackButton">LOAD</button>
|
||||
<button id=MTLocalBtn class="yellowButton">LOCAL</button>
|
||||
<button id=MTRemoteBtn class="yellowButton">REMOTE</button>
|
||||
<button id=MTWriteRingBtn class="redButton">WRITE RING</button>
|
||||
<button id=MTRewindBtn class="blackButton">REWIND</button>
|
||||
-->
|
||||
|
||||
<img id=MTReel src="MagTapeReel.jpg">
|
||||
|
||||
<!--
|
||||
<span id=MTUnloadedLight class=annunciator>UNLOADED</span>
|
||||
<span id=MTAtBOTLight class=annunciator>AT BOT</span>
|
||||
<span id=MTAtEOTLight class=annunciator>AT EOT</span>
|
||||
<span id=MTRewindingLight class=annunciator>REWINDING</span>
|
||||
|
||||
<input id=MTFileName type=text size=60 READONLY>
|
||||
-->
|
||||
|
||||
<progress id=MTProgressBar min=0 max=100 value=0 title="Click to clear input hopper"></progress>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,244 +0,0 @@
|
||||
// Domenic Denicola's implementation of the proposed setImmediate() API.
|
||||
// Downloaded 2013-06-30 from https://github.com/NobleJS/setImmediate.
|
||||
|
||||
/* License:
|
||||
Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
(function (global, undefined) {
|
||||
"use strict";
|
||||
|
||||
var tasks = (function () {
|
||||
function Task(handler, args) {
|
||||
this.handler = handler;
|
||||
this.args = args;
|
||||
}
|
||||
Task.prototype.run = function () {
|
||||
// See steps in section 5 of the spec.
|
||||
if (typeof this.handler === "function") {
|
||||
// Choice of `thisArg` is not in the setImmediate spec; `undefined` is in the setTimeout spec though:
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html
|
||||
this.handler.apply(undefined, this.args);
|
||||
} else {
|
||||
var scriptSource = "" + this.handler;
|
||||
/*jshint evil: true */
|
||||
eval(scriptSource);
|
||||
}
|
||||
};
|
||||
|
||||
var nextHandle = 1; // Spec says greater than zero
|
||||
var tasksByHandle = {};
|
||||
var currentlyRunningATask = false;
|
||||
|
||||
return {
|
||||
addFromSetImmediateArguments: function (args) {
|
||||
var handler = args[0];
|
||||
var argsToHandle = Array.prototype.slice.call(args, 1);
|
||||
var task = new Task(handler, argsToHandle);
|
||||
|
||||
var thisHandle = nextHandle++;
|
||||
tasksByHandle[thisHandle] = task;
|
||||
return thisHandle;
|
||||
},
|
||||
runIfPresent: function (handle) {
|
||||
// From the spec: "Wait until any invocations of this algorithm started before this one have completed."
|
||||
// So if we're currently running a task, we'll need to delay this invocation.
|
||||
if (!currentlyRunningATask) {
|
||||
var task = tasksByHandle[handle];
|
||||
if (task) {
|
||||
currentlyRunningATask = true;
|
||||
try {
|
||||
task.run();
|
||||
} finally {
|
||||
delete tasksByHandle[handle];
|
||||
currentlyRunningATask = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
|
||||
// "too much recursion" error.
|
||||
global.setTimeout(function () {
|
||||
tasks.runIfPresent(handle);
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
remove: function (handle) {
|
||||
delete tasksByHandle[handle];
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
||||
function canUseNextTick() {
|
||||
// Don't get fooled by e.g. browserify environments.
|
||||
return typeof process === "object" &&
|
||||
Object.prototype.toString.call(process) === "[object process]";
|
||||
}
|
||||
|
||||
function canUseMessageChannel() {
|
||||
return !!global.MessageChannel;
|
||||
}
|
||||
|
||||
function canUsePostMessage() {
|
||||
// The test against `importScripts` prevents this implementation from being installed inside a web worker,
|
||||
// where `global.postMessage` means something completely different and can't be used for this purpose.
|
||||
|
||||
if (!global.postMessage || global.importScripts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var postMessageIsAsynchronous = true;
|
||||
var oldOnMessage = global.onmessage;
|
||||
global.onmessage = function () {
|
||||
postMessageIsAsynchronous = false;
|
||||
};
|
||||
global.postMessage("", "*");
|
||||
global.onmessage = oldOnMessage;
|
||||
|
||||
return postMessageIsAsynchronous;
|
||||
}
|
||||
|
||||
function canUseReadyStateChange() {
|
||||
return "document" in global && "onreadystatechange" in global.document.createElement("script");
|
||||
}
|
||||
|
||||
function installNextTickImplementation(attachTo) {
|
||||
attachTo.setImmediate = function () {
|
||||
var handle = tasks.addFromSetImmediateArguments(arguments);
|
||||
|
||||
process.nextTick(function () {
|
||||
tasks.runIfPresent(handle);
|
||||
});
|
||||
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
function installMessageChannelImplementation(attachTo) {
|
||||
var channel = new global.MessageChannel();
|
||||
channel.port1.onmessage = function (event) {
|
||||
var handle = event.data;
|
||||
tasks.runIfPresent(handle);
|
||||
};
|
||||
attachTo.setImmediate = function () {
|
||||
var handle = tasks.addFromSetImmediateArguments(arguments);
|
||||
|
||||
channel.port2.postMessage(handle);
|
||||
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
function installPostMessageImplementation(attachTo) {
|
||||
// Installs an event handler on `global` for the `message` event: see
|
||||
// * https://developer.mozilla.org/en/DOM/window.postMessage
|
||||
// * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
|
||||
|
||||
var MESSAGE_PREFIX = "com.bn.NobleJS.setImmediate" + Math.random();
|
||||
|
||||
function isStringAndStartsWith(string, putativeStart) {
|
||||
return typeof string === "string" && string.substring(0, putativeStart.length) === putativeStart;
|
||||
}
|
||||
|
||||
function onGlobalMessage(event) {
|
||||
// This will catch all incoming messages (even from other windows!), so we need to try reasonably hard to
|
||||
// avoid letting anyone else trick us into firing off. We test the origin is still this window, and that a
|
||||
// (randomly generated) unpredictable identifying prefix is present.
|
||||
if (event.source === global && isStringAndStartsWith(event.data, MESSAGE_PREFIX)) {
|
||||
var handle = event.data.substring(MESSAGE_PREFIX.length);
|
||||
tasks.runIfPresent(handle);
|
||||
}
|
||||
}
|
||||
if (global.addEventListener) {
|
||||
global.addEventListener("message", onGlobalMessage, false);
|
||||
} else {
|
||||
global.attachEvent("onmessage", onGlobalMessage);
|
||||
}
|
||||
|
||||
attachTo.setImmediate = function () {
|
||||
var handle = tasks.addFromSetImmediateArguments(arguments);
|
||||
|
||||
// Make `global` post a message to itself with the handle and identifying prefix, thus asynchronously
|
||||
// invoking our onGlobalMessage listener above.
|
||||
global.postMessage(MESSAGE_PREFIX + handle, "*");
|
||||
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
function installReadyStateChangeImplementation(attachTo) {
|
||||
attachTo.setImmediate = function () {
|
||||
var handle = tasks.addFromSetImmediateArguments(arguments);
|
||||
|
||||
// Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
|
||||
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
|
||||
var scriptEl = global.document.createElement("script");
|
||||
scriptEl.onreadystatechange = function () {
|
||||
tasks.runIfPresent(handle);
|
||||
|
||||
scriptEl.onreadystatechange = null;
|
||||
scriptEl.parentNode.removeChild(scriptEl);
|
||||
scriptEl = null;
|
||||
};
|
||||
global.document.documentElement.appendChild(scriptEl);
|
||||
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
function installSetTimeoutImplementation(attachTo) {
|
||||
attachTo.setImmediate = function () {
|
||||
var handle = tasks.addFromSetImmediateArguments(arguments);
|
||||
|
||||
global.setTimeout(function () {
|
||||
tasks.runIfPresent(handle);
|
||||
}, 0);
|
||||
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
if (!global.setImmediate) {
|
||||
// If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.
|
||||
var attachTo = typeof Object.getPrototypeOf === "function" && "setTimeout" in Object.getPrototypeOf(global) ?
|
||||
Object.getPrototypeOf(global)
|
||||
: global;
|
||||
|
||||
if (canUseNextTick()) {
|
||||
// For Node.js before 0.9
|
||||
installNextTickImplementation(attachTo);
|
||||
} else if (canUsePostMessage()) {
|
||||
// For non-IE10 modern browsers
|
||||
installPostMessageImplementation(attachTo);
|
||||
} else if (canUseMessageChannel()) {
|
||||
// For web workers, where supported
|
||||
installMessageChannelImplementation(attachTo);
|
||||
} else if (canUseReadyStateChange()) {
|
||||
// For IE 6–8
|
||||
installReadyStateChangeImplementation(attachTo);
|
||||
} else {
|
||||
// For older browsers
|
||||
installSetTimeoutImplementation(attachTo);
|
||||
}
|
||||
|
||||
attachTo.clearImmediate = tasks.remove;
|
||||
}
|
||||
}(typeof global === "object" && global ? global : this));
|
||||
Reference in New Issue
Block a user