1
0
mirror of https://github.com/pkimpel/retro-b5500.git synced 2026-02-12 03:07:30 +00:00

Release emulator version 0.16:

1. Fix zero-length datacom message read in IOUnit.
2. Insert additional delay at end-of-message for B5500DatacomUnit.
3. Fix minor behavioral problems in B5500MagTapeDrive.
4. Minor wiki corrections and enhancements.
This commit is contained in:
paul.kimpel@digm.com
2013-11-21 04:31:13 +00:00
parent 2e13aedbb6
commit 3da0844cc7
8 changed files with 78 additions and 141 deletions

View File

@@ -184,6 +184,7 @@ B5500CardReader.prototype.CRProgressBar_onclick = function CRProgressBar_onclick
this.bufLength = 0;
this.bufIndex = 0;
this.progressBar.value = 0;
this.$$("CRFileSelector").value = null; // reset the control
while (this.outHopper.childNodes.length > 0) {
this.outHopper.removeChild(this.outHopper.firstChild);
}

View File

@@ -44,6 +44,7 @@ BUTTON.greenButton {
border-radius: 4px}
BUTTON.greenLit {
color: black;
background-color: #0F0}
BUTTON.blackBorder {

View File

@@ -29,7 +29,7 @@ function B5500DatacomUnit(mnemonic, unitIndex, designate, statusChange, signal)
this.maxScrollLines = 1500; // Maximum amount of printer scrollback
this.charPeriod = 100; // Printer speed, milliseconds per character
this.bufferSize = 112; // 4 B487 buffer segments
this.bufferSize = 112; // 4 28-character B487 buffer segments
this.mnemonic = mnemonic; // Unit mnemonic
this.unitIndex = unitIndex; // Ready-mask bit number
@@ -247,14 +247,13 @@ B5500DatacomUnit.prototype.appendEmptyLine = function appendEmptyLine() {
/* Removes excess lines already printed, then appends a new <pre> element
to the <iframe>, creating an empty text node inside the new element */
var count = this.paper.childNodes.length;
var line = this.doc.createTextNode("");
while (count-- > this.maxScrollLines) {
this.paper.removeChild(this.paper.firstChild);
}
this.paper.lastChild.nodeValue += String.fromCharCode(0x0A); // newline
this.endOfPaper.scrollIntoView();
this.paper.appendChild(line);
this.paper.appendChild(this.doc.createTextNode(""));
};
/**************************************/
@@ -350,7 +349,7 @@ B5500DatacomUnit.prototype.terminateInput = function terminateInput() {
/**************************************/
B5500DatacomUnit.prototype.keyPress = function keyPress(ev) {
/* Handles keyboard character events. Depending on the state of the unit,
/* Handles keyboard character events. Depending on the state of the buffer,
either buffers the character for transmission to the I/O Unit, echos
it to the printer, or ignores it altogether */
var c = ev.charCode;
@@ -407,14 +406,17 @@ B5500DatacomUnit.prototype.keyPress = function keyPress(ev) {
if (this.bufState == this.bufReadReady && this.fullBuffer) {
this.interrupt = true;
this.setState(this.bufInputBusy);
this.signal(); // buffer overflow
setCallback(this.signal, this, delay); // buffer overflow
ev.stopPropagation();
ev.preventDefault();
} else if (this.bufState == this.bufInputBusy || this.bufState == this.bufIdle) {
switch (c) {
case 0x7E: // ~ left-arrow (Group Mark), end of message
this.inTimer = setCallback(this.printChar, this, delay, c);
this.nextCharTime = nextTime;
this.terminateInput();
this.nextCharTime = this.charPeriod + nextTime;
setCallback(this.terminateInput, this, this.charPeriod+delay);
ev.stopPropagation();
ev.preventDefault();
break;
case 0x3C: // <, backspace
if (this.bufIndex > 0) {
@@ -423,40 +425,41 @@ B5500DatacomUnit.prototype.keyPress = function keyPress(ev) {
this.inTimer = setCallback(this.printChar, this, delay, c);
this.nextCharTime = nextTime;
ev.stopPropagation();
ev.preventDefault();
break;
case 0x21: // ! EOT, disconnect
//this.buffer[this.bufIndex++] = 0x7D; // } greater-or-equal code
this.buffer[this.bufIndex++] = 0x7D; // } greater-or-equal code
this.interrupt = true;
this.abnomal = true;
this.setState(this.bufReadReady);
this.signal();
setCallback(this.signal, this, delay);
this.inTimer = setCallback(this.printChar, this, delay, c);
this.nextCharTime = nextTime;
ev.stopPropagation();
ev.preventDefault();
break;
case 0x02: // Ctrl-B, STX, break on output
if (this.bufState == this.bufOutputBusy) {
this.interrupt = true;
this.abnormal = true;
this.setState(this.bufReadReady);
this.signal();
} else if (this.bufState == this.bufInputBusy) {
this.bufIndex = this.bufLength = 0;
this.setState(this.bufIdle);
}
case 0x02: // Ctrl-B, STX, break on input
this.bufIndex = this.bufLength = 0;
this.setState(this.bufIdle);
ev.stopPropagation();
ev.preventDefault();
break;
case 0x05: // Ctrl-E, ENQ, who-are-you (WRU)
if (this.bufState == this.bufIdle || this.bufState == this.bufInputBusy) {
this.interrupt = true;
this.abnormal = true;
this.setState(this.bufWriteReady);
this.signal();
setCallback(this.signal, this, delay);
}
ev.stopPropagation();
ev.preventDefault();
break;
case 0x0C: // Ctrl-L, FF, clear input buffer
if (this.bufState == this.bufInputBusy) {
this.bufIndex = this.bufLength = 0;
}
ev.stopPropagation();
ev.preventDefault();
break;
case 0x3F: // ? question-mark, set abnormal for control message
this.abnormal = true;
@@ -468,96 +471,28 @@ B5500DatacomUnit.prototype.keyPress = function keyPress(ev) {
this.buffer[this.bufIndex++] = c;
this.inTimer = setCallback(this.printChar, this, delay, c);
this.nextCharTime = nextTime;
ev.stopPropagation();
if (this.bufIndex < this.bufferSize) {
this.setState(this.bufInputBusy);
} else {
this.interrupt = true;
this.fullBuffer = true;
this.setState(this.bufReadReady);
this.signal(); // full buffer, no GM detected
setCallback(this.signal, this, this.charPeriod+delay); // full buffer, no GM detected
}
ev.stopPropagation();
ev.preventDefault();
}
break;
}
}
ev.preventDefault();
}
};
/**************************************/
B5500DatacomUnit.prototype.keyDown = function keyDown(ev) {
/* Handles key-down events to capture Ctrl-E (ENQ), Ctrl-L (FF), Ctrl-Q (DC1),
and Enter keystrokes */
var c = ev.keyCode;
var delay;
var nextTime;
var result = true;
var stamp;
//this.$$("KeyCode").innerHTML = "KC " + c.toString() + ":0x" + c.toString(16) +
// (ev.shiftKey ? "S" : " ") + (ev.ctrlKey ? "C" : " ") + (ev.altKey ? "A" : " ");
if (this.connected) {
stamp = new Date().getTime();
nextTime = (this.nextCharTime < stamp ? stamp : this.nextCharTime) + this.charPeriod;
delay = nextTime - stamp;
switch (c) {
case 0x08: // Backspace
if (this.bufState == this.bufInputBusy) {
if (this.bufIndex > 0) {
this.bufIndex--;
}
this.inTimer = setCallback(this.printChar, this, delay, 0x3C);
this.nextCharTime = nextTime;
}
break;
case 0x0D: // Enter
//case 0x11: // Ctrl-Q, DC1 (X-ON)
if (this.bufState == this.bufInputBusy || this.bufState == this.bufIdle) {
this.inTimer = setCallback(this.printChar, this, delay, 0x7E);
this.nextCharTime = nextTime;
this.terminateInput();
ev.preventDefault();
ev.stopPropagation();
break;
}
break;
case 0x02: // Ctrl-B, STX, break on output
if (this.bufState == this.bufOutputBusy) {
} // switch c
} else if (this.bufState == this.bufOutputBusy) {
if (c == 0x02) { // Ctrl-B, STX, break on output
this.interrupt = true;
this.abnormal = true;
this.setState(this.bufReadReady);
this.signal();
} else if (this.bufState == this.bufInputBusy) {
this.bufIndex = this.bufLength = 0;
this.setState(this.bufIdle);
setCallback(this.signal, this, delay);
ev.stopPropagation();
ev.preventDefault();
}
break;
case 0x05: // Ctrl-E, ENQ, who-are-you (WRU)
if (this.bufState == this.bufIdle || this.bufState == this.bufInputBusy) {
this.interrupt = true;
this.abnormal = true;
this.setState(this.bufWriteReady);
this.signal();
}
break;
case 0x0C: // Ctrl-L, FF, clear input buffer
if (this.bufState == this.bufInputBusy) {
this.bufIndex = this.bufLength = 0;
}
break;
default:
result = false;
break;
}
this.showBufferIndex();
if (result) {
ev.preventDefault();
ev.stopPropagation();
}
}
};
@@ -590,7 +525,7 @@ B5500DatacomUnit.prototype.datacomOnload = function datacomOnload() {
var x;
this.doc = this.window.document;
this.doc.title = "retro-B5500 " + this.mnemonic + " TU1/BUF0";
this.doc.title = "retro-B5500 " + this.mnemonic + ": TU/BUF=01/00";
this.paper = this.doc.createElement("pre");
this.paper.appendChild(this.doc.createTextNode(""));
this.$$("TermOut").contentDocument.body.appendChild(this.paper);
@@ -641,13 +576,13 @@ B5500DatacomUnit.prototype.read = function read(finish, buffer, length, mode, co
this.errorMask |= 0x34; // not connected -- set buffer not ready
break;
case this.bufState == this.bufReadReady:
// Copy the adaptor buffer to the IOUnit buffer
// Copy the adapter buffer to the IOUnit buffer
actualLength = (transparent ? this.bufferSize : this.bufIndex);
for (x=0; x<actualLength; x++) {
buffer[x] = this.buffer[x];
}
// Set the state bits in the result and reset the adaptor to idle
// Set the state bits in the result and reset the adapter to idle
if (this.abnormal) {
this.errorMask |= 0x200; // set abnormal bit
}
@@ -803,9 +738,14 @@ B5500DatacomUnit.prototype.writeInterrogate = function writeInterrogate(finish,
this.errorMask |= 0x34; // not a valid TU/BUF -- report not ready
} else if (tuNr == 1 && bufNr > 0) {
this.errorMask |= 0x34 // not a valid BUF for TU#1 -- report not ready
} else if (this.interrupt) {
tuNr = 1; // it must be for TU=1, BUF=0
bufNr = 0;
} else if (tuNr == 0) {
if (this.interrupt) {
tuNr = 1;
bufNr = 0;
}
}
if (tuNr == 1 && bufNr == 0) {
switch (this.bufState) {
case this.bufReadReady:
this.errorMask |= 0x100;

View File

@@ -218,7 +218,7 @@ B5500MagTapeDrive.prototype.setAtEOT = function setAtEOT(atEOT) {
B5500MagTapeDrive.prototype.setTapeUnloaded = function setTapeUnloaded() {
/* Controls the loaded/unloaded-state of the tape drive */
if (this.tapeState != this.tapeRemote) {
if (this.tapeState == this.tapeLocal && this.atBOT) {
this.tapeState = this.tapeUnloaded;
this.image = null; // release the tape image to GC
this.imgIndex = this.imgLength = 0;
@@ -298,7 +298,8 @@ B5500MagTapeDrive.prototype.setWriteRing = function setWriteRing(writeRing) {
B5500MagTapeDrive.prototype.tapeRewind = function tapeRewind(makeReady) {
/* Rewinds the tape. Makes the drive not-ready and delays for an appropriate amount
of time depending on how far up-tape we are. If makeReady is true, then readies
the unit again when the rewind is complete (valid only from this.rewind()) */
the unit again when the rewind is complete [valid only when called from
this.rewind()] */
var inches;
var inchFactor = this.imgIndex/this.tapeInches;
var lastStamp = new Date().getTime();
@@ -307,8 +308,11 @@ B5500MagTapeDrive.prototype.tapeRewind = function tapeRewind(makeReady) {
var stamp = new Date().getTime();
var interval = stamp - lastStamp;
if (interval <= 0) {
interval = 1;
}
if (this.tapeInches > 0) {
inches = interval/1000*this.rewindSpeed + 1;
inches = interval/1000*this.rewindSpeed;
this.tapeInches -= inches;
this.spinReel(-inches);
this.progressBar.value = this.imgLength - this.tapeInches*inchFactor;
@@ -333,7 +337,7 @@ B5500MagTapeDrive.prototype.tapeRewind = function tapeRewind(makeReady) {
this.setAtEOT(false);
this.addClass(this.$$("MTRewindingLight"), "whiteLit");
this.timer = setCallback(rewindDelay, this,
this.startStopTime*1000 + this.initiateStamp - new Date().getTime());
this.startStopTime*1000 + this.initiateStamp - lastStamp);
}
};
@@ -772,16 +776,12 @@ B5500MagTapeDrive.prototype.read = function read(finish, buffer, length, mode, c
count = this.bcdReadBackward(mode);
residue = 7 - count % 8;
imgCount -= this.imgIndex;
if (imgCount > 0) {
inches = -imgCount/this.density - this.gapLength;
}
inches = -imgCount/this.density - this.gapLength;
} else {
count = this.bcdReadForward(mode);
residue = count % 8;
imgCount = this.imgIndex - imgCount;
if (imgCount > 0) {
inches = imgCount/this.density + this.gapLength;
}
inches = imgCount/this.density + this.gapLength;
}
this.tapeInches += inches;
@@ -800,7 +800,7 @@ B5500MagTapeDrive.prototype.read = function read(finish, buffer, length, mode, c
}
this.buffer = null;
}
//console.log(this.mnemonic + " read: c=" + control + ", length=" + length + ", mode=" + mode +
//console.log(this.mnemonic + " read: c=" + control + ", length=" + length + ", mode=" + mode +
// ", count=" + count + ", inches=" + this.tapeInches +
// ", index=" + this.imgIndex + ", mask=" + this.errorMask.toString(8));
//console.log(String.fromCharCode.apply(null, buffer.subarray(0, 80)).substring(0, count));
@@ -827,15 +827,11 @@ B5500MagTapeDrive.prototype.space = function space(finish, length, control) {
if (control) {
this.bcdSpaceBackward(true);
imgCount -= this.imgIndex;
if (imgCount > 0) {
inches = -imgCount/this.density - this.gapLength;
}
inches = -imgCount/this.density - this.gapLength;
} else {
this.bcdSpaceForward(true);
imgCount = this.imgIndex - imgCount;
if (imgCount > 0) {
inches = imgCount/this.density + this.gapLength;
}
inches = imgCount/this.density + this.gapLength;
}
this.tapeInches += inches;
@@ -853,7 +849,7 @@ B5500MagTapeDrive.prototype.space = function space(finish, length, control) {
this.progressBar.value = 0;
}
}
//console.log(this.mnemonic + " space: c=" + control + ", length=" + length +
//console.log(this.mnemonic + " space: c=" + control + ", length=" + length +
// ", count=" + imgCount + ", inches=" + this.tapeInches +
// ", index=" + this.imgIndex + ", mask=" + this.errorMask.toString(8));
};
@@ -889,7 +885,7 @@ B5500MagTapeDrive.prototype.rewind = function rewind(finish) {
this.buildErrorMask(0);
finish(this.errorMask, 0);
}
//console.log(this.mnemonic + " rewind: mask=" + this.errorMask.toString(8));
//console.log(this.mnemonic + " rewind: mask=" + this.errorMask.toString(8));
};
/**************************************/

View File

@@ -60,7 +60,7 @@ BUTTON.greenButton {
color: black;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: 8pt;
font-weight: bold;
font-weight: normal;
width: 60px;
height: 40px;
border: 1px solid black;
@@ -72,7 +72,7 @@ BUTTON.yellowButton {
color: black;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: 8pt;
font-weight: bold;
font-weight: normal;
width: 60px;
height: 40px;
border: 1px solid black;

View File

@@ -153,17 +153,16 @@ B5500SPOUnit.prototype.setRemote = function setRemote() {
/**************************************/
B5500SPOUnit.prototype.appendEmptyLine = function appendEmptyLine() {
/* Removes excess lines already printed, then appends a new <pre> element
to the <iframe>, creating an empty text node inside the new element */
/* Removes excess lines already printed, then appends a new text node
to the <pre> element within the <iframe> */
var count = this.paper.childNodes.length;
var line = this.doc.createTextNode("");
this.printChar(0x0A); // newline
while (count-- > this.maxScrollLines) {
this.paper.removeChild(this.paper.firstChild);
}
this.endOfPaper.scrollIntoView();
this.paper.appendChild(line);
this.paper.appendChild(this.doc.createTextNode(""));
this.printCol = 0;
};
@@ -459,7 +458,7 @@ B5500SPOUnit.prototype.read = function read(finish, buffer, length, mode, contro
this.bufIndex = 0;
this.nextCharTime = new Date().getTime();
this.finish = finish;
//this.window.focus(); // interferes with datacom terminal window
this.window.focus();
break;
case this.spoOutput:
case this.spoInput: