2014-12-14 16:26:09 +01:00
|
|
|
"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/
|
2014-01-08 03:14:33 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/** @const */
|
|
|
|
var DLAB = 0x80;
|
|
|
|
|
2014-12-14 16:26:09 +01:00
|
|
|
/** @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 */
|
|
|
|
|
|
|
|
|
2015-09-12 01:10:38 +02:00
|
|
|
/**
|
|
|
|
* @constructor
|
2015-02-25 18:21:54 +01:00
|
|
|
* @param {CPU} cpu
|
|
|
|
* @param {number} port
|
2015-12-30 23:30:54 +01:00
|
|
|
* @param {BusConnector} bus
|
2013-11-06 01:12:55 +01:00
|
|
|
*/
|
2014-12-25 02:32:18 +01:00
|
|
|
function UART(cpu, port, bus)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2015-12-30 23:30:54 +01:00
|
|
|
/** @const @type {BusConnector} */
|
2014-12-25 02:32:18 +01:00
|
|
|
this.bus = bus;
|
|
|
|
|
2015-09-15 21:58:40 +02:00
|
|
|
/** @const @type {CPU} */
|
2015-05-17 01:56:50 +02:00
|
|
|
this.cpu = cpu;
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2015-01-10 02:17:19 +01:00
|
|
|
this.ints = 0;
|
2014-12-14 16:26:09 +01:00
|
|
|
|
2014-10-04 20:50:31 +02:00
|
|
|
this.baud_rate = 0;
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2014-10-04 20:50:31 +02:00
|
|
|
this.line_control = 0;
|
|
|
|
this.line_status = 0;
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2014-10-04 20:50:31 +02:00
|
|
|
this.fifo_control = 0;
|
2014-12-14 16:26:09 +01:00
|
|
|
|
|
|
|
// interrupts enable
|
|
|
|
this.ier = 0;
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2014-10-04 20:50:31 +02:00
|
|
|
// interrupt identification register
|
|
|
|
this.iir = 1;
|
2014-06-02 22:31:25 +02:00
|
|
|
|
2014-10-04 20:50:31 +02:00
|
|
|
this.modem_control = 0;
|
|
|
|
this.modem_status = 0;
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2014-10-04 20:50:31 +02:00
|
|
|
this.scratch_register = 0;
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2014-10-04 20:50:31 +02:00
|
|
|
this.irq = 0;
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2014-10-04 20:50:31 +02:00
|
|
|
this.input = new ByteQueue(4096);
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2015-04-12 16:45:03 +02:00
|
|
|
this.current_line = [];
|
2015-01-09 04:03:38 +01:00
|
|
|
|
2014-01-08 03:14:33 +01:00
|
|
|
if(port === 0x3E8 || port === 0x3F8)
|
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
this.irq = 4;
|
2014-01-08 03:14:33 +01:00
|
|
|
}
|
|
|
|
else if(port === 0x3E8 || port === 0x3E8)
|
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
this.irq = 3;
|
2014-01-08 03:14:33 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_log("Invalid port: " + h(port), LOG_SERIAL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-12-25 02:32:18 +01:00
|
|
|
this.bus.register("serial0-input", function(data)
|
|
|
|
{
|
|
|
|
this.data_received(data);
|
|
|
|
}, this);
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2014-10-04 20:50:31 +02:00
|
|
|
var io = cpu.io;
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2016-06-26 20:59:09 +02:00
|
|
|
function write_data(out_byte)
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
if(this.line_control & DLAB)
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
this.baud_rate = this.baud_rate & ~0xFF | out_byte;
|
2014-01-08 03:14:33 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-12-14 16:26:09 +01:00
|
|
|
dbg_log("data: " + h(out_byte), LOG_SERIAL);
|
|
|
|
|
2015-01-10 02:17:19 +01:00
|
|
|
this.ThrowTHRI();
|
2014-01-08 03:14:33 +01:00
|
|
|
|
|
|
|
if(out_byte === 0xFF)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-09 04:03:38 +01:00
|
|
|
var char = String.fromCharCode(out_byte);
|
|
|
|
|
|
|
|
this.bus.send("serial0-output-char", char);
|
|
|
|
|
2015-04-12 16:45:03 +02:00
|
|
|
this.current_line.push(out_byte);
|
|
|
|
|
|
|
|
if(char === "\n")
|
2015-01-12 00:40:39 +01:00
|
|
|
{
|
2015-04-12 16:45:03 +02:00
|
|
|
this.bus.send("serial0-output-line", String.fromCharCode.apply("", this.current_line));
|
|
|
|
this.current_line = [];
|
2015-01-12 00:40:39 +01:00
|
|
|
}
|
2016-06-26 20:59:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
io.register_write(port, this, function(out_byte)
|
|
|
|
{
|
|
|
|
write_data.call(this, out_byte);
|
|
|
|
}, function(out_word)
|
|
|
|
{
|
|
|
|
write_data.call(this, out_word & 0xFF);
|
|
|
|
write_data.call(this, out_word >> 8);
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_write(port | 1, this, function(out_byte)
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
if(this.line_control & DLAB)
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
this.baud_rate = this.baud_rate & 0xFF | out_byte << 8;
|
|
|
|
dbg_log("baud rate: " + h(this.baud_rate), LOG_SERIAL);
|
2014-01-08 03:14:33 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-12-14 16:26:09 +01:00
|
|
|
this.ier = out_byte;
|
2014-01-08 03:14:33 +01:00
|
|
|
dbg_log("interrupt enable: " + h(out_byte), LOG_SERIAL);
|
2015-01-10 02:17:19 +01:00
|
|
|
this.NextInterrupt();
|
2014-01-08 03:14:33 +01:00
|
|
|
}
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_read(port, this, function()
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
if(this.line_control & DLAB)
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
return this.baud_rate & 0xFF;
|
2014-01-08 03:14:33 +01:00
|
|
|
}
|
|
|
|
else
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
var data = this.input.shift();
|
2014-01-05 03:19:09 +01:00
|
|
|
|
2014-01-08 03:14:33 +01:00
|
|
|
if(data === -1)
|
|
|
|
{
|
2014-12-14 16:26:09 +01:00
|
|
|
dbg_log("Read input empty", LOG_SERIAL);
|
2014-01-08 03:14:33 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-12-14 16:26:09 +01:00
|
|
|
dbg_log("Read input: " + h(data), LOG_SERIAL);
|
2014-01-08 03:14:33 +01:00
|
|
|
}
|
|
|
|
|
2015-01-12 00:47:43 +01:00
|
|
|
if(this.input.length)
|
|
|
|
{
|
|
|
|
this.ThrowCTI();
|
|
|
|
}
|
|
|
|
|
2014-01-08 03:14:33 +01:00
|
|
|
return data;
|
|
|
|
}
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_read(port | 1, this, function()
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
if(this.line_control & DLAB)
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
return this.baud_rate >> 8;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-12-14 16:26:09 +01:00
|
|
|
return this.ier;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
2013-12-20 22:05:49 +01:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_read(port | 2, this, function()
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
2014-12-14 16:26:09 +01:00
|
|
|
var ret = this.iir & 0xF | 0xC0;
|
2014-10-04 20:50:31 +02:00
|
|
|
dbg_log("read interrupt identification: " + h(this.iir), LOG_SERIAL);
|
2014-12-14 16:26:09 +01:00
|
|
|
|
|
|
|
if(this.iir === UART_IIR_THRI)
|
|
|
|
{
|
2015-01-12 00:47:43 +01:00
|
|
|
this.ClearInterrupt(UART_IIR_THRI);
|
2014-12-14 16:26:09 +01:00
|
|
|
}
|
|
|
|
else if(this.iir === UART_IIR_CTI)
|
|
|
|
{
|
2015-01-12 00:47:43 +01:00
|
|
|
this.ClearInterrupt(UART_IIR_CTI);
|
2014-12-14 16:26:09 +01:00
|
|
|
}
|
2014-06-02 22:31:25 +02:00
|
|
|
|
|
|
|
return ret;
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
|
|
|
io.register_write(port | 2, this, function(out_byte)
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
|
|
|
dbg_log("fifo control: " + h(out_byte), LOG_SERIAL);
|
2014-10-04 20:50:31 +02:00
|
|
|
this.fifo_control = out_byte;
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_read(port | 3, this, function()
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
dbg_log("read line control: " + h(this.line_control), LOG_SERIAL);
|
|
|
|
return this.line_control;
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
|
|
|
io.register_write(port | 3, this, function(out_byte)
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
|
|
|
dbg_log("line control: " + h(out_byte), LOG_SERIAL);
|
2014-10-04 20:50:31 +02:00
|
|
|
this.line_control = out_byte;
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
2014-01-08 03:14:33 +01:00
|
|
|
|
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_read(port | 4, this, function()
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
return this.modem_control;
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
|
|
|
io.register_write(port | 4, this, function(out_byte)
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
|
|
|
dbg_log("modem control: " + h(out_byte), LOG_SERIAL);
|
2014-10-04 20:50:31 +02:00
|
|
|
this.modem_control = out_byte;
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_read(port | 5, this, function()
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
|
|
|
var line_status = 0;
|
|
|
|
|
2014-10-04 20:50:31 +02:00
|
|
|
if(this.input.length)
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
|
|
|
line_status |= 1;
|
|
|
|
}
|
|
|
|
|
2014-06-02 22:31:25 +02:00
|
|
|
line_status |= 0x20 | 0x40;
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2014-06-02 22:31:25 +02:00
|
|
|
dbg_log("read line status: " + h(line_status), LOG_SERIAL);
|
2014-01-08 03:14:33 +01:00
|
|
|
return line_status;
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
|
|
|
io.register_write(port | 5, this, function(out_byte)
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
2014-06-02 22:31:25 +02:00
|
|
|
dbg_log("Factory test write", LOG_SERIAL);
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_read(port | 6, this, function()
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
dbg_log("read modem status: " + h(this.modem_status), LOG_SERIAL);
|
|
|
|
return this.modem_status;
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
|
|
|
io.register_write(port | 6, this, function(out_byte)
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
2014-06-02 22:31:25 +02:00
|
|
|
dbg_log("Unkown register write (base+6)", LOG_SERIAL);
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
2014-01-08 03:14:33 +01:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_read(port | 7, this, function()
|
2014-01-08 03:14:33 +01:00
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
return this.scratch_register;
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
|
|
|
io.register_write(port | 7, this, function(out_byte)
|
2013-12-20 22:05:49 +01:00
|
|
|
{
|
2014-10-04 20:50:31 +02:00
|
|
|
this.scratch_register = out_byte;
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-10-04 20:50:31 +02:00
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
UART.prototype.get_state = function()
|
|
|
|
{
|
|
|
|
var state = [];
|
|
|
|
|
|
|
|
state[0] = this.ints;
|
|
|
|
state[1] = this.baud_rate;
|
|
|
|
state[2] = this.line_control;
|
|
|
|
state[3] = this.line_status;
|
|
|
|
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.line_status = 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];
|
|
|
|
};
|
|
|
|
|
2014-10-04 20:50:31 +02:00
|
|
|
UART.prototype.push_irq = function()
|
|
|
|
{
|
2014-12-14 16:26:09 +01:00
|
|
|
dbg_log("Push irq", LOG_SERIAL);
|
2015-05-17 01:56:50 +02:00
|
|
|
this.cpu.device_raise_irq(this.irq);
|
2014-10-04 20:50:31 +02:00
|
|
|
};
|
|
|
|
|
2015-01-12 00:47:43 +01:00
|
|
|
UART.prototype.ClearInterrupt = function(line)
|
2014-12-14 16:26:09 +01:00
|
|
|
{
|
2015-01-10 02:17:19 +01:00
|
|
|
this.ints &= ~(1 << line);
|
2014-12-14 16:26:09 +01:00
|
|
|
this.iir = UART_IIR_NO_INT;
|
|
|
|
|
2015-09-12 01:10:38 +02:00
|
|
|
if(line === this.iir)
|
2014-12-14 16:26:09 +01:00
|
|
|
{
|
2015-01-12 00:47:43 +01:00
|
|
|
this.NextInterrupt();
|
2015-01-10 02:17:19 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
UART.prototype.ThrowCTI = function() {
|
|
|
|
this.ints |= 1 << UART_IIR_CTI;
|
|
|
|
if (!(this.ier & UART_IER_RDI)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ((this.iir != UART_IIR_RLSI) && (this.iir != UART_IIR_RDI)) {
|
|
|
|
this.iir = UART_IIR_CTI;
|
|
|
|
this.push_irq();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
UART.prototype.ThrowTHRI = function() {
|
|
|
|
this.ints |= 1 << UART_IIR_THRI;
|
|
|
|
if (!(this.ier & UART_IER_THRI)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ((this.iir & UART_IIR_NO_INT) || (this.iir == UART_IIR_MSI) || (this.iir == UART_IIR_THRI)) {
|
|
|
|
this.iir = UART_IIR_THRI;
|
|
|
|
this.push_irq();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
UART.prototype.NextInterrupt = function() {
|
|
|
|
if ((this.ints & (1 << UART_IIR_CTI)) && (this.ier & UART_IER_RDI)) {
|
|
|
|
this.ThrowCTI();
|
2014-12-14 16:26:09 +01:00
|
|
|
}
|
2015-01-10 02:17:19 +01:00
|
|
|
else if ((this.ints & (1 << UART_IIR_THRI)) && (this.ier & UART_IER_THRI)) {
|
|
|
|
this.ThrowTHRI();
|
2014-12-14 16:26:09 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
this.iir = UART_IIR_NO_INT;
|
|
|
|
//this.intdev.ClearInterrupt(0x2);
|
|
|
|
}
|
|
|
|
};
|
2014-12-21 22:57:17 +01:00
|
|
|
|
2015-01-10 02:17:19 +01:00
|
|
|
|
2015-09-12 01:10:38 +02:00
|
|
|
/**
|
2014-12-21 22:57:17 +01:00
|
|
|
* @param {number} data
|
|
|
|
*/
|
|
|
|
UART.prototype.data_received = function(data)
|
|
|
|
{
|
|
|
|
dbg_log("input: " + h(data), LOG_SERIAL);
|
|
|
|
this.input.push(data);
|
2015-01-10 02:17:19 +01:00
|
|
|
this.ints |= 1 << UART_IIR_CTI;
|
2014-12-21 22:57:17 +01:00
|
|
|
|
|
|
|
if(this.ier & UART_IER_RDI)
|
|
|
|
{
|
2015-01-10 02:17:19 +01:00
|
|
|
this.ThrowCTI();
|
2014-12-21 22:57:17 +01:00
|
|
|
}
|
|
|
|
};
|