diff --git a/emulator/B220Processor.js b/emulator/B220Processor.js
index f19d47c..a47edca 100644
--- a/emulator/B220Processor.js
+++ b/emulator/B220Processor.js
@@ -256,7 +256,7 @@ function B220Processor(config, devices) {
* Global Constants *
***********************************************************************/
-B220Processor.version = "0.03c";
+B220Processor.version = "0.04";
B220Processor.tick = 1000/200000; // milliseconds per clock cycle (200KHz)
B220Processor.cyclesPerMilli = 1/B220Processor.tick;
@@ -1166,6 +1166,41 @@ B220Processor.prototype.bcdAdd = function bcdAdd(a, d, digits, complement, initi
return am;
};
+/**************************************/
+B220Processor.prototype.clearAdd = function clearAdd(absolute) {
+ /* After accessing memory, algebraically add the addend (IB) to zero. If
+ "absolute" is true, then the sign-bit of the word from memory is forced to the
+ subtract toggle. All values are BCD with the sign in the 11th digit position.
+ Sets the Digit Check alarm as necessary */
+ var am = 0; // augend mantissa
+ var dm; // addend mantissa
+ var dSign; // addend sign
+
+ this.opTime = 0.095;
+ this.E.set(this.CADDR);
+ this.readMemory();
+ if (this.MET.value) { // invalid address
+ this.A.set(am); // sign is zero
+ return; // exit to Operation Complete
+ }
+
+ dm = this.IB.value % 0x10000000000;
+ dSign = ((this.IB.value - dm)/0x10000000000);
+ if (absolute) { // force sign bit to SUT
+ dSign = (dSign & 0x0E) | this.SUT.value;
+ } else if (this.SUT.value) { // complement the sign bit
+ dSign = dSign ^ 0x01;
+ }
+
+ am = this.bcdAdd(am, dm, 11);
+
+ // Set toggles for display purposes and return the result
+ this.DST.set(dSign%2);
+ this.SGT.set(dSign%2);
+ this.D.set(dSign*0x10000000000 + dm);
+ this.A.set(dSign*0x10000000000 + am);
+};
+
/**************************************/
B220Processor.prototype.integerAdd = function integerAdd(absolute, toD) {
/* After accessing memory, algebraically add the addend (IB) to the augend (A).
@@ -3142,15 +3177,13 @@ B220Processor.prototype.execute = function execute() {
case 0x10: //--------------------- CAD/CAA Clear add/add absolute
this.SUT.set(0);
- this.A.value = this.IB.value - this.IB.value%0x10000000000; // 0 with sign of IB
- this.integerAdd(this.CCONTROL % 0x10 == 1, false);
+ this.clearAdd(this.CCONTROL % 0x10 == 1);
this.operationComplete();
break;
case 0x11: //--------------------- CSU/CSA Clear subtract/subtract absolute
this.SUT.set(1);
- this.A.value = this.IB.value - this.IB.value%0x10000000000; // 0 with sign of IB
- this.integerAdd(this.CCONTROL % 0x10 == 1, false);
+ this.clearAdd(this.CCONTROL % 0x10 == 1);
this.operationComplete();
break;
@@ -3451,7 +3484,7 @@ B220Processor.prototype.execute = function execute() {
if (this.CCONTROL%0x10 == 1) { // Load B complement
this.B.set(this.bcdAdd(this.IB.value, 0, 4, 1, 1));
} else { // Load B
- this.B.set(this.IB.value);
+ this.B.set(this.IB.value%0x10000);
}
}
this.operationComplete();
@@ -3538,9 +3571,14 @@ B220Processor.prototype.execute = function execute() {
case 0x49: //--------------------- SL* Shift (rotate) left A/A and R/A with sign
switch (this.CCONTROL%0x10) {
case 1: // SLT: Shift Left A and R
- x = B220Processor.bcdBinary(this.CADDR % 0x20);
- this.opTime = 0.210 - x*0.005;
- this.DC.set(B220Processor.binaryBCD(x));
+ x = this.CADDR % 0x20;
+ if (x < 0x10) {
+ this.opTime = 0.210 - x*0.005;
+ } else {
+ this.opTime = 0.160 - (x-0x10)*0.005;
+ }
+
+ this.DC.set(x);
w = this.R.value % 0x10000000000; // R sign is not affected
this.A.value %= 0x10000000000; // discard the A sign
while (this.DC.value < 0x20) {
@@ -3553,10 +3591,12 @@ B220Processor.prototype.execute = function execute() {
this.R.set(this.R.value - this.R.value%0x10000000000 + w); // restore the R sign
break;
case 2: // SLS: Shift Left A with Sign
- x = B220Processor.bcdBinary(this.CADDR % 0x10);
+ x = this.CADDR % 0x10;
this.opTime = 0.160 - x*0.005;
- this.DC.set(B220Processor.binaryBCD(10+x));
+ this.DC.set(0x10+x);
w = this.A.value % 0x100000000000; // A sign is included
+ d = w % 0x10; // do one more rotate right
+ w = (w-d)/0x10 + d*0x10000000000; // than the count calls for
while (this.DC.value < 0x20) {
d = w % 0x10;
w = (w-d)/0x10 + d*0x10000000000;
@@ -3565,9 +3605,9 @@ B220Processor.prototype.execute = function execute() {
this.A.set(w);
break;
default: // SLA: Shift Left A
- x = B220Processor.bcdBinary(this.CADDR % 0x10);
+ x = this.CADDR % 0x10;
this.opTime = 0.160 - x*0.005;
- this.DC.set(B220Processor.binaryBCD(10+x));
+ this.DC.set(0x10+x);
w = this.A.value % 0x10000000000; // A sign is not affected
while (this.DC.value < 0x20) {
d = w % 0x10;
@@ -3721,7 +3761,7 @@ B220Processor.prototype.execute = function execute() {
} else if (this.magTape.controlBusy) {
this.opTime = 0.01;
} else {
- opTime = 0.14;
+ this.opTime = 0.14;
if (this.CCONTROL%0x10 == 1) { // MIE
if (this.magTape.testUnitAtEOT(this.D.value)) {
this.P.set(this.CADDR);
@@ -4251,41 +4291,6 @@ B220Processor.prototype.powerDown = function powerDown() {
B220Processor.prototype.loadDefaultProgram = function loadDefaultProgram() {
/* Loads a set of default demo programs to the memory drum */
- // TEMP // Tape tests
- this.MM[ 0] = 0x1008500000; // MRW 1
- this.MM[ 1] = 0x1002580000; // MPE 1
- this.MM[ 2] = 0x1000540000; // MIW 0,1,10,100
- this.MM[ 3] = 0x1750540100; // MIW 100,1,7,50
- this.MM[ 4] = 0x1500550079; // MIR 79,1,5,00
- this.MM[ 5] = 0x1101542000; // MIW 2000,1,1,1 // write an EOT block
-
- this.MM[ 6] = 0x1008500000; // MRW 1
- this.MM[ 7] = 0x1000560000; // MOW 0,1,10,100
- this.MM[ 8] = 0x1750560100; // MOW 100,1,7,50
- this.MM[ 9] = 0x1500570079; // MOR 79,1,5,00
- //this.MM[ 10] = 0x1101562000; // MOW 2000,1,1,1
- this.MM[ 10] = 0x1110562000; // MOW 2000,1,1,10 // TEMP: block-length=10, should fire EOT control word
-
- this.MM[ 11] = 0x1008500000; // MRW 1
- this.MM[ 12] = 0x1000523000; // MRD 3000,1,10,0
- this.MM[ 13] = 0x1700524000; // MRD 4000,1,7,0
- this.MM[ 14] = 0x1500534350; // MRR 4350,1,5,0
- this.MM[ 15] = 0x1100534800; // MRR 4800,1,1,0 // should be an EOT block
-
- this.MM[ 16] = 0x1009500000; // MDA 1
- this.MM[ 17] = 0x7777009999; // HLT 9999,7777
-
- this.MM[ 79] = 0x1900000000; // preface for 19 words, 80-98
- this.MM[ 99] = 0x4000000000; // preface for 40 words, 100-139
- this.MM[ 140] = 0x5800000000; // preface for 58 words, 141-198
- this.MM[ 199] = 0x9900000000; // preface for 99 words, 200-298
- this.MM[ 299] = 0x0000000000; // preface for 100 words, 300-399
-
- this.MM[2000] = 0x9920012002; // end-of-tape control word
- this.MM[2001] = 0x9999999999; // storage for end-of-tape block state
- this.MM[2002] = 0x9999008421; // HLT: target for end-of-tape control branch
- this.MM[2003] = 0x0000300011; // branch to read test sequence
-
// Simple counter speed test
this.MM[ 80] = 0x0000120082; // ADD 82
this.MM[ 81] = 0x0000300080; // BUN 80
@@ -4469,4 +4474,39 @@ B220Processor.prototype.loadDefaultProgram = function loadDefaultProgram() {
this.MM[ 377]= 0x20202021616; // CR CNST 20202021616 NEWLINES
this.MM[1000]= 0x00000000000; // F DEFN * ARRAY F[2800]
+
+ // TEMP // Tape tests
+ this.MM[ 400] = 0x1008500000; // MRW 1
+ this.MM[ 401] = 0x1002580000; // MPE 1
+ this.MM[ 402] = 0x1000540000; // MIW 0,1,10,100
+ this.MM[ 403] = 0x1750540100; // MIW 100,1,7,50
+ this.MM[ 404] = 0x1500550079; // MIR 79,1,5,00
+ this.MM[ 405] = 0x1101542000; // MIW 2000,1,1,1 // write an EOT block
+
+ this.MM[ 406] = 0x1008500000; // MRW 1
+ this.MM[ 407] = 0x1000560000; // MOW 0,1,10,100
+ this.MM[ 408] = 0x1750560100; // MOW 100,1,7,50
+ this.MM[ 409] = 0x1500570079; // MOR 79,1,5,00
+ //this.MM[ 410] = 0x1101562000; // MOW 2000,1,1,1
+ this.MM[ 410] = 0x1110562000; // MOW 2000,1,1,10 // TEMP: block-length=10, should fire EOT control word
+
+ this.MM[ 411] = 0x1008500000; // MRW 1
+ this.MM[ 412] = 0x1000523000; // MRD 3000,1,10,0
+ this.MM[ 413] = 0x1700524000; // MRD 4000,1,7,0
+ this.MM[ 414] = 0x1500534350; // MRR 4350,1,5,0
+ this.MM[ 415] = 0x1100534800; // MRR 4800,1,1,0 // should be an EOT block
+
+ this.MM[ 416] = 0x1009500000; // MDA 1
+ this.MM[ 417] = 0x7777009999; // HLT 9999,7777
+
+ this.MM[ 79] = 0x1900000000; // preface for 19 words, 80-98
+ this.MM[ 99] = 0x4000000000; // preface for 40 words, 100-139
+ this.MM[ 140] = 0x5800000000; // preface for 58 words, 141-198
+ this.MM[ 199] = 0x9900000000; // preface for 99 words, 200-298
+ this.MM[ 299] = 0x0000000000; // preface for 100 words, 300-399
+
+ this.MM[2000] = 0x9920012002; // end-of-tape control word
+ this.MM[2001] = 0x9999999999; // storage for end-of-tape block state
+ this.MM[2002] = 0x9999008421; // HLT: target for end-of-tape control branch
+ this.MM[2003] = 0x0000300411; // branch to read test sequence
};
\ No newline at end of file
diff --git a/index.html b/index.html
index 91fe71c..85d939e 100644
--- a/index.html
+++ b/index.html
@@ -18,10 +18,6 @@
Burroughs 220 Emulator – Hosting Site Home
-
- (Under Construction)
-
-
This site hosts the current version of the retro-220 emulator, an implementation of the Burroughs 220 computer system that runs in a web browser.
@@ -69,7 +65,7 @@
Copyright (c) 2017, Paul Kimpel • Licensed under the MIT License
Revised
- 2016-12-25
+ 2017-11-19
diff --git a/webUI/B220CardatronControl.js b/webUI/B220CardatronControl.js
index aa48b4b..176112c 100644
--- a/webUI/B220CardatronControl.js
+++ b/webUI/B220CardatronControl.js
@@ -164,9 +164,9 @@ B220CardatronControl.prototype.cardatronOnLoad = function cardatronOnLoad() {
// Events
- this.window.addEventListener("beforeunload", B220CardatronControl.prototype.beforeUnload);
+ this.window.addEventListener("beforeunload", B220CardatronControl.prototype.beforeUnload, false);
this.$$("ClearBtn").addEventListener("click",
- B220Util.bindMethod(this, B220CardatronControl.prototype.ClearBtn_onClick));
+ B220Util.bindMethod(this, B220CardatronControl.prototype.ClearBtn_onClick), false);
this.clear();
@@ -305,6 +305,6 @@ B220CardatronControl.prototype.shutDown = function shutDown() {
}
}
- this.window.removeEventListener("beforeunload", B220CardatronControl.prototype.beforeUnload);
+ this.window.removeEventListener("beforeunload", B220CardatronControl.prototype.beforeUnload, false);
this.window.close();
};
\ No newline at end of file
diff --git a/webUI/B220CardatronInput.js b/webUI/B220CardatronInput.js
index 96e8424..bdcc53e 100644
--- a/webUI/B220CardatronInput.js
+++ b/webUI/B220CardatronInput.js
@@ -615,21 +615,21 @@ B220CardatronInput.prototype.readerOnLoad = function readerOnLoad() {
this.clearUnit(); // will actually set the state and lamps correctly
this.window.addEventListener("beforeunload",
- B220CardatronInput.prototype.beforeUnload);
+ B220CardatronInput.prototype.beforeUnload, false);
this.$$("CIFileSelector").addEventListener("change",
- B220Util.bindMethod(this, B220CardatronInput.prototype.fileSelector_onChange));
+ B220Util.bindMethod(this, B220CardatronInput.prototype.fileSelector_onChange), false);
this.$$("FormatColumn").addEventListener("change",
- B220Util.bindMethod(this, B220CardatronInput.prototype.format_onChange));
+ B220Util.bindMethod(this, B220CardatronInput.prototype.format_onChange), false);
this.$$("FormatSelect").addEventListener("change",
- B220Util.bindMethod(this, B220CardatronInput.prototype.format_onChange));
+ B220Util.bindMethod(this, B220CardatronInput.prototype.format_onChange), false);
this.$$("CIStartBtn").addEventListener("click",
- B220Util.bindMethod(this, B220CardatronInput.prototype.CIStartBtn_onClick));
+ B220Util.bindMethod(this, B220CardatronInput.prototype.CIStartBtn_onClick), false);
this.$$("CIStopBtn").addEventListener("click",
- B220Util.bindMethod(this, B220CardatronInput.prototype.CIStopBtn_onClick));
+ B220Util.bindMethod(this, B220CardatronInput.prototype.CIStopBtn_onClick), false);
this.$$("ClearBtn").addEventListener("click",
- B220Util.bindMethod(this, B220CardatronInput.prototype.ClearBtn_onClick));
+ B220Util.bindMethod(this, B220CardatronInput.prototype.ClearBtn_onClick), false);
this.hopperBar.addEventListener("click",
- B220Util.bindMethod(this, B220CardatronInput.prototype.CIHopperBar_onClick));
+ B220Util.bindMethod(this, B220CardatronInput.prototype.CIHopperBar_onClick), false);
this.window.resizeBy(de.scrollWidth - this.window.innerWidth + 4, // kludge for right-padding/margin
de.scrollHeight - this.window.innerHeight);
@@ -896,6 +896,6 @@ B220CardatronInput.prototype.shutDown = function shutDown() {
if (this.timer) {
clearCallback(this.timer);
}
- this.window.removeEventListener("beforeunload", B220CardatronInput.prototype.beforeUnload);
+ this.window.removeEventListener("beforeunload", B220CardatronInput.prototype.beforeUnload, false);
this.window.close();
};
diff --git a/webUI/B220CardatronOutput.js b/webUI/B220CardatronOutput.js
index 3bd8dd3..a4fb77f 100644
--- a/webUI/B220CardatronOutput.js
+++ b/webUI/B220CardatronOutput.js
@@ -758,7 +758,7 @@ B220CardatronOutput.prototype.deviceOnLoad = function deviceOnLoad() {
this.window.addEventListener("beforeunload",
B220CardatronOutput.prototype.beforeUnload, false);
this.supply.addEventListener("dblclick",
- B220Util.bindMethod(this, B220CardatronOutput.prototype.copySupply));
+ B220Util.bindMethod(this, B220CardatronOutput.prototype.copySupply), false);
this.$$("COStopBtn").addEventListener("click",
B220Util.bindMethod(this, B220CardatronOutput.prototype.COStopBtn_onClick), false);
this.$$("COStartBtn").addEventListener("click",
@@ -772,7 +772,7 @@ B220CardatronOutput.prototype.deviceOnLoad = function deviceOnLoad() {
this.$$("COSetZSBtn").addEventListener("click",
B220Util.bindMethod(this, B220CardatronOutput.prototype.COSetZSBtn_onClick), false);
this.$$("ClearBtn").addEventListener("click",
- B220Util.bindMethod(this, B220CardatronOutput.prototype.ClearBtn_onClick));
+ B220Util.bindMethod(this, B220CardatronOutput.prototype.ClearBtn_onClick), false);
if (!this.isPrinter) {
this.$$("COEndOfSupplyBtn").innerHTML = "OUT OF
CARDS";
@@ -1031,7 +1031,7 @@ B220CardatronOutput.prototype.shutDown = function shutDown() {
if (this.timer) {
clearCallback(this.timer);
}
- this.window.removeEventListener("beforeunload", B220CardatronOutput.prototype.beforeUnload);
+ this.window.removeEventListener("beforeunload", B220CardatronOutput.prototype.beforeUnload, false);
this.window.close();
if (this.zsWindow && !this.zsWindow.closed) {
this.zsWindow.close();
diff --git a/webUI/B220ConsolePrinter.css b/webUI/B220ConsolePrinter.css
index a1f4190..ce3c45d 100644
--- a/webUI/B220ConsolePrinter.css
+++ b/webUI/B220ConsolePrinter.css
@@ -185,6 +185,20 @@
top: 66px;
width: 220px}
+#SpeedSwitch {
+ position: absolute;
+ left: 60px;
+ top: 160px;
+ width: 24px}
+#WhippetSpeedCaption {
+ left: 48px;
+ top: 146px;
+ width: 48px}
+#TTYSpeedCaption {
+ left: 48px;
+ top: 190px;
+ width: 48px}
+
#UnitSwitch0 {
position: absolute;
left: 134px;
diff --git a/webUI/B220ConsolePrinter.html b/webUI/B220ConsolePrinter.html
index d63703d..09eedbe 100644
--- a/webUI/B220ConsolePrinter.html
+++ b/webUI/B220ConsolePrinter.html
@@ -63,6 +63,9 @@
TAB STOPS
+ SPEED
WHIPPET
+ TTY
+
SPO
1
2
diff --git a/webUI/B220ConsolePrinter.js b/webUI/B220ConsolePrinter.js
index 22f4d23..b07a15a 100644
--- a/webUI/B220ConsolePrinter.js
+++ b/webUI/B220ConsolePrinter.js
@@ -32,12 +32,13 @@ function B220ConsolePrinter(mnemonic, unitIndex, config) {
this.unitSwitch = new Array(11); // unit selection switch objects
this.tabStop = []; // 0-relative tab stop positions
this.zeroSuppress = 0; // zero-suppression switch setting
+ this.charPeriod = 0; // printer speed, ms/char
- this.boundButton_Click = B220Util.bindMethod(this, B220ConsolePrinter.prototype.button_Click);
- this.boundText_OnChange = B220Util.bindMethod(this, B220ConsolePrinter.prototype.text_OnChange);
- this.boundFlipSwitch = B220Util.bindMethod(this, B220ConsolePrinter.prototype.flipSwitch);
- this.boundReceiveSign = B220Util.bindMethod(this, B220ConsolePrinter.prototype.receiveSign);
- this.boundReceiveChar = B220Util.bindMethod(this, B220ConsolePrinter.prototype.receiveChar);
+ this.boundButton_Click = B220ConsolePrinter.prototype.button_Click.bind(this);
+ this.boundText_OnChange = B220ConsolePrinter.prototype.text_OnChange.bind(this);
+ this.boundFlipSwitch = B220ConsolePrinter.prototype.flipSwitch.bind(this);
+ this.boundReceiveSign = B220ConsolePrinter.prototype.receiveSign.bind(this);
+ this.boundReceiveChar = B220ConsolePrinter.prototype.receiveChar.bind(this);
this.clear();
@@ -58,8 +59,8 @@ function B220ConsolePrinter(mnemonic, unitIndex, config) {
B220ConsolePrinter.offSwitchImage = "./resources/ToggleDown.png";
B220ConsolePrinter.onSwitchImage = "./resources/ToggleUp.png";
-B220ConsolePrinter.charsPerSecond = 10; // Printer speed
-B220ConsolePrinter.charPeriod = 1000/B220ConsolePrinter.charsPerSecond;
+B220ConsolePrinter.ttySpeed = 10; // TTY printer speed, char/sec
+B220ConsolePrinter.whippetSpeed = 200; // Whippet printer speed, char/sec
// Inter-character period, ms
B220ConsolePrinter.pageSize = 66; // lines/page for form-feed
B220ConsolePrinter.maxScrollLines = 15000;
@@ -264,7 +265,7 @@ B220ConsolePrinter.prototype.flipSwitch = function flipSwitch(ev) {
break;
case "MapMemorySwitch":
this.mapMemorySwitch.flip();
- prefs.mapMemory, this.mapMemory = this.mapMemorySwitch.state;
+ prefs.mapMemory = this.mapMemory = this.mapMemorySwitch.state;
break;
case "RemoteKnob":
this.remoteKnob.step();
@@ -276,6 +277,15 @@ B220ConsolePrinter.prototype.flipSwitch = function flipSwitch(ev) {
prefs.format = this.formatKnob.position;
this.format = this.formatKnob.position;
break;
+ case "SpeedSwitch":
+ this.speedSwitch.flip();
+ prefs.printerSpeed = this.speedSwitch.state;
+ if (this.speedSwitch.state) {
+ this.charPeriod = 1000/B220ConsolePrinter.whippetSpeed;
+ } else {
+ this.charPeriod = 1000/B220ConsolePrinter.ttySpeed;
+ }
+ break;
default:
x = id.indexOf("UnitSwitch");
if (x == 0) {
@@ -394,6 +404,14 @@ B220ConsolePrinter.prototype.printerOnLoad = function printerOnLoad() {
B220ConsolePrinter.offSwitchImage, B220ConsolePrinter.onSwitchImage);
this.mapMemorySwitch.set(prefs.mapMemory);
this.mapMemory = this.mapMemorySwitch.state;
+ this.speedSwitch = new ToggleSwitch(body, null, null, "SpeedSwitch",
+ B220ConsolePrinter.offSwitchImage, B220ConsolePrinter.onSwitchImage);
+ this.speedSwitch.set(prefs.printerSpeed);
+ if (this.speedSwitch.state) {
+ this.charPeriod = 1000/B220ConsolePrinter.whippetSpeed;
+ } else {
+ this.charPeriod = 1000/B220ConsolePrinter.ttySpeed;
+ }
mask = 0x001;
this.unitMask = prefs.unitMask;
@@ -421,11 +439,11 @@ B220ConsolePrinter.prototype.printerOnLoad = function printerOnLoad() {
// Events
this.window.addEventListener("beforeunload",
- B220ConsolePrinter.prototype.beforeUnload);
+ B220ConsolePrinter.prototype.beforeUnload, false);
this.window.addEventListener("resize",
- B220Util.bindMethod(this, B220ConsolePrinter.prototype.resizeWindow));
+ B220Util.bindMethod(this, B220ConsolePrinter.prototype.resizeWindow), false);
this.paper.addEventListener("dblclick",
- B220Util.bindMethod(this, B220ConsolePrinter.prototype.copyPaper));
+ B220Util.bindMethod(this, B220ConsolePrinter.prototype.copyPaper), false);
this.$$("OpenPanelBtn").addEventListener("click", this.boundButton_Click);
this.$$("ClosePanelBtn").addEventListener("click", this.boundButton_Click);
@@ -434,6 +452,7 @@ B220ConsolePrinter.prototype.printerOnLoad = function printerOnLoad() {
this.zeroSuppressSwitch.addEventListener("click", this.boundFlipSwitch);
this.mapMemorySwitch.addEventListener("click", this.boundFlipSwitch);
+ this.speedSwitch.addEventListener("click", this.boundFlipSwitch);
this.remoteKnob.addEventListener("click", this.boundFlipSwitch);
this.formatKnob.addEventListener("click", this.boundFlipSwitch);
this.$$("Columns").addEventListener("change", this.boundText_OnChange);
@@ -459,7 +478,7 @@ B220ConsolePrinter.prototype.receiveSign = function receiveSign(char, successor)
/* Receives the sign character from the processor and handles it according
to the value of the sign and the setting of the Map Memory and LZ Suppress
switches */
- var delay = B220ConsolePrinter.charPeriod; // default character delay
+ var delay = this.charPeriod; // default character delay
var stamp = performance.now(); // current time
switch (true) {
@@ -506,7 +525,7 @@ B220ConsolePrinter.prototype.receiveSign = function receiveSign(char, successor)
B220ConsolePrinter.prototype.receiveChar = function receiveChar(char, successor) {
/* Receives a non-sign character from the processor and outputs it. Special handling
is provided for tabs, carriage returns, form feeds, and end-of-word characters */
- var delay = B220ConsolePrinter.charPeriod; // default character delay
+ var delay = this.charPeriod; // default character delay
var nextReceiver = this.boundReceiveChar; // default routine to receive next char
var stamp = performance.now(); // current time
@@ -572,7 +591,7 @@ B220ConsolePrinter.prototype.shutDown = function shutDown() {
}
if (this.window) {
- this.window.removeEventListener("beforeunload", B220ConsolePrinter.prototype.beforeUnload);
+ this.window.removeEventListener("beforeunload", B220ConsolePrinter.prototype.beforeUnload, false);
this.window.close();
this.window = null;
}
diff --git a/webUI/B220MagTapeControl.js b/webUI/B220MagTapeControl.js
index 324e8d6..b167c13 100644
--- a/webUI/B220MagTapeControl.js
+++ b/webUI/B220MagTapeControl.js
@@ -126,30 +126,15 @@ B220MagTapeControl.prototype.clear = function clear() {
};
/**************************************/
-B220MagTapeControl.prototype.findDesignate = function findDesignate(u) {
- /* Searches this.tapeUnit[] to find the internal index of the unit that is
- designated as "u". If found, returns the internal index; if not found,
- returns -1. If more than one ready unit with the same designate is found,
- returns -2 */
- var index = -1;
- var unit;
- var x;
+B220MagTapeControl.prototype.clearMisc = function clearMisc() {
+ /* Resets this.regMisc and the individual lamps for that register */
+ var bitNr;
+ var m = this.regMisc;
- for (x=this.tapeUnit.length-1; x>=0; --x) {
- unit = this.tapeUnit[x];
- if (unit && unit.ready) {
- if (unit.unitDesignate == u) {
- if (index == -1) {
- index = x;
- } else {
- index = -2;
- break; // out of for loop
- }
- }
- }
- } // for x
-
- return index;
+ m.update(0);
+ for (bitNr=m.bits-1; bitNr>= 0; --bitNr) {
+ m.lamps[bitNr].set(0);
+ }
};
/**************************************/
@@ -175,126 +160,73 @@ B220MagTapeControl.prototype.storeWord = function storeWord(initialStore, word)
};
/**************************************/
-B220MagTapeControl.prototype.queuePendingOperation = function queuePendingOperation(callee, args) {
- /* Queues a pending tape operation */
+B220MagTapeControl.prototype.reportStatus = function reportStatus(state) {
+ /* Sets bits in the MISC register to indicate various drive and control unit
+ status and error conditions */
- //console.log(this.mnemonic + " queuePendingOperation: " + args[0].toString(16));
- if (this.pendingCallee !== null) {
- throw new Error("More than one pending tape control operation");
- }
-
- this.pendingCallee = callee;
- this.pendingArgs = args;
-};
-
-/**************************************/
-B220MagTapeControl.prototype.dequeuePendingOperation = function dequeuePendingOperation() {
- /* Dequeues and reinitiates a pending tape operation */
- var args = this.pendingArgs; // pending Arguments object
- var callee = this.pendingCallee; // pending method to call
-
- this.pendingCallee = this.pendingArgs = null;
- callee.apply(this, args);
-};
-
-/**************************************/
-B220MagTapeControl.prototype.loadCommand = function loadCommand(dReg, callee, args) {
- /* If the control unit or the tape unit addressed by the unit field in dReg
- are currently busy, queues the args parameter (an Arguments object) in
- this.pendingCallee and -Args, and returns false. If the control is idle but
- the tape unit is not ready or not present, or two units have the same designate,
- calls this.releaseProcessor and returns false. If the control and tape unit
- are ready for their next operation, loads the contents of the processor's
- D register passed to the operation routines into the T, C, and MISC registers.
- Sets this.unitNr, and this.unitIndex from the digits in T. Sets this.
- currentUnit to the current tape unit object. Then returns true to inidicate
- the I/O can proceed */
- var c; // scratch
- var proceed = false; // return value: true => proceed with I/O
- var t = dReg%0x10000000000; // temp to partition fields of Processor's D register
- var ux; // internal unit index
-
- //console.log(this.mnemonic + " loadCommand: " + dReg.toString(16));
- if (this.controlBusy) {
- this.queuePendingOperation(callee, args);
- } else {
- this.T = t;
- this.regT.update(this.T);
- this.unitNr = (t - t%0x1000000000)/0x1000000000;
- t = (t - t%0x10000)/0x10000;
- c = t%0x10; // low-order digit of op code
- t = (t - t%0x100)/0x100; // control digits from instruction
- this.C = this.unitNr*0x100000 + t*0x10 + c;
- this.regC.update(this.C);
+ switch (state) {
+ case this.driveState.driveNoError:
this.clearMisc();
- this.unitIndex = ux = this.findDesignate(this.unitNr);
- if (ux < 0) {
- this.reportStatus(this.driveState.driveNotReady); // drive not ready, not present
- this.releaseProcessor(false, 0);
- } else {
- this.currentUnit = this.tapeUnit[ux];
- if (this.currentUnit.busy || this.currentUnit.rewindLock) {
- this.queuePendingOperation(callee, args);
- } else {
- proceed = true;
- this.driveState.startTime = performance.now();
- this.driveState.completionDelay = 0;
- this.driveState.state = this.driveState.driveNoError;
+ break;
+ case this.driveState.driveNotReady:
+ this.TX2Lamp.set(1);
+ this.TX10Lamp.set(1);
+ break;
+ case this.driveState.drivePrefaceCheck:
+ this.p.setMagneticTapeCheck(1);
+ this.TPCLamp.set(1);
+ break;
+ case this.driveState.drivePrefaceMismatch:
+ this.p.setMagneticTapeCheck(1);
+ this.TCFLamp.set(1);
+ this.C = (this.C & 0x00FFFF) | 0xFF0000;
+ this.regC.update(this.C);
+ break;
+ case this.driveState.driveReadCheck:
+ this.p.setMagneticTapeCheck(1);
+ this.TYC1Lamp.set(1);
+ this.TYC2Lamp.set(1);
+ this.C = (this.C & 0xFFF00F) | 0x000F90;
+ this.regC.update(this.C);
+ break;
+ case this.driveState.driveInvalidBlockLength:
+ this.p.setMagneticTapeCheck(1);
+ this.TX2Lamp.set(1);
+ this.TX4Lamp.set(1);
+ this.C = (this.C & 0x000F0F) | 0xB010F0;
+ this.regC.update(this.C);
+ break;
+ case this.driveState.driveNotEditedTape:
+ this.p.setMagneticTapeCheck(1);
+ break;
+ } // switch code
+};
+
+/**************************************/
+B220MagTapeControl.prototype.findDesignate = function findDesignate(u) {
+ /* Searches this.tapeUnit[] to find the internal index of the unit that is
+ designated as "u". If found, returns the internal index; if not found,
+ returns -1. If more than one ready unit with the same designate is found,
+ returns -2 */
+ var index = -1;
+ var unit;
+ var x;
+
+ for (x=this.tapeUnit.length-1; x>=0; --x) {
+ unit = this.tapeUnit[x];
+ if (unit && unit.ready) {
+ if (unit.unitDesignate == u) {
+ if (index == -1) {
+ index = x;
+ } else {
+ index = -2;
+ break; // out of for loop
+ }
}
}
- }
+ } // for x
- return proceed;
-};
-
-/**************************************/
-B220MagTapeControl.prototype.releaseControl = function releaseControl(param) {
- /* Releases the busy status of the control. If an error is present, sets the
- bits in the MISC register and the Processor's Magnetic Tape Check alarm, as
- appropriate. If another operation is pending, initiates that operation.
- Returns but does not use its parameter so that it can be used with
- Promise.then() */
-
- this.TFLamp.set(0);
- this.TBLamp.set(0);
- this.controlBusy = false;
- if (this.driveState.state != this.driveState.driveNoError) {
- this.currentUnit.releaseUnit(this.driveState);
- this.reportStatus(this.driveState.state);
- }
-
- if (this.pendingCallee !== null) {
- this.dequeuePendingOperation();
- }
-
- return param;
-};
-
-/**************************************/
-B220MagTapeControl.prototype.cancelIO = function cancelIO(param) {
- /* Terminates the current I/O operation by releasing the Processor, tape
- unit, and tape control unit. Returns but does not use its parameter so it
- can be used with Promise.then() */
-
- this.releaseProcessor(false, 0);
- this.currentUnit.releaseUnit();
- this.releaseControl();
- return param;
-};
-
-/**************************************/
-B220MagTapeControl.prototype.tapeUnitFinished = function tapeUnitFinished(param) {
- /* Call-back function passed to tape unit methods to signal when the unit has
- completed its asynchronous operation. Returns but does not use "param", so
- that it can be used with Promise.then() */
-
- if (!this.controlBusy) { // if the control unit is currently idle...
- if (this.pendingCallee !== null) {
- this.dequeuePendingOperation();
- }
- }
-
- return param;
+ return index;
};
/**************************************/
@@ -436,58 +368,126 @@ B220MagTapeControl.prototype.compareKeywordField = function compareKeywordField(
};
/**************************************/
-B220MagTapeControl.prototype.clearMisc = function clearMisc() {
- /* Resets this.regMisc and the individual lamps for that register */
- var bitNr;
- var m = this.regMisc;
+B220MagTapeControl.prototype.queuePendingOperation = function queuePendingOperation(callee, args) {
+ /* Queues a pending tape operation */
- m.update(0);
- for (bitNr=m.bits-1; bitNr>= 0; --bitNr) {
- m.lamps[bitNr].set(0);
+ //console.log(this.mnemonic + " queuePendingOperation: " + args[0].toString(16));
+ if (this.pendingCallee !== null) {
+ throw new Error("More than one pending tape control operation");
}
+
+ this.pendingCallee = callee;
+ this.pendingArgs = args;
};
/**************************************/
-B220MagTapeControl.prototype.reportStatus = function reportStatus(state) {
- /* Sets bits in the MISC register to indicate various drive and control unit
- status and error conditions */
+B220MagTapeControl.prototype.dequeuePendingOperation = function dequeuePendingOperation() {
+ /* Dequeues and reinitiates a pending tape operation */
+ var args = this.pendingArgs; // pending Arguments object
+ var callee = this.pendingCallee; // pending method to call
- switch (state) {
- case this.driveState.driveNoError:
+ this.pendingCallee = this.pendingArgs = null;
+ callee.apply(this, args);
+};
+
+/**************************************/
+B220MagTapeControl.prototype.loadCommand = function loadCommand(dReg, callee, args) {
+ /* If the control unit or the tape unit addressed by the unit field in dReg
+ are currently busy, queues the args parameter (an Arguments object) in
+ this.pendingCallee and -Args, and returns false. If the control is idle but
+ the tape unit is not ready or not present, or two units have the same designate,
+ calls this.releaseProcessor and returns false. If the control and tape unit
+ are ready for their next operation, loads the contents of the processor's
+ D register passed to the operation routines into the T, C, and MISC registers.
+ Sets this.unitNr, and this.unitIndex from the digits in T. Sets this.
+ currentUnit to the current tape unit object. Then returns true to inidicate
+ the I/O can proceed */
+ var c; // scratch
+ var proceed = false; // return value: true => proceed with I/O
+ var t = dReg%0x10000000000; // temp to partition fields of Processor's D register
+ var ux; // internal unit index
+
+ //console.log(this.mnemonic + " loadCommand: " + dReg.toString(16));
+ if (this.controlBusy) {
+ this.queuePendingOperation(callee, args);
+ } else {
+ this.T = t;
+ this.regT.update(this.T);
+ this.unitNr = (t - t%0x1000000000)/0x1000000000;
+ t = (t - t%0x10000)/0x10000;
+ c = t%0x10; // low-order digit of op code
+ t = (t - t%0x100)/0x100; // control digits from instruction
+ this.C = this.unitNr*0x100000 + t*0x10 + c;
+ this.regC.update(this.C);
this.clearMisc();
- break;
- case this.driveState.driveNotReady:
- this.TX2Lamp.set(1);
- this.TX10Lamp.set(1);
- break;
- case this.driveState.drivePrefaceCheck:
- this.p.setMagneticTapeCheck(1);
- this.TPCLamp.set(1);
- break;
- case this.driveState.drivePrefaceMismatch:
- this.p.setMagneticTapeCheck(1);
- this.TCFLamp.set(1);
- this.C = (this.C & 0x00FFFF) | 0xFF0000;
- this.regC.update(this.C);
- break;
- case this.driveState.driveReadCheck:
- this.p.setMagneticTapeCheck(1);
- this.TYC1Lamp.set(1);
- this.TYC2Lamp.set(1);
- this.C = (this.C & 0xFFF00F) | 0x000F90;
- this.regC.update(this.C);
- break;
- case this.driveState.driveInvalidBlockLength:
- this.p.setMagneticTapeCheck(1);
- this.TX2Lamp.set(1);
- this.TX4Lamp.set(1);
- this.C = (this.C & 0x000F0F) | 0xB010F0;
- this.regC.update(this.C);
- break;
- case this.driveState.driveNotEditedTape:
- this.p.setMagneticTapeCheck(1);
- break;
- } // switch code
+ this.unitIndex = ux = this.findDesignate(this.unitNr);
+ if (ux < 0) {
+ this.reportStatus(this.driveState.driveNotReady); // drive not ready, not present
+ this.releaseProcessor(false, 0);
+ } else {
+ this.currentUnit = this.tapeUnit[ux];
+ if (this.currentUnit.busy || this.currentUnit.rewindLock) {
+ this.queuePendingOperation(callee, args);
+ } else {
+ proceed = true;
+ this.driveState.startTime = performance.now();
+ this.driveState.completionDelay = 0;
+ this.driveState.state = this.driveState.driveNoError;
+ }
+ }
+ }
+
+ return proceed;
+};
+
+/**************************************/
+B220MagTapeControl.prototype.releaseControl = function releaseControl(param) {
+ /* Releases the busy status of the control. If an error is present, sets the
+ bits in the MISC register and the Processor's Magnetic Tape Check alarm, as
+ appropriate. If another operation is pending, initiates that operation.
+ Returns but does not use its parameter so that it can be used with
+ Promise.then() */
+
+ this.TFLamp.set(0);
+ this.TBLamp.set(0);
+ this.controlBusy = false;
+ if (this.driveState.state != this.driveState.driveNoError) {
+ this.currentUnit.releaseUnit(this.driveState);
+ this.reportStatus(this.driveState.state);
+ }
+
+ if (this.pendingCallee !== null) {
+ this.dequeuePendingOperation();
+ }
+
+ return param;
+};
+
+/**************************************/
+B220MagTapeControl.prototype.cancelIO = function cancelIO(param) {
+ /* Terminates the current I/O operation by releasing the Processor, tape
+ unit, and tape control unit. Returns but does not use its parameter so it
+ can be used with Promise.then() */
+
+ this.releaseProcessor(false, 0);
+ this.currentUnit.releaseUnit();
+ this.releaseControl();
+ return param;
+};
+
+/**************************************/
+B220MagTapeControl.prototype.tapeUnitFinished = function tapeUnitFinished(param) {
+ /* Call-back function passed to tape unit methods to signal when the unit has
+ completed its asynchronous operation. Returns but does not use "param", so
+ that it can be used with Promise.then() */
+
+ if (!this.controlBusy) { // if the control unit is currently idle...
+ if (this.pendingCallee !== null) {
+ this.dequeuePendingOperation();
+ }
+ }
+
+ return param;
};
/**************************************/
@@ -568,7 +568,7 @@ B220MagTapeControl.prototype.magTapeOnLoad = function magTapeOnLoad() {
// Events
- this.window.addEventListener("beforeunload", B220MagTapeControl.prototype.beforeUnload);
+ this.window.addEventListener("beforeunload", B220MagTapeControl.prototype.beforeUnload, false);
this.$$("ClearBtn").addEventListener("click", this.boundSwitch_Click, false);
this.regMisc.rightClearBar.addEventListener("click", this.boundSwitch_Click, false);
this.regC.rightClearBar.addEventListener("click", this.boundSwitch_Click, false);
diff --git a/webUI/B220MagTapeDrive.js b/webUI/B220MagTapeDrive.js
index 05fb1cc..3760860 100644
--- a/webUI/B220MagTapeDrive.js
+++ b/webUI/B220MagTapeDrive.js
@@ -209,13 +209,6 @@ B220MagTapeDrive.prototype.clear = function clear() {
this.tapeState = this.tapeUnloaded; // tape drive state
};
-/**************************************/
-B220MagTapeDrive.prototype.nil = function nil() {
- /* An empty function that just returns */
-
- return;
-};
-
/**************************************/
B220MagTapeDrive.prototype.releaseUnit = function releaseUnit(param) {
/* Releases the busy status of the unit. Returns but does not use its
@@ -255,105 +248,6 @@ B220MagTapeDrive.prototype.setUnitDesignate = function setUnitDesignate(index) {
}
};
-/**************************************/
-B220MagTapeDrive.prototype.spinReel = function spinReel(inches) {
- /* Rotates the reel image icon an appropriate amount based on the "inches"
- of tape to be moved. 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.maxTapeInches/2);
- var degrees = inches/circumference*360;
-
- if (degrees > this.maxSpinAngle) {
- degrees = this.maxSpinAngle;
- } else if (degrees < -this.maxSpinAngle) {
- degrees = -this.maxSpinAngle;
- }
-
- this.reelAngle = (this.reelAngle + degrees)%360;
- this.reelIcon.style.transform = "rotate(" + this.reelAngle.toFixed(0) + "deg)";
-
- this.tapeInches += inches;
- if (this.tapeInches < this.maxTapeInches) {
- this.reelBar.value = this.maxTapeInches - this.tapeInches;
- } else {
- this.reelBar.value = 0;
- }
-};
-
-/**************************************/
-B220MagTapeDrive.prototype.moveTape = function moveTape(inches, delay, successor, param) {
- /* Delays the I/O during tape motion, during which it animates the reel image
- icon. At the completion of the "delay" time in milliseconds, "successor" is
- called with "param" as a parameter */
- var delayLeft = Math.abs(delay); // milliseconds left to delay
- var direction = (inches < 0 ? -1 : 1);
- var inchesLeft = inches; // inches left to move tape
- var initiallyReady = this.ready; // remember initial ready state to detect change
- var lastStamp = performance.now(); // last timestamp for spinDelay
-
- function spinFinish() {
- this.timer = 0;
- if (inchesLeft != 0) {
- this.spinReel(inchesLeft);
- }
- successor.call(this, param);
- }
-
- function spinDelay() {
- var motion;
- var stamp = performance.now();
- var interval = stamp - lastStamp;
-
- if (interval <= 0) {
- interval = this.spinUpdateInterval/2;
- if (interval > delayLeft) {
- interval = delayLeft;
- }
- }
-
- if (initiallyReady && !this.ready) { // drive went not ready
- inchesLeft = 0;
- this.timer = setCallback(this.mnemonic, this, this.spinUpdateInterval, spinFinish);
- } else {
- delayLeft -= interval;
- if (delayLeft > this.spinUpdateInterval) {
- lastStamp = stamp;
- this.timer = setCallback(this.mnemonic, this, this.spinUpdateInterval, spinDelay);
- } else {
- this.timer = setCallback(this.mnemonic, this, delayLeft, spinFinish);
- }
-
- motion = inchesLeft*interval/delayLeft;
- if (inchesLeft*direction <= 0) { // inchesLeft crossed zero
- motion = inchesLeft = 0;
- } else if (motion*direction <= inchesLeft*direction) {
- inchesLeft -= motion;
- } else {
- motion = inchesLeft;
- inchesLeft = 0;
- }
-
- this.spinReel(motion);
- }
- }
-
- spinDelay.call(this);
-};
-
-/**************************************/
-B220MagTapeDrive.prototype.moveTapeTo = function moveTapeTo(index, result) {
- /* Advances the tape to the specified image index and returns a Promise
- that will resolve when tape motion completes */
-
- return new Promise((resolve, reject) => {
- var len = index - this.imgIndex; // number of words passed
- var delay = len*this.millisPerWord; // amount of tape spin time
-
- this.imgIndex = index;
- this.moveTape(len*this.inchesPerWord, delay, resolve, result);
- });
-};
-
/**************************************/
B220MagTapeDrive.prototype.setAtBOT = function setAtBOT(atBOT) {
/* Controls the at-Beginning-of-Tape state of the tape drive */
@@ -478,6 +372,165 @@ B220MagTapeDrive.prototype.setTapeUnloaded = function setTapeUnloaded() {
}
};
+/**************************************/
+B220MagTapeDrive.prototype.tapeRewind = function tapeRewind(laneNr, lockout) {
+ /* Rewinds the tape. Makes the drive not-ready and delays for an appropriate
+ amount of time depending on how far up-tape we are. Readies the unit again
+ when the rewind is complete unless lockout is truthy. Returns a Promise that
+ resolves when the rewind completes */
+
+ return new Promise((resolve, reject) => {
+ var lastStamp;
+
+ function rewindFinish() {
+ this.timer = 0;
+ this.tapeState = this.tapeLocal;
+ B220Util.removeClass(this.$$("MTRewindingLight"), "annunciatorLit");
+ this.rewindLock = (lockout ? true : false);
+ this.rwlLamp.set(this.rewindLock ? 1 : 0);
+ this.setTapeReady(!this.rewindLock);
+ resolve(this.setLane(laneNr, null));
+ }
+
+ function rewindDelay() {
+ var inches;
+ var stamp = performance.now();
+ var interval = stamp - lastStamp;
+
+ if (interval <= 0) {
+ interval = this.spinUpdateInterval/2;
+ }
+ if (this.tapeInches <= 0) {
+ this.setAtBOT(true);
+ this.timer = setCallback(this.mnemonic, this, 1000, rewindFinish);
+ } else {
+ inches = interval*this.rewindSpeed;
+ lastStamp = stamp;
+ this.timer = setCallback(this.mnemonic, this, this.spinUpdateInterval, rewindDelay);
+ this.spinReel(-inches);
+ }
+ }
+
+ function rewindStart() {
+ this.designatedLamp.set(0);
+ lastStamp = performance.now();
+ this.timer = setCallback(this.mnemonic, this, this.spinUpdateInterval, rewindDelay);
+ }
+
+ if (this.timer) {
+ clearCallback(this.timer);
+ this.timer = 0;
+ }
+
+ if (this.tapeState != this.tapeUnloaded && this.tapeState != this.tapeRewinding) {
+ this.busy = true;
+ this.tapeState = this.tapeRewinding;
+ this.setAtEOT(false);
+ B220Util.addClass(this.$$("MTRewindingLight"), "annunciatorLit");
+ this.timer = setCallback(this.mnemonic, this, 1000, rewindStart);
+ }
+ });
+};
+
+/**************************************/
+B220MagTapeDrive.prototype.spinReel = function spinReel(inches) {
+ /* Rotates the reel image icon an appropriate amount based on the "inches"
+ of tape to be moved. 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.maxTapeInches/2);
+ var degrees = inches/circumference*360;
+
+ if (degrees > this.maxSpinAngle) {
+ degrees = this.maxSpinAngle;
+ } else if (degrees < -this.maxSpinAngle) {
+ degrees = -this.maxSpinAngle;
+ }
+
+ this.reelAngle = (this.reelAngle + degrees)%360;
+ this.reelIcon.style.transform = "rotate(" + this.reelAngle.toFixed(0) + "deg)";
+
+ this.tapeInches += inches;
+ if (this.tapeInches < this.maxTapeInches) {
+ this.reelBar.value = this.maxTapeInches - this.tapeInches;
+ } else {
+ this.reelBar.value = 0;
+ }
+};
+
+/**************************************/
+B220MagTapeDrive.prototype.moveTape = function moveTape(inches, delay, successor, param) {
+ /* Delays the I/O during tape motion, during which it animates the reel image
+ icon. At the completion of the "delay" time in milliseconds, "successor" is
+ called with "param" as a parameter */
+ var delayLeft = Math.abs(delay); // milliseconds left to delay
+ var direction = (inches < 0 ? -1 : 1);
+ var inchesLeft = inches; // inches left to move tape
+ var initiallyReady = this.ready; // remember initial ready state to detect change
+ var lastStamp = performance.now(); // last timestamp for spinDelay
+
+ function spinFinish() {
+ this.timer = 0;
+ if (inchesLeft != 0) {
+ this.spinReel(inchesLeft);
+ }
+ successor.call(this, param);
+ }
+
+ function spinDelay() {
+ var motion;
+ var stamp = performance.now();
+ var interval = stamp - lastStamp;
+
+ if (interval <= 0) {
+ interval = this.spinUpdateInterval/2;
+ if (interval > delayLeft) {
+ interval = delayLeft;
+ }
+ }
+
+ if (initiallyReady && !this.ready) { // drive went not ready
+ inchesLeft = 0;
+ this.timer = setCallback(this.mnemonic, this, this.spinUpdateInterval, spinFinish);
+ } else {
+ delayLeft -= interval;
+ if (delayLeft > this.spinUpdateInterval) {
+ lastStamp = stamp;
+ this.timer = setCallback(this.mnemonic, this, this.spinUpdateInterval, spinDelay);
+ } else {
+ this.timer = setCallback(this.mnemonic, this, delayLeft, spinFinish);
+ }
+
+ motion = inchesLeft*interval/delayLeft;
+ if (inchesLeft*direction <= 0) { // inchesLeft crossed zero
+ motion = inchesLeft = 0;
+ } else if (motion*direction <= inchesLeft*direction) {
+ inchesLeft -= motion;
+ } else {
+ motion = inchesLeft;
+ inchesLeft = 0;
+ }
+
+ this.spinReel(motion);
+ }
+ }
+
+ spinDelay.call(this);
+};
+
+/**************************************/
+B220MagTapeDrive.prototype.moveTapeTo = function moveTapeTo(index, result) {
+ /* Advances the tape to the specified image index and returns a Promise
+ that will resolve when tape motion completes */
+
+ return new Promise((resolve, reject) => {
+ var len = index - this.imgIndex; // number of words passed
+ var delay = len*this.millisPerWord; // amount of tape spin time
+
+ this.imgIndex = index;
+ this.moveTape(len*this.inchesPerWord, delay, resolve, result);
+ });
+};
+
/**************************************/
B220MagTapeDrive.prototype.loadTape = function loadTape() {
/* Loads a tape into memory based on selections in the MTLoad window */
@@ -967,66 +1020,6 @@ B220MagTapeDrive.prototype.unloadTape = function unloadTape() {
win.addEventListener("load", unloadSetup, false);
};
-/**************************************/
-B220MagTapeDrive.prototype.tapeRewind = function tapeRewind(laneNr, lockout) {
- /* Rewinds the tape. Makes the drive not-ready and delays for an appropriate
- amount of time depending on how far up-tape we are. Readies the unit again
- when the rewind is complete unless lockout is truthy. Returns a Promise that
- resolves when the rewind completes */
-
- return new Promise((resolve, reject) => {
- var lastStamp;
-
- function rewindFinish() {
- this.timer = 0;
- this.tapeState = this.tapeLocal;
- B220Util.removeClass(this.$$("MTRewindingLight"), "annunciatorLit");
- this.rewindLock = (lockout ? true : false);
- this.rwlLamp.set(this.rewindLock ? 1 : 0);
- this.setTapeReady(!this.rewindLock);
- resolve(this.setLane(laneNr, null));
- }
-
- function rewindDelay() {
- var inches;
- var stamp = performance.now();
- var interval = stamp - lastStamp;
-
- if (interval <= 0) {
- interval = this.spinUpdateInterval/2;
- }
- if (this.tapeInches <= 0) {
- this.setAtBOT(true);
- this.timer = setCallback(this.mnemonic, this, 1000, rewindFinish);
- } else {
- inches = interval*this.rewindSpeed;
- lastStamp = stamp;
- this.timer = setCallback(this.mnemonic, this, this.spinUpdateInterval, rewindDelay);
- this.spinReel(-inches);
- }
- }
-
- function rewindStart() {
- this.designatedLamp.set(0);
- lastStamp = performance.now();
- this.timer = setCallback(this.mnemonic, this, this.spinUpdateInterval, rewindDelay);
- }
-
- if (this.timer) {
- clearCallback(this.timer);
- this.timer = 0;
- }
-
- if (this.tapeState != this.tapeUnloaded && this.tapeState != this.tapeRewinding) {
- this.busy = true;
- this.tapeState = this.tapeRewinding;
- this.setAtEOT(false);
- B220Util.addClass(this.$$("MTRewindingLight"), "annunciatorLit");
- this.timer = setCallback(this.mnemonic, this, 1000, rewindStart);
- }
- });
-};
-
/**************************************/
B220MagTapeDrive.prototype.LoadBtn_onclick = function LoadBtn_onclick(ev) {
/* Handle the click event for the LOAD button */
@@ -1217,6 +1210,16 @@ B220MagTapeDrive.prototype.startUpBackward = function startUpBackward(driveState
}
};
+/**************************************/
+B220MagTapeDrive.prototype.reverseDirection = function reverseDirection(driveState) {
+ /* Generates a delay to allow the drive to stop and reverse direction.
+ Returns a Promise that resolves when the delay is complete */
+
+ return new Promise((resolve, reject) => {
+ setCallback(this.mnemonic, this, this.turnaroundTime, resolve, driveState);
+ });
+};
+
/**************************************/
B220MagTapeDrive.prototype.reposition = function reposition(driveState) {
/* Reverses tape direction after a forward tape operation and repositions
@@ -1245,6 +1248,7 @@ B220MagTapeDrive.prototype.reposition = function reposition(driveState) {
switch (state) {
case 1: // initial state: skip backwards until erase-gap or BOT flaw-marker words
if (lane[x] == this.markerEOB) {
+ --x;
state = 2;
} else if (lane[x] == this.markerFlaw) {
state = 0;
@@ -1278,16 +1282,6 @@ B220MagTapeDrive.prototype.reposition = function reposition(driveState) {
});
};
-/**************************************/
-B220MagTapeDrive.prototype.reverseDirection = function reverseDirection(driveState) {
- /* Generates a delay to allow the drive to stop and reverse direction.
- Returns a Promise that resolves when the delay is complete */
-
- return new Promise((resolve, reject) => {
- setCallback(this.mnemonic, this, this.turnaroundTime, resolve, driveState);
- });
-};
-
/**************************************/
B220MagTapeDrive.prototype.scanBlock = function scanBlock(driveState, wordIndex) {
/* Scans one block in a forward direction. Terminates with either the control
@@ -1318,6 +1312,7 @@ B220MagTapeDrive.prototype.scanBlock = function scanBlock(driveState, wordIndex)
switch (state) {
case 1: // initial state: skip over flaw and intra-block words
if (w == this.markerGap) {
+ ++x;
state = 2;
} else {
++x;
@@ -1397,6 +1392,7 @@ B220MagTapeDrive.prototype.scanBlock = function scanBlock(driveState, wordIndex)
case 7: // step through remaining words in the block until normal EOB
if (w == this.markerEOB) {
+ ++x;
state = 8;
} else {
++x;
@@ -1452,6 +1448,7 @@ B220MagTapeDrive.prototype.searchForwardBlock = function searchForwardBlock(driv
switch (state) {
case 1: // initial state: skip over flaw and intra-block words
if (w == this.markerGap) {
+ ++x;
state = 2;
} else {
++x;
@@ -1553,6 +1550,7 @@ B220MagTapeDrive.prototype.searchBackwardBlock = function searchBackwardBlock(dr
switch (state) {
case 1: // initial state: skip over flaw and magnetic EOT words
if (w == this.markerGap) {
+ --x;
state = 2;
} else if (w == this.markerFlaw) {
--x;
@@ -1573,6 +1571,7 @@ B220MagTapeDrive.prototype.searchBackwardBlock = function searchBackwardBlock(dr
case 3: // search for start of block (first prior inter-block gap word)
if (w == this.markerGap) {
+ --x;
state = 4;
} else if (w < 0) {
count = 0;
@@ -1650,6 +1649,7 @@ B220MagTapeDrive.prototype.readNextBlock = function readNextBlock(driveState, re
switch (state) {
case 1: // initial state: skip over flaw and intra-block words
if (w == this.markerGap) {
+ ++x;
state = 2;
} else {
++x;
@@ -1774,6 +1774,7 @@ B220MagTapeDrive.prototype.readNextBlock = function readNextBlock(driveState, re
case 7: // check for proper end-of-block
if (w == this.markerEOB) {
+ ++x;
state = 9;
} else {
state = 0; // block was longer than preface indicated
@@ -1784,6 +1785,7 @@ B220MagTapeDrive.prototype.readNextBlock = function readNextBlock(driveState, re
case 8: // step through remaining words in the block until normal EOB
if (w == this.markerEOB) {
+ ++x;
state = 9;
} else {
++x;
@@ -1801,6 +1803,7 @@ B220MagTapeDrive.prototype.readNextBlock = function readNextBlock(driveState, re
case 10: // step through remaining words in the block until EOB for error
if (w == this.markerEOB) {
+ ++x;
state = 11;
} else {
++x;
@@ -1856,6 +1859,7 @@ B220MagTapeDrive.prototype.overwriteBlock = function overwriteBlock(driveState,
switch (state) {
case 1: // initial state: skip over flaw and intra-block words
if (w == this.markerGap) {
+ ++x;
state = 2;
} else {
++x;
@@ -1945,6 +1949,7 @@ B220MagTapeDrive.prototype.overwriteBlock = function overwriteBlock(driveState,
case 6: // step through remaining words in the block until normal EOB
if (w == this.markerEOB) {
+ ++x;
state = 7;
} else {
++x;
@@ -1962,6 +1967,7 @@ B220MagTapeDrive.prototype.overwriteBlock = function overwriteBlock(driveState,
case 8: // step through remaining words in the block until EOB for error
if (w == this.markerEOB) {
+ ++x;
state = 9;
} else {
++x;
@@ -2168,6 +2174,7 @@ B220MagTapeDrive.prototype.spaceForwardBlock = function spaceForwardBlock(driveS
switch (state) {
case 1: // initial state: skip over flaw and intra-block words
if (w == this.markerGap) {
+ ++x;
state = 2;
} else {
++x;
@@ -2184,6 +2191,7 @@ B220MagTapeDrive.prototype.spaceForwardBlock = function spaceForwardBlock(driveS
case 3: // found preface: search for end of block (next erase-gap word)
if (w == this.markerEOB) {
+ ++x;
state = 4;
} else {
++x;
@@ -2238,6 +2246,7 @@ B220MagTapeDrive.prototype.spaceBackwardBlock = function spaceBackwardBlock(driv
switch (state) {
case 1: // initial state: skip over flaw and magnetic EOT words
if (w == this.markerGap) {
+ --x;
state = 2;
} else if (w == this.markerFlaw) {
--x;
@@ -2342,6 +2351,7 @@ B220MagTapeDrive.prototype.spaceEOIBlock = function spaceEOIBlock(driveState) {
case 3: // search for end of block (next erase-gap word)
if (w == this.markerEOB) {
+ ++x;
state = 4;
} else {
++x;
diff --git a/webUI/B220Manifest.appcache b/webUI/B220Manifest.appcache
index e249001..5f86e9f 100644
--- a/webUI/B220Manifest.appcache
+++ b/webUI/B220Manifest.appcache
@@ -1,5 +1,5 @@
CACHE MANIFEST
-# retro-220 emulator 0.03c, 2017-11-17 06:15
+# retro-220 emulator 0.04, 2017-11-19 15:00
CACHE:
../emulator/B220Processor.js
B220.css
diff --git a/webUI/B220PaperTapePunch.js b/webUI/B220PaperTapePunch.js
index 59eca68..39e1d35 100644
--- a/webUI/B220PaperTapePunch.js
+++ b/webUI/B220PaperTapePunch.js
@@ -58,16 +58,22 @@ B220PaperTapePunch.codeXlate = [ // translate internal B220 code to ANSI
// so B220 carriage-return (16) translates to "|". To avoid space-expansion
// of tabs (26), they are translated to "~". The 02 "blank" code is "_".
// Form-feed (15) translates to "^".
- " ", "?", "_", ".", "\u00A4", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 00-0F
- "&", "?", "?", "$", "*", "^", "|", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 10-1F
- "-", "/", "?", ",", "%", "?", "~", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 20-2F
- "?", "?", "?", "#", "@", "!", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 30-3F
- "?", "A", "B", "C", "D", "E", "F", "G", "H", "I", "?", "?", "?", "?", "?", "?", // 40-4F
- "?", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "?", "?", "?", "?", "?", "?", // 50-5F
- "?", "?", "S", "T", "U", "V", "W", "X", "Y", "Z", "?", "?", "?", "?", "?", "?", // 60-6F
- "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 70-7F
- "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "?", "?", "?", "?", "?", "?", // 80-8F
- "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]; // 90-9F
+ " ", "?", "_", ".", "\u00A4", "?", "?", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 00-0F
+ "&", "?", "?", "$", "*", "^", "|", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 10-1F
+ "-", "/", "?", ",", "%", "?", "~", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 20-2F
+ "?", "?", "?", "#", "@", "\\", "?", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 30-3F
+ "?", "A", "B", "C", "D", "E", "F", "G", "H", "I", "!", "!", "!", "!", "!", "!", // 40-4F
+ "?", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "!", "!", "!", "!", "!", "!", // 50-5F
+ "?", "?", "S", "T", "U", "V", "W", "X", "Y", "Z", "!", "!", "!", "!", "!", "!", // 60-6F
+ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 70-7F
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "!", "!", "!", "!", "!", // 80-8F
+ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 90-9F
+ "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // A0-AF
+ "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // B0-BF
+ "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // C0-CF
+ "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // D0-DF
+ "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // E0-EF
+ "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"]; // F0-FF
/**************************************/
@@ -105,6 +111,7 @@ B220PaperTapePunch.prototype.punchEmptyLine = function punchEmptyLine(text) {
}
paper.lastChild.nodeValue += "\n"; // newline
paper.appendChild(this.doc.createTextNode(line));
+ this.punchEOP.scrollIntoView();
};
/**************************************/
diff --git a/webUI/B220PaperTapeReader.js b/webUI/B220PaperTapeReader.js
index 9b2b3a8..0d89235 100644
--- a/webUI/B220PaperTapeReader.js
+++ b/webUI/B220PaperTapeReader.js
@@ -247,7 +247,7 @@ B220PaperTapeReader.prototype.readerOnload = function readerOnload() {
body = this.$$("PaperTapeReader")
this.remoteSwitch = new ToggleSwitch(body, null, null, "RemoteSwitch",
B220PaperTapeReader.offSwitchImage, B220PaperTapeReader.onSwitchImage);
- this.remoteSwitch.set(prefs.remote);
+ this.remoteSwitch.set(0); // ignore prefs.remote, always initialize as LOCAL
this.readyLamp = new ColoredLamp(body, null, null, "ReadyLamp", "blueLamp lampCollar", "blueLit");
this.setReaderReady(this.remoteSwitch.state != 0);
@@ -360,36 +360,30 @@ B220PaperTapeReader.prototype.readTapeChar = function readTapeChar(receiver) {
this.window.focus(); // call attention to the tape reader
} else {
this.busy = false;
- do {
- if (x >= bufLength) { // end of buffer -- send finish
- this.sendTapeChar(0x20, 0x35, receiver);
- this.setReaderEmpty();
- break; // out of do loop
- } else {
- c = this.buffer.charCodeAt(x) % 0x100;
- if (c == 0x0D) { // carriage return -- send EOW and check for LF
- if (++x < bufLength && this.buffer.charCodeAt(x) == 0x0A) {
- ++x;
- }
- this.sendTapeChar(0x20, 0x35, receiver);
- if (x >= bufLength) {
- this.setReaderEmpty();
- }
- break; // out of do loop
- } else if (c == 0x0A) { // line feed -- send EOW
+ if (x >= bufLength) { // end of buffer -- send finish
+ this.sendTapeChar(0x20, 0x35, receiver);
+ this.setReaderEmpty();
+ } else {
+ c = this.buffer.charCodeAt(x) % 0x100;
+ if (c == 0x0D) { // carriage return -- send EOW and check for LF
+ if (++x < bufLength && this.buffer.charCodeAt(x) == 0x0A) {
++x;
- this.sendTapeChar(0x20, 0x35, receiver);
- if (x >= bufLength) {
- this.setReaderEmpty();
- }
- break; // out of do loop
- } else { // translate character and send its code
- ++x;
- this.sendTapeChar(c, B220PaperTapeReader.xlate220[c], receiver);
- break; // out of do loop
}
+ this.sendTapeChar(0x20, 0x35, receiver);
+ if (x >= bufLength) {
+ this.setReaderEmpty();
+ }
+ } else if (c == 0x0A) { // line feed -- send EOW
+ ++x;
+ this.sendTapeChar(0x20, 0x35, receiver);
+ if (x >= bufLength) {
+ this.setReaderEmpty();
+ }
+ } else { // translate character and send its code
+ ++x;
+ this.sendTapeChar(c, B220PaperTapeReader.xlate220[c], receiver);
}
- } while (true);
+ }
this.tapeSupplyBar.value = bufLength-x;
this.bufIndex = x;
diff --git a/webUI/B220SystemConfig.js b/webUI/B220SystemConfig.js
index 537d6a1..4d4ba00 100644
--- a/webUI/B220SystemConfig.js
+++ b/webUI/B220SystemConfig.js
@@ -76,7 +76,7 @@ B220SystemConfig.defaultConfig = {
ConsoleOutput: {
units: [
- {type: "TTYA", unitMask: 0x001, remote: 1, format: 0, zeroSuppress: 0, mapMemory: 0,
+ {type: "TTYA", unitMask: 0x001, remote: 1, format: 0, zeroSuppress: 0, mapMemory: 0, printerSpeed: 0,
columns: 72, tabs: "9,17,25,33,41,49,57,65,73,81"},
{type: "NONE"},
{type: "NONE"},
@@ -460,6 +460,7 @@ B220SystemConfig.prototype.saveConfigDialog = function saveConfigDialog() {
unit.remote = (unit.remote || 0);
unit.zeroSuppress = (unit.zeroSuppress || 0);
unit.mapMemory = (unit.mapMemory || 0);
+ unit.printerSpeed = (unit.printerSpeed || 0);
e = this.$$(prefix + "Format");
unit.format = (e.selectedIndex < 0 ? "NONE" : e.options[e.selectedIndex].value);
unit.columns = (unit.columns ? unit.columns : 72);