Enabled smooth scrolling, connected SerialPort UART transmitter status to the ChipSet FLAGS buffer (matching how the Keyboard UART transmitter status is connected), enabled binding of terminal I/O to the Control Panel output window (if any), and added simulation of received data at the programmed baud rate (but a better solution has been noted and needs to be implemented)

This commit is contained in:
Jeff Parsons 2016-08-16 09:34:41 -07:00
commit cf24023d5d
8 changed files with 609 additions and 477 deletions

View file

@ -36,7 +36,6 @@ if (NODE) {
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var Messages = require("./messages");
var ChipSet = require("./chipset");
var State = require("./state");
}
@ -115,6 +114,15 @@ function SerialPort(parmsSerial) {
*/
Component.bindExternalControl(this, sBinding, SerialPort.sIOBuffer);
}
/*
* Define a setTimeout() function that receiveData() can use when there's more data to receive.
*/
this.fnCheckDataReceived = function(serial) {
return function() {
serial.receiveData();
}
}(this);
}
/*
@ -146,7 +154,7 @@ SerialPort.UART8251 = {
PARITY_ENABLE: 0x10,
EVEN_PARITY: 0x20,
STOP_BITS: 0xC0, // 00=invalid, 01=1, 10=1.5, 11=2
INIT: 0x8E // 16x baudrate, 8 data bits, no parity, 1.5 stop bits
INIT: 0x8E // 16x baud rate, 8 data bits, no parity, 1.5 stop bits
},
/*
* Format of COMMAND byte written to CONTROL port 0x1
@ -177,9 +185,10 @@ SerialPort.UART8251 = {
INIT: 0x85 // XMIT_READY | XMIT_EMPTY | DSR
},
/*
* Format of BAUDRATE byte written to port 0x2
* Format of BAUDRATES byte written to port 0x2
*
* Each nibble is an index (0x0-0xF) into a set internal CPU clock divisors that yield the following baud rates:
* Each nibble is an index (0x0-0xF) into a set of internal CPU clock divisors that yield the
* following baud rates:
*
* Index Divisor Baud Rate
* ----- ------- ---------
@ -199,15 +208,15 @@ SerialPort.UART8251 = {
* 0xD 36 4800
* 0xE 18 9600 (default)
* 0xF 9 19200
*
* TODO: I'm not really sure at this point which nibble is for the XMIT rate and which is for the RECV
* rate; I just made a random guess.
*/
BAUDRATE: {
XMIT_RATE: 0x0F,
RECV_RATE: 0xF0,
BAUDRATES: {
RECV_RATE: 0x0F,
XMIT_RATE: 0xF0,
INIT: 0xEE // default to 9600 (0xE) for both XMIT and RECV
}
},
BAUDTABLE: [
50, 75, 110, 134.5, 150, 200, 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 9600, 19200
]
};
SerialPort.UART8251.INIT = [
@ -217,7 +226,7 @@ SerialPort.UART8251.INIT = [
SerialPort.UART8251.STATUS.INIT,
SerialPort.UART8251.MODE.INIT,
SerialPort.UART8251.COMMAND.INIT,
SerialPort.UART8251.BAUDRATE.INIT
SerialPort.UART8251.BAUDRATES.INIT
];
/*
@ -272,7 +281,7 @@ SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
if (keyCode === 0x08 || event.ctrlKey && keyCode >= 0x41 && keyCode <= 0x5A) {
if (event.preventDefault) event.preventDefault();
if (keyCode > 0x40) keyCode -= 0x40;
serial.sendByteIn(keyCode);
serial.receiveByte(keyCode);
}
return true;
};
@ -284,7 +293,7 @@ SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
*/
event = event || window.event;
var keyCode = event.which || event.keyCode;
// serial.sendRBR([keyCode]);
serial.receiveByte(keyCode);
/*
* Since we're going to remove the "readonly" attribute from the <textarea> control
* (so that the soft keyboard activates on iOS), instead of calling preventDefault() for
@ -317,8 +326,8 @@ SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
*/
sValue = sValue.replace(/\\n/g, "\n").replace(/\\r/g, "\r");
control.onclick = function onClickTest(event) {
serial.sDataIn = sValue;
serial.sendDataIn();
serial.sDataReceived = sValue;
serial.receiveData();
/*
* Give focus back to the machine (since clicking the button takes focus away).
*/
@ -444,7 +453,7 @@ SerialPort.prototype.initState = function(data)
this.bStatus = data[i++];
this.bMode = data[i++];
this.bCommand = data[i++];
this.bBaudRate = data[i];
this.bBaudRates = data[i];
return true;
};
@ -464,18 +473,37 @@ SerialPort.prototype.saveRegisters = function()
data[i++] = this.bStatus;
data[i++] = this.bMode;
data[i++] = this.bCommand;
data[i] = this.bBaudRate;
data[i] = this.bBaudRates;
return data;
};
/**
* sendByteIn(b)
* getBaudTimeout(maskRate)
*
* @this {SerialPort}
* @param {number} maskRate (either SerialPort.UART8251.BAUDRATES.RECV_RATE or SerialPort.UART8251.BAUDRATES.XMIT_RATE)
* @return {number} (number of milliseconds per byte)
*/
SerialPort.prototype.getBaudTimeout = function(maskRate)
{
var indexRate = (this.bBaudRates & maskRate);
if (!(maskRate & 0xf)) indexRate >>= 4;
var nBaud = SerialPort.UART8251.BAUDTABLE[indexRate];
var nBits = ((this.bMode & SerialPort.UART8251.MODE.DATA_BITS) >> 2) + 6; // includes an extra +1 for start bit
if (this.bMode & SerialPort.UART8251.MODE.PARITY_ENABLE) nBits++;
nBits += ((((this.bMode & SerialPort.UART8251.MODE.STOP_BITS) >> 6) + 1) >> 1);
var nBytesPerSecond = Math.round(nBaud / nBits);
return 1000 / nBytesPerSecond;
};
/**
* receiveByte(b)
*
* @this {SerialPort}
* @param {number} b
* @return {boolean}
*/
SerialPort.prototype.sendByteIn = function(b)
SerialPort.prototype.receiveByte = function(b)
{
if (!(this.bStatus & SerialPort.UART8251.STATUS.RECV_FULL)) {
this.bDataIn = b;
@ -487,15 +515,31 @@ SerialPort.prototype.sendByteIn = function(b)
};
/**
* sendDataIn()
* receiveData()
*
* @this {SerialPort}
*/
SerialPort.prototype.sendDataIn = function()
SerialPort.prototype.receiveData = function()
{
if (this.sDataIn) {
if (this.sendByteIn(this.sDataIn.charCodeAt(0))) {
this.sDataIn = this.sDataIn.substr(1);
if (this.sDataReceived) {
if (this.receiveByte(this.sDataReceived.charCodeAt(0))) {
this.sDataReceived = this.sDataReceived.substr(1);
}
/*
* TODO: If data has become undeliverable for some reason (eg, the Debugger has paused execution),
* we should stop setting timeouts, and add one or more notification mechanisms to kickstart it again.
*/
if (this.sDataReceived) {
/*
* TODO: setTimeout() is a less-than-ideal solution, because it's too slow; timeouts won't fire until
* the end of a CPU burst. So instead of calculating a number of milliseconds, we should calculate a
* number of CPU cycles, and create a CPU notification mechanism that calls us back after that many cycles
* have elapsed (and which will automatically shorten the current CPU burst as needed).
*
* This will also solve the other issue noted above, because if the CPU has been halted, it won't be
* generating any notifications either.
*/
setTimeout(this.fnCheckDataReceived, this.getBaudTimeout(SerialPort.UART8251.BAUDRATES.RECV_RATE));
}
}
};
@ -548,6 +592,19 @@ SerialPort.prototype.echoByte = function(b)
return false;
};
/**
* isTransmitterReady()
*
* Called whenever a ChipSet circuit needs the SerialPort UART's transmitter status.
*
* @this {SerialPort}
* @return {boolean} (true if ready, false if not)
*/
SerialPort.prototype.isTransmitterReady = function()
{
return !!(this.bStatus & SerialPort.UART8251.STATUS.XMIT_READY);
};
/**
* inData(port, addrFrom)
*
@ -561,7 +618,6 @@ SerialPort.prototype.inData = function(port, addrFrom)
var b = this.bDataIn;
this.printMessageIO(port, null, addrFrom, "DATA", b);
this.bStatus &= ~SerialPort.UART8251.STATUS.RECV_FULL;
this.sendDataIn(); // if there is still queued incoming data, send another byte
return b;
};
@ -592,6 +648,10 @@ 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.echoByte(bOut)) {
this.bStatus |= (SerialPort.UART8251.STATUS.XMIT_READY | SerialPort.UART8251.STATUS.XMIT_EMPTY);
}
};
/**
@ -622,17 +682,17 @@ SerialPort.prototype.outControl = function(port, bOut, addrFrom)
};
/**
* outBaudRate(port, bOut, addrFrom)
* outBaudRates(port, bOut, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x2)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort.prototype.outBaudRate = function(port, bOut, addrFrom)
SerialPort.prototype.outBaudRates = function(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "BAUDRATE");
this.bBaudRate = bOut;
this.printMessageIO(port, bOut, addrFrom, "BAUDRATES");
this.bBaudRates = bOut;
};
/*
@ -650,7 +710,7 @@ SerialPort.aPortInput = {
SerialPort.aPortOutput = {
0x0: SerialPort.prototype.outData,
0x1: SerialPort.prototype.outControl,
0x2: SerialPort.prototype.outBaudRate
0x2: SerialPort.prototype.outBaudRates
};
/**