123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- "use strict";
- /*
- * Serial ports
- * http://wiki.osdev.org/UART
- * https://github.com/s-macke/jor1k/blob/master/js/worker/dev/uart.js
- * https://www.freebsd.org/doc/en/articles/serial-uart/
- */
- /** @const */
- var DLAB = 0x80;
- /** @const */ var UART_IER_MSI = 0x08; /* Modem Status Changed int. */
- /** @const */ var UART_IER_THRI = 0x02; /* Enable Transmitter holding register int. */
- /** @const */ var UART_IER_RDI = 0x01; /* Enable receiver data interrupt */
- /** @const */var UART_IIR_MSI = 0x00; /* Modem status interrupt (Low priority) */
- /** @const */var UART_IIR_NO_INT = 0x01;
- /** @const */var UART_IIR_THRI = 0x02; /* Transmitter holding register empty */
- /** @const */var UART_IIR_RDI = 0x04; /* Receiver data interrupt */
- /** @const */var UART_IIR_RLSI = 0x06; /* Receiver line status interrupt (High p.) */
- /** @const */var UART_IIR_CTI = 0x0c; /* Character timeout */
- /** @const */ var UART_LSR_DATA_READY = 0x1; // data available
- /** @const */ var UART_LSR_TX_EMPTY = 0x20; // TX (THR) buffer is empty
- /** @const */ var UART_LSR_TRANSMITTER_EMPTY = 0x40; // TX empty and line is idle
- // Modem status register
- /** @const */ var UART_MSR_DCD = 0x7; // Data Carrier Detect
- /** @const */ var UART_MSR_RI = 0x6; // Ring Indicator
- /** @const */ var UART_MSR_DSR = 0x5; // Data Set Ready
- /** @const */ var UART_MSR_CTS = 0x4; // Clear To Send
- // Delta bits
- /** @const */ var UART_MSR_DDCD = 0x3; // Delta DCD
- /** @const */ var UART_MSR_TERI = 0x2; // Trailing Edge RI
- /** @const */ var UART_MSR_DDSR = 0x1; // Delta DSR
- /** @const */ var UART_MSR_DCTS = 0x0; // Delta CTS
- /**
- * @constructor
- * @param {CPU} cpu
- * @param {number} port
- * @param {BusConnector} bus
- */
- function UART(cpu, port, bus)
- {
- /** @const @type {BusConnector} */
- this.bus = bus;
- /** @const @type {CPU} */
- this.cpu = cpu;
- this.ints = 1 << UART_IIR_THRI;
- this.baud_rate = 0;
- this.line_control = 0;
- // line status register
- this.lsr = UART_LSR_TRANSMITTER_EMPTY | UART_LSR_TX_EMPTY;
- this.fifo_control = 0;
- // interrupts enable
- this.ier = 0;
- // interrupt identification register
- this.iir = UART_IIR_NO_INT;
- this.modem_control = 0;
- this.modem_status = 0;
- this.scratch_register = 0;
- this.irq = 0;
- this.input = [];
- this.current_line = "";
- switch(port)
- {
- case 0x3F8:
- this.com = 0;
- this.irq = 4;
- break;
- case 0x2F8:
- this.com = 1;
- this.irq = 3;
- break;
- case 0x3E8:
- this.com = 2;
- this.irq = 4;
- break;
- case 0x2E8:
- this.com = 3;
- this.irq = 3;
- break;
- default:
- dbg_log("Invalid serial port: " + h(port), LOG_SERIAL);
- this.com = 0;
- this.irq = 4;
- }
- this.bus.register("serial" + this.com + "-input", function(data)
- {
- this.data_received(data);
- }, this);
- this.bus.register("serial" + this.com + "-modem-status-input", function(data)
- {
- this.set_modem_status(data);
- }, this);
- // Set individual modem status bits
- this.bus.register("serial" + this.com + "-carrier-detect-input", function(data)
- {
- const status = data ?
- this.modem_status | (1 << UART_MSR_DCD) | (1 << UART_MSR_DDCD) :
- this.modem_status & ~(1 << UART_MSR_DCD) & ~(1 << UART_MSR_DDCD);
- this.set_modem_status(status);
- }, this);
- this.bus.register("serial" + this.com + "-ring-indicator-input", function(data)
- {
- const status = data ?
- this.modem_status | (1 << UART_MSR_RI) | (1 << UART_MSR_TERI) :
- this.modem_status & ~(1 << UART_MSR_RI) & ~(1 << UART_MSR_TERI);
- this.set_modem_status(status);
- }, this);
- this.bus.register("serial" + this.com + "-data-set-ready-input", function(data)
- {
- const status = data ?
- this.modem_status | (1 << UART_MSR_DSR) | (1 << UART_MSR_DDSR) :
- this.modem_status & ~(1 << UART_MSR_DSR) & ~(1 << UART_MSR_DDSR);
- this.set_modem_status(status);
- }, this);
- this.bus.register("serial" + this.com + "-clear-to-send-input", function(data)
- {
- const status = data ?
- this.modem_status | (1 << UART_MSR_CTS) | (1 << UART_MSR_DCTS) :
- this.modem_status & ~(1 << UART_MSR_CTS) & ~(1 << UART_MSR_DCTS);
- this.set_modem_status(status);
- }, this);
- var io = cpu.io;
- io.register_write(port, this, function(out_byte)
- {
- this.write_data(out_byte);
- }, function(out_word)
- {
- this.write_data(out_word & 0xFF);
- this.write_data(out_word >> 8);
- });
- io.register_write(port | 1, this, function(out_byte)
- {
- if(this.line_control & DLAB)
- {
- this.baud_rate = this.baud_rate & 0xFF | out_byte << 8;
- dbg_log("baud rate: " + h(this.baud_rate), LOG_SERIAL);
- }
- else
- {
- if((this.ier & UART_IIR_THRI) === 0 && (out_byte & UART_IIR_THRI))
- {
- // re-throw THRI if it was masked
- this.ThrowInterrupt(UART_IIR_THRI);
- }
- this.ier = out_byte & 0xF;
- dbg_log("interrupt enable: " + h(out_byte), LOG_SERIAL);
- this.CheckInterrupt();
- }
- });
- io.register_read(port, this, function()
- {
- if(this.line_control & DLAB)
- {
- return this.baud_rate & 0xFF;
- }
- else
- {
- let data = 0;
- if(this.input.length === 0)
- {
- dbg_log("Read input empty", LOG_SERIAL);
- }
- else
- {
- data = this.input.shift();
- dbg_log("Read input: " + h(data), LOG_SERIAL);
- }
- if(this.input.length === 0)
- {
- this.lsr &= ~UART_LSR_DATA_READY;
- this.ClearInterrupt(UART_IIR_CTI);
- this.ClearInterrupt(UART_IIR_RDI);
- }
- return data;
- }
- });
- io.register_read(port | 1, this, function()
- {
- if(this.line_control & DLAB)
- {
- return this.baud_rate >> 8;
- }
- else
- {
- return this.ier & 0xF;
- }
- });
- io.register_read(port | 2, this, function()
- {
- var ret = this.iir & 0xF;
- dbg_log("read interrupt identification: " + h(this.iir), LOG_SERIAL);
- if (this.iir == UART_IIR_THRI) {
- this.ClearInterrupt(UART_IIR_THRI);
- }
- if(this.fifo_control & 1) ret |= 0xC0;
- return ret;
- });
- io.register_write(port | 2, this, function(out_byte)
- {
- dbg_log("fifo control: " + h(out_byte), LOG_SERIAL);
- this.fifo_control = out_byte;
- });
- io.register_read(port | 3, this, function()
- {
- dbg_log("read line control: " + h(this.line_control), LOG_SERIAL);
- return this.line_control;
- });
- io.register_write(port | 3, this, function(out_byte)
- {
- dbg_log("line control: " + h(out_byte), LOG_SERIAL);
- this.line_control = out_byte;
- });
- io.register_read(port | 4, this, function()
- {
- return this.modem_control;
- });
- io.register_write(port | 4, this, function(out_byte)
- {
- dbg_log("modem control: " + h(out_byte), LOG_SERIAL);
- this.modem_control = out_byte;
- });
- io.register_read(port | 5, this, function()
- {
- dbg_log("read line status: " + h(this.lsr), LOG_SERIAL);
- return this.lsr;
- });
- io.register_write(port | 5, this, function(out_byte)
- {
- dbg_log("Factory test write", LOG_SERIAL);
- });
- io.register_read(port | 6, this, function()
- {
- dbg_log("read modem status: " + h(this.modem_status), LOG_SERIAL);
- // Clear delta bits
- this.modem_status &= 0xF0;
- return this.modem_status;
- });
- io.register_write(port | 6, this, function(out_byte)
- {
- dbg_log("write modem status: " + h(out_byte), LOG_SERIAL);
- this.set_modem_status(out_byte);
- });
- io.register_read(port | 7, this, function()
- {
- return this.scratch_register;
- });
- io.register_write(port | 7, this, function(out_byte)
- {
- this.scratch_register = out_byte;
- });
- }
- UART.prototype.get_state = function()
- {
- var state = [];
- state[0] = this.ints;
- state[1] = this.baud_rate;
- state[2] = this.line_control;
- state[3] = this.lsr;
- state[4] = this.fifo_control;
- state[5] = this.ier;
- state[6] = this.iir;
- state[7] = this.modem_control;
- state[8] = this.modem_status;
- state[9] = this.scratch_register;
- state[10] = this.irq;
- return state;
- };
- UART.prototype.set_state = function(state)
- {
- this.ints = state[0];
- this.baud_rate = state[1];
- this.line_control = state[2];
- this.lsr = state[3];
- this.fifo_control = state[4];
- this.ier = state[5];
- this.iir = state[6];
- this.modem_control = state[7];
- this.modem_status = state[8];
- this.scratch_register = state[9];
- this.irq = state[10];
- };
- UART.prototype.CheckInterrupt = function() {
- if ((this.ints & (1 << UART_IIR_CTI)) && (this.ier & UART_IER_RDI)) {
- this.iir = UART_IIR_CTI;
- this.cpu.device_raise_irq(this.irq);
- } else
- if ((this.ints & (1 << UART_IIR_RDI)) && (this.ier & UART_IER_RDI)) {
- this.iir = UART_IIR_RDI;
- this.cpu.device_raise_irq(this.irq);
- } else
- if ((this.ints & (1 << UART_IIR_THRI)) && (this.ier & UART_IER_THRI)) {
- this.iir = UART_IIR_THRI;
- this.cpu.device_raise_irq(this.irq);
- } else
- if ((this.ints & (1 << UART_IIR_MSI)) && (this.ier & UART_IER_MSI)) {
- this.iir = UART_IIR_MSI;
- this.cpu.device_raise_irq(this.irq);
- } else {
- this.iir = UART_IIR_NO_INT;
- this.cpu.device_lower_irq(this.irq);
- }
- };
- UART.prototype.ThrowInterrupt = function(line) {
- this.ints |= (1 << line);
- this.CheckInterrupt();
- };
- UART.prototype.ClearInterrupt = function(line) {
- this.ints &= ~(1 << line);
- this.CheckInterrupt();
- };
- /**
- * @param {number} data
- */
- UART.prototype.data_received = function(data)
- {
- dbg_log("input: " + h(data), LOG_SERIAL);
- this.input.push(data);
- this.lsr |= UART_LSR_DATA_READY;
- if(this.fifo_control & 1)
- {
- this.ThrowInterrupt(UART_IIR_CTI);
- }
- else
- {
- this.ThrowInterrupt(UART_IIR_RDI);
- }
- };
- UART.prototype.write_data = function(out_byte)
- {
- if(this.line_control & DLAB)
- {
- this.baud_rate = this.baud_rate & ~0xFF | out_byte;
- return;
- }
- dbg_log("data: " + h(out_byte), LOG_SERIAL);
- this.ThrowInterrupt(UART_IIR_THRI);
- this.bus.send("serial" + this.com + "-output-byte", out_byte);
- if(DEBUG)
- {
- var char = String.fromCharCode(out_byte);
- this.current_line += char;
- if(char === "\n")
- {
- const line = this.current_line.trimRight().replace(/[\x00-\x08\x0b-\x1f\x7f\x80-\xff]/g, "");
- dbg_log("SERIAL: " + line);
- this.current_line = "";
- }
- }
- };
- UART.prototype.set_modem_status = function(status)
- {
- dbg_log("modem status: " + h(status), LOG_SERIAL);
- const prev_delta_bits = this.modem_status & 0x0F;
- // compare the bits that have changed and shift them into the delta bits
- let delta = (this.modem_status ^ status) >> 4;
- // The delta should stay set if they were previously set
- delta |= prev_delta_bits;
- // update the current modem status
- this.modem_status = status;
- // update the delta bits based on the changes and previous
- // values, but also leave the delta bits set if they were
- // passed in as part of the status
- this.modem_status |= delta;
- };
|