Adding clocking mechanism for serial data transmission

This commit is contained in:
Jeff Parsons 2016-08-17 14:09:02 -07:00
commit 140e1cf094
9 changed files with 526 additions and 400 deletions

View file

@ -361,9 +361,13 @@ SerialPort.prototype.initBus = function(cmp, bus, cpu, dbg)
this.dbg = dbg;
var serial = this;
this.timerReceiveData = this.cpu.addTimer(function() {
serial.printMessage("timerReceiveData()");
serial.receiveData()
this.timerReceiveNext = this.cpu.addTimer(function() {
serial.printMessage("timerReceiveNext()");
serial.receiveData();
});
this.timerTransmitNext = this.cpu.addTimer(function() {
serial.printMessage("timerTransmitNext()");
serial.transmitData();
});
this.chipset = /** @type {ChipSet} */ (cmp.getMachineComponent("ChipSet"));
@ -570,6 +574,11 @@ SerialPort.prototype.receiveByte = function(b)
/**
* receiveData()
*
* Helper for clocking received data at the expected RECV_RATE.
*
* Currently, this is only use for test data that we "cram" down the terminal's throat, ensuring that we don't
* cram it too rapidly.
*
* @this {SerialPort}
*/
SerialPort.prototype.receiveData = function()
@ -579,7 +588,7 @@ SerialPort.prototype.receiveData = function()
this.sDataReceived = this.sDataReceived.substr(1);
}
if (this.sDataReceived && this.cpu) {
this.cpu.setTimer(this.timerReceiveData, this.getBaudTimeout(SerialPort.UART8251.BAUDRATES.RECV_RATE));
this.cpu.setTimer(this.timerReceiveNext, this.getBaudTimeout(SerialPort.UART8251.BAUDRATES.RECV_RATE));
}
}
};
@ -602,10 +611,7 @@ SerialPort.prototype.transmitByte = function(b)
}
if (this.controlIOBuffer) {
if (b == 0x0D) {
this.iLogicalCol = 0;
}
else if (b == 0x08) {
if (b == 0x08) {
this.controlIOBuffer.value = this.controlIOBuffer.value.slice(0, -1);
/*
* TODO: Back up the correct number of columns if the character erased was a tab.
@ -613,13 +619,17 @@ SerialPort.prototype.transmitByte = function(b)
if (this.iLogicalCol > 0) this.iLogicalCol--;
}
else {
var s = String.fromCharCode(b);
var nChars = (b >= 0x20? 1 : 0);
var s = str.toASCIICode(b);
var nChars = s.length;
if (b == 0x09) {
var tabSize = this.tabSize || 8;
nChars = tabSize - (this.iLogicalCol % tabSize);
if (this.tabSize) s = str.pad("", nChars);
}
else if (b == 0x0D) {
this.iLogicalCol = nChars = 0;
s = "\n";
}
if (this.charBOL && !this.iLogicalCol && nChars) s = String.fromCharCode(this.charBOL) + s;
this.controlIOBuffer.value += s;
this.controlIOBuffer.scrollTop = this.controlIOBuffer.scrollHeight;
@ -641,6 +651,21 @@ SerialPort.prototype.transmitByte = function(b)
return fTransmitted;
};
/**
* transmitData()
*
* Helper for clocking transmitted data at the expected XMIT_RATE.
*
* When timerTransmitNext fires, we have honored the programmed XMIT_RATE period, so we can
* set XMIT_READY (and XMIT_EMPTY), which signals the firmware that another byte can be transmitted.
*
* @this {SerialPort}
*/
SerialPort.prototype.transmitData = function()
{
this.bStatus |= (SerialPort.UART8251.STATUS.XMIT_READY | SerialPort.UART8251.STATUS.XMIT_EMPTY);
};
/**
* isTransmitterReady()
*
@ -698,8 +723,20 @@ SerialPort.prototype.outData = function(port, bOut, addrFrom)
this.printMessageIO(port, bOut, addrFrom, "DATA");
this.bDataOut = bOut;
this.bStatus &= ~(SerialPort.UART8251.STATUS.XMIT_READY | SerialPort.UART8251.STATUS.XMIT_EMPTY);
if (this.transmitByte(bOut)) {
this.bStatus |= (SerialPort.UART8251.STATUS.XMIT_READY | SerialPort.UART8251.STATUS.XMIT_EMPTY);
/*
* If we're transmitting to a virtual device that has no measurable delay, this code may clear XMIT_READY
* too quickly.
*
* if (this.transmitByte(bOut)) {
* this.bStatus |= (SerialPort.UART8251.STATUS.XMIT_READY | SerialPort.UART8251.STATUS.XMIT_EMPTY);
* }
*
* A better solution is to arm a timer based on the XMIT_RATE baud rate, and clear the above bits when that
* timer fires. Consequently, we no longer care what transmitByte() reports.
*/
this.transmitByte(bOut);
if (this.cpu) {
this.cpu.setTimer(this.timerTransmitNext, this.getBaudTimeout(SerialPort.UART8251.BAUDRATES.XMIT_RATE));
}
};

View file

@ -1115,7 +1115,7 @@ Video.prototype.updateVT100 = function(fForced)
/*
* Cell cache logic is complicated by the fact that a line may be single-width one frame and double-width
* the next. So we store the visible line length at the start of each row in the cache, which must match if
* the cache is considered valid for the current line.
* the cache can be considered valid for the current line.
*/
var fLineCacheValid = this.fCellCacheValid && (this.aCellCache[iCell] == nColsVisible);
this.aCellCache[iCell++] = nColsVisible;
@ -1153,6 +1153,9 @@ Video.prototype.updateVT100 = function(fForced)
* cache entry, to guarantee that it's redrawn on the next update.
*/
this.assert(iCellUpdated >= 0);
if (DEBUG && (this.aCellCache[iCellUpdated] & 0x7f) == 0x48) {
console.log("spurious character?");
}
this.aCellCache[iCellUpdated] = -1;
cUpdated = 0;
}

View file

@ -381,7 +381,7 @@ str.replaceArray = function(a, s)
* @param {string} s is a string
* @param {number} cch is desired length
* @param {boolean} [fPadLeft] (default is padding on the right)
* @returns {string} the original string (s) with spaces padding it to the specified length
* @return {string} the original string (s) with spaces padding it to the specified length
*/
str.pad = function(s, cch, fPadLeft)
{
@ -393,7 +393,7 @@ str.pad = function(s, cch, fPadLeft)
* trim(s)
*
* @param {string} s
* @returns {string}
* @return {string}
*/
str.trim = function(s)
{
@ -403,4 +403,56 @@ str.trim = function(s)
return s.replace(/^\s+|\s+$/g, "");
};
str.aASCIICodes = {
0x00: "NUL",
0x01: "SOH", // Start of Heading
0x02: "STX", // Start of Text
0x03: "ETX", // End of Text
0x04: "EOT", // End of Transmission
0x05: "ENQ", // Enquiry
0x06: "ACK", // Acknowledge
0x07: "BEL", // Bell
0x08: "BS", // Backspace
0x09: "TAB", // Horizontal Tab
0x0A: "LF", // Line Feed (New Line)
0x0B: "VT", // Vertical Tab
0x0C: "FF", // Form Feed (New Page)
0x0D: "CR", // Carriage Return
0x0E: "SO", // Shift Out
0x0F: "SI", // Shift In
0x10: "DLE", // Data Link Escape
0x11: "DC1", // Device Control 1
0x12: "DC2", // Device Control 2
0x13: "DC3", // Device Control 3
0x14: "DC4", // Device Control 4
0x15: "NAK", // Negative Acknowledge
0x16: "SYN", // Synchronous Idle
0x17: "ETB", // End of Transmission Block
0x18: "CAN", // Cancel
0x19: "EM", // End of Medium
0x1A: "SUB", // Substitute
0x1B: "ESC", // Escape
0x1C: "FS", // File Separator
0x1D: "GS", // Group Separator
0x1E: "RS", // Record Separator
0x1F: "US" // Unit Separator
};
/**
* toASCIICode(b)
*
* @param {number} b
* @return {string}
*/
str.toASCIICode = function(b)
{
var s = str.aASCIICodes[b];
if (s) {
s = '<' + s + '>';
} else {
s = String.fromCharCode(b);
}
return s;
};
if (NODE) module.exports = str;