1
0
mirror of https://github.com/pkimpel/retro-220.git synced 2026-04-15 16:10:49 +00:00

Commit 220 emulator version 0.4:

1. Minor improvements to magnetic tape implementation and rearrangement of code.
2. Correct Processor CAD/CAA/CSU/CSA operation for operand signs other than 0 or 1.
3. Correct Processor LDB to load only the low-order four digits of the operand into B.
4. Correct Processor SLS shift count; improve mechanization of other shift-left operations.
5. Fix beforeunload event registration for Cardatron and Console devices.
6. Implement "Whippet" mode for Console TTY devices to print at 200 CPS instead of 10 CPS.
7. Expand B220PaperTapePunch translate table to cover all 256 8-bit codes; scroll end of punched data into view.
8. Minor code cleanup in B220PaperTapeReader.
This commit is contained in:
Paul Kimpel
2017-11-19 17:56:09 -08:00
parent 654af4f683
commit 6c51a5803b
14 changed files with 567 additions and 483 deletions

View File

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

View File

@@ -18,10 +18,6 @@
Burroughs 220 Emulator &ndash; Hosting Site Home
</h1>
<p style="text-align:center; font-size:larger; font-weight:bold; color:red">
(Under Construction)
</p>
<p>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.</p>
<p class=center>
@@ -69,7 +65,7 @@
Copyright (c) 2017, Paul Kimpel &bull; Licensed under the <a href="LICENSE.txt">MIT License</a>
</div>
<div id=lastModDiv>Revised
2016-12-25
2017-11-19
</div>
</p>

View File

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

View File

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

View File

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

View File

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

View File

@@ -63,6 +63,9 @@
<div id=TabStopsCaption class=caption>TAB STOPS</div>
<input id=TabStops class=data type=text maxlength=80 title="comma-delimited list of 1-relative tab stops">
<div id=WhippetSpeedCaption class=caption>SPEED<br>WHIPPET</div>
<div id=TTYSpeedCaption class=caption>TTY</div>
<div id=UnitSwitch0Caption class=caption>SPO</div>
<div id=UnitSwitch1Caption class=caption>1</div>
<div id=UnitSwitch2Caption class=caption>2</div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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