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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ BUTTON.greenButton {
|
||||
border-radius: 4px}
|
||||
|
||||
BUTTON.greenLit {
|
||||
color: black;
|
||||
background-color: #0F0}
|
||||
|
||||
BUTTON.blackBorder {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user