mirror of
https://github.com/holub/mame
synced 2025-04-26 18:23:08 +03:00
dc7085: overhaul
This commit is contained in:
parent
fcdf72bdaa
commit
139fc67f93
@ -31,419 +31,340 @@
|
||||
DEFINE_DEVICE_TYPE(DC7085, dc7085_device, "dc7085", "Digital Equipment Corporation DC7085 Quad UART")
|
||||
DEFINE_DEVICE_TYPE(DC7085_CHANNEL, dc7085_channel, "dc7085_channel", "DC7085 UART channel")
|
||||
|
||||
dc7085_device::dc7085_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, DC7085, tag, owner, clock),
|
||||
m_chan0(*this, "ch0"),
|
||||
m_chan1(*this, "ch1"),
|
||||
m_chan2(*this, "ch2"),
|
||||
m_chan3(*this, "ch3"),
|
||||
m_int_cb(*this),
|
||||
write_0_tx(*this),
|
||||
write_1_tx(*this),
|
||||
write_2_tx(*this),
|
||||
write_3_tx(*this)
|
||||
enum csr_mask : u16
|
||||
{
|
||||
CSR_TRDY = 0x8000, // transmitter ready
|
||||
CSR_TIE = 0x4000, // transmitter interrupt enable
|
||||
CSR_TLINE = 0x0300, // transmitter line number
|
||||
CSR_RDONE = 0x0080, // receiver done
|
||||
CSR_RIE = 0x0040, // receiver interrupt enable
|
||||
CSR_MSE = 0x0020, // master scan enable
|
||||
CSR_CLR = 0x0010, // master clear
|
||||
CSR_MAINT = 0x0008, // maintenance (loopback)
|
||||
};
|
||||
|
||||
enum rbuf_mask : u16
|
||||
{
|
||||
RBUF_DVAL = 0x8000, // data valid
|
||||
RBUF_OERR = 0x4000, // overrun error
|
||||
RBUF_FERR = 0x2000, // framing error
|
||||
RBUF_PERR = 0x1000, // parity error
|
||||
RBUF_RLINE = 0x0300, // received line number
|
||||
RBUF_RLINE3 = 0x0300,
|
||||
RBUF_RLINE2 = 0x0200,
|
||||
RBUF_RLINE1 = 0x0100,
|
||||
RBUF_RLINE0 = 0x0000,
|
||||
RBUF_DATA = 0x00ff, // received character
|
||||
};
|
||||
|
||||
enum lpr_mask : u16
|
||||
{
|
||||
LPR_RXENAB = 0x1000, // receiver enable
|
||||
LPR_SC = 0x0f00, // speed code
|
||||
LPR_ODDPAR = 0x0080, // odd parity
|
||||
LPR_PARENB = 0x0040, // parity enable
|
||||
LPR_STOP = 0x0020, // stop code
|
||||
LPR_CHAR = 0x0018, // character length
|
||||
LPR_LINE = 0x0003, // parameter line number
|
||||
};
|
||||
|
||||
enum tcr_mask : u16
|
||||
{
|
||||
TCR_DTR3 = 0x0800, // modem control
|
||||
TCR_DTR2 = 0x0400,
|
||||
TCR_DTR1 = 0x0200,
|
||||
TCR_DTR0 = 0x0100,
|
||||
TCR_LNENB3 = 0x0008, // transmitter line enable
|
||||
TCR_LNENB2 = 0x0004,
|
||||
TCR_LNENB1 = 0x0002,
|
||||
TCR_LNENB0 = 0x0001,
|
||||
};
|
||||
|
||||
enum tdr_mask : u16
|
||||
{
|
||||
TDR_BRK3 = 0x0800, // break control
|
||||
TDR_BRK2 = 0x0400,
|
||||
TDR_BRK1 = 0x0200,
|
||||
TDR_BRK0 = 0x0100,
|
||||
TDR_TBUF = 0x00ff, // transmitter buffer
|
||||
};
|
||||
|
||||
dc7085_device::dc7085_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
|
||||
: device_t(mconfig, DC7085, tag, owner, clock)
|
||||
, m_chan(*this, "ch%u", 0U)
|
||||
, m_int_cb(*this)
|
||||
, m_tx_cb(*this)
|
||||
, m_dtr_cb(*this)
|
||||
, m_int_state(false)
|
||||
{
|
||||
std::fill_n(&rx_fifo[0], DC7085_RX_FIFO_SIZE, 0);
|
||||
rx_fifo_num = 0;
|
||||
}
|
||||
|
||||
void dc7085_device::device_add_mconfig(machine_config &config)
|
||||
{
|
||||
DC7085_CHANNEL(config, m_chan0, 0);
|
||||
DC7085_CHANNEL(config, m_chan1, 0);
|
||||
DC7085_CHANNEL(config, m_chan2, 0);
|
||||
DC7085_CHANNEL(config, m_chan3, 0);
|
||||
/*
|
||||
* Configure all four channels such that:
|
||||
* - line numbers are inesrted into received data words
|
||||
* - transmitter output is looped back to receiver when enabled
|
||||
* - transmitter completion is signalled
|
||||
*/
|
||||
for (unsigned i = 0; i < std::size(m_chan); i++)
|
||||
{
|
||||
DC7085_CHANNEL(config, m_chan[i], 0);
|
||||
|
||||
m_chan[i]->rx_done().set([this, i](u16 data) { rx_done((i << 8) | data); });
|
||||
m_chan[i]->tx_cb().set([this, i](int state) { m_tx_cb[i](state); if (m_csr & CSR_MAINT) m_chan[i]->rx_w(state); });
|
||||
m_chan[i]->tx_done().set(*this, FUNC(dc7085_device::tx_done));
|
||||
}
|
||||
}
|
||||
|
||||
void dc7085_device::map(address_map &map)
|
||||
{
|
||||
map(0x00, 0x01).rw(FUNC(dc7085_device::status_r), FUNC(dc7085_device::control_w));
|
||||
map(0x04, 0x05).rw(FUNC(dc7085_device::rxbuffer_r), FUNC(dc7085_device::lineparams_w));
|
||||
map(0x08, 0x09).rw(FUNC(dc7085_device::txparams_r), FUNC(dc7085_device::txparams_w));
|
||||
map(0x0c, 0x0d).rw(FUNC(dc7085_device::modem_status_r), FUNC(dc7085_device::txdata_w));
|
||||
map(0x00, 0x01).rw(FUNC(dc7085_device::csr_r), FUNC(dc7085_device::csr_w));
|
||||
map(0x04, 0x05).rw(FUNC(dc7085_device::rbuf_r), FUNC(dc7085_device::lpr_w));
|
||||
map(0x08, 0x09).rw(FUNC(dc7085_device::tcr_r), FUNC(dc7085_device::tcr_w));
|
||||
map(0x0c, 0x0d).rw(FUNC(dc7085_device::msr_r), FUNC(dc7085_device::tdr_w));
|
||||
}
|
||||
|
||||
void dc7085_device::device_start()
|
||||
{
|
||||
m_int_cb.resolve_safe();
|
||||
write_0_tx.resolve_safe();
|
||||
write_1_tx.resolve_safe();
|
||||
write_2_tx.resolve_safe();
|
||||
write_3_tx.resolve_safe();
|
||||
m_tx_cb.resolve_all_safe();
|
||||
m_dtr_cb.resolve_all_safe();
|
||||
|
||||
save_item(NAME(m_status));
|
||||
save_item(NAME(rx_fifo));
|
||||
save_item(NAME(rx_fifo_read_ptr));
|
||||
save_item(NAME(rx_fifo_write_ptr));
|
||||
save_item(NAME(rx_fifo_num));
|
||||
save_item(NAME(m_csr));
|
||||
save_item(NAME(m_tcr));
|
||||
save_item(NAME(m_msr));
|
||||
//save_item(NAME(m_fifo));
|
||||
save_item(NAME(m_rx_buf));
|
||||
save_item(NAME(m_int_state));
|
||||
}
|
||||
|
||||
void dc7085_device::device_reset()
|
||||
{
|
||||
m_chan0->clear();
|
||||
m_chan1->clear();
|
||||
m_chan2->clear();
|
||||
m_chan3->clear();
|
||||
m_status = 0;
|
||||
std::fill_n(&rx_fifo[0], DC7085_RX_FIFO_SIZE, 0);
|
||||
rx_fifo_write_ptr = rx_fifo_read_ptr = 0;
|
||||
rx_fifo_num = 0;
|
||||
m_csr = 0;
|
||||
m_tcr = 0;
|
||||
m_msr = 0;
|
||||
|
||||
m_fifo.clear();
|
||||
m_rx_buf = 0;
|
||||
|
||||
set_int(false);
|
||||
}
|
||||
|
||||
u16 dc7085_device::status_r()
|
||||
u16 dc7085_device::rbuf_r()
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
u16 dc7085_device::rxbuffer_r()
|
||||
{
|
||||
u16 rv;
|
||||
|
||||
LOGMASKED(LOG_RX, "rxbuffer_r: rx_fifo_num %d\n", rx_fifo_num);
|
||||
|
||||
if (rx_fifo_num == 0)
|
||||
{
|
||||
LOGMASKED(LOG_RX, "rx fifo underflow\n");
|
||||
m_status &= ~CTRL_RX_DONE;
|
||||
recalc_irqs();
|
||||
if (m_fifo.empty())
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = rx_fifo[rx_fifo_read_ptr++];
|
||||
if (rx_fifo_read_ptr == DC7085_RX_FIFO_SIZE)
|
||||
{
|
||||
rx_fifo_read_ptr = 0;
|
||||
}
|
||||
u16 const data = m_fifo.dequeue();
|
||||
|
||||
rx_fifo_num--;
|
||||
if (rx_fifo_num == 0)
|
||||
LOGMASKED(LOG_RX, "rbuf_r 0x%04x fifo_length %d\n", data, m_fifo.queue_length());
|
||||
|
||||
if (m_fifo.empty())
|
||||
m_csr &= ~CSR_RDONE;
|
||||
|
||||
// FIXME: insert pending data into fifo
|
||||
if (m_rx_buf & RBUF_DVAL)
|
||||
{
|
||||
m_status &= ~CTRL_RX_DONE;
|
||||
rx_fifo_push(m_rx_buf);
|
||||
m_rx_buf = 0;
|
||||
}
|
||||
|
||||
recalc_irqs();
|
||||
|
||||
//printf("Rx read %02x\n", rv);
|
||||
|
||||
return rv;
|
||||
return data;
|
||||
}
|
||||
|
||||
u16 dc7085_device::txparams_r()
|
||||
void dc7085_device::csr_w(u16 data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
LOGMASKED(LOG_REG, "csr_w %04x tie %d rie %d scan %d clear %d loopback %d\n", data,
|
||||
bool(data & CSR_TIE), bool(data & CSR_RIE), bool(data & CSR_MSE), bool(data & CSR_CLR), bool(data & CSR_MAINT));
|
||||
|
||||
u16 dc7085_device::modem_status_r()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dc7085_device::control_w(u16 data)
|
||||
{
|
||||
LOGMASKED(LOG_REG, "control_w %04x\n", data);
|
||||
LOGMASKED(LOG_REG, "\tTx IRQ %d Rx IRQ %d\n", (data & CTRL_TX_IRQ_ENABLE) ? "1" : "0", (data & CTRL_RX_IRQ_ENABLE) ? "1" : "0");
|
||||
LOGMASKED(LOG_REG, "\tScan enable %d Master clear %d\n", (data & CTRL_MASTER_SCAN) ? "1" : "0", (data & CTRL_MASTER_CLEAR) ? "1" : "0");
|
||||
LOGMASKED(LOG_REG, "\tLocal loopback %d\n", (data & CTRL_LOOPBACK) ? "1" : "0");
|
||||
|
||||
if (data & CTRL_MASTER_CLEAR)
|
||||
if (!(data & CSR_CLR))
|
||||
{
|
||||
m_chan0->clear();
|
||||
m_chan1->clear();
|
||||
m_chan2->clear();
|
||||
m_chan3->clear();
|
||||
m_status = 0;
|
||||
rx_fifo_write_ptr = rx_fifo_read_ptr = 0;
|
||||
rx_fifo_num = 0;
|
||||
return;
|
||||
data &= (CSR_TIE | CSR_RIE | CSR_MSE | CSR_MAINT);
|
||||
m_csr &= ~(CSR_TIE | CSR_RIE | CSR_MSE | CSR_MAINT);
|
||||
m_csr |= data;
|
||||
}
|
||||
|
||||
data &= (CTRL_TX_IRQ_ENABLE|CTRL_RX_IRQ_ENABLE|CTRL_MASTER_SCAN|CTRL_LOOPBACK);
|
||||
m_status &= ~(CTRL_TX_IRQ_ENABLE|CTRL_RX_IRQ_ENABLE|CTRL_MASTER_SCAN|CTRL_LOOPBACK);
|
||||
m_status |= data;
|
||||
else
|
||||
reset();
|
||||
}
|
||||
|
||||
void dc7085_device::lineparams_w(u16 data)
|
||||
void dc7085_device::lpr_w(u16 data)
|
||||
{
|
||||
static const int bauds[] = { 50, 75, 110, 134, 150, 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 7200, 9600, 19800 };
|
||||
|
||||
LOGMASKED(LOG_REG, "lineparams_w %04x\n", data);
|
||||
LOGMASKED(LOG_REG, "\tline %d baud %d rx enabled %d\n", data & 3, bauds[(data>>8) & 0x0f], (data & 0x1000) ? 1 : 0);
|
||||
LOGMASKED(LOG_REG, "\tline %d %d data bits, %d stop bits\n", data & 3, ((data>>3) & 3) + 5, ((data>>5) & 1));
|
||||
LOGMASKED(LOG_REG, "\tline %d parity %s %s\n", data & 3, (data & LPARAM_ODD_PARITY) ? "odd" : "even", (data & LPARAM_PARITY_ENB) ? "enabled" : "disabled");
|
||||
unsigned const baud = (data & LPR_SC) >> 8;
|
||||
unsigned const data_bits = ((data & LPR_CHAR) >> 3) + 5;
|
||||
unsigned const parity = (data & LPR_PARENB) ? ((data & LPR_ODDPAR) ? 1 : 2) : 0;
|
||||
unsigned const stop_bits = (data & LPR_STOP) ? 2 : 1;
|
||||
|
||||
int parity = -1;
|
||||
if (data & LPARAM_PARITY_ENB)
|
||||
{
|
||||
parity = (data & LPARAM_ODD_PARITY) ? 1 : 0;
|
||||
}
|
||||
|
||||
switch (data & 3)
|
||||
{
|
||||
case 0:
|
||||
m_chan0->set_format(((data>>3) & 3) + 5, parity, ((data>>5) & 1));
|
||||
m_chan0->set_baud_rate(bauds[(data>>8) & 0x0f]);
|
||||
m_chan0->set_rx_enable(data & LPARAM_RX_ENABLE);
|
||||
break;
|
||||
case 1:
|
||||
m_chan1->set_format(((data>>3) & 3) + 5, parity, ((data>>5) & 1));
|
||||
m_chan1->set_baud_rate(bauds[(data>>8) & 0x0f]);
|
||||
m_chan1->set_rx_enable(data & LPARAM_RX_ENABLE);
|
||||
break;
|
||||
case 2:
|
||||
m_chan2->set_format(((data>>3) & 3) + 5, parity, ((data>>5) & 1));
|
||||
m_chan2->set_baud_rate(bauds[(data>>8) & 0x0f]);
|
||||
m_chan2->set_rx_enable(data & LPARAM_RX_ENABLE);
|
||||
break;
|
||||
case 3:
|
||||
m_chan3->set_format(((data>>3) & 3) + 5, parity, ((data>>5) & 1));
|
||||
m_chan3->set_baud_rate(bauds[(data>>8) & 0x0f]);
|
||||
m_chan3->set_rx_enable(data & LPARAM_RX_ENABLE);
|
||||
break;
|
||||
}
|
||||
m_chan[data & LPR_LINE]->set_format(bauds[baud], data_bits, parity, stop_bits);
|
||||
m_chan[data & LPR_LINE]->set_enable(data & LPR_RXENAB);
|
||||
}
|
||||
|
||||
void dc7085_device::txparams_w(u16 data)
|
||||
void dc7085_device::tcr_w(u16 data)
|
||||
{
|
||||
LOGMASKED(LOG_REG, "txparams_w %04x\n", data);
|
||||
LOGMASKED(LOG_REG, "tcr_w %04x\n", data);
|
||||
|
||||
if ((data ^ m_tcr) & TCR_DTR0)
|
||||
m_dtr_cb[0](bool(data & TCR_DTR0));
|
||||
if ((data ^ m_tcr) & TCR_DTR1)
|
||||
m_dtr_cb[1](bool(data & TCR_DTR1));
|
||||
if ((data ^ m_tcr) & TCR_DTR2)
|
||||
m_dtr_cb[2](bool(data & TCR_DTR2));
|
||||
if ((data ^ m_tcr) & TCR_DTR3)
|
||||
m_dtr_cb[3](bool(data & TCR_DTR3));
|
||||
|
||||
m_tcr = data;
|
||||
|
||||
m_chan0->set_tx_enable(data & TXCTRL_LINE0_ENB);
|
||||
m_chan1->set_tx_enable(data & TXCTRL_LINE1_ENB);
|
||||
m_chan2->set_tx_enable(data & TXCTRL_LINE2_ENB);
|
||||
m_chan3->set_tx_enable(data & TXCTRL_LINE3_ENB);
|
||||
recalc_irqs();
|
||||
}
|
||||
|
||||
void dc7085_device::txdata_w(u16 data)
|
||||
void dc7085_device::tdr_w(u16 data)
|
||||
{
|
||||
LOGMASKED(LOG_REG, "txdata_w %04x\n", data);
|
||||
switch ((m_status >> 8) & 3)
|
||||
{
|
||||
case 0:
|
||||
m_chan0->write_TX(data&0xff);
|
||||
break;
|
||||
case 1:
|
||||
m_chan1->write_TX(data&0xff);
|
||||
break;
|
||||
case 2:
|
||||
m_chan2->write_TX(data&0xff);
|
||||
break;
|
||||
case 3:
|
||||
m_chan3->write_TX(data&0xff);
|
||||
break;
|
||||
}
|
||||
LOGMASKED(LOG_REG, "tdr_w %04x (%s)\n", data, machine().describe_context());
|
||||
|
||||
unsigned const ch = (m_csr & CSR_TLINE) >> 8;
|
||||
|
||||
if (BIT(m_tcr, ch))
|
||||
m_chan[ch]->tx_w(data & TDR_TBUF);
|
||||
|
||||
m_csr &= ~CSR_TRDY;
|
||||
recalc_irqs();
|
||||
}
|
||||
|
||||
void dc7085_device::recalc_irqs()
|
||||
{
|
||||
bool bIRQ = false;
|
||||
|
||||
LOGMASKED(LOG_IRQ, "recalc_irqs enter\n");
|
||||
if (m_chan0->is_tx_ready())
|
||||
m_csr &= ~(CSR_TRDY | CSR_TLINE);
|
||||
for (unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
m_status |= CTRL_TRDY;
|
||||
m_status &= ~CTRL_LINE_MASK;
|
||||
LOGMASKED(LOG_IRQ, "ch 0: set TRDY\n");
|
||||
}
|
||||
else if (m_chan1->is_tx_ready())
|
||||
{
|
||||
m_status |= CTRL_TRDY;
|
||||
m_status &= ~CTRL_LINE_MASK;
|
||||
m_status |= (1 << 8);
|
||||
LOGMASKED(LOG_IRQ, "ch 1: set TRDY\n");
|
||||
}
|
||||
else if (m_chan2->is_tx_ready())
|
||||
{
|
||||
m_status |= CTRL_TRDY;
|
||||
m_status &= ~CTRL_LINE_MASK;
|
||||
m_status |= (2 << 8);
|
||||
LOGMASKED(LOG_IRQ, "ch 2: set TRDY\n");
|
||||
}
|
||||
else if (m_chan3->is_tx_ready())
|
||||
{
|
||||
m_status |= CTRL_TRDY;
|
||||
m_status &= ~CTRL_LINE_MASK;
|
||||
m_status |= (3 << 8);
|
||||
LOGMASKED(LOG_IRQ, "ch 3: set TRDY\n");
|
||||
if (BIT(m_tcr, i) && m_chan[i]->tx_ready())
|
||||
{
|
||||
m_csr |= CSR_TRDY;
|
||||
m_csr |= (i << 8);
|
||||
LOGMASKED(LOG_IRQ, "ch %u: set TRDY\n", i);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_status & CTRL_TRDY) && (m_status & CTRL_TX_IRQ_ENABLE))
|
||||
{
|
||||
bIRQ = true;
|
||||
}
|
||||
else if ((m_status & CTRL_RX_DONE) && (m_status & CTRL_RX_IRQ_ENABLE))
|
||||
{
|
||||
bIRQ = true;
|
||||
}
|
||||
|
||||
if (bIRQ)
|
||||
{
|
||||
m_int_cb(ASSERT_LINE);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_int_cb(CLEAR_LINE);
|
||||
}
|
||||
set_int(((m_csr & CSR_TIE) && (m_csr & CSR_TRDY)) || ((m_csr & CSR_RIE) && (m_csr & CSR_RDONE)));
|
||||
}
|
||||
|
||||
void dc7085_device::rx_fifo_push(uint16_t data, uint16_t errors)
|
||||
void dc7085_device::rx_fifo_push(u16 data)
|
||||
{
|
||||
if (rx_fifo_num >= DC7085_RX_FIFO_SIZE)
|
||||
if (!m_fifo.full())
|
||||
{
|
||||
LOGMASKED(LOG_RX, "DC7085: FIFO overflow\n");
|
||||
data |= dc7085_device::RXMASK_OVERRUN_ERR;
|
||||
return;
|
||||
LOGMASKED(LOG_RX, "rx_fifo_push 0x%04x fifo_length %d\n", data, m_fifo.queue_length());
|
||||
|
||||
m_fifo.enqueue(data);
|
||||
|
||||
m_csr |= CSR_RDONE;
|
||||
}
|
||||
else
|
||||
throw emu_fatalerror("fifo overflow\n");
|
||||
}
|
||||
|
||||
void dc7085_device::rx_done(u16 data)
|
||||
{
|
||||
// check if receive buffer is full
|
||||
if (m_rx_buf & RBUF_DVAL)
|
||||
{
|
||||
// push buffer into fifo if not full
|
||||
if (!m_fifo.full())
|
||||
{
|
||||
rx_fifo_push(m_rx_buf);
|
||||
m_rx_buf = 0;
|
||||
}
|
||||
else
|
||||
// flag buffer overrun
|
||||
data |= RBUF_OERR;
|
||||
}
|
||||
|
||||
rx_fifo[rx_fifo_write_ptr++] = data | errors;
|
||||
if (rx_fifo_write_ptr == DC7085_RX_FIFO_SIZE)
|
||||
rx_fifo_write_ptr = 0;
|
||||
// store received data in fifo or buffer
|
||||
if (!m_fifo.full())
|
||||
rx_fifo_push(data);
|
||||
else
|
||||
m_rx_buf = data;
|
||||
|
||||
rx_fifo_num++;
|
||||
|
||||
LOGMASKED(LOG_RX, "ch %d, got %02x, fifo_num %d\n", (data>>8) & 3, data&0xff, rx_fifo_num);
|
||||
|
||||
m_status |= dc7085_device::CTRL_RX_DONE;
|
||||
recalc_irqs();
|
||||
}
|
||||
|
||||
// UART channel class stuff
|
||||
void dc7085_device::tx_done(int state)
|
||||
{
|
||||
recalc_irqs();
|
||||
}
|
||||
|
||||
dc7085_channel::dc7085_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
dc7085_channel::dc7085_channel(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
|
||||
: device_t(mconfig, DC7085_CHANNEL, tag, owner, clock)
|
||||
, device_serial_interface(mconfig, *this)
|
||||
, rx_enabled(0)
|
||||
, tx_enabled(0)
|
||||
, m_tx_cb(*this)
|
||||
, m_tx_done(*this)
|
||||
, m_rx_done(*this)
|
||||
, m_rx_enabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
void dc7085_channel::device_start()
|
||||
{
|
||||
m_base = downcast<dc7085_device *>(owner());
|
||||
m_ch = m_base->get_ch(this); // get our channel number
|
||||
m_tx_cb.resolve_safe();
|
||||
m_rx_done.resolve_safe();
|
||||
m_tx_done.resolve_safe();
|
||||
|
||||
save_item(NAME(baud_rate));
|
||||
save_item(NAME(rx_enabled));
|
||||
save_item(NAME(tx_enabled));
|
||||
save_item(NAME(tx_data));
|
||||
save_item(NAME(tx_ready));
|
||||
save_item(NAME(m_rx_enabled));
|
||||
}
|
||||
|
||||
void dc7085_channel::device_reset()
|
||||
{
|
||||
set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1);
|
||||
transmit_register_reset();
|
||||
|
||||
baud_rate = 0;
|
||||
rx_enabled = 0;
|
||||
tx_enabled = 0;
|
||||
tx_ready = 1;
|
||||
set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1);
|
||||
set_tra_rate(0);
|
||||
set_rcv_rate(0);
|
||||
|
||||
m_rx_enabled = false;
|
||||
}
|
||||
|
||||
// serial device virtual overrides
|
||||
void dc7085_channel::rcv_complete()
|
||||
{
|
||||
receive_register_extract();
|
||||
|
||||
//printf("%s ch %d rcv complete\n", tag(), m_ch);
|
||||
|
||||
if (rx_enabled)
|
||||
if (m_rx_enabled)
|
||||
{
|
||||
uint16_t errors = 0;
|
||||
if (is_receive_framing_error())
|
||||
errors |= dc7085_device::RXMASK_FRAMING_ERR;
|
||||
if (is_receive_parity_error())
|
||||
errors |= dc7085_device::RXMASK_PARITY_ERR;
|
||||
u16 data = RBUF_DVAL | get_received_char();
|
||||
|
||||
m_base->rx_fifo_push(get_received_char() | (m_ch << 8) | dc7085_device::RXMASK_DATA_VALID, errors);
|
||||
if (is_receive_framing_error())
|
||||
data |= RBUF_FERR;
|
||||
if (is_receive_parity_error())
|
||||
data |= RBUF_PERR;
|
||||
|
||||
m_rx_done(data);
|
||||
}
|
||||
}
|
||||
|
||||
void dc7085_channel::tra_complete()
|
||||
{
|
||||
LOGMASKED(LOG_TX, "ch %d Tx complete\n", m_ch);
|
||||
tx_ready = 1;
|
||||
|
||||
// if local loopback is on, write the transmitted data as if a byte had been received
|
||||
if (m_base->m_status & dc7085_device::CTRL_LOOPBACK)
|
||||
m_base->rx_fifo_push(tx_data | (m_ch << 8) | dc7085_device::RXMASK_DATA_VALID, 0);
|
||||
|
||||
m_base->recalc_irqs();
|
||||
m_tx_done(1);
|
||||
}
|
||||
|
||||
void dc7085_channel::tra_callback()
|
||||
{
|
||||
int bit = transmit_register_get_data_bit();
|
||||
|
||||
LOGMASKED(LOG_TX, "transmit %d\n", bit);
|
||||
switch (m_ch)
|
||||
{
|
||||
case 0: m_base->write_0_tx(bit); break;
|
||||
case 1: m_base->write_1_tx(bit); break;
|
||||
case 2: m_base->write_2_tx(bit); break;
|
||||
case 3: m_base->write_3_tx(bit); break;
|
||||
}
|
||||
m_tx_cb(transmit_register_get_data_bit());
|
||||
}
|
||||
|
||||
void dc7085_channel::set_baud_rate(int baud)
|
||||
void dc7085_channel::set_format(unsigned baud, unsigned data_bits, unsigned parity, unsigned stop_bits)
|
||||
{
|
||||
set_data_frame(1, data_bits, parity ? (parity == 1 ? PARITY_ODD : PARITY_EVEN) : PARITY_NONE,
|
||||
stop_bits == 1 ? STOP_BITS_1 : (data_bits == 5 ? STOP_BITS_1_5 : STOP_BITS_2));
|
||||
|
||||
set_tra_rate(baud);
|
||||
set_rcv_rate(baud);
|
||||
baud_rate = baud;
|
||||
}
|
||||
|
||||
void dc7085_channel::set_format(int data_bits, int parity, int stop_bits)
|
||||
void dc7085_channel::tx_w(u8 data)
|
||||
{
|
||||
switch (parity)
|
||||
{
|
||||
case -1:
|
||||
set_data_frame(1, data_bits, PARITY_NONE, stop_bits ? STOP_BITS_1 : STOP_BITS_0);
|
||||
break;
|
||||
|
||||
case 0:
|
||||
set_data_frame(1, data_bits, PARITY_EVEN, stop_bits ? STOP_BITS_1 : STOP_BITS_0);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
set_data_frame(1, data_bits, PARITY_ODD, stop_bits ? STOP_BITS_1 : STOP_BITS_0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// called on a master clear
|
||||
void dc7085_channel::clear()
|
||||
{
|
||||
transmit_register_reset();
|
||||
set_baud_rate(0);
|
||||
rx_enabled = 0;
|
||||
tx_enabled = 0;
|
||||
tx_ready = 1;
|
||||
}
|
||||
|
||||
void dc7085_channel::set_tx_enable(bool bEnabled)
|
||||
{
|
||||
LOGMASKED(LOG_TX, "ch %d set_tx_enable %s\n", m_ch, bEnabled ? "true" : "false");
|
||||
tx_enabled = bEnabled ? 1 : 0;
|
||||
}
|
||||
|
||||
void dc7085_channel::set_rx_enable(bool bEnabled)
|
||||
{
|
||||
rx_enabled = bEnabled ? 1 : 0;
|
||||
}
|
||||
|
||||
void dc7085_channel::write_TX(uint8_t data)
|
||||
{
|
||||
tx_data = data;
|
||||
|
||||
if (!tx_ready)
|
||||
{
|
||||
LOGMASKED(LOG_TX, "Write %02x to TX when TX not ready!\n", data);
|
||||
}
|
||||
|
||||
LOGMASKED(LOG_TX, "ch %d Tx [%02x] (%d baud)\n", m_ch, data, baud_rate);
|
||||
|
||||
tx_ready = 0;
|
||||
|
||||
// send tx_data
|
||||
transmit_register_setup(tx_data);
|
||||
|
||||
m_base->recalc_irqs();
|
||||
if (is_transmit_register_empty())
|
||||
transmit_register_setup(data);
|
||||
}
|
||||
|
@ -8,176 +8,103 @@
|
||||
|
||||
#include "diserial.h"
|
||||
|
||||
#define DC7085_RX_FIFO_SIZE (64)
|
||||
|
||||
// forward declaration
|
||||
class dc7085_device;
|
||||
|
||||
class dc7085_channel : public device_t, public device_serial_interface
|
||||
class dc7085_channel
|
||||
: public device_t
|
||||
, public device_serial_interface
|
||||
{
|
||||
public:
|
||||
dc7085_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
friend class dc7085_device;
|
||||
|
||||
// device-level overrides
|
||||
public:
|
||||
auto tx_cb() { return m_tx_cb.bind(); }
|
||||
|
||||
dc7085_channel(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
|
||||
|
||||
protected:
|
||||
// device_t overrides
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
// device_serial overrides
|
||||
virtual void rcv_complete() override; // Rx completed receiving byte
|
||||
virtual void tra_complete() override; // Tx completed sending byte
|
||||
virtual void tra_callback() override; // Tx send bit
|
||||
virtual void rcv_complete() override;
|
||||
virtual void tra_complete() override;
|
||||
virtual void tra_callback() override;
|
||||
|
||||
void set_baud_rate(int baud);
|
||||
void set_format(int data_bits, int parity, int stop_bits);
|
||||
void clear();
|
||||
void set_tx_enable(bool bEnabled);
|
||||
void set_rx_enable(bool bEnabled);
|
||||
void write_TX(uint8_t data);
|
||||
bool is_tx_ready()
|
||||
{
|
||||
if (!tx_enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// parent interface
|
||||
auto rx_done() { return m_rx_done.bind(); }
|
||||
auto tx_done() { return m_tx_done.bind(); }
|
||||
|
||||
return tx_ready ? true : false;
|
||||
}
|
||||
void set_format(unsigned baud, unsigned data_bits, unsigned parity, unsigned stop_bits);
|
||||
void set_enable(bool enable) { m_rx_enabled = enable; }
|
||||
bool tx_ready() const { return is_transmit_register_empty(); }
|
||||
void tx_w(u8 data);
|
||||
|
||||
private:
|
||||
/* Receiver */
|
||||
u8 rx_enabled;
|
||||
devcb_write_line m_tx_cb;
|
||||
devcb_write_line m_tx_done;
|
||||
devcb_write16 m_rx_done;
|
||||
|
||||
/* Shared */
|
||||
int baud_rate;
|
||||
int m_ch;
|
||||
|
||||
/* Transmitter */
|
||||
u8 tx_enabled;
|
||||
u8 tx_data;
|
||||
u8 tx_ready;
|
||||
|
||||
dc7085_device *m_base;
|
||||
bool m_rx_enabled;
|
||||
};
|
||||
|
||||
class dc7085_device : public device_t
|
||||
{
|
||||
friend class dc7085_channel;
|
||||
|
||||
public:
|
||||
required_device<dc7085_channel> m_chan0, m_chan1, m_chan2, m_chan3;
|
||||
|
||||
dc7085_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
dc7085_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
|
||||
|
||||
void map(address_map &map);
|
||||
|
||||
auto int_cb() { return m_int_cb.bind(); }
|
||||
auto ch0_tx_cb() { return write_0_tx.bind(); }
|
||||
auto ch1_tx_cb() { return write_1_tx.bind(); }
|
||||
auto ch2_tx_cb() { return write_2_tx.bind(); }
|
||||
auto ch3_tx_cb() { return write_3_tx.bind(); }
|
||||
|
||||
template <unsigned Ch> void rx_w(int state) { m_chan[Ch]->rx_w(state); }
|
||||
template <unsigned Ch> auto tx_cb() { return m_tx_cb[Ch].bind(); }
|
||||
template <unsigned Ch> auto dtr_cb() { return m_dtr_cb[Ch].bind(); }
|
||||
|
||||
protected:
|
||||
// standard device_interface overrides
|
||||
// device_t overrides
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
virtual void device_add_mconfig(machine_config &config) override;
|
||||
|
||||
void rx_fifo_push(uint16_t data, uint16_t errors);
|
||||
// register accessors
|
||||
u16 csr_r() { return m_csr; }
|
||||
u16 rbuf_r();
|
||||
u16 tcr_r() { return m_tcr; }
|
||||
u16 msr_r() { return m_msr; }
|
||||
void csr_w(u16 data);
|
||||
void lpr_w(u16 data);
|
||||
void tcr_w(u16 data);
|
||||
void tdr_w(u16 data);
|
||||
|
||||
// helpers
|
||||
void rx_fifo_push(u16 data);
|
||||
void rx_done(u16 data);
|
||||
void tx_done(int state);
|
||||
void recalc_irqs();
|
||||
|
||||
devcb_write_line m_int_cb;
|
||||
devcb_write_line write_0_tx, write_1_tx, write_2_tx, write_3_tx;
|
||||
|
||||
u16 m_status;
|
||||
|
||||
u16 status_r();
|
||||
u16 rxbuffer_r();
|
||||
u16 txparams_r();
|
||||
u16 modem_status_r();
|
||||
void control_w(u16 data);
|
||||
void lineparams_w(u16 data);
|
||||
void txparams_w(u16 data);
|
||||
void txdata_w(u16 data);
|
||||
|
||||
int get_ch(dc7085_channel *ch)
|
||||
void set_int(bool state)
|
||||
{
|
||||
if (ch == m_chan0)
|
||||
if (state != m_int_state)
|
||||
{
|
||||
return 0;
|
||||
m_int_state = state;
|
||||
m_int_cb(state);
|
||||
}
|
||||
else if (ch == m_chan1)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (ch == m_chan2)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
private:
|
||||
u16 rx_fifo[DC7085_RX_FIFO_SIZE];
|
||||
int rx_fifo_read_ptr;
|
||||
int rx_fifo_write_ptr;
|
||||
int rx_fifo_num;
|
||||
required_device_array<dc7085_channel, 4> m_chan;
|
||||
|
||||
enum control_status_mask : u16
|
||||
{
|
||||
CTRL_TRDY = 0x8000,
|
||||
CTRL_TX_IRQ_ENABLE = 0x4000,
|
||||
CTRL_LINE_MASK = 0x0300,
|
||||
CTRL_RX_DONE = 0x0080,
|
||||
CTRL_RX_IRQ_ENABLE = 0x0040,
|
||||
CTRL_MASTER_SCAN = 0x0020,
|
||||
CTRL_MASTER_CLEAR = 0x0010,
|
||||
CTRL_LOOPBACK = 0x0008
|
||||
};
|
||||
devcb_write_line m_int_cb;
|
||||
devcb_write_line::array<4> m_tx_cb;
|
||||
devcb_write_line::array<4> m_dtr_cb;
|
||||
|
||||
enum rx_buffer_mask : u16
|
||||
{
|
||||
RXMASK_DATA_VALID = 0x8000,
|
||||
RXMASK_OVERRUN_ERR = 0x4000,
|
||||
RXMASK_FRAMING_ERR = 0x2000,
|
||||
RXMASK_PARITY_ERR = 0x1000,
|
||||
RXMASK_LINE_MASK = 0x0300,
|
||||
RXMASK_DATA_MASK = 0x00ff
|
||||
};
|
||||
u16 m_csr;
|
||||
u16 m_tcr;
|
||||
u16 m_msr;
|
||||
|
||||
enum line_param_mask : u16
|
||||
{
|
||||
LPARAM_RX_ENABLE = 0x1000,
|
||||
LPARAM_BAUD_MASK = 0x0f00,
|
||||
LPARAM_ODD_PARITY = 0x0080,
|
||||
LPARAM_PARITY_ENB = 0x0040,
|
||||
LPARAM_STOP_BITS = 0x0020,
|
||||
LPARAM_CHARLEN_MASK = 0x0018,
|
||||
LPARAM_LINE_MASK = 0x0003
|
||||
};
|
||||
util::fifo<u16, 64> m_fifo;
|
||||
u16 m_rx_buf;
|
||||
|
||||
enum tx_control_mask : u16
|
||||
{
|
||||
TXCTRL_DTR2 = 0x0400,
|
||||
TXCTRL_LINE3_ENB = 0x0008,
|
||||
TXCTRL_LINE2_ENB = 0x0004,
|
||||
TXCTRL_LINE1_ENB = 0x0002,
|
||||
TXCTRL_LINE0_ENB = 0x0001
|
||||
};
|
||||
|
||||
enum modem_status_mask : u16
|
||||
{
|
||||
MSTAT_DSR2 = 0x0400
|
||||
};
|
||||
|
||||
enum tx_data_mask : u16
|
||||
{
|
||||
TXDATA_LINE3_BREAK = 0x0800,
|
||||
TXDATA_LINE2_BREAK = 0x0400,
|
||||
TXDATA_LINE1_BREAK = 0x0200,
|
||||
TXDATA_LINE0_BREAK = 0x0100,
|
||||
TXDATA_DATA_MASK = 0x00ff
|
||||
};
|
||||
bool m_int_state;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(DC7085, dc7085_device)
|
||||
|
Loading…
Reference in New Issue
Block a user