1
0
mirror of https://github.com/pkimpel/retro-220.git synced 2026-04-25 19:51:55 +00:00

Commit emulator version 1.00.

1. Rework Processor internal timing and throttling mechanism during I/O.
2. Revise Console statistics display, add instruction counter.
3. Correct (again) CFA/CFR instruction when sign is included in field.
4. Correct B-register modification of words during Cardatron and
magnetic tape input.
5. Clear Processor alarms on Reset/Transfer.
6. Add links to wiki on index and home pages.
7. Eliminate B220Util CSS class functions in favor of DOM classList
methods.
8. Attempt to reproduce "Sundland Beige" color for the panels.
9. Correct formatting of tab stops for B220ConsolePrinter.
10. Reduce Whippet printer speed from 5000 to 1000 cps.
11. Reduce Console update frequency from every 50 to 100 ms; increase
lamp glow update factor from 0.25 to 0.75.
12. Allow click of white button below console register lamps in addition
to clicking the lamps themselves to toggle the lamp state.
13. Rework the way that white vertical bars are drawn on registers.
14. Allow B220PaperTapeReader to properly send sign-2 alphanumeric words
with trailing spaces if the tape image file has been space-trimmed on
the right.
15. Clear the paper tape reader view window when loading new tapes.
16. Revise yet again the setCallback() delay deviation adjustment
algorithm.
This commit is contained in:
Paul Kimpel
2018-07-17 06:57:01 -07:00
parent 653cbe98cb
commit 67958aa65a
21 changed files with 447 additions and 267 deletions

View File

@@ -66,11 +66,16 @@
<td id=StatusMsg>
<td class=rj><a href="http://datatron.blogspot.com/" target="_blank">
Burroughs 205 &amp; 220 Blog</a>
<tr>
<td><a href="https://github.com/pkimpel/retro-220/wiki" target="_blank">
Project Wiki</a>
<td class=center>&nbsp;
<td class=rj>&nbsp;
</table>
<div id=CenteredBody>
<img id=B220Image src="./resources/B220-Site.jpg"
alt="Burroughs 220 System, ca. 1960">
alt="Burroughs 220 System, Michigan National Bank, ca. 1960">
<br>
<button id=StartUpBtn>
Start the Emulator

View File

@@ -121,9 +121,8 @@ window.addEventListener("load", function() {
if (!window.JSON) {missing += ", JSON"}
if (!window.localStorage) {missing += ", LocalStorage"}
if (!window.indexedDB) {missing += ", IndexedDB"}
if (!window.postMessage) {missing += ", window.postMessage"}
if (!(window.performance && "now" in performance)) {missing += ", performance.now"}
if (!window.Promise) {missing += ", Promise"}
if (!(window.performance && "now" in performance)) {missing += ", performance.now"}
if (missing.length == 0) {
return true;
@@ -147,7 +146,7 @@ window.addEventListener("load", function() {
document.getElementById("ConfigureBtn").disabled = false;
document.getElementById("ConfigureBtn").addEventListener("click", configureSystem, false);
document.getElementById("StatusMsg").textContent = "The Application Cache feature has been deimplemented";
clearStatusMsg(30);
//document.getElementById("StatusMsg").textContent = "The Application Cache feature has been deimplemented";
//clearStatusMsg(30);
}
}, false);

View File

@@ -79,7 +79,7 @@ function B220CardatronInput(mnemonic, unitIndex, config) {
/**************************************/
B220CardatronInput.prototype.eolRex = /([^\n\r\f]*)((:?\r[\n\f]?)|\n|\f)?/g;
B220CardatronInput.prototype.cardsPerMinute = 240; // IBM Type 087/089 collator
B220CardatronInput.prototype.cardsPerMinute = 240; // 240=IBM Type 087/089 collator, 100=Type 523 Summary Punch
B220CardatronInput.prototype.eodBias = -0x900000000000; // signals end-of-data to Processor
B220CardatronInput.trackSize = 319; // digits
@@ -184,12 +184,12 @@ B220CardatronInput.prototype.setReaderReady = function setReaderReady(ready) {
this.$$("CIFileSelector").disabled = ready;
if (ready && !this.ready) {
B220Util.addClass(this.$$("CIStartBtn"), "greenLit")
B220Util.removeClass(this.$$("CIStopBtn"), "redLit");
this.$$("CIStartBtn").classList.add("greenLit")
this.$$("CIStopBtn").classList.remove("redLit");
this.ready = true;
} else if (this.ready && !ready) {
B220Util.removeClass(this.$$("CIStartBtn"), "greenLit")
B220Util.addClass(this.$$("CIStopBtn"), "redLit");
this.$$("CIStartBtn").classList.remove("greenLit")
this.$$("CIStopBtn").classList.add("redLit");
this.ready = false;
}
};
@@ -444,7 +444,7 @@ B220CardatronInput.prototype.determineFormatBand = function determineFormatBand(
case "7":
format = 7;
break;
case "`": // 1-8 punch
case "\`": // 1-8 punch
format = 1;
this.noReload = true;
this.setFormatLockout(true);
@@ -464,7 +464,7 @@ B220CardatronInput.prototype.determineFormatBand = function determineFormatBand(
this.noReload = true;
this.setFormatLockout(true);
break;
case "'": // 5-8 punch
case "\'": // 5-8 punch
case "|": // translates to a 5-numeric digit
format = 5;
this.noReload = true;

View File

@@ -87,8 +87,7 @@
position: absolute;
top: 32px;
left: 384px;
right: 0;
width: calc(100% - 346px);
width: calc(100% - 394px);
height: 18px;
border: 1px solid white}

View File

@@ -209,12 +209,12 @@ B220CardatronOutput.prototype.setDeviceReady = function setDeviceReady(ready) {
this.runoutSupplyCount = 0;
if (ready && !this.ready) {
B220Util.addClass(this.$$("COStartBtn"), "greenLit")
B220Util.removeClass(this.$$("COStopBtn"), "redLit");
this.$$("COStartBtn").classList.add("greenLit")
this.$$("COStopBtn").classList.remove("redLit");
this.ready = true;
} else if (!ready && this.ready) {
B220Util.removeClass(this.$$("COStartBtn"), "greenLit")
B220Util.addClass(this.$$("COStopBtn"), "redLit");
this.$$("COStartBtn").classList.remove("greenLit")
this.$$("COStopBtn").classList.add("redLit");
this.ready = false;
}
};
@@ -224,7 +224,7 @@ B220CardatronOutput.prototype.runoutSupply = function runoutSupply(ev) {
/* Handles an event to clear the supply from the printer/punch */
this.runoutSupplyCount = 0;
B220Util.removeClass(this.$$("COEndOfSupplyBtn"), "redLit");
this.$$("COEndOfSupplyBtn").classList.remove("redLit");
this.supplyMeter.value = this.supplyLeft = this.maxSupplyLines;
this.groupLinesLeft = 0;
while (this.supply.firstChild) {
@@ -251,7 +251,7 @@ B220CardatronOutput.prototype.copySupply = function copySupply(ev) {
barGroup = barGroup.nextSibling;
}
B220Util.openPopup(this.window, "./B220FramePaper.html", this.mnemonic + "-Snapshot",
B220Util.openPopup(this.window, "./B220FramePaper.html", "",
"scrollbars,resizable,width=500,height=500",
this, function(ev) {
var doc = ev.target;
@@ -333,10 +333,10 @@ B220CardatronOutput.prototype.printLine = function printLine(text, spaceBefore)
this.appendLine(text || "\xA0");
if (this.supplyLeft > 0) {
this.supplyMeter.value = this.supplyLeft -= 1;
this.supplyMeter.value = (this.supplyLeft -= 1);
} else {
this.setDeviceReady(false);
B220Util.addClass(this.$$("COEndOfSupplyBtn"), "redLit");
this.$$("COEndOfSupplyBtn").classList.add("redLit");
}
};
@@ -430,12 +430,12 @@ B220CardatronOutput.prototype.initiateWrite = function initiateWrite() {
}
// Convert to ASCII line image and determine carriage control
line = String.fromCharCode.apply(null, this.lineBuffer.subarray(lx, this.lineWidth+lx));
line = String.fromCharCode.apply(null, this.lineBuffer.subarray(lx, this.lineWidth+lx))
.replace(this.rtrimRex, '');
if (this.useAlgolGlyphs) {
line = B220Util.xlateASCIIToAlgol(line.replace(this.rtrimRex, ''));
} else {
line = line.replace(this.rtrimRex, '');
line = B220Util.xlateASCIIToAlgol(line);
}
switch (this.cDigit) {
case 1: // Relay 1 (eject page after printing)
case 9: // same as 1
@@ -544,10 +544,14 @@ B220CardatronOutput.prototype.COStopBtn_onClick = function COStopBtn_onClick(ev)
/**************************************/
B220CardatronOutput.prototype.CORunoutSupplyBtn_onClick = function CORunoutSupplyBtn_onClick(ev) {
/* Handle the click event for the Skip To Heading button */
/* Handle the click event for the Skip To Heading or Runout Supply button */
if (!this.ready) {
this.printLine("", -1);
if (this.atTopOfForm) {
this.appendLine("\xA0"); // force start of a new greenbar group
}
this.skipToChannel();
this.endOfSupply.scrollIntoView();
if (++this.runoutSupplyCount >= 3) {
if (this.window.confirm("Do you want to clear the output from the device?")) {
@@ -569,7 +573,7 @@ B220CardatronOutput.prototype.COEndOfSupplyBtn_onClick = function COEndOfSupplyB
if (this.supplyLeft <= 0 && !this.ready) {
this.runoutSupplyCount = 0;
B220Util.removeClass(this.$$("COEndOfSupplyBtn"), "redLit");
this.$$("COEndOfSupplyBtn").classList.remove("redLit");
this.setDeviceReady(true);
}
};
@@ -651,8 +655,8 @@ B220CardatronOutput.prototype.COSetZSBtn_onClick = function COSetZSBtn_onClick(e
zsCol = tron.parseZeroSuppressList(text, win);
if (zsCol !== null) {
tron.zsCol = zsCol;
B220Util.removeClass(tron.$$("COSetZSBtn"), (zsCol.length > 0 ? "blackButton1" : "greenButton1"));
B220Util.addClass(tron.$$("COSetZSBtn"), (zsCol.length > 0 ? "greenButton1" : "blackButton1"));
tron.$$("COSetZSBtn").classList.remove(zsCol.length > 0 ? "blackButton1" : "greenButton1");
tron.$$("COSetZSBtn").classList.add(zsCol.length > 0 ? "greenButton1" : "blackButton1");
// Store the new list in the system configuration object
text = zsCol.join(",");
@@ -758,8 +762,8 @@ B220CardatronOutput.prototype.deviceOnLoad = function deviceOnLoad(ev) {
if (zsCol !== null) {
this.zsCol = zsCol;
if (zsCol.length > 0) {
B220Util.removeClass(this.$$("COSetZSBtn"), (zsCol.length > 0 ? "blackButton1" : "greenButton1"));
B220Util.addClass(this.$$("COSetZSBtn"), (zsCol.length > 0 ? "greenButton1" : "blackButton1"));
this.$$("COSetZSBtn").classList.remove(zsCol.length > 0 ? "blackButton1" : "greenButton1");
this.$$("COSetZSBtn").classList.add(zsCol.length > 0 ? "greenButton1" : "blackButton1");
}
}

View File

@@ -138,7 +138,6 @@ DIV.neonLamp {
height: 16px;
font-size: 4px;
border-radius: 50%;
/* border: 2px solid #999; */
background-image: radial-gradient(circle, #999, #333)}
DIV.neonLit1 {
background-image: radial-gradient(circle, #A85, #444)}
@@ -308,7 +307,7 @@ DIV.blackControlKnobBottomCaption {
DIV.panelSurface {
position: absolute;
background-color: #D8C5BC; /* was #E4DDCD; putty #EDEAE8; */
background-color: #EADAD1; /* was #D8C5BC; #E4DDCD; putty #EDEAE8; */
color: black}
DIV.panelRegister {
@@ -374,8 +373,8 @@ DIV.panelTopCaption {
top: 0.75em;
left: 4px;
right: 4px;
border-top: 2px solid #D8C5BC;
color: #D8C5BC}
border-top: 2px solid #EADAD1;
color: #EADAD1}
SPAN.panelTopCaptionSpan {
position: relative;
@@ -383,7 +382,7 @@ SPAN.panelTopCaptionSpan {
font-size: 7px;
padding-left: 4px;
padding-right: 4px;
color: #D8C5BC;
color: #EADAD1;
background-color: #333}
DIV.panelRegCaption {
@@ -505,7 +504,7 @@ BUTTON.plainButton {
width: auto;
height: 24px;
padding: 2px;
color: #D8C5BC;
color: #EADAD1;
background-color: #999;
box-shadow: 3px 3px 2px #999;
border: 1px solid #DDD;
@@ -515,10 +514,10 @@ BUTTON.panelLabel {
position: absolute;
width: 32px;
height: 18px;
color: #D8C5BC;
color: #EADAD1;
background-color: #333;
border-radius: 4px;
border: 1px solid #D8C5BC;
border: 1px solid #EADAD1;
line-height: 5px;
font-size: 5px;
font-weight: normal}

View File

@@ -45,6 +45,7 @@ function B220ConsolePrinter(mnemonic, unitIndex, config) {
// Create the printer window and onload event
this.doc = null;
this.window = null;
this.paper = null;
this.printerEOP = null;
this.printerLine = 0;
@@ -61,8 +62,8 @@ B220ConsolePrinter.onSwitchImage = "./resources/ToggleUp.png";
B220ConsolePrinter.ttySpeed = 10; // TTY printer speed, char/sec
B220ConsolePrinter.ttyNewLine = 200; // TTY carriage-return delay, ms
B220ConsolePrinter.whippetSpeed = 5000; // Whippet printer speed, char/sec
B220ConsolePrinter.whippetNewLine = 200;// Whippet carriage-return delay, ms
B220ConsolePrinter.whippetSpeed = 1000; // Whippet printer speed, char/sec
B220ConsolePrinter.whippetNewLine = 75; // Whippet carriage-return delay, ms
B220ConsolePrinter.formFeedPeriod = 500;// form-feed average delay, ms
B220ConsolePrinter.pageSize = 66; // lines/page for form-feed
@@ -166,8 +167,8 @@ B220ConsolePrinter.prototype.printChar = function printChar(code) {
/**************************************/
B220ConsolePrinter.prototype.printTab = function printTab() {
/* Simulates tabulation by outputting an appropriate number of spaces */
var tabCol; // tabulation column
var x; // scratch index
var tabCol = this.columns+1; // tabulation column (defaults to line overflow)
var x = 0; // scratch index
for (x=0; x<this.tabStop.length; ++x) {
if (this.tabStop[x] > this.printerCol) {
@@ -210,7 +211,7 @@ B220ConsolePrinter.prototype.copyPaper = function copyPaper(ev) {
var text = this.paper.textContent;
var title = "B220 " + this.mnemonic + " Text Snapshot";
B220Util.openPopup(this.window, "./B220FramePaper.html", this.mnemonic + "-Snapshot",
B220Util.openPopup(this.window, "./B220FramePaper.html", "",
"scrollbars,resizable,width=500,height=500",
this, function(ev) {
var doc = ev.target;
@@ -314,7 +315,7 @@ B220ConsolePrinter.prototype.text_OnChange = function text_OnChange(ev) {
/* Handler for text onchange events */
var prefs = this.config.getNode("ConsoleOutput.units", this.unitIndex);
var text = ev.target.value;
var v;
var v = null;
switch (ev.target.id) {
case "Columns":
@@ -329,7 +330,7 @@ B220ConsolePrinter.prototype.text_OnChange = function text_OnChange(ev) {
v = this.parseTabStops(prefs.tabs || "", this.window);
if (v !== null) {
this.tabStop = v;
ev.target.value = text = v.join(",");
ev.target.value = text = this.formatTabStops(v);
prefs.tabs = text;
}
break;
@@ -341,7 +342,21 @@ B220ConsolePrinter.prototype.text_OnChange = function text_OnChange(ev) {
};
/**************************************/
B220ConsolePrinter.prototype.parseTabStops = function parsetabStops(text, alertWin) {
B220ConsolePrinter.prototype.formatTabStops = function formatTabStops(tabStops) {
/* Formats the array "tabStops" of 0-relative tab stop positions as a comma-
delimited string of 1-relative numbers */
var s = (tabStops[0]+1).toString();
var x = 0;
for (x=1; x<tabStops.length; ++x) {
s += "," + (tabStops[x]+1).toString();
}
return s;
};
/**************************************/
B220ConsolePrinter.prototype.parseTabStops = function parseTabStops(text, alertWin) {
/* Parses a comma-delimited list of 1-relative tab stops. If the list is parsed
successfully, returns an array of 0-relative tab stop positions; otherwise
returns null. An alert is displayed on the window for the first parsing or
@@ -386,7 +401,7 @@ B220ConsolePrinter.prototype.printerOnLoad = function printerOnLoad(ev) {
var id;
var mask;
var prefs = this.config.getNode("ConsoleOutput.units", this.unitIndex);
var tabStop;
var tabStop = null;
var x;
this.doc = ev.target;
@@ -442,7 +457,7 @@ B220ConsolePrinter.prototype.printerOnLoad = function printerOnLoad(ev) {
tabStop = this.parseTabStops(prefs.tabs || "", this.window);
if (tabStop !== null) {
this.tabStop = tabStop;
this.$$("TabStops").value = tabStop.join(",");
this.$$("TabStops").value = this.formatTabStops(tabStop);
}
// Events

View File

@@ -90,22 +90,28 @@
#ProcDelta {
position: absolute;
width: 64px;
width: 48px;
text-align: right;
left: 120px;
bottom: 12px}
left: 132px;
bottom: 16px}
#ProcSlack {
position: absolute;
width: 64px;
text-align: right;
left: 190px;
bottom: 12px}
left: 180px;
bottom: 16px}
#ProcRun {
position: absolute;
width: 64px;
width: 48px;
text-align: right;
left: 260px;
bottom: 12px}
left: 244px;
bottom: 16px}
#ICount {
position: absolute;
width: 80px;
text-align: right;
left: 292px;
bottom: 16px}
#PanelSurface {
height: 100%;

View File

@@ -151,8 +151,9 @@
<button id=RightPanelBtn class=plainButton title="Some day there may be a Right Panel, too">Right Panel</button>
<div id=ProcDelta title="Average Processor delay delta (ms)"></div>
<div id=ProcSlack title="Average Processor slack time (ms)"></div>
<div id=ProcSlack title="Average % Processor slack time"></div>
<div id=ProcRun title="Average Processor run time (ms)"></div>
<div id=ICount title="Total instructions executed"></div>
<div id=VersionDiv class=caption>
retro-220 <span id=EmulatorVersion>?.??</span>

View File

@@ -103,13 +103,13 @@ function B220ControlConsole(p, systemShutdown) {
}
/**************************************/
B220ControlConsole.displayRefreshPeriod = 50; // milliseconds
B220ControlConsole.displayRefreshPeriod = 100; // milliseconds
B220ControlConsole.offSwitchImage = "./resources/ToggleDown.png";
B220ControlConsole.onSwitchImage = "./resources/ToggleUp.png";
B220ControlConsole.offOrganSwitchImage = "./resources/Organ-Switch-Up.png"
B220ControlConsole.onOrganSwitchImage = "./resources/Organ-Switch-Down.png"
B220ControlConsole.codeXlate = [ // translate internal B220 code to ANSI
B220ControlConsole.codeXlate = [ // translate internal 220 code to ANSI
" ", "_", " ", ".", "\u00A4", "_", "_", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 00-0F
"&", "_", "_", "$", "*", "^", "~", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 10-1F
"-", "/", "_", ",", "%", "_", "|", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 20-2F
@@ -392,7 +392,7 @@ B220ControlConsole.prototype.meatballMemdump = function meatballMemdump() {
}
// Outer block of meatBallMemdump
B220Util.openPopup(this.window, "./B220FramePaper.html", this.mnemonic + "-MEMDUMP",
B220Util.openPopup(window, "./B220FramePaper.html", "",
"location=no,scrollbars=yes,resizable,width=800,height=600",
this, memdumpSetup);
};
@@ -470,7 +470,7 @@ B220ControlConsole.prototype.updatePanel = function updatePanel() {
text = (timer/1000 + 10000).toFixed(1);
this.intervalTimer.textContent = text.substring(text.length-6);
p.updateLampGlow(p.AST.value ? 0.25 : 0);
p.updateLampGlow(p.AST.value ? 0.75 : 0);
eLevel = (p.RUT.value ? p.EXT.glow : p.EXT.value);
// Primary Registers
@@ -506,9 +506,16 @@ B220ControlConsole.prototype.updatePanel = function updatePanel() {
this.equalLamp.set(p.compareEqualLamp.glow);
this.highLamp.set(p.compareHighLamp.glow);
this.$$("ProcDelta").textContent = p.delayDeltaAvg.toFixed(2) + " D";
this.$$("ProcSlack").textContent = p.procSlackAvg.toFixed(2) + " S";
this.$$("ProcRun").textContent = p.procRunAvg.toFixed(2) + " R";
// Compute the timing statistics
timer = this.p.procTimer;
while (timer <= 0) {
timer += stamp;
}
this.$$("ProcDelta").textContent = p.delayDeltaAvg.toFixed(2) + "=D";
this.$$("ProcSlack").textContent = (p.procSlack/timer*100).toFixed(2) + "%S";
this.$$("ProcRun").textContent = p.procRunAvg.toFixed(2) + "=R";
this.$$("ICount").textContent = B220Util.toFixedWithCommas(p.instructionCount) + "=I";
/********** DEBUG **********
this.displayCallbackState();
***************************/
@@ -526,47 +533,43 @@ B220ControlConsole.prototype.lamp_Click = function lamp_Click(ev) {
var reg; // register prefix from id
if (p.poweredOn) {
if (ix < 0) {
return;
} else if (ix > 0) {
if (ix > 0) {
reg = id.substring(0, ix);
bit = parseInt(id.substring(ix+1), 10);
if (isNaN(bit)) {
return;
if (!isNaN(bit)) {
ev.preventDefault();
ev.stopPropagation();
switch (reg) {
case "A":
p.A.flipBit(bit);
break;
case "B":
p.B.flipBit(bit);
break;
case "C":
p.C.flipBit(bit);
break;
case "D":
p.D.flipBit(bit);
break;
case "E":
p.E.flipBit(bit);
break;
case "P":
p.P.flipBit(bit);
break;
case "R":
p.R.flipBit(bit);
break;
case "S":
p.S.flipBit(bit);
break;
} // switch reg
}
}
switch (reg) {
case "A":
p.A.flipBit(bit);
break;
case "B":
p.B.flipBit(bit);
break;
case "C":
p.C.flipBit(bit);
break;
case "D":
p.D.flipBit(bit);
break;
case "E":
p.E.flipBit(bit);
break;
case "P":
p.P.flipBit(bit);
break;
case "R":
p.R.flipBit(bit);
break;
case "S":
p.S.flipBit(bit);
break;
} // switch reg
}
ev.preventDefault();
ev.stopPropagation();
return false;
};
/**************************************/
@@ -608,7 +611,6 @@ B220ControlConsole.prototype.switch_Click = function switch_Click(ev) {
break;
case "StepSwitch":
this.stepSwitch.flip();
//this.keyboard.keyboardEnable(0);
p.step();
break;
case "ClearSwitch":
@@ -776,9 +778,17 @@ B220ControlConsole.prototype.switch_Click = function switch_Click(ev) {
p.tracing = !p.tracing;
this.$$("LeftPanelBtn").focus(); // release any selection by the click
if (p.tracing) {
B220Util.addClass(ev.target, "tracing");
ev.target.classList.add("tracing");
} else {
B220Util.removeClass(ev.target, "tracing");
ev.target.classList.remove("tracing");
}
break;
case "Blank2LampLabel": // initialize to boot from cards (undocumented)
if (!p.RUT.value) {
p.clear();
p.C.set(0x1000600000); // CRD unit 1
p.setCycle(1);
}
break;
} // switch ev.target.id
@@ -792,11 +802,11 @@ B220ControlConsole.prototype.switch_Click = function switch_Click(ev) {
/**************************************/
B220ControlConsole.prototype.consoleOnLoad = function consoleOnLoad(ev) {
/* Initializes the Supervisory Panel window and user interface */
var body;
var barStyles = {backgroundColor: "white"};
var body = null;
var p = this.p; // local copy of processor object
var panel;
var panel = null;
var prefs = this.config.getNode("ControlConsole");
var x;
this.doc = ev.target;
this.window = this.doc.defaultView;
@@ -815,10 +825,14 @@ B220ControlConsole.prototype.consoleOnLoad = function consoleOnLoad(ev) {
this.regP = new PanelRegister(this.$$("PRegPanel"), 4*4, 4, "P_", "P");
this.regS = new PanelRegister(this.$$("SRegPanel"), 4*4, 4, "S_", "S");
this.regA.drawBox(6, 2, 4, "2px solid white", "2px solid white");
this.regC.drawBox(5, 2, 4, "2px solid white", "2px solid white");
this.regD.drawBox(6, 2, 4, "2px solid white", "2px solid white");
this.regR.drawBox(6, 2, 4, "2px solid white", "2px solid white");
this.regA.drawBar(6, 4, barStyles);
this.regA.drawBar(8, 4, barStyles);
this.regC.drawBar(5, 4, barStyles);
this.regC.drawBar(7, 4, barStyles);
this.regD.drawBar(6, 4, barStyles);
this.regD.drawBar(8, 4, barStyles);
this.regR.drawBar(6, 4, barStyles);
this.regR.drawBar(8, 4, barStyles);
// Status Panels
@@ -998,6 +1012,7 @@ B220ControlConsole.prototype.consoleOnLoad = function consoleOnLoad(ev) {
this.$$("BurroughsMeatball").addEventListener("click", this.boundMeatballMemdump, false);
this.$$("B220Logo").addEventListener("dblclick", this.boundSwitch_Click);
this.$$("Blank2LampLabel").addEventListener("click", this.boundSwitch_Click);
this.$$("IntervalTimerResetBtn").addEventListener("click", this.boundResetTimer, false);
this.$$("PowerOffBtn").addEventListener("dblclick", this.boundPowerBtn_Click, false);

View File

@@ -34,9 +34,11 @@ window.addEventListener("load", function(ev) {
var p = null; // the Processor object
var delayDeltaNode = document.getElementById("DelayDeltaAvg").firstChild;
var execClockNode = document.getElementById("ExecClock").firstChild;
var lastDelayNode = document.getElementById("Delay").firstChild;
var procRunNode = document.getElementById("ProcRunAvg").firstChild;
var procSlackNode = document.getElementById("ProcSlackAvg").firstChild;
var procTimeNode = document.getElementById("ProcTime").firstChild;
var stampDeltaNode = document.getElementById("StampDelta").firstChild;
var timeStampNode = document.getElementById("TimeStamp").firstChild;
function clockIn(stamp, val) {
@@ -53,7 +55,7 @@ window.addEventListener("load", function(ev) {
function refreshStats() {
/* Obtains the current "delayDev" has from the SetCallback mechanism and
formats the data to DiagBody */
var cat;
var cat = "";
var cell;
var textNode;
var delayDev;
@@ -66,10 +68,12 @@ window.addEventListener("load", function(ev) {
p = window.global.B220Processor.instance;
} else {
timeStampNode.nodeValue = p.runStamp.toFixed(2);
execClockNode.nodeValue = clockIn(stamp, p.execClock).toFixed(2);
execClockNode.nodeValue = p.execClock.toFixed(2);
stampDeltaNode.nodeValue = (p.execClock-p.runStamp).toFixed(2);
lastDelayNode.nodeValue = p.delayRequested.toFixed(2);
procTimeNode.nodeValue = clockIn(stamp, p.procTime).toFixed(2);
delayDeltaNode.nodeValue = p.delayDeltaAvg.toFixed(2);
procSlackNode.nodeValue = p.procSlackAvg.toFixed(4);
procSlackNode.nodeValue = p.procSlackAvg.toFixed(2);
procRunNode.nodeValue = p.procRunAvg.toFixed(4);
}
@@ -121,6 +125,8 @@ window.addEventListener("load", function(ev) {
<tbody id=DiagBody>
<tr><td id=TimeStamp class=rj> <td>Time Stamp
<tr><td id=ExecClock class=rj> <td>Exec Clock
<tr><td id=StampDelta class=rj> <td>Stamp Delta
<tr><td id=Delay class=rj> <td>Last delay
<tr><td id=ProcTime class=rj> <td>Proc Time
<tr><td id=DelayDeltaAvg class=rj> <td>Delay Delta Avg
<tr><td id=ProcSlackAvg class=rj> <td>Proc Slack Avg

View File

@@ -274,12 +274,12 @@ B220MagTapeDrive.prototype.setAtBOT = function setAtBOT(atBOT) {
if (atBOT ^ this.atBOT) {
this.atBOT = atBOT;
if (!atBOT) {
B220Util.removeClass(this.$$("MTAtBOTLight"), "annunciatorLit");
this.$$("MTAtBOTLight").classList.remove("annunciatorLit");
} else {
this.imgIndex = 0;
this.tapeInches = 0;
this.reelAngle = 0;
B220Util.addClass(this.$$("MTAtBOTLight"), "annunciatorLit");
this.$$("MTAtBOTLight").classList.add("annunciatorLit");
this.reelBar.value = this.maxTapeInches;
this.reelIcon.style.transform = "none";
}
@@ -303,9 +303,9 @@ B220MagTapeDrive.prototype.setAtEOT = function setAtEOT(atEOT) {
if (atEOT ^ this.atEOT) {
this.atEOT = atEOT;
if (!atEOT) {
B220Util.removeClass(this.$$("MTAtEOTLight"), "annunciatorLit");
this.$$("MTAtEOTLight").classList.remove("annunciatorLit");
} else {
B220Util.addClass(this.$$("MTAtEOTLight"), "annunciatorLit");
this.$$("MTAtEOTLight").classList.add("annunciatorLit");
this.reelBar.value = 0;
}
}
@@ -379,7 +379,7 @@ B220MagTapeDrive.prototype.setTapeUnloaded = function setTapeUnloaded() {
this.reelIcon.style.visibility = "hidden";
this.$$("MTFileName").value = "";
this.$$("MTLaneNrLight").style.visibility = "hidden";
B220Util.addClass(this.$$("MTUnloadedLight"), "annunciatorLit");
this.$$("MTUnloadedLight").classList.add("annunciatorLit");
if (this.timer) {
clearCallback(this.timer);
this.timer = 0;
@@ -400,7 +400,7 @@ B220MagTapeDrive.prototype.tapeRewind = function tapeRewind(laneNr, lockout) {
function rewindFinish() {
this.timer = 0;
this.tapeState = this.tapeLocal;
B220Util.removeClass(this.$$("MTRewindingLight"), "annunciatorLit");
this.$$("MTRewindingLight").classList.remove("annunciatorLit");
this.rewindLock = (lockout ? true : false);
this.rwlLamp.set(this.rewindLock ? 1 : 0);
this.setTapeReady(!this.rewindLock);
@@ -441,7 +441,7 @@ B220MagTapeDrive.prototype.tapeRewind = function tapeRewind(laneNr, lockout) {
this.busy = true;
this.tapeState = this.tapeRewinding;
this.setAtEOT(false);
B220Util.addClass(this.$$("MTRewindingLight"), "annunciatorLit");
this.$$("MTRewindingLight").classList.add("annunciatorLit");
this.timer = setCallback(this.mnemonic, this, 1000, rewindStart);
}
});
@@ -581,7 +581,7 @@ B220MagTapeDrive.prototype.loadTape = function loadTape() {
mt.$$("MTLaneNrLight").style.visibility = "visible";
mt.setTapeReady(true);
mt.reelIcon.style.visibility = "visible";
B220Util.removeClass(mt.$$("MTUnloadedLight"), "annunciatorLit");
mt.$$("MTUnloadedLight").classList.remove("annunciatorLit");
}
function writeBlockStart(length) {
@@ -1088,7 +1088,7 @@ B220MagTapeDrive.prototype.unloadTape = function unloadTape() {
}
// Outer block of unloadTape
B220Util.openPopup(this.window, "./B220FramePaper.html", this.mnemonic + "-Unload",
B220Util.openPopup(this.window, "./B220FramePaper.html", "",
"location=no,scrollbars=yes,resizable,width=800,height=600",
this, unloadSetup);
};

View File

@@ -54,8 +54,9 @@
</div>
<div id=MTLoadNotes>
To load a blank tape, simply click <b>OK</b>; otherwise
select a tape-image file before clicking <b>OK</b>.
To load a blank tape, select the type from the <b>Format</b> list and
click <b>OK</b>; otherwise select a tape-image file before clicking
<b>OK</b>.
</div>
<div id=MTLoadButtonGroup>

View File

@@ -818,6 +818,29 @@ PanelRegister.prototype.panelHeight = function panelHeight(rows) {
return (rows-1)*PanelRegister.vSpacing + PanelRegister.vOffset*2 + PanelRegister.lampDiameter;
};
/**************************************/
PanelRegister.prototype.drawBar = function drawBar(col, rows, styles) {
/* Creates a bar along the left border of a group of lamps in a register.
styles is a JS object with name/value pairs to set additional styles for
the bar's <div> element */
var bar = document.createElement("div");
var e = "";
bar.style.position = "absolute";
bar.style.left = (this.xCoord(col) - (PanelRegister.hSpacing-PanelRegister.lampDiameter)/2 + 1).toString() + "px";
bar.style.width = "2px";
bar.style.top = this.yCoord(1).toString() + "px";
bar.style.height = (this.yCoord(rows+1) - this.yCoord(1)).toString() + "px";
if (styles) {
for (e in styles) {
bar.style[e] = styles[e];
}
}
this.element.appendChild(bar);
return bar;
};
/**************************************/
PanelRegister.prototype.drawBox = function drawBox(col, lamps, rows, leftStyle, rightStyle) {
/* Creates a box centered around a specified group of lamps in a register.

View File

@@ -157,7 +157,7 @@ B220PaperTapePunch.prototype.punchCopyTape = function punchCopyTape(ev) {
var text = this.punchTape.textContent;
var title = "B220 " + this.mnemonic + " Text Snapshot";
B220Util.openPopup(this.window, "./B220FramePaper.html", this.mnemonic + "-Snapshot",
B220Util.openPopup(this.window, "./B220FramePaper.html", "",
"scrollbars,resizable,width=500,height=500",
this, function(ev) {
var doc = ev.target;

View File

@@ -35,8 +35,10 @@ function B220PaperTapeReader(mnemonic, unitIndex, config) {
this.boundReadTapeChar = B220PaperTapeReader.prototype.readTapeChar.bind(this);
this.readResult = { // object passed back to Processor for each read
code: 0,
readChar: this.boundReadTapeChar
code: 0, // 220 char code sent to processor
signDigit: 0, // copy of first digit read for word
frameCount: 0, // count of tape frames read for word
readChar: this.boundReadTapeChar // callback function for next char
};
this.clear();
@@ -154,6 +156,7 @@ B220PaperTapeReader.prototype.fileSelector_onChange = function fileSelector_onCh
}
}
this.tapeView.value = "";
for (x=f.length-1; x>=0; x--) {
tape = new FileReader();
tape.onload = fileLoader_onLoad;
@@ -302,6 +305,9 @@ B220PaperTapeReader.prototype.initiateInput = function initiateInput(successor)
the processor and gets the ball rolling */
var stamp = performance.now();
this.readResult.code = 0;
this.readResult.signDigit = 0;
this.readResult.frameCount = 0;
if (stamp-this.nextCharTime < B220PaperTapeReader.idleTime) {
this.readTapeChar(successor);
} else {
@@ -320,6 +326,11 @@ B220PaperTapeReader.prototype.sendTapeChar = function sendTapeChar(c, code, rece
var text = this.tapeView.value;
this.readResult.code = code;
++this.readResult.frameCount;
if (this.readResult.frameCount == 1) {
this.readResult.signDigit = code;
}
if (this.nextCharTime < stamp) {
delay = 0;
this.nextCharTime = stamp + this.charPeriod;
@@ -339,6 +350,29 @@ B220PaperTapeReader.prototype.sendTapeChar = function sendTapeChar(c, code, rece
this.tapeView.setSelectionRange(length-1, length);
};
/**************************************/
B220PaperTapeReader.prototype.conditionalSendEOW = function conditionalSendEOW(receiver) {
/* Checks for the special case of sending an alphanumeric word (sign=2) with
less than five alpha characters. Since text editors may trim trailing blanks
from the end of lines, alpha words with trailing blanks may be trimmed in the
tape image file. If the sign digit was 2 and fewer than six characters (sign
plus five alpha codes) have been sent to the processor, sends a space code to
pad the alpha word and returns false. Otherwise, sends an End-of-Word code and
returns true */
var code = 0x35; // EOW code
var result = true;
if (this.readResult.signDigit == 0x82) { // sign digit was a "2"
if (this.readResult.frameCount < 6) {
code = 0x00; // space code
result = false;
}
}
this.sendTapeChar(0x20, code, receiver);
return result;
};
/**************************************/
B220PaperTapeReader.prototype.readTapeChar = function readTapeChar(receiver) {
/* Reads one character frame from the paper-tape buffer and passes it to the
@@ -352,7 +386,7 @@ B220PaperTapeReader.prototype.readTapeChar = function readTapeChar(receiver) {
loaded, the fileSelector_onChange event will set ready, notice the hanging
read (this.busy=true) and restart the read */
var bufLength = this.bufLength; // current buffer length
var c; // current character ANSI code
var c = 0; // current character ANSI code
var x = this.bufIndex; // current buffer index
if (!this.ready) {
@@ -362,23 +396,26 @@ B220PaperTapeReader.prototype.readTapeChar = function readTapeChar(receiver) {
} else {
this.busy = false;
if (x >= bufLength) { // end of buffer -- send finish
this.sendTapeChar(0x20, 0x35, receiver);
this.setReaderEmpty();
if (this.conditionalSendEOW(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();
if (this.conditionalSendEOW(receiver)) {
if (++x < bufLength && this.buffer.charCodeAt(x) == 0x0A) {
++x;
}
if (x >= bufLength) {
this.setReaderEmpty();
}
}
} else if (c == 0x0A) { // line feed -- send EOW
++x;
this.sendTapeChar(0x20, 0x35, receiver);
if (x >= bufLength) {
this.setReaderEmpty();
if (this.conditionalSendEOW(receiver)) {
++x;
if (x >= bufLength) {
this.setReaderEmpty();
}
}
} else { // translate character and send its code
++x;
@@ -386,8 +423,8 @@ B220PaperTapeReader.prototype.readTapeChar = function readTapeChar(receiver) {
}
}
this.tapeSupplyBar.value = bufLength-x;
this.bufIndex = x;
this.tapeSupplyBar.value = bufLength-x;
}
};

View File

@@ -80,12 +80,13 @@
* retro-205 project.
* 2017-10-16 P.Kimpel
* Replace window.postMessage yield mechanism with one based on Promise().
* 2018-07-05 P.Kimpel
* Simplify delay and deviation adjustment algorithm.
***********************************************************************/
"use strict";
(function (global) {
/* Define a closure for the setCallback() mechanism */
var alpha = 0.25; // decay factor for delay deviation adjustment
var delayDev = {NUL: 0}; // hash of delay time deviations by category
var minTimeout = 4; // minimum setTimeout() threshold, milliseconds
var lastTokenNr = 0; // last setCallback token return value
@@ -145,11 +146,10 @@
to "fcn". If the delay is less than "minTimeout", a setImmediate-like mechanism
based on DOM Promise() will be used; otherwise the environment's standard
setTimeout mechanism will be used */
var adj = 0; // adjustment to delay and delayDev[]
var categoryName = (category || "NUL").toString();
var delay = callbackDelay || 0; // actual delay to be generated
var delayBias; // current amount of delay deviation
var thisCallback; // call-back object to be used
var delayBias = 0; // current amount of delay deviation
var thisCallback = null; // call-back object to be used
var token = ++lastTokenNr; // call-back token number
var tokenName = token.toString(); // call-back token ID
@@ -161,7 +161,14 @@
pool[poolLength] = null;
}
// Fill in the call-back object and tank it in pendingCallbacks.
thisCallback.startStamp = perf.now();
thisCallback.category = categoryName;
thisCallback.delay = delay;
thisCallback.context = context || this;
thisCallback.fcn = fcn;
thisCallback.arg = arg;
pendingCallbacks[tokenName] = thisCallback;
// Adjust the requested delay based on the current delay deviation
// for this category.
@@ -171,42 +178,19 @@
} else {
if (delayBias > 0) {
// We are delaying too much and should try to delay less.
if (delay < 0) {
adj = 0; // don't make delay any more negative
} else {
adj = -Math.min(delay, delayBias, minTimeout)*alpha;
if (delay > 0) { // don't make a negative delay any more so
delay -= Math.min(delay, delayBias, minTimeout);
}
} else { // delayBias < 0
} else { // delayBias <= 0
// We are delaying too little and should try to delay more.
if (delay < 0) {
if (delay - minTimeout < delayBias) {
adj = -delayBias;
} else {
adj = minTimeout - delay;
}
delay -= Math.max(delay, delayBias);
} else {
if (delay > minTimeout) {
adj = 0;
} else if (delay - minTimeout < delayBias) {
adj = -delayBias;
} else {
adj = minTimeout - delay;
}
delay += Math.min(-delayBias, minTimeout);
}
}
delay += adj;
delayDev[categoryName] += adj;
}
// Fill in the call-back object and tank it in pendingCallbacks.
thisCallback.category = categoryName;
thisCallback.delay = delay;
thisCallback.context = context || this;
thisCallback.fcn = fcn;
thisCallback.arg = arg;
pendingCallbacks[tokenName] = thisCallback;
// Decide whether to do a time wait or just a yield.
if (delay > minTimeout) {
thisCallback.cancelToken = global.setTimeout(activateCallback, delay, token);

View File

@@ -49,24 +49,18 @@ B220Util.$$ = function $$(e) {
};
/**************************************/
B220Util.hasClass = function hasClass(e, name) {
/* returns true if element "e" has class "name" in its class list */
B220Util.toFixedWithCommas = function toFixedWithCommas(v) {
/* Formats the value "v" as an integer with commas separating thousands */
var s = v.toFixed();
var t = "";
var x = s.length;
return e.classList.contains(name);
};
while (x > 3) {
t = "," + s.substring(x-3, x) + t;
x -= 3;
}
/**************************************/
B220Util.addClass = function addClass(e, name) {
/* Adds a class "name" to the element "e"s class list */
e.classList.add(name);
};
/**************************************/
B220Util.removeClass = function removeClass(e, name) {
/* Removes the class "name" from the element "e"s class list */
e.classList.remove(name);
return s.substring(0, x) + t;
};
/**************************************/
@@ -164,7 +158,7 @@ B220Util.xlateDOMTreeText = function xlateDOMTreeText(n, xlate) {
/**************************************/
B220Util.openPopup = function openPopup(parent, url, windowName, options, context, onload) {
/* Schedules the opening of a pop-up window so that browsers such as Apple
Safari (11.0+) will not block the opens if they occur too close together.
Safari (11.0+) will not block the opens if they occur too close together.
Parameters:
parent: parent window for the pop-up
url: url of window context, passed to window.open()
@@ -222,7 +216,7 @@ B220Util.dequeuePopup = function dequeuePopup() {
}
loader2 = function(ev) { // remove the load event listeners after loading
win.removeEventListener("load", loader2, false);
win.removeEventListener("load", loader2, false);
if (loader1) {
win.removeEventListener("load", loader1, false);
}