summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/dl11.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/dl11.cpp')
-rw-r--r--src/devices/machine/dl11.cpp464
1 files changed, 464 insertions, 0 deletions
diff --git a/src/devices/machine/dl11.cpp b/src/devices/machine/dl11.cpp
new file mode 100644
index 00000000000..d791a758df0
--- /dev/null
+++ b/src/devices/machine/dl11.cpp
@@ -0,0 +1,464 @@
+// license:BSD-3-Clause
+// copyright-holders:Sergey Svishchev
+/***************************************************************************
+
+ DEC DL11-type SLU (serial line unit) and compatible devices.
+
+ Frame format is not software-configurable; hardcoded to 8N1 for now.
+
+ dl11_device implements all features of DL11-D Unibus device.
+
+ 1801VP1-065 is a single chip implementation used in Soviet clones:
+ - error bits are in RCSR, not RBUF, and are not cleared by new data
+ - on overflow, RBUF keeps first received byte
+ - reading TBUF returns transmit interrupt vector
+ - writing to RBUF does nothing
+ - INIT does not set DONE in TCSR
+ - supports RTS/CTS flow control
+ - always sends 2 stop bits
+
+ http://www.ibiblio.org/pub/academic/computer-science/history/pdp-11/hardware/micronotes/numerical/micronote33.txt
+ bitsavers://pdf/dec/unibus/EK-DL11-TM-003_DL11_Asynchronous_Line_Interface_Manual_Sep75.pdf
+ https://github.com/1801BM1/k1801/blob/master/065/
+
+***************************************************************************/
+
+#include "emu.h"
+#include "dl11.h"
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+DEFINE_DEVICE_TYPE(DL11, dl11_device, "dl11", "DEC DL11-D SLU")
+DEFINE_DEVICE_TYPE(K1801VP065, k1801vp065_device, "1801vp1_065", "1801VP1-065")
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+enum
+{
+ DLRCSR = 0,
+ DLRBUF,
+ DLTCSR,
+ DLTBUF
+};
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// dl11_device - constructor
+//-------------------------------------------------
+
+dl11_device::dl11_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock)
+ , device_serial_interface(mconfig, *this)
+ , device_z80daisy_interface(mconfig, *this)
+ , m_installed(false)
+ , m_write_txd(*this)
+ , m_write_rxrdy(*this)
+ , m_write_txrdy(*this)
+ , m_rxc(0)
+ , m_txc(0)
+ , m_rxvec(0)
+ , m_txvec(0)
+{
+}
+
+dl11_device::dl11_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : dl11_device(mconfig, DL11, tag, owner, clock)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void dl11_device::device_start()
+{
+ // save state
+ save_item(NAME(m_rcsr));
+ save_item(NAME(m_rbuf));
+ save_item(NAME(m_tcsr));
+ save_item(NAME(m_tbuf));
+
+ m_installed = false;
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void dl11_device::device_reset()
+{
+ if (!m_installed)
+ {
+ set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1);
+
+ // create the timers
+ if (m_rxc > 0)
+ set_rcv_rate(m_rxc);
+
+ if (m_txc > 0)
+ set_tra_rate(m_txc);
+
+ m_rcsr = m_rbuf = m_tbuf = 0;
+ m_tcsr = CSR_DONE;
+ m_rxrdy = m_txrdy = CLEAR_LINE;
+
+ m_write_txd(1);
+
+ m_installed = true;
+ }
+ else
+ {
+ m_rbuf &= 255; // clear error bits
+ m_rcsr &= ~(CSR_IE | CSR_DONE | DLRCSR_ACT);
+ m_tcsr &= ~(DLTCSR_XBRK | DLTCSR_MAINT | CSR_IE);
+ m_tcsr |= CSR_DONE;
+ clear_virq(m_write_rxrdy, 1, 1, m_rxrdy);
+ clear_virq(m_write_txrdy, 1, 1, m_txrdy);
+ }
+}
+
+int dl11_device::z80daisy_irq_state()
+{
+ if (m_rxrdy == ASSERT_LINE || m_txrdy == ASSERT_LINE)
+ return Z80_DAISY_INT;
+ else
+ return 0;
+}
+
+int dl11_device::z80daisy_irq_ack()
+{
+ int vec = -1;
+
+ if (m_rxrdy == ASSERT_LINE)
+ {
+ m_rxrdy = CLEAR_LINE;
+ vec = m_rxvec;
+ }
+ else if (m_txrdy == ASSERT_LINE)
+ {
+ m_txrdy = CLEAR_LINE;
+ vec = m_txvec;
+ }
+
+ return vec;
+}
+
+void dl11_device::z80daisy_irq_reti()
+{
+}
+
+
+//-------------------------------------------------
+// tra_callback -
+//-------------------------------------------------
+
+void dl11_device::tra_callback()
+{
+ if (m_tcsr & DLTCSR_XBRK)
+ {
+ transmit_register_get_data_bit();
+ if (m_tcsr & DLTCSR_MAINT)
+ rx_w(0);
+ else
+ m_write_txd(0);
+ }
+ else if (!is_transmit_register_empty())
+ {
+ if (m_tcsr & DLTCSR_MAINT)
+ rx_w(transmit_register_get_data_bit());
+ else
+ m_write_txd(transmit_register_get_data_bit());
+ }
+}
+
+
+//-------------------------------------------------
+// tra_complete -
+//-------------------------------------------------
+
+void dl11_device::tra_complete()
+{
+ m_tcsr |= CSR_DONE;
+ raise_virq(m_write_txrdy, m_tcsr, CSR_IE, m_txrdy);
+}
+
+
+//-------------------------------------------------
+// rcv_callback -
+//-------------------------------------------------
+
+void dl11_device::rx_w(int state)
+{
+ device_serial_interface::rx_w(state);
+ if (is_receive_register_synchronized())
+ m_rcsr |= DLRCSR_ACT;
+}
+
+
+//-------------------------------------------------
+// rcv_complete -
+//-------------------------------------------------
+
+void dl11_device::rcv_complete()
+{
+ receive_register_extract();
+ m_rbuf = get_received_char();
+ if (is_receive_framing_error())
+ {
+ m_rbuf |= DLRBUF_ERR | DLRBUF_RBRK;
+ }
+ if (is_receive_parity_error())
+ {
+ m_rbuf |= DLRBUF_ERR | DLRBUF_PERR;
+ }
+ if (m_rcsr & CSR_DONE)
+ {
+ m_rbuf |= DLRBUF_ERR | DLRBUF_OVR;
+ }
+ else
+ {
+ m_rcsr |= CSR_DONE;
+ }
+ m_rcsr &= ~DLRCSR_ACT;
+ raise_virq(m_write_rxrdy, m_rcsr, CSR_IE, m_rxrdy);
+}
+
+
+//-------------------------------------------------
+// read - register read
+//-------------------------------------------------
+
+uint16_t dl11_device::read(offs_t offset)
+{
+ uint16_t data = 0;
+
+ switch (offset & 3)
+ {
+ case DLRCSR:
+ data = m_rcsr & DLRCSR_RD;
+ break;
+
+ case DLRBUF:
+ data = m_rbuf;
+ if (!machine().side_effects_disabled())
+ {
+ m_rcsr &= ~CSR_DONE;
+ clear_virq(m_write_rxrdy, m_rcsr, CSR_IE, m_rxrdy);
+ }
+ break;
+
+ case DLTCSR:
+ data = m_tcsr & DLTCSR_RD;
+ break;
+
+ case DLTBUF:
+ data = m_tbuf;
+ break;
+ }
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// write - register write
+//-------------------------------------------------
+
+void dl11_device::write(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ switch (offset & 3)
+ {
+ case DLRCSR:
+ if ((data & CSR_IE) == 0)
+ {
+ clear_virq(m_write_rxrdy, 1, 1, m_rxrdy);
+ }
+ else if ((m_rcsr & (CSR_DONE + CSR_IE)) == CSR_DONE)
+ {
+ raise_virq(m_write_rxrdy, 1, 1, m_rxrdy);
+ }
+ m_rcsr = ((m_rcsr & ~DLRCSR_WR) | (data & DLRCSR_WR));
+ if (data & DLRCSR_RDRENB)
+ {
+ m_rcsr &= ~CSR_DONE;
+ clear_virq(m_write_rxrdy, m_rcsr, CSR_IE, m_rxrdy);
+ }
+ break;
+
+ case DLRBUF:
+ m_rcsr &= ~CSR_DONE;
+ clear_virq(m_write_rxrdy, m_rcsr, CSR_IE, m_rxrdy);
+ break;
+
+ case DLTCSR:
+ if ((data & CSR_IE) == 0)
+ {
+ clear_virq(m_write_txrdy, 1, 1, m_txrdy);
+ }
+ else if ((m_tcsr & (CSR_DONE + CSR_IE)) == CSR_DONE)
+ {
+ raise_virq(m_write_txrdy, 1, 1, m_txrdy);
+ }
+ if ((m_tcsr ^ data) & DLTCSR_XBRK)
+ {
+ if (data & DLTCSR_XBRK)
+ {
+ transmit_register_setup(0);
+ }
+ else
+ {
+ transmit_register_reset();
+ if (m_tcsr & DLTCSR_MAINT)
+ rx_w(1);
+ else
+ m_write_txd(1);
+ }
+ }
+ m_tcsr = ((m_tcsr & ~DLTCSR_WR) | (data & DLTCSR_WR));
+ break;
+
+ case DLTBUF:
+ m_tbuf = data & 255;
+ m_tcsr &= ~CSR_DONE;
+ clear_virq(m_write_txrdy, m_tcsr, CSR_IE, m_txrdy);
+ transmit_register_setup((m_tcsr & DLTCSR_XBRK) ? 0 : m_tbuf);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// rxrdy_r - receiver ready
+//-------------------------------------------------
+
+int dl11_device::rxrdy_r()
+{
+ return ((m_rcsr & (CSR_DONE | CSR_IE)) == (CSR_DONE | CSR_IE)) ? ASSERT_LINE : CLEAR_LINE;
+}
+
+
+//-------------------------------------------------
+// txrdy_r - transmitter empty
+//-------------------------------------------------
+
+int dl11_device::txrdy_r()
+{
+ return ((m_tcsr & (CSR_DONE | CSR_IE)) == (CSR_DONE | CSR_IE)) ? ASSERT_LINE : CLEAR_LINE;
+}
+
+
+//-------------------------------------------------
+// k1801vp065_device - constructor
+//-------------------------------------------------
+
+k1801vp065_device::k1801vp065_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : dl11_device(mconfig, K1801VP065, tag, owner, clock)
+ , m_write_rts(*this)
+{
+}
+
+void k1801vp065_device::device_reset()
+{
+ if (!m_installed)
+ {
+ dl11_device::device_reset();
+ set_data_frame(1, 8, PARITY_NONE, STOP_BITS_2);
+ m_write_rts(0);
+ }
+ else
+ {
+ m_rcsr &= ~(DLRCSR_RBRK | DLRCSR_OVR | DLRCSR_PERR | CSR_IE | CSR_DONE);
+ m_tcsr &= ~(DLTCSR_XBRK | DLTCSR_MAINT | CSR_IE);
+ clear_virq(m_write_rxrdy, 1, 1, m_rxrdy);
+ clear_virq(m_write_txrdy, 1, 1, m_txrdy);
+ }
+}
+
+void k1801vp065_device::rcv_complete()
+{
+ receive_register_extract();
+ if (m_rcsr & CSR_DONE)
+ {
+ m_rcsr |= DLRCSR_OVR;
+ }
+ else
+ {
+ m_rbuf = get_received_char();
+ m_rcsr |= CSR_DONE;
+ if (is_receive_framing_error())
+ {
+ m_rcsr |= DLRCSR_RBRK;
+ }
+ if (is_receive_parity_error())
+ {
+ m_rcsr |= DLRCSR_PERR;
+ }
+ }
+ raise_virq(m_write_rxrdy, m_rcsr, CSR_IE, m_rxrdy);
+ m_write_rts(1);
+}
+
+//-------------------------------------------------
+// read - register read
+//-------------------------------------------------
+
+uint16_t k1801vp065_device::read(offs_t offset)
+{
+ uint16_t data = 0;
+
+ switch (offset & 3)
+ {
+ case DLRCSR:
+ data = m_rcsr & DLRCSR_RD;
+ break;
+
+ case DLRBUF:
+ data = m_rbuf;
+ if (!machine().side_effects_disabled())
+ {
+ m_rcsr &= ~(CSR_DONE | DLRCSR_PERR | DLRCSR_OVR | DLRCSR_RBRK);
+ clear_virq(m_write_rxrdy, m_rcsr, CSR_IE, m_rxrdy);
+ m_write_rts(0);
+ }
+ break;
+
+ case DLTCSR:
+ data = m_tcsr & DLTCSR_RD;
+ break;
+
+ case DLTBUF:
+ data = m_rxvec;
+ break;
+ }
+
+ return data;
+}
+
+//-------------------------------------------------
+// write - register write
+//-------------------------------------------------
+
+void k1801vp065_device::write(offs_t offset, uint16_t data)
+{
+ switch (offset & 3)
+ {
+ case DLRCSR:
+ data &= ~DLRCSR_RDRENB;
+ break;
+
+ case DLRBUF:
+ return;
+ }
+ dl11_device::write(offset, data);
+}