diff --git a/emulator/B5500CentralControl.js b/emulator/B5500CentralControl.js index 91735cb..7dd301c 100644 --- a/emulator/B5500CentralControl.js +++ b/emulator/B5500CentralControl.js @@ -50,7 +50,7 @@ function B5500CentralControl() { 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 setTimeout id. + this.timer = null; // Reference to the RTC setCallback id. // Establish contexts for asynchronously-called methods this.boundTock = B5500CentralControl.bindMethod(this.tock, this); @@ -61,11 +61,10 @@ function B5500CentralControl() { /**************************************/ /* Global constants */ -B5500CentralControl.version = "0.12"; +B5500CentralControl.version = "0.13"; 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) -B5500CentralControl.minDelay = 4; // minimum setTimeout() delay, ms B5500CentralControl.rtcTick = 1000/60; // Real-time clock period, milliseconds B5500CentralControl.pow2 = [ // powers of 2 from 0 to 52 @@ -105,7 +104,7 @@ B5500CentralControl.mask2 = [ // (2**n)-1 For n From 0 to 52 // which is why they are in the range 17..47. The [0] dimension determines the index // when writing; the [1] dimension determines the index when reading. This approach // is necessary since some unit designates map to two different devices depending -// on IOD.[24:1], e.g. designate 14=CPA/CRA (status bits 23/24). +// on the read bit in IOD.[24:1], e.g. designate 14=CPA/CRA (status bits 23/24). B5500CentralControl.unitIndex = [ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @@ -166,7 +165,7 @@ B5500CentralControl.prototype.clear = function clear() { real-time clock */ if (this.timer) { - clearTimeout(this.timer); + clearCallback(this.timer); this.timer = null; } @@ -572,12 +571,7 @@ B5500CentralControl.prototype.tock = function tock() { } } interval = (this.nextTimeStamp += B5500CentralControl.rtcTick) - thisTime; - if (interval >= B5500CentralControl.minDelay) { - this.timer = setTimeout(this.boundTock, interval); - } else { - this.timer = null; - setImmediate(this.boundTock); - } + this.timer = setCallback(this.boundTock, this, interval); }; /**************************************/ @@ -734,7 +728,7 @@ B5500CentralControl.prototype.halt = function halt() { /* Halts the processors. Any in-process I/Os are allowed to complete */ if (this.timer) { - clearTimeout(this.timer); + clearCallback(this.timer); this.timer = null; } @@ -1014,13 +1008,12 @@ B5500CentralControl.prototype.powerOn = function powerOn() { B5500CentralControl.prototype.powerOff = function powerOff() { /* Powers down the system and deallocates the hardware modules. Redundant power-offs are ignored. */ - var that = this; function shutDown() { var x; if (this.timer) { - clearTimeout(this.timer); + clearCallback(this.timer); this.timer = null; } @@ -1050,8 +1043,6 @@ B5500CentralControl.prototype.powerOff = function powerOff() { if (this.poweredUp) { this.halt(); // Wait a little while for I/Os, etc., to finish - setTimeout(function powerOffAnon() { - shutDown.call(that); - }, 1000) + setCallback(shutDown, this, 1000); } }; diff --git a/emulator/B5500IOUnit.js b/emulator/B5500IOUnit.js index a2d1fff..8cd8aad 100644 --- a/emulator/B5500IOUnit.js +++ b/emulator/B5500IOUnit.js @@ -45,7 +45,7 @@ function B5500IOUnit(ioUnitID, cc) { this.ioUnitID = ioUnitID; // I/O Unit ID ("1", "2", "3", or "4") this.cc = cc; // Reference back to Central Control module - this.forkHandle = null; // Reference to current setImmediate id + this.forkHandle = null; // Reference to current setCallback id this.accessor = { // Memory access control block requestorID: ioUnitID, // Memory requestor ID addr: 0, // Memory address @@ -181,7 +181,7 @@ B5500IOUnit.prototype.clear = function clear() { this.ioUnitSlack = 0; // Total I/O Unit throttling delay, milliseconds if (this.forkHandle) { - clearImmediate(this.forkHandle); + clearCallback(this.forkHandle); } }; @@ -733,7 +733,7 @@ B5500IOUnit.prototype.forkIO = function forkIO() { var u; // peripheral unit object var x; // temp number variable - this.forkHandle = null; // clear the setImmediate() handle + this.forkHandle = null; // clear the setCallback() handle x = this.D; // explode the D-register into its fields this.Dunit = (x%0x200000000000 - x%0x10000000000)/0x10000000000; // [3:5] @@ -863,7 +863,7 @@ B5500IOUnit.prototype.initiate = function initiate() { } else { this.D31F = 0; // reset the IOD-fetch error condition this.D = this.W; - this.forkHandle = setImmediate(this.boundForkIO); + this.forkHandle = setCallback(this.boundForkIO, this, 0); } } }; diff --git a/emulator/B5500Processor.js b/emulator/B5500Processor.js index 0b08559..c79a5cb 100644 --- a/emulator/B5500Processor.js +++ b/emulator/B5500Processor.js @@ -34,8 +34,7 @@ function B5500Processor(procID, cc) { this.processorID = procID; // Processor ID ("A" or "B") this.cc = cc; // Reference back to Central Control module - this.schedImmediate = null; // Reference to current setImmediate token - this.schedTimeout = null; // Reference to current setTimeout token + this.scheduler = null; // Reference to current setCallback token this.accessor = { // Memory access control block requestorID: procID, // Memory requestor ID addr: 0, // Memory address @@ -45,18 +44,16 @@ function B5500Processor(procID, cc) { MAED: 0 // Truthy if memory address/inhibit error }; - // Establish context for asynchronously-called methods - this.boundSchedule = B5500CentralControl.bindMethod(this.schedule, this); - this.clear(); // Create and initialize the processor state - this.delayDeltaAvg = 0; // Average difference between requested and actual setTimeout() delays, ms - this.delayLastStamp = 0; // Timestamp of last setTimeout() delay, ms - this.delayRequested = 0; // Last requested setTimeout() delay, ms + this.delayDeltaAvg = 0; // Average difference between requested and actual setCallback() delays, ms + this.delayLastStamp = 0; // Timestamp of last setCallback() delay, ms + this.delayRequested = 0; // Last requested setCallback() delay, ms } /**************************************/ +B5500Processor.cyclesPerMilli = 1000; // clock cycles per millisecond (1000 = 1.0 MHz) B5500Processor.timeSlice = 4000; // this.run() timeslice, clocks B5500Processor.delaySamples = 1000; // this.delayDeltaAvg sampling average basis @@ -1294,7 +1291,6 @@ B5500Processor.prototype.storeForInterrupt = function storeForInterrupt(forTest) this.T = 0x89; // inject 0211=ITI into T register } else { this.stop(); // idle the processor - this.cc.HP2F = 1; this.cc.P2BF = 0; // tell P1 we've stopped } this.CWMF = 0; @@ -1311,7 +1307,6 @@ B5500Processor.prototype.storeForInterrupt = function storeForInterrupt(forTest) this.V = 0; } else { this.stop(); // idle the processor - this.cc.HP2F = 1; this.cc.P2BF = 0; // tell P1 we've stopped } } @@ -1341,7 +1336,7 @@ B5500Processor.prototype.start = function start() { this.procTime -= stamp; this.delayLastStamp = stamp; this.delayRequested = 0; - this.schedImmediate = setImmediate(this.boundSchedule, 1); + this.scheduler = setCallback(this.schedule, this, 0); }; /**************************************/ @@ -1354,13 +1349,9 @@ B5500Processor.prototype.stop = function stop() { this.PROF = 0; this.busy = 0; this.cycleLimit = 0; // exit this.run() - if (this.schedImmediate) { - clearImmediate(this.schedImmediate); - this.schedImmediate = null; - } - if (this.schedTimeout) { - clearTimeout(this.schedTimeout); - this.schedTimeout = null; + if (this.scheduler) { + clearCallback(this.scheduler); + this.scheduler = null; } while (this.procTime < 0) { this.procTime += stamp; @@ -1463,7 +1454,9 @@ B5500Processor.prototype.initiate = function initiate(forTest) { this.T = this.cc.fieldIsolate(this.P, this.L*12, 12); this.TROF = 1; - if (forTest) { + if (!forTest) { + this.NCSF = 1; + } else { this.NCSF = (this.TM >>> 4) & 0x01; this.CCCF = (this.TM >>> 5) & 0x01; this.MWOF = (this.TM >>> 6) & 0x01; @@ -1472,9 +1465,6 @@ B5500Processor.prototype.initiate = function initiate(forTest) { if (!this.CCCF) { this.TM |= 0x80; } - } else { - this.NCSF = 1; - this.busy = 1; } }; @@ -1483,12 +1473,12 @@ B5500Processor.prototype.initiateAsP2 = function initiateAsP2() { /* Called from Central Control to initiate the processor as P2. Fetches the INCW from @10 and calls initiate() */ + this.NCSF = 0; // make sure P2 is in control state to execute the IP1 & access low mem this.M = 0x08; // address of the INCW this.loadBviaM(); // B = [M] this.AROF = 0; // make sure A is invalid this.T = 0x849; // inject 4111=IP1 into P2's T register this.TROF = 1; - this.NCSF = 0; // make sure P2 is in control state to execute the IP1 // Now start scheduling P2 on the Javascript thread this.start(); @@ -3024,7 +3014,7 @@ B5500Processor.prototype.run = function run() { switch (variant) { case 0x14: // 2411: ZPI=Conditional Halt if (this.US14X) { // STOP OPERATOR switch on - this.busy = 0; + this.stop(); this.cycleLimit = 0; // exit this.run() } break; @@ -3745,12 +3735,13 @@ B5500Processor.prototype.run = function run() { case 0x12: // 2211: HP2=Halt Processor 2 if (!this.NCSF) { // control-state only this.cc.haltP2(); + this.cycleLimit = 0; // give P2 a chance to clean up } break; case 0x14: // 2411: ZPI=Conditional Halt if (this.US14X) { // STOP OPERATOR switch on - this.busy = 0; + this.stop(); this.cycleLimit = 0; // exit this.run() } break; @@ -4582,7 +4573,7 @@ B5500Processor.prototype.run = function run() { }; /**************************************/ -B5500Processor.prototype.schedule = function schedule(which) { +B5500Processor.prototype.schedule = function schedule() { /* Schedules the processor running time and attempts to throttle performance to approximate that of a real B5500 -- well, at least we hope this will run fast enough that the performance will need to be throttled. It establishes @@ -4597,17 +4588,11 @@ B5500Processor.prototype.schedule = function schedule(which) { var delayTime; // delay from/until next run() for this processor, ms var runTime; // real-world processor running time, ms - if (which) { - this.schedImmediate = null; - } else { - this.schedTimeout = null; - } + this.scheduler = null; delayTime = clockOff - this.delayLastStamp; this.procSlack += delayTime; - if (this.delayRequested) { - this.delayDeltaAvg = (this.delayDeltaAvg*(B5500Processor.delaySamples-1) + - delayTime - this.delayRequested)/B5500Processor.delaySamples; - } + this.delayDeltaAvg = (this.delayDeltaAvg*(B5500Processor.delaySamples-1) + + delayTime - this.delayRequested)/B5500Processor.delaySamples; if (this.busy) { this.cycleLimit = B5500Processor.timeSlice; @@ -4625,20 +4610,15 @@ B5500Processor.prototype.schedule = function schedule(which) { runTime += clockOff; } - delayTime = this.totalCycles/1000 - runTime; + delayTime = this.totalCycles/B5500Processor.cyclesPerMilli - runTime; // delayTime is the number of milliseconds the processor is running ahead of - // real-world time. Web browsers have a certain minimum delay. If the delay - // is less than our estimate of that minimum, we yield to the event loop but - // otherwise continue (real time should eventually catch up -- we hope). If the + // real-world time. Web browsers have a certain minimum setTimeout() delay. If the + // delay is less than our estimate of that minimum, we yield to the event loop + // but otherwise continue (real time should eventually catch up -- we hope). If the // delay is greater than the minimum, we reschedule ourselves after that delay. - if (delayTime < B5500CentralControl.minDelay) { - this.delayRequested = 0; - this.schedImmediate = setImmediate(this.boundSchedule, 1); // just yield to the event loop - } else { - this.delayRequested = delayTime; - this.schedTimeout = setTimeout(this.boundSchedule, delayTime, 0); - } + this.delayRequested = delayTime; + this.scheduler = setCallback(this.schedule, this, delayTime); } } }; diff --git a/webUI/B5500CardPunch.css b/webUI/B5500CardPunch.css index bc5d49b..1493bc1 100644 --- a/webUI/B5500CardPunch.css +++ b/webUI/B5500CardPunch.css @@ -25,8 +25,8 @@ DIV#CPDiv { position: relative; color: white; background-color: #666; - width: 700px; - height: 300px; + width: 550px; + height: 194px; border: 1px solid black; border-radius: 8px; padding: 0; @@ -36,7 +36,7 @@ BUTTON.greenButton { background-color: #060; color: white; font-family: Arial Rounded, Arial, Helvetica, sans-serif; - font-size: 10px; + font-size: 8pt; font-weight: bold; width: 60px; height: 40px; @@ -47,7 +47,7 @@ BUTTON.blackButton { background-color: black; color: white; font-family: Arial Rounded, Arial, Helvetica, sans-serif; - font-size: 10px; + font-size: 8pt; font-weight: bold; width: 60px; height: 40px; @@ -58,7 +58,7 @@ BUTTON.redButton { background-color: #900; color: white; font-family: Arial Rounded, Arial, Helvetica, sans-serif; - font-size: 10px; + font-size: 8pt; font-weight: bold; width: 60px; height: 40px; @@ -96,44 +96,44 @@ BUTTON.redLit { top: 56px; left: 8px; right: 8px; - height: 160px; + height: 80px; font-weight: bold} #CPStacker1Bar { border: 1px solid white; - width: 610px} + width: 450px} #CPStacker1Frame { width: 100%; - height: 140px; + height: 60px; margin-top: 1px; border: 1px solid white; color: black; background-color: white; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace; - font-size: 10pt; + font-size: 8pt; font-weight: normal} #CPStacker2Div { position: absolute; - top: 220px; + top: 140px; left: 8px; right: 8px; - height: 70px; + height: 44px; font-weight: bold} #CPStacker2Bar { border: 1px solid white; - width: 610px} + width: 450px} #CPStacker2Frame { width: 100%; - height: 50px; + height: 30px; margin-top: 1px; border: 1px solid white; color: black; background-color: white; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace; - font-size: 10pt; + font-size: 8pt; font-weight: normal} diff --git a/webUI/B5500CardPunch.js b/webUI/B5500CardPunch.js index 0656675..a2e6c27 100644 --- a/webUI/B5500CardPunch.js +++ b/webUI/B5500CardPunch.js @@ -42,7 +42,7 @@ function B5500CardPunch(mnemonic, unitIndex, designate, statusChange, signal) { this.stacker2 = null; this.endOfStacker2 = null; this.window = window.open("/B5500/webUI/B5500CardPunch.html", mnemonic, - "scrollbars=no,resizable,width=700,height=500"); + "scrollbars=no,resizable,width=560,height=204,left=0,top=220"); this.window.addEventListener("load", function windowLoad() { that.punchOnload(); }, false); @@ -199,8 +199,8 @@ B5500CardPunch.prototype.punchOnload = function punchOnload() { this.stacker1Frame = this.$$("CPStacker1Frame"); this.stacker1Frame.contentDocument.head.innerHTML += ""; this.stacker1 = this.doc.createElement("pre"); this.stacker1Frame.contentDocument.body.appendChild(this.stacker1); @@ -209,18 +209,14 @@ B5500CardPunch.prototype.punchOnload = function punchOnload() { this.stacker2Frame = this.$$("CPStacker2Frame"); this.stacker2Frame.contentDocument.head.innerHTML += ""; this.stacker2 = this.doc.createElement("pre"); this.stacker2Frame.contentDocument.body.appendChild(this.stacker2); this.endOfStacker2 = this.doc.createElement("div"); this.stacker2Frame.contentDocument.body.appendChild(this.endOfStacker2); - this.window.moveTo(0, 180); - this.window.resizeTo(this.window.outerWidth+this.$$("CPDiv").scrollWidth-this.window.innerWidth+12, - this.window.outerHeight+this.$$("CPDiv").scrollHeight-this.window.innerHeight+12); - this.window.addEventListener("beforeunload", this.beforeUnload, false); this.armRunout(false); diff --git a/webUI/B5500CardReader.css b/webUI/B5500CardReader.css index a21a6c2..3cee1a5 100644 --- a/webUI/B5500CardReader.css +++ b/webUI/B5500CardReader.css @@ -18,7 +18,7 @@ BODY { DIV#CRDiv { position: relative; background-color: #666; - width: 700px; + width: 550px; height: 150px; border: 1px solid black; border-radius: 8px; @@ -29,7 +29,7 @@ BUTTON.greenButton { background-color: #060; color: white; font-family: Arial Rounded, Arial, Helvetica, sans-serif; - font-size: 10px; + font-size: 8pt; font-weight: bold; width: 60px; height: 40px; @@ -40,7 +40,7 @@ BUTTON.blackButton { background-color: black; color: white; font-family: Arial Rounded, Arial, Helvetica, sans-serif; - font-size: 10px; + font-size: 8pt; font-weight: bold; width: 60px; height: 40px; @@ -51,7 +51,7 @@ BUTTON.redButton { background-color: #900; color: white; font-family: Arial Rounded, Arial, Helvetica, sans-serif; - font-size: 10px; + font-size: 8pt; font-weight: bold; width: 60px; height: 40px; @@ -59,7 +59,7 @@ BUTTON.redButton { border-radius: 4px} BUTTON.greenLit { - background-color: green} + background-color: #0F0} BUTTON.redLit { background-color: #F00} @@ -88,14 +88,14 @@ BUTTON.redLit { position: absolute; border: 1px solid white; color: white; - width: 680px; + width: 530px; top: 54px; left: 8px} #CRProgressBar { position: absolute; border: 1px solid white; - width: 680px; + width: 530px; top: 84px; left: 8px} @@ -103,7 +103,7 @@ BUTTON.redLit { position: absolute; top: 106px; left: 8px; - width: 680px; + width: 530px; height: 35px; border: 1px solid white; color: black; diff --git a/webUI/B5500CardReader.js b/webUI/B5500CardReader.js index 5916464..b353b91 100644 --- a/webUI/B5500CardReader.js +++ b/webUI/B5500CardReader.js @@ -19,6 +19,7 @@ function B5500CardReader(mnemonic, unitIndex, designate, statusChange, signal) { /* Constructor for the CardReader object */ var that = this; + var x = (mnemonic == "CRA" ? 0 : 30); this.mnemonic = mnemonic; // Unit mnemonic this.unitIndex = unitIndex; // Ready-mask bit number @@ -38,7 +39,7 @@ function B5500CardReader(mnemonic, unitIndex, designate, statusChange, signal) { } this.doc = null; this.window = window.open("/B5500/webUI/B5500CardReader.html", mnemonic, - "scrollbars=no,resizable,width=700,height=150"); + "scrollbars=no,resizable,width=560,height=160,left="+x+",top="+x); this.window.addEventListener("load", function windowLoad() { that.readerOnload(); }, false); @@ -320,19 +321,14 @@ B5500CardReader.prototype.beforeUnload = function beforeUnload(ev) { B5500CardReader.prototype.readerOnload = function readerOnload() { /* Initializes the reader window and user interface */ var that = this; - var x = (this.mnemonic == "CRA" ? 0 : this.window.outerWidth + 16); this.doc = this.window.document; this.doc.title = "retro-B5500 " + this.mnemonic; - this.window.moveTo(x, 0); - this.window.resizeTo(this.window.outerWidth+this.$$("CRDiv").scrollWidth-this.window.innerWidth+12, - this.window.outerHeight+this.$$("CRDiv").scrollHeight-this.window.innerHeight+12); - this.outHopperFrame = this.$$("CROutHopperFrame"); this.outHopperFrame.contentDocument.head.innerHTML += ""; this.outHopper = this.doc.createElement("pre"); this.outHopperFrame.contentDocument.body.appendChild(this.outHopper); diff --git a/webUI/B5500ColdLoader.html b/webUI/B5500ColdLoader.html index b4c3597..6e767ad 100644 --- a/webUI/B5500ColdLoader.html +++ b/webUI/B5500ColdLoader.html @@ -1261,7 +1261,10 @@ window.addEventListener("load", function() { row.appendChild(cell); // File ID cell = document.createElement("td"); - cell.appendChild(document.createTextNode(tapeDir[x])); + e = document.createElement("label"); + e.appendChild(document.createTextNode(tapeDir[x])); + e.htmlFor = "File_" + x; + cell.appendChild(e); row.appendChild(cell); // Load as MCP selection radio button cell = document.createElement("td"); @@ -2037,7 +2040,8 @@ window.addEventListener("load", function() { if (!checkBrowser()) { $$("FileSelector").addEventListener("change", fileSelector_onChange, false); $$("ColdstartBtn").addEventListener("click", function(ev) { - if (confirm("Are you sure you want to do a COLD START?")) { + if (confirm("Are you sure you want to do a COLD START?\n" + + "This will PERMANENTLY DELETE all files in the B5500 disk subsystem.")) { initializeDisk(); } }, false); diff --git a/webUI/B5500Console.css b/webUI/B5500Console.css index f2233f8..6cca70f 100644 --- a/webUI/B5500Console.css +++ b/webUI/B5500Console.css @@ -48,7 +48,7 @@ DIV#RetroVersion { right: 170px; color: white; font-family: Arial Rounded, Arial, Helvetica, sans-serif; - font-size: x-small; + font-size: 7pt; font-weight: bold} IMG#BurroughsLogoImage { @@ -65,7 +65,31 @@ BUTTON.whiteButton { background-color: #CCC; color: black; font-family: Arial Rounded, Arial, Helvetica, sans-serif; - font-size: x-small; + font-size: 8pt; + font-weight: bold; + width: 60px; + height: 40px; + border: 1px solid #DDD; + border-radius: 4px} + +BUTTON.greenButton { + position: absolute; + background-color: #060; + color: black; + font-family: Arial Rounded, Arial, Helvetica, sans-serif; + font-size: 8pt; + font-weight: bold; + width: 60px; + height: 40px; + border: 1px solid #DDD; + border-radius: 4px} + +BUTTON.redButton { + position: absolute; + background-color: #900; + color: white; + font-family: Arial Rounded, Arial, Helvetica, sans-serif; + font-size: 8pt; font-weight: bold; width: 60px; height: 40px; @@ -77,7 +101,7 @@ BUTTON.blackButton { background-color: black; color: #999; font-family: Arial Rounded, Arial, Helvetica, sans-serif; - font-size: x-small; + font-size: 8pt; font-weight: bold; width: 60px; height: 40px; @@ -89,7 +113,7 @@ BUTTON.yellowButton { background-color: #990; color: black; font-family: Arial Rounded, Arial, Helvetica, sans-serif; - font-size: x-small; + font-size: 8pt; font-weight: bold; width: 60px; height: 40px; @@ -102,6 +126,12 @@ BUTTON.whiteLit { BUTTON.blackLit { color: #FFF} +BUTTON.greenLit { + background-color: #0F0} + +BUTTON.redLit { + background-color: #F00} + BUTTON.yellowLit { background-color: #FF0} BUTTON.yellowLit5 { @@ -132,7 +162,7 @@ BUTTON#NotReadyBtn { top: 31px; left: 132px} -BUTTON#LoadSelectBtn { +BUTTON#MemoryCheckBtn { top: 31px; left: 202px} @@ -140,7 +170,8 @@ BUTTON#LoadBtn { top: 31px; left: 272px} -BUTTON#MemoryCheckBtn { +BUTTON#LoadSelectBtn { + line-height: 100%; top: 31px; left: 372px} @@ -171,9 +202,10 @@ BUTTON#PowerOffBtn { TABLE#CentralControl { position: absolute; bottom: 0; + width: 100%; color: #666; font-family: Arial Rounded, Arial, Helvetica, sans-serif; - font-size: x-small; + font-size: 7pt; font-weight: bold} COL.AnnunciatorCol { @@ -182,6 +214,9 @@ COL.AnnunciatorCol { TD#procDelay, TD#procSlack { color: white; text-align: right} +TD.statLabel { + color: white; + text-align: left} .busy { diff --git a/webUI/B5500Console.html b/webUI/B5500Console.html index 737e67e..7ed43c4 100644 --- a/webUI/B5500Console.html +++ b/webUI/B5500Console.html @@ -7,7 +7,7 @@ - + @@ -68,11 +68,12 @@ window.addEventListener("load", function() { } function PowerOnBtn_Click(ev) { - $$("PowerOnBtn").className = "whiteButton whiteLit"; - $$("AControlBtn").className = "yellowButton yellowLit"; + $$("PowerOnBtn").className = "greenButton greenLit"; + $$("HaltBtn").className = "redButton redLit"; cc.powerOn(); $$("PowerOnBtn").disabled = true; $$("PowerOffBtn").disabled = false; + $$("LoadSelectBtn").disabled = false; $$("LoadBtn").disabled = false; $$("HaltBtn").disabled = true; boundBlinkenlicht(); @@ -82,15 +83,21 @@ window.addEventListener("load", function() { } function PowerOffBtn_Click(ev) { - $$("PowerOnBtn").className = "whiteButton"; + $$("PowerOnBtn").className = "greenButton"; $$("ANormalBtn").className = "yellowButton"; $$("AControlBtn").className = "yellowButton"; $$("BNormalBtn").className = "yellowButton"; + $$("BControlBtn").className = "yellowButton"; + $$("LoadSelectBtn").className = "yellowButton"; + $$("MemoryCheckBtn").className = "redButton"; + $$("NotReadyBtn").className = "whiteButton"; + $$("HaltBtn").className = "redButton"; cc.powerOff(); $$("PowerOnBtn").disabled = false; $$("PowerOffBtn").disabled = true; - $$("HaltBtn").disabled = true; + $$("LoadSelectBtn").disabled = true; $$("LoadBtn").disabled = true; + $$("HaltBtn").disabled = true; if (timer) { clearTimeout(timer); timer = null; @@ -99,6 +106,7 @@ window.addEventListener("load", function() { } function HaltBtn_Click(ev) { + $$("HaltBtn").className = "redButton redLit"; cc.halt(); $$("HaltBtn").disabled = true; $$("LoadBtn").disabled = false; @@ -110,6 +118,7 @@ window.addEventListener("load", function() { result = cc.load(false); switch (result) { case 0: // load initiated successfully + $$("HaltBtn").className = "redButton"; $$("HaltBtn").disabled = false; $$("LoadBtn").disabled = true; break; @@ -131,10 +140,10 @@ window.addEventListener("load", function() { function LoadSelectBtn_Click(ev) { if (cc.cardLoadSelect) { cc.cardLoadSelect = 0; - $$("LoadSelectBtn").className = "blackButton blackLit silverBorder"; + $$("LoadSelectBtn").className = "yellowButton"; } else { cc.cardLoadSelect = 1; - $$("LoadSelectBtn").className = "blackButton blackLit yellowBorder"; + $$("LoadSelectBtn").className = "yellowButton yellowLit"; } } @@ -340,7 +349,7 @@ window.addEventListener("load", function() { while (et < 0) { et += stamp; } - procDelay.innerHTML = p1.delayDeltaAvg.toFixed(3); + procDelay.innerHTML = p1.delayDeltaAvg.toFixed(1); procSlack.innerHTML = (p1.procSlack/et*100).toFixed(1) + "%"; if (showAnnunciators) { @@ -418,19 +427,19 @@ window.addEventListener("load", function() {
| DK2F | - | P1 Slack + | P1 Slack | |||||||||||||
| DRA | DRB @@ -506,7 +515,7 @@ window.addEventListener("load", function() { | MTS | MTT | - | P1 Delay + | P1 Delay | ||||||||||
+
-
+
diff --git a/webUI/B5500SPOUnit.js b/webUI/B5500SPOUnit.js
index db2fa76..e10ee39 100644
--- a/webUI/B5500SPOUnit.js
+++ b/webUI/B5500SPOUnit.js
@@ -19,7 +19,6 @@
/**************************************/
function B5500SPOUnit(mnemonic, unitIndex, designate, statusChange, signal) {
/* Constructor for the SPOUnit object */
- var that = this;
this.maxScrollLines = 500; // Maximum amount of printer scrollback
this.charPeriod = 100; // Printer speed, milliseconds per character
@@ -31,15 +30,11 @@ 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 setTimeout() token
- this.outTimer = null; // output setTimeout() token
+ this.inTimer = null; // input setCallback() token
+ this.outTimer = null; // output setCallback() token
this.clear();
- this.backspaceChar.that = this; // Store object context for these functions
- this.printChar.that = this;
- this.outputChar.that = this;
-
this.window = window.open("", mnemonic);
if (this.window) {
this.shutDown(); // destroy the previously-existing window
@@ -49,10 +44,9 @@ function B5500SPOUnit(mnemonic, unitIndex, designate, statusChange, signal) {
this.paper = null;
this.endOfPaper = null;
this.window = window.open("/B5500/webUI/B5500SPOUnit.html", mnemonic,
- "scrollbars,resizable,width=600,height=500");
- this.window.addEventListener("load", function windowOnLoad() {
- that.spoOnload();
- }, false);
+ "scrollbars,resizable,width=758,height=508");
+ this.window.moveTo(screen.availWidth-this.window.outerWidth, screen.availHeight-this.window.outerHeight);
+ this.window.addEventListener("load", B5500CentralControl.bindMethod(B5500SPOUnit.prototype.spoOnload, this), false);
}
// this.spoState enumerations
@@ -130,21 +124,20 @@ B5500SPOUnit.prototype.removeClass = function removeClass(e, name) {
B5500SPOUnit.prototype.setLocal = function setLocal() {
/* Sets the status of the SPO to Local */
- if (this.spoState == this.spoRemote) {
- this.spoState = this.spoLocal;
- this.addClass(this.$$("SPOLocalBtn"), "yellowLit");
- this.removeClass(this.$$("SPORemoteBtn"), "yellowLit");
- this.removeClass(this.$$("SPOInputRequestBtn"), "yellowLit");
- this.statusChange(0);
+ this.spoLocalRequested = false;
+ this.spoState = this.spoLocal;
+ this.addClass(this.$$("SPOLocalBtn"), "yellowLit");
+ this.removeClass(this.$$("SPORemoteBtn"), "yellowLit");
+ this.removeClass(this.$$("SPOInputRequestBtn"), "yellowLit");
+ this.statusChange(0);
- // Set up to echo characters from the keyboard
- this.buffer = null;
- this.bufLength = 0;
- this.bufIndex = 0;
- this.printCol = 0;
- this.nextCharTime = new Date().getTime();
- this.finish = null;
- }
+ // Set up to echo characters from the keyboard
+ this.buffer = null;
+ this.bufLength = 0;
+ this.bufIndex = 0;
+ this.printCol = 0;
+ this.nextCharTime = new Date().getTime();
+ this.finish = null;
};
/**************************************/
@@ -177,14 +170,13 @@ B5500SPOUnit.prototype.appendEmptyLine = function appendEmptyLine() {
/**************************************/
B5500SPOUnit.prototype.backspaceChar = function backspaceChar() {
/* Handles backspace for SPO input */
- var that = backspaceChar.that;
- var line = that.paper.lastChild;
+ var line = this.paper.lastChild;
- if (that.bufLength > 0) {
- that.bufIndex--;
+ if (this.bufLength > 0) {
+ this.bufIndex--;
}
- if (that.printCol > 0) {
- that.printCol--;
+ if (this.printCol > 0) {
+ this.printCol--;
}
if (line.nodeValue.length > 0) {
line.nodeValue = line.nodeValue.substring(0, line.nodeValue.length-1);
@@ -194,8 +186,7 @@ B5500SPOUnit.prototype.backspaceChar = function backspaceChar() {
/**************************************/
B5500SPOUnit.prototype.printChar = function printChar(c) {
/* Echoes the character code "c" to the SPO printer */
- var that = printChar.that;
- var line = that.paper.lastChild;
+ var line = this.paper.lastChild;
var len = line.nodeValue.length;
if (len < 1) {
@@ -212,38 +203,35 @@ B5500SPOUnit.prototype.outputChar = function outputChar() {
/* Outputs one character from the buffer to the SPO. If more characters remain
to be printed, schedules itself 100 ms later to print the next one, otherwise
calls finished(). If the column counter exceeds 72, a CR/LF pair is output.
- A CR/LF pair is also output at the end of the message. Note the use of the local
- function property "that" (initialized in the constructor), which supplies the
- necessary SPOUnit object context across setTimeout() calls */
- var that = outputChar.that; // retrieve our object context
- var nextTime = that.nextCharTime + that.charPeriod;
+ 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();
- that.nextCharTime = nextTime;
- if (that.printCol < 72) { // print the character
- if (that.bufIndex < that.bufLength) {
- that.printChar(that.buffer[that.bufIndex]);
- that.bufIndex++;
- that.printCol++;
- this.outTimer = setTimeout(that.outputChar, delay);
+ this.nextCharTime = nextTime;
+ if (this.printCol < 72) { // print the character
+ if (this.bufIndex < this.bufLength) {
+ this.printChar(this.buffer[this.bufIndex]);
+ this.bufIndex++;
+ this.printCol++;
+ this.outTimer = setCallback(this.outputChar, this, delay);
} else { // set up for the final CR/LF
- that.printCol = 72;
- this.outTimer = setTimeout(that.outputChar, delay);
+ this.printCol = 72;
+ this.outTimer = setCallback(this.outputChar, this, delay);
}
- } else if (that.printCol == 72) { // delay to fake the output of a carriage-return
- that.printCol++;
- this.outTimer = setTimeout(that.outputChar, delay+that.charPeriod);
+ } 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);
} else { // actually output the CR/LF
- that.appendEmptyLine();
- if (that.bufIndex < that.bufLength) {
- that.printCol = 0; // more characters to print after the CR/LF
- this.outTimer = setTimeout(that.outputChar, delay);
+ this.appendEmptyLine();
+ if (this.bufIndex < this.bufLength) {
+ this.printCol = 0; // more characters to print after the CR/LF
+ this.outTimer = setCallback(this.outputChar, this, delay);
} else { // message text is exhausted
- that.finish(that.errorMask, that.bufLength); // report finish with any errors
- if (that.spoLocalRequested) {
- that.setLocal();
+ this.finish(this.errorMask, this.bufLength); // report finish with any errors
+ if (this.spoLocalRequested) {
+ this.setLocal();
} else {
- that.spoState = that.spoRemote;
+ this.spoState = this.spoRemote;
}
}
}
@@ -251,7 +239,7 @@ B5500SPOUnit.prototype.outputChar = function outputChar() {
/**************************************/
B5500SPOUnit.prototype.terminateInput = function terminateInput() {
- /* Handles the End of Message event. Turns off then Input Request lamp, then
+ /* Handles the End of Message event. Turns off the Ready lamp, then
calls outputChar(), which will find bufIndex==bufLength, output a new-line,
set the state to Remote, and call finish() for us. Slick, eh? */
@@ -268,12 +256,9 @@ B5500SPOUnit.prototype.cancelInput = function cancelInput() {
/* Handles the Error message event. This is identical to terminateInput(),
but it also sets a parity error so the input message will be rejected */
- if (this.spoState = this.spoInput) {
- this.removeClass(this.$$("SPOReadyBtn"), "yellowLit");
+ if (this.spoState == this.spoInput) {
this.errorMask |= 0x10; // set parity/error-button bit
- this.bufLength = this.bufIndex;
- this.nextCharTime = new Date().getTime();
- this.outputChar();
+ this.terminateInput();
}
};
@@ -282,7 +267,6 @@ B5500SPOUnit.prototype.keyPress = function keyPress(ev) {
/* Handles keyboard character events. Depending on the state of the unit,
either buffers the character for transmission to the I/O Unit, simply echos
it to the printer, or ignores it altogether */
- var that = this;
var c = ev.charCode;
var index = this.bufLength;
var nextTime;
@@ -300,27 +284,21 @@ B5500SPOUnit.prototype.keyPress = function keyPress(ev) {
if (this.printCol < 72) {
this.printCol++;
}
- this.inTimer = setTimeout(function keyPressChar() {
- that.printChar(c);
- }, nextTime-stamp);
+ this.inTimer = setCallback(this.printChar, this, nextTime-stamp, c);
}
if (c == 126) { // "~" (B5500 group-mark)
c = this.keyFilter[c];
if (this.printCol < 72) {
this.printCol++;
}
- this.inTimer = setTimeout(function keyPressGM() {
- that.printChar(c);
- }, nextTime-stamp);
+ this.inTimer = setCallback(this.printChar, this, nextTime-stamp, c);
this.nextCharTime = nextTime + this.charPeriod;
this.terminateInput();
}
} else if (this.spoState == this.spoLocal) {
if (c >= 32 && c <= 126) {
c = this.keyFilter[c];
- this.inTimer = setTimeout(function keyPressLocalChar() {
- that.printChar(c);
- }, nextTime-stamp);
+ this.inTimer = setCallback(this.printChar, this, nextTime-stamp, c);
}
}
@@ -330,7 +308,6 @@ B5500SPOUnit.prototype.keyPress = function keyPress(ev) {
/**************************************/
B5500SPOUnit.prototype.keyDown = function keyDown(ev) {
/* Handles key-down events to capture ESC, BS, and Enter keystrokes */
- var that = this;
var c = ev.keyCode;
var nextTime;
var result = true;
@@ -361,7 +338,7 @@ B5500SPOUnit.prototype.keyDown = function keyDown(ev) {
switch (this.spoState) {
case this.spoInput:
case this.spoLocal:
- this.inTimer = setTimeout(this.backspaceChar, nextTime-stamp);
+ this.inTimer = setCallback(this.backspaceChar, this, nextTime-stamp);
this.nextCharTime = nextTime;
result = false;
break;
@@ -375,9 +352,7 @@ B5500SPOUnit.prototype.keyDown = function keyDown(ev) {
result = false;
break
case this.spoLocal:
- this.inTimer = setTimeout(function keyDownLocal() {
- that.appendEmptyLine();
- }, nextTime-stamp+this.charPeriod);
+ this.inTimer = setCallback(this.appendEmptyLine, this, nextTime-stamp+this.charPeriod);
this.nextCharTime = nextTime;
result = false;
break;
@@ -424,7 +399,6 @@ B5500SPOUnit.prototype.beforeUnload = function beforeUnload(ev) {
/**************************************/
B5500SPOUnit.prototype.spoOnload = function spoOnload() {
/* Initializes the SPO window and user interface */
- var that = this;
var x;
this.doc = this.window.document;
@@ -436,56 +410,47 @@ B5500SPOUnit.prototype.spoOnload = function spoOnload() {
this.endOfPaper.appendChild(this.doc.createTextNode("\xA0"));
this.$$("SPOUT").contentDocument.body.appendChild(this.endOfPaper);
this.$$("SPOUT").contentDocument.head.innerHTML += "";
- this.window.resizeTo(this.window.outerWidth+this.$$("SPODiv").scrollWidth-this.window.innerWidth+8,
- this.window.outerHeight+this.$$("SPODiv").scrollHeight-this.window.innerHeight+8);
- this.window.moveTo(0, screen.availHeight-this.window.outerHeight);
this.window.focus();
this.window.addEventListener("beforeunload", this.beforeUnload, false);
- this.window.addEventListener("keypress", function windowKeyPress(ev) {
- that.keyPress(ev);
- }, false);
+ this.window.addEventListener("keypress", B5500CentralControl.bindMethod(B5500SPOUnit.prototype.keyPress, this), false);
- this.window.addEventListener("keydown", function windowKeyDown(ev) {
- that.keyDown(ev);
- }, false);
+ this.window.addEventListener("keydown", B5500CentralControl.bindMethod(B5500SPOUnit.prototype.keyDown, this), false);
- this.$$("SPORemoteBtn").addEventListener("click", function remoteClick() {
- that.setRemote();
- }, false);
+ this.$$("SPORemoteBtn").addEventListener("click", B5500CentralControl.bindMethod(B5500SPOUnit.prototype.setRemote, this), false);
- this.$$("SPOLocalBtn").addEventListener("click", function localClick() {
- that.setLocal();
- }, false);
-
- this.$$("SPOInputRequestBtn").addEventListener("click", function inputRequestClick() {
- if (that.spoState == that.spoRemote || that.spoState == that.spoOutput) {
- that.addClass(that.$$("SPOInputRequestBtn"), "yellowLit");
- that.signal();
+ this.$$("SPOLocalBtn").addEventListener("click", B5500CentralControl.bindMethod(function localClick() {
+ if (this.spoState == this.spoRemote) {
+ this.setLocal();
+ } else {
+ this.spoLocalRequested = true;
}
- }, false);
+ }, this), false);
- this.$$("SPOErrorBtn").addEventListener("click", function errorClick() {
- that.cancelInput();
- }, false);
+ this.$$("SPOInputRequestBtn").addEventListener("click", B5500CentralControl.bindMethod(function inputRequestClick() {
+ if (this.spoState == this.spoRemote || this.spoState == this.spoOutput) {
+ this.addClass(this.$$("SPOInputRequestBtn"), "yellowLit");
+ this.signal();
+ }
+ }, this), false);
- this.$$("SPOEndOfMessageBtn").addEventListener("click", function endOfMessageClick() {
- that.terminateInput();
- }, false);
+ this.$$("SPOErrorBtn").addEventListener("click", B5500CentralControl.bindMethod(B5500SPOUnit.prototype.cancelInput, this), false);
+
+ this.$$("SPOEndOfMessageBtn").addEventListener("click", B5500CentralControl.bindMethod(B5500SPOUnit.prototype.terminateInput, this), false);
for (x=0; x<32; x++) {
this.appendEmptyLine();
}
- this.printText("retro-B5500 Emulator Version " + B5500CentralControl.version, function initComplete() {
+ this.printText("retro-B5500 Emulator Version " + B5500CentralControl.version, B5500CentralControl.bindMethod(function initComplete() {
this.window.focus();
- that.setRemote();
- that.appendEmptyLine();
- });
+ this.setRemote();
+ this.appendEmptyLine();
+ }, this));
};
/**************************************/
diff --git a/webUI/B5500SetCallback.js b/webUI/B5500SetCallback.js
new file mode 100644
index 0000000..db66544
--- /dev/null
+++ b/webUI/B5500SetCallback.js
@@ -0,0 +1,166 @@
+/***********************************************************************
+* retro-b5500/webUI B5500SetCallback.js
+************************************************************************
+* Copyright (c) 2013, Nigel Williams and Paul Kimpel.
+* Licensed under the MIT License, see
+* http://www.opensource.org/licenses/mit-license.php
+************************************************************************
+* B5500 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
+* that setTimeout() is used, except that for low values of the timeout parameter,
+* it merely yields control to any other pending events and timers before calling
+* the call-back function.
+*
+* This facility is needed because modern browsers implement a minimum delay
+* when calling setTimeout(). HTML5 specs require 4ms, but on Microsoft Windows
+* systems (at least through Win7), the minimum precision of setTimeout() is
+* 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.
+*
+* 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.
+*
+* The SetCallback mechanism defines two functions, which become members of the
+* global (window) object:
+*
+* cookie = setCallback(fcn, context, delay, args...)
+*
+* 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
+* 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.
+*
+* clearCallBack(cookie)
+*
+* Cancels a pending call-back event, if in fact it is still pending.
+* The "cookie" parameter is a value returned from setCallback().
+*
+* This implementation has been inspired by Domenic Denicola's shim for the
+* setImmediate() API at https://github.com/NobleJS/setImmediate, and
+* David Baron's setZeroTimeout() implemenmentation described in his blog
+* at http://dbaron.org/log/20100309-faster-timeouts.
+*
+* Stole a little of their code, too.
+*
+************************************************************************
+* 2013-08-04 P.Kimpel
+* Original version, cloned from B5500DiskUnit.js.
+***********************************************************************/
+"use strict";
+
+(function (global) {
+ /* Define a closure for the setCallback() mechanism */
+ 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);
+
+ /**************************************/
+ function activateCallback(cookieName) {
+ /* Activates a callback after its delay period has expired */
+ var thisCallback;
+
+ if (cookieName in pendingCallbacks) {
+ thisCallback = pendingCallbacks[cookieName];
+ delete pendingCallbacks[cookieName];
+ try {
+ thisCallback.fcn.apply(thisCallback.context, thisCallback.args);
+ } catch (err) {
+ console.log("B5500SetCallback.activateCallback: " + err);
+ }
+ }
+ }
+
+ /**************************************/
+ function clearCallback(cookie) {
+ /* Disables a pending callback, if it still exists and is still pending */
+ var cookieName = cookie.toString();
+ var thisCallback;
+
+ if (cookieName in pendingCallbacks) {
+ thisCallback = pendingCallbacks[cookieName];
+ delete pendingCallbacks[cookieName];
+ if (thisCallback.cancelToken) {
+ if (thisCallback.type == 2) {
+ global.clearTimeout(thisCallback.cancelToken);
+ }
+ }
+ }
+ }
+
+ /**************************************/
+ function setCallback(fcn, context, callbackDelay, args) {
+ /* 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
+ 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,
+ };
+
+ pendingCallbacks[cookieName] = thisCallback;
+ if (arguments.length > 3) {
+ thisCallback.args = Array.slice(arguments, 3);
+ }
+
+ if (delay < minTimeout) {
+ thisCallback.type = 1;
+ global.postMessage(secretPrefix + cookieName, "*");
+ } else {
+ thisCallback.type = 2;
+ thisCallback.cancelToken = global.setTimeout(activateCallback, delay, cookieName);
+ }
+
+ return cookie;
+ }
+
+ /**************************************/
+ function onMessage(ev) {
+ /* Handler for the global.onmessage event. Activates the callback */
+ var cookieName;
+ var payload;
+
+ if (ev.source === global) {
+ payload = ev.data.toString();
+ if (payload.substring(0, secretPrefix.length) === secretPrefix) {
+ cookieName = payload.substring(secretPrefix.length);
+ activateCallback(cookieName);
+ }
+ }
+ }
+
+ /********** Outer block of anonymous closure **********/
+ if (!global.setCallback && global.postMessage && !global.importScripts) {
+ // Attach to the prototype of global, if possible, otherwise to global itself
+ var attachee = global;
+
+ /*****
+ if (typeof Object.getPrototypeOf === "function") {
+ if ("setTimeout" in Object.getPrototypeOf(global)) {
+ attachee = Object.getPrototypeOf(global);
+ }
+ }
+ *****/
+
+ global.addEventListener("message", onMessage, false);
+ attachee.setCallback = setCallback;
+ attachee.clearCallback = clearCallback;
+ }
+}(typeof global === "object" && global ? global : this));