From 2c539b679104468f7f7c18d8e3b3c0680442b4c2 Mon Sep 17 00:00:00 2001 From: shattered Date: Thu, 23 Jan 2025 02:38:15 +0300 Subject: [PATCH] Improve DL11 emulation (maintenance mode, break rx/tx) and add support for (#13184) 1801VP1-065 variant. Passes ZDLDH0 test and works with TU58 driver (DD.SYS). --- src/devices/machine/dl11.cpp | 237 ++++++++++++++++++++++++++++++----- src/devices/machine/dl11.h | 57 +++++++-- 2 files changed, 255 insertions(+), 39 deletions(-) diff --git a/src/devices/machine/dl11.cpp b/src/devices/machine/dl11.cpp index ef81cf5911e..d791a758df0 100644 --- a/src/devices/machine/dl11.cpp +++ b/src/devices/machine/dl11.cpp @@ -2,11 +2,24 @@ // copyright-holders:Sergey Svishchev /*************************************************************************** - DEC DL11-type SLU (serial line unit). + 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/ ***************************************************************************/ @@ -19,7 +32,8 @@ //************************************************************************** // device type definition -DEFINE_DEVICE_TYPE(DL11, dl11_device, "dl11", "DEC DL11-type SLU") +DEFINE_DEVICE_TYPE(DL11, dl11_device, "dl11", "DEC DL11-D SLU") +DEFINE_DEVICE_TYPE(K1801VP065, k1801vp065_device, "1801vp1_065", "1801VP1-065") //************************************************************************** @@ -42,10 +56,11 @@ enum // dl11_device - constructor //------------------------------------------------- -dl11_device::dl11_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, DL11, tag, owner, clock) +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) @@ -56,6 +71,11 @@ dl11_device::dl11_device(const machine_config &mconfig, const char *tag, device_ { } +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 @@ -68,8 +88,9 @@ void dl11_device::device_start() save_item(NAME(m_rbuf)); save_item(NAME(m_tcsr)); save_item(NAME(m_tbuf)); -} + m_installed = false; +} //------------------------------------------------- // device_reset - device-specific reset @@ -77,22 +98,34 @@ void dl11_device::device_start() void dl11_device::device_reset() { - set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1); + 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); + // create the timers + if (m_rxc > 0) + set_rcv_rate(m_rxc); - if (m_txc > 0) - set_tra_rate(m_txc); + 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_rcsr = m_rbuf = m_tbuf = 0; + m_tcsr = CSR_DONE; + m_rxrdy = m_txrdy = CLEAR_LINE; - m_write_txd(1); - m_write_rxrdy(m_rxrdy); - m_write_txrdy(m_txrdy); + 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() @@ -134,11 +167,18 @@ void dl11_device::tra_callback() { if (m_tcsr & DLTCSR_XBRK) { - m_write_txd(0); + transmit_register_get_data_bit(); + if (m_tcsr & DLTCSR_MAINT) + rx_w(0); + else + m_write_txd(0); } else if (!is_transmit_register_empty()) { - m_write_txd(transmit_register_get_data_bit()); + if (m_tcsr & DLTCSR_MAINT) + rx_w(transmit_register_get_data_bit()); + else + m_write_txd(transmit_register_get_data_bit()); } } @@ -154,6 +194,18 @@ void dl11_device::tra_complete() } +//------------------------------------------------- +// 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 - //------------------------------------------------- @@ -161,13 +213,10 @@ void dl11_device::tra_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; - } - else - { - m_rbuf = get_received_char(); + m_rbuf |= DLRBUF_ERR | DLRBUF_RBRK; } if (is_receive_parity_error()) { @@ -181,6 +230,7 @@ void dl11_device::rcv_complete() { m_rcsr |= CSR_DONE; } + m_rcsr &= ~DLRCSR_ACT; raise_virq(m_write_rxrdy, m_rcsr, CSR_IE, m_rxrdy); } @@ -201,8 +251,11 @@ uint16_t dl11_device::read(offs_t offset) case DLRBUF: data = m_rbuf; - m_rcsr &= ~CSR_DONE; - clear_virq(m_write_rxrdy, m_rcsr, CSR_IE, m_rxrdy); + if (!machine().side_effects_disabled()) + { + m_rcsr &= ~CSR_DONE; + clear_virq(m_write_rxrdy, m_rcsr, CSR_IE, m_rxrdy); + } break; case DLTCSR: @@ -236,9 +289,16 @@ void dl11_device::write(offs_t offset, uint16_t data, uint16_t mem_mask) 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: @@ -250,14 +310,29 @@ void dl11_device::write(offs_t offset, uint16_t data, uint16_t mem_mask) { 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; + m_tbuf = data & 255; m_tcsr &= ~CSR_DONE; clear_virq(m_write_txrdy, m_tcsr, CSR_IE, m_txrdy); - transmit_register_setup(data & 0xff); + transmit_register_setup((m_tcsr & DLTCSR_XBRK) ? 0 : m_tbuf); break; } } @@ -281,3 +356,109 @@ 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); +} diff --git a/src/devices/machine/dl11.h b/src/devices/machine/dl11.h index ebd575847ca..5d57cc83826 100644 --- a/src/devices/machine/dl11.h +++ b/src/devices/machine/dl11.h @@ -46,9 +46,16 @@ public: int rxrdy_r(); int txrdy_r(); - void rx_w(int state) { device_serial_interface::rx_w(state); } + void rx_w(int state); + + // device_z80daisy_interface overrides + virtual int z80daisy_irq_state() override; + virtual int z80daisy_irq_ack() override; + virtual void z80daisy_irq_reti() override; protected: + dl11_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + // device-level overrides virtual void device_start() override ATTR_COLD; virtual void device_reset() override ATTR_COLD; @@ -58,25 +65,24 @@ protected: virtual void tra_complete() override; virtual void rcv_complete() override; - // device_z80daisy_interface overrides - virtual int z80daisy_irq_state() override; - virtual int z80daisy_irq_ack() override; - virtual void z80daisy_irq_reti() override; - -private: /* registers */ - static constexpr uint16_t DLRCSR_RD = CSR_DONE | CSR_IE; + static constexpr uint16_t DLRCSR_ACT = 0004000; + static constexpr uint16_t DLRCSR_RDRENB = 0000001; + static constexpr uint16_t DLRCSR_RD = CSR_DONE | CSR_IE | DLRCSR_ACT; static constexpr uint16_t DLRCSR_WR = CSR_IE; static constexpr uint16_t DLRBUF_ERR = 0100000; static constexpr uint16_t DLRBUF_OVR = 0040000; static constexpr uint16_t DLRBUF_RBRK = 0020000; - static constexpr uint16_t DLRBUF_PERR = 0020000; + static constexpr uint16_t DLRBUF_PERR = 0010000; + static constexpr uint16_t DLTCSR_MAINT = 0000004; static constexpr uint16_t DLTCSR_XBRK = 0000001; - static constexpr uint16_t DLTCSR_RD = CSR_DONE | CSR_IE | DLTCSR_XBRK; - static constexpr uint16_t DLTCSR_WR = CSR_IE | DLTCSR_XBRK; + static constexpr uint16_t DLTCSR_RD = CSR_DONE | CSR_IE | DLTCSR_MAINT | DLTCSR_XBRK; + static constexpr uint16_t DLTCSR_WR = CSR_IE | DLTCSR_MAINT | DLTCSR_XBRK; + + bool m_installed; devcb_write_line m_write_txd; devcb_write_line m_write_rxrdy; @@ -96,8 +102,37 @@ private: uint16_t m_tbuf; }; +class k1801vp065_device : public dl11_device +{ +public: + // construction/destruction + k1801vp065_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + uint16_t read(offs_t offset); + void write(offs_t offset, uint16_t data); + + auto rts_wr_callback() { return m_write_rts.bind(); } + +protected: + // device-level overrides + virtual void device_reset() override; + + // device_serial_interface overrides + virtual void rcv_complete() override; + +private: + static constexpr uint16_t DLRCSR_PERR = 0100000; + static constexpr uint16_t DLRCSR_OVR = 0010000; + static constexpr uint16_t DLRCSR_RBRK = 0000001; + static constexpr uint16_t DLRCSR_RD = CSR_DONE | CSR_IE | DLRCSR_PERR | DLRCSR_OVR | DLRCSR_RBRK; + static constexpr uint16_t DLRCSR_WR = CSR_IE; + + devcb_write_line m_write_rts; +}; + // device type definition DECLARE_DEVICE_TYPE(DL11, dl11_device) +DECLARE_DEVICE_TYPE(K1801VP065, k1801vp065_device) #endif