diff options
Diffstat (limited to 'src/devices/machine/z80sio.cpp')
-rw-r--r-- | src/devices/machine/z80sio.cpp | 986 |
1 files changed, 749 insertions, 237 deletions
diff --git a/src/devices/machine/z80sio.cpp b/src/devices/machine/z80sio.cpp index f0a867833b8..15561da267c 100644 --- a/src/devices/machine/z80sio.cpp +++ b/src/devices/machine/z80sio.cpp @@ -3,6 +3,9 @@ /*************************************************************************** Z80-SIO Serial Input/Output emulation + Z80-DART Dual Asynchronous Receiver/Transmitter emulation + Intel 8274 Multi-Protocol Serial Controller emulation + NEC µPD7201 Multiprotocol Serial Communications Controller emulation The variants in the SIO family are only different in the packaging but has the same register features. However, since some signals are @@ -11,7 +14,10 @@ check that an operation is invalid because of package type but relies on the software to be adapated for the particular version. - Package: DIP40 SIO/0, SIO/1, SIO/2, SIO/9 + Package: DIP40 SIO/0 (RxCB and TxCB share one pin) + DIP40 SIO/1 (no DTRB pin) + DIP40 SIO/2 (no SYNCB pin) + DIP40 SIO/9 (channel B is nonfunctional) QFP44 SIO/3 PLCC44 SIO/4 ------------------------------------------------------------------- @@ -45,6 +51,15 @@ * Transmitter FIFO 1 ------------------------------------------------------------------------- * = Features that has been implemented n/a = features that will not + + Mostek not only second-sourced the Z80 SIO but redesigned it for + 68000 compatibility as the MK68564 SIO. This 48-pin device has a + revamped register interface with five address inputs to make every + register separately selectable, and most control registers may be read + back as written. The RxRDY and TxRDY pins are separate here, and many + control bits have been shifted around. The MK68564 also features a + built-in baud rate generator (not compatible with the Z8530 SCC's). + ***************************************************************************/ #include "emu.h" @@ -57,19 +72,20 @@ // MACROS / CONSTANTS //************************************************************************** -//#define LOG_GENERAL (1U << 0) -#define LOG_SETUP (1U << 1) -#define LOG_READ (1U << 2) -#define LOG_INT (1U << 3) -#define LOG_CMD (1U << 4) -#define LOG_TX (1U << 5) -#define LOG_RCV (1U << 6) -#define LOG_CTS (1U << 7) -#define LOG_DCD (1U << 8) -#define LOG_SYNC (1U << 9) -#define LOG_BIT (1U << 10) - -//#define VERBOSE (LOG_INT | LOG_READ | LOG_SETUP | LOG_TX | LOG_CMD | LOG_CTS) +#define LOG_SETUP (1U << 1) +#define LOG_READ (1U << 2) +#define LOG_INT (1U << 3) +#define LOG_CMD (1U << 4) +#define LOG_TX (1U << 5) +#define LOG_RCV (1U << 6) +#define LOG_CTS (1U << 7) +#define LOG_DCD (1U << 8) +#define LOG_SYNC (1U << 9) +#define LOG_BIT (1U << 10) +#define LOG_RTS (1U << 11) +#define LOG_BRG (1U << 12) + +//#define VERBOSE (LOG_CMD | LOG_SETUP | LOG_SYNC | LOG_BIT | LOG_TX ) //#define LOG_OUTPUT_STREAM std::cout #include "logmacro.h" @@ -81,9 +97,11 @@ #define LOGTX(...) LOGMASKED(LOG_TX, __VA_ARGS__) #define LOGRCV(...) LOGMASKED(LOG_RCV, __VA_ARGS__) #define LOGCTS(...) LOGMASKED(LOG_CTS, __VA_ARGS__) +#define LOGRTS(...) LOGMASKED(LOG_RTS, __VA_ARGS__) #define LOGDCD(...) LOGMASKED(LOG_DCD, __VA_ARGS__) #define LOGSYNC(...) LOGMASKED(LOG_SYNC, __VA_ARGS__) #define LOGBIT(...) LOGMASKED(LOG_BIT, __VA_ARGS__) +#define LOGBRG(...) LOGMASKED(LOG_BRG, __VA_ARGS__) #ifdef _MSC_VER #define FUNCNAME __func__ @@ -101,7 +119,7 @@ enum : uint8_t RR0_INTERRUPT_PENDING = 0x02, RR0_TX_BUFFER_EMPTY = 0x04, RR0_DCD = 0x08, - RR0_SYNC_HUNT = 0x10, + RR0_SYNC_HUNT = 0x10, // RI on DART RR0_CTS = 0x20, RR0_TX_UNDERRUN = 0x40, RR0_BREAK_ABORT = 0x80 @@ -110,6 +128,12 @@ enum : uint8_t enum : uint8_t { RR1_ALL_SENT = 0x01, + // This bit is not actually present in RR1 register + // It's enqueued in the rx error FIFO to mark the spot + // where "interrupt on 1st rx character" should occur. + // It's stripped off before head of error FIFO is dequeued + // into RR1 + RR1_HIDDEN_1ST_MARKER = 0x01, RR1_RESIDUE_CODE_MASK = 0x0e, RR1_PARITY_ERROR = 0x10, RR1_RX_OVERRUN_ERROR = 0x20, @@ -155,7 +179,7 @@ enum : uint8_t WR1_RX_INT_ALL_PARITY = 0x10, WR1_RX_INT_ALL = 0x18, WR1_WRDY_ON_RX_TX = 0x20, - WR1_WRDY_FUNCTION = 0x40, // WAIT not supported + WR1_WRDY_FUNCTION = 0x40, WR1_WRDY_ENABLE = 0x80 }; @@ -235,11 +259,15 @@ constexpr uint16_t SDLC_RESIDUAL = 0x1d0f; //************************************************************************** // device type definition -DEFINE_DEVICE_TYPE(Z80SIO_CHANNEL, z80sio_channel, "z80sio_channel", "Z80 SIO channel") -DEFINE_DEVICE_TYPE(I8274_CHANNEL, i8274_channel, "i8274_channel", "Intel 8274 MPSC channel") -DEFINE_DEVICE_TYPE(Z80SIO, z80sio_device, "z80sio", "Z80 SIO") -DEFINE_DEVICE_TYPE(I8274_NEW, i8274_new_device, "i8274_new", "Intel 8274 MPSC (new)") // Remove trailing N when z80dart.cpp's 8274 implementation is fully replaced -DEFINE_DEVICE_TYPE(UPD7201_NEW, upd7201_new_device, "upd7201_new", "NEC uPD7201 MPSC (new)") // Remove trailing N when z80dart.cpp's 7201 implementation is fully replaced +DEFINE_DEVICE_TYPE(Z80SIO_CHANNEL, z80sio_channel, "z80sio_channel", "Z80 SIO channel") +DEFINE_DEVICE_TYPE(Z80DART_CHANNEL, z80dart_channel, "z80dart_channel", "Z80 DART channel") +DEFINE_DEVICE_TYPE(I8274_CHANNEL, i8274_channel, "i8274_channel", "Intel 8274 MPSC channel") +DEFINE_DEVICE_TYPE(MK68564_CHANNEL, mk68564_channel, "mk68564_channel", "Mostek MK68564 SIO channel") +DEFINE_DEVICE_TYPE(Z80SIO, z80sio_device, "z80sio", "Z80 SIO") +DEFINE_DEVICE_TYPE(Z80DART, z80dart_device, "z80dart", "Z80 DART") +DEFINE_DEVICE_TYPE(I8274, i8274_device, "i8274", "Intel 8274 MPSC") +DEFINE_DEVICE_TYPE(UPD7201, upd7201_device, "upd7201", "NEC uPD7201 MPSC") +DEFINE_DEVICE_TYPE(MK68564, mk68564_device, "mk68564", "Mostek MK68564 SIO") //------------------------------------------------- // device_add_mconfig - add device configuration @@ -250,12 +278,24 @@ void z80sio_device::device_add_mconfig(machine_config &config) Z80SIO_CHANNEL(config, CHANB_TAG, 0); } -void i8274_new_device::device_add_mconfig(machine_config &config) +void z80dart_device::device_add_mconfig(machine_config &config) +{ + Z80DART_CHANNEL(config, CHANA_TAG, 0); + Z80DART_CHANNEL(config, CHANB_TAG, 0); +} + +void i8274_device::device_add_mconfig(machine_config &config) { I8274_CHANNEL(config, CHANA_TAG, 0); I8274_CHANNEL(config, CHANB_TAG, 0); } +void mk68564_device::device_add_mconfig(machine_config &config) +{ + MK68564_CHANNEL(config, CHANA_TAG, 0); + MK68564_CHANNEL(config, CHANB_TAG, 0); +} + //************************************************************************** // LIVE DEVICE @@ -276,11 +316,31 @@ inline void z80sio_channel::out_dtr_cb(int state) m_uart->m_out_dtr_cb[m_index](state); } -inline void z80sio_channel::set_ready(bool ready) +inline void z80sio_channel::update_wait_ready() { - // WAIT mode not supported yet - if (m_wr1 & WR1_WRDY_FUNCTION) - m_uart->m_out_wrdy_cb[m_index](ready ? 0 : 1); + bool ready = false; + + if (m_wr1 & WR1_WRDY_ENABLE) + { + if (m_wr1 & WR1_WRDY_ON_RX_TX) + { + // Monitor rx + ready = bool(m_rr0 & RR0_RX_CHAR_AVAILABLE); + } + else + { + // Monitor tx + ready = get_tx_empty(); + } + if (!(m_wr1 & WR1_WRDY_FUNCTION)) + { + // Ready function has opposite polarity of wait function + ready = !ready; + } + } + + // ready/wait is active low + m_uart->m_out_wrdy_cb[m_index](ready ? 0 : 1); } inline bool z80sio_channel::receive_allowed() const @@ -288,16 +348,21 @@ inline bool z80sio_channel::receive_allowed() const return (m_wr3 & WR3_RX_ENABLE) && (!(m_wr3 & WR3_AUTO_ENABLES) || !m_dcd); } -inline bool z80sio_channel::transmit_allowed() const +bool z80sio_channel::transmit_allowed() const { return (m_wr5 & WR5_TX_ENABLE) && (!(m_wr3 & WR3_AUTO_ENABLES) || !m_cts); } +bool mk68564_channel::transmit_allowed() const +{ + return (m_wr5 & WR5_TX_ENABLE) && (!m_tx_auto_enable || !m_cts); +} + inline void z80sio_channel::set_rts(int state) { if (bool(m_rts) != bool(state)) { - LOG("%s(%d) \"%s\" Channel %c \n", FUNCNAME, state, owner()->tag(), 'A' + m_index); + LOGRTS("%s(%d) \"%s\" Channel %c \n", FUNCNAME, state, owner()->tag(), 'A' + m_index); out_rts_cb(m_rts = state); } } @@ -327,7 +392,7 @@ inline void z80sio_channel::tx_setup(uint16_t data, int bits, bool framing, bool LOGBIT("%.6f TX_SR %05x data %04x flags %x\n" , machine().time().as_double() , m_tx_sr & TX_SR_MASK , data , m_tx_flags); } -inline void z80sio_channel::tx_setup_idle() +void z80sio_channel::tx_setup_idle() { switch (m_wr4 & WR4_SYNC_MODE_MASK) { @@ -341,12 +406,19 @@ inline void z80sio_channel::tx_setup_idle() break; case WR4_SYNC_MODE_SDLC: // SDLC transmit examples don't show flag being loaded, implying it's hard-coded on the transmit side - tx_setup(0x7e, 8, true, false, false); + //tx_setup(0x7e, 8, true, false, false); + // Verified on a 8274, the 0x7e SYNC byte is required in CR7 to start transmitting, other values fails + tx_setup(m_wr7, 8, true, false, false); break; } m_tx_in_pkt = false; } +void z80dart_channel::tx_setup_idle() +{ + logerror("%s (sync mode not supported by DART)\n", FUNCNAME); +} + //------------------------------------------------- // z80sio_device - constructor //------------------------------------------------- @@ -356,14 +428,14 @@ z80sio_device::z80sio_device(const machine_config &mconfig, device_type type, co m_chanA(*this, CHANA_TAG), m_chanB(*this, CHANB_TAG), m_hostcpu(*this, finder_base::DUMMY_TAG), - m_out_txd_cb{ { *this }, { *this } }, - m_out_dtr_cb{ { *this }, { *this } }, - m_out_rts_cb{ { *this }, { *this } }, - m_out_wrdy_cb{ { *this }, { *this } }, - m_out_sync_cb{ { *this }, { *this } }, + m_out_txd_cb(*this), + m_out_dtr_cb(*this), + m_out_rts_cb(*this), + m_out_wrdy_cb(*this), + m_out_sync_cb(*this), m_out_int_cb(*this), - m_out_rxdrq_cb{ { *this }, { *this } }, - m_out_txdrq_cb{ { *this }, { *this } } + m_out_rxdrq_cb(*this), + m_out_txdrq_cb(*this) { for (auto & elem : m_int_state) elem = 0; @@ -374,18 +446,28 @@ z80sio_device::z80sio_device(const machine_config &mconfig, const char *tag, dev { } -i8274_new_device::i8274_new_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : +z80dart_device::z80dart_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + z80sio_device(mconfig, Z80DART, tag, owner, clock) +{ +} + +i8274_device::i8274_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : z80sio_device(mconfig, type, tag, owner, clock) { } -i8274_new_device::i8274_new_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : - i8274_new_device(mconfig, I8274_NEW, tag, owner, clock) +i8274_device::i8274_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + i8274_device(mconfig, I8274, tag, owner, clock) { } -upd7201_new_device::upd7201_new_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : - i8274_new_device(mconfig, UPD7201_NEW, tag, owner, clock) +upd7201_device::upd7201_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + i8274_device(mconfig, UPD7201, tag, owner, clock) +{ +} + +mk68564_device::mk68564_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + i8274_device(mconfig, MK68564, tag, owner, clock) { } @@ -399,31 +481,6 @@ void z80sio_device::device_validity_check(validity_checker &valid) const } //------------------------------------------------- -// device_resolve_objects - device-specific setup -//------------------------------------------------- -void z80sio_device::device_resolve_objects() -{ - LOG("%s\n", FUNCNAME); - - // resolve callbacks - m_out_txd_cb[CHANNEL_A].resolve_safe(); - m_out_dtr_cb[CHANNEL_A].resolve_safe(); - m_out_rts_cb[CHANNEL_A].resolve_safe(); - m_out_wrdy_cb[CHANNEL_A].resolve_safe(); - m_out_sync_cb[CHANNEL_A].resolve_safe(); - m_out_txd_cb[CHANNEL_B].resolve_safe(); - m_out_dtr_cb[CHANNEL_B].resolve_safe(); - m_out_rts_cb[CHANNEL_B].resolve_safe(); - m_out_wrdy_cb[CHANNEL_B].resolve_safe(); - m_out_sync_cb[CHANNEL_B].resolve_safe(); - m_out_int_cb.resolve_safe(); - m_out_rxdrq_cb[CHANNEL_A].resolve_safe(); - m_out_txdrq_cb[CHANNEL_A].resolve_safe(); - m_out_rxdrq_cb[CHANNEL_B].resolve_safe(); - m_out_txdrq_cb[CHANNEL_B].resolve_safe(); -} - -//------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void z80sio_device::device_start() @@ -456,7 +513,7 @@ int z80sio_device::z80daisy_irq_state() // loop over all interrupt sources int state = 0; - for (int i = 0; ARRAY_LENGTH(m_int_state) > i; ++i) + for (int i = 0; std::size(m_int_state) > i; ++i) { // if we're servicing a request, don't indicate more interrupts if (m_int_state[prio[i]] & Z80_DAISY_IEO) @@ -481,7 +538,7 @@ int z80sio_device::z80daisy_irq_ack() // loop over all interrupt sources int const *const prio = interrupt_priorities(); - for (int i = 0; ARRAY_LENGTH(m_int_state) > i; ++i) + for (int i = 0; std::size(m_int_state) > i; ++i) { // find the first channel with an interrupt requested if (m_int_state[prio[i]] & Z80_DAISY_INT) @@ -508,7 +565,7 @@ int z80sio_device::z80daisy_irq_ack() return -1; } -int i8274_new_device::z80daisy_irq_ack() +int i8274_device::z80daisy_irq_ack() { // FIXME: we're not modelling the full behaviour of this chip // The 8274 is designed to work with Intel processors with multiple interrupt acknowledge cycles @@ -547,7 +604,7 @@ int i8274_new_device::z80daisy_irq_ack() { // loop over all interrupt sources int const *const prio = interrupt_priorities(); - for (int i = 0; ARRAY_LENGTH(m_int_state) > i; ++i) + for (int i = 0; std::size(m_int_state) > i; ++i) { // find the first channel with an interrupt requested if (m_int_state[prio[i]] & Z80_DAISY_INT) @@ -586,7 +643,7 @@ void z80sio_device::z80daisy_irq_reti() return_from_interrupt(); } -void i8274_new_device::z80daisy_irq_reti() +void i8274_device::z80daisy_irq_reti() { LOGINT("%s - i8274/uPD7201 lacks RETI detection, no action taken\n", FUNCNAME); } @@ -627,6 +684,8 @@ void z80sio_device::trigger_interrupt(int index, int type) {{"INT_TRANSMIT", "INT_EXTERNAL", "INT_RECEIVE"}}[type]); // trigger interrupt + if (m_int_state[(index * 3) + type] & Z80_DAISY_INT) + return; m_int_state[(index * 3) + type] |= Z80_DAISY_INT; m_chanA->m_rr0 |= RR0_INTERRUPT_PENDING; @@ -644,6 +703,8 @@ void z80sio_device::clear_interrupt(int index, int type) {{"INT_TRANSMIT", "INT_EXTERNAL", "INT_RECEIVE"}}[type]); // clear interrupt + if (!(m_int_state[(index * 3) + type] & Z80_DAISY_INT)) + return; m_int_state[(index * 3) + type] &= ~Z80_DAISY_INT; if (std::find_if(std::begin(m_int_state), std::end(m_int_state), [] (int state) { return bool(state & Z80_DAISY_INT); }) == std::end(m_int_state)) m_chanA->m_rr0 &= ~RR0_INTERRUPT_PENDING; @@ -661,7 +722,7 @@ void z80sio_device::return_from_interrupt() { // loop over all interrupt sources int const *const prio = interrupt_priorities(); - for (int i = 0; ARRAY_LENGTH(m_int_state) > i; ++i) + for (int i = 0; std::size(m_int_state) > i; ++i) { // find the first channel with an interrupt requested if (m_int_state[prio[i]] & (Z80_DAISY_IEO)) @@ -691,7 +752,7 @@ uint8_t z80sio_device::read_vector() // modify vector for highest-priority pending interrupt int const *const prio = interrupt_priorities(); vec &= 0xf1U; - for (int i = 0; ARRAY_LENGTH(m_int_state) > i; ++i) + for (int i = 0; std::size(m_int_state) > i; ++i) { if (m_int_state[prio[i]] & Z80_DAISY_INT) { @@ -702,9 +763,7 @@ uint8_t z80sio_device::read_vector() case 0 + z80sio_channel::INT_EXTERNAL: return vec | 0x0aU; case 0 + z80sio_channel::INT_RECEIVE: - if (((m_chanA->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL_PARITY) && (m_chanA->m_rr1 & (m_chanA->get_special_rx_mask() | RR1_PARITY_ERROR))) - return vec | 0x0eU; - else if (((m_chanA->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL) && (m_chanA->m_rr1 & m_chanA->get_special_rx_mask())) + if (m_chanA->m_rr1 & m_chanA->get_special_rx_mask()) return vec | 0x0eU; else return vec | 0x0cU; @@ -713,9 +772,7 @@ uint8_t z80sio_device::read_vector() case 3 + z80sio_channel::INT_EXTERNAL: return vec | 0x02U; case 3 + z80sio_channel::INT_RECEIVE: - if (((m_chanB->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL_PARITY) && (m_chanB->m_rr1 & (m_chanB->get_special_rx_mask() | RR1_PARITY_ERROR))) - return vec | 0x06U; - else if (((m_chanB->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL) && (m_chanB->m_rr1 & m_chanB->get_special_rx_mask())) + if (m_chanB->m_rr1 & m_chanB->get_special_rx_mask()) return vec | 0x06U; else return vec | 0x04U; @@ -734,7 +791,7 @@ uint8_t z80sio_device::read_vector() interrrupt is generated, otherwise it will indicate the previous state." 8274: "If RR2 is specified but not read, no internal interrupts, regardless of priority, are accepted." */ -uint8_t i8274_new_device::read_vector() +uint8_t i8274_device::read_vector() { // 8086 and 8085 modes have different variable bits bool const aff(m_chanB->m_wr1 & WR1_STATUS_VECTOR); @@ -747,7 +804,7 @@ uint8_t i8274_new_device::read_vector() // modify vector for highest-priority pending interrupt int const *const prio = interrupt_priorities(); - for (int i = 0; ARRAY_LENGTH(m_int_state) > i; ++i) + for (int i = 0; std::size(m_int_state) > i; ++i) { if (m_int_state[prio[i]] & Z80_DAISY_INT) { @@ -809,7 +866,7 @@ int const *z80sio_device::interrupt_priorities() const return prio; } -int const *i8274_new_device::interrupt_priorities() const +int const *i8274_device::interrupt_priorities() const { static constexpr EQUIVALENT_ARRAY(m_int_state, int) prio_a{ 0 + z80sio_channel::INT_RECEIVE, 3 + z80sio_channel::INT_RECEIVE, @@ -832,12 +889,6 @@ int z80sio_device::m1_r() return z80daisy_irq_ack(); } -int i8274_new_device::m1_r() -{ - LOGINT("%s %s \n",FUNCNAME, tag()); - return 0; -} - //------------------------------------------------- // cd_ba_r - @@ -918,12 +969,13 @@ z80sio_channel::z80sio_channel( , m_rx_count(0) , m_rx_bit(0) , m_rx_sr(0) - , m_rx_first(0) + , m_rx_first(false) , m_rxd(1) , m_tx_data(0) , m_tx_clock(0), m_tx_count(0), m_tx_parity(0), m_tx_sr(0), m_tx_crc(0), m_tx_hist(0), m_tx_flags(0) , m_txd(1), m_dtr(0), m_rts(0) - , m_ext_latched(0), m_brk_latched(0), m_cts(0), m_dcd(0), m_sync(0) + , m_ext_latched(false), m_brk_latched(false) + , m_cts(0), m_dcd(0), m_sync(0) , m_rr1_auto_reset(rr1_auto_reset) { LOG("%s\n",FUNCNAME); @@ -934,7 +986,12 @@ z80sio_channel::z80sio_channel( } z80sio_channel::z80sio_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : z80sio_channel(mconfig, Z80SIO_CHANNEL, tag, owner, clock, RR1_END_OF_FRAME | RR1_CRC_FRAMING_ERROR | RR1_RESIDUE_CODE_MASK) + : z80sio_channel(mconfig, Z80SIO_CHANNEL, tag, owner, clock, RR1_CRC_FRAMING_ERROR | RR1_RESIDUE_CODE_MASK) +{ +} + +z80dart_channel::z80dart_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : z80sio_channel(mconfig, Z80DART_CHANNEL, tag, owner, clock, RR1_END_OF_FRAME | RR1_CRC_FRAMING_ERROR | RR1_RESIDUE_CODE_MASK) { } @@ -943,6 +1000,16 @@ i8274_channel::i8274_channel(const machine_config &mconfig, const char *tag, dev { } +mk68564_channel::mk68564_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : z80sio_channel(mconfig, MK68564_CHANNEL, tag, owner, clock, RR1_END_OF_FRAME | RR1_CRC_FRAMING_ERROR | RR1_RESIDUE_CODE_MASK) + , m_tx_auto_enable(false) + , m_brg_tc(0) + , m_brg_control(0) + , m_brg_state(false) + , m_brg_timer(nullptr) +{ +} + //------------------------------------------------- // resove_objects - channel setup @@ -970,23 +1037,13 @@ void z80sio_channel::device_start() save_item(NAME(m_wr3)); save_item(NAME(m_wr4)); save_item(NAME(m_wr5)); - save_item(NAME(m_wr6)); - save_item(NAME(m_wr7)); save_item(NAME(m_rx_fifo_depth)); save_item(NAME(m_rx_data_fifo)); save_item(NAME(m_rx_error_fifo)); save_item(NAME(m_rx_clock)); save_item(NAME(m_rx_count)); - save_item(NAME(m_dlyd_rxd)); save_item(NAME(m_rx_bit)); - save_item(NAME(m_rx_bit_limit)); - save_item(NAME(m_rx_sync_fsm)); - save_item(NAME(m_rx_one_cnt)); save_item(NAME(m_rx_sr)); - save_item(NAME(m_rx_sync_sr)); - save_item(NAME(m_rx_crc_delay)); - save_item(NAME(m_rx_crc)); - save_item(NAME(m_rx_crc_en)); save_item(NAME(m_rx_parity)); save_item(NAME(m_rx_first)); save_item(NAME(m_tx_data)); @@ -994,10 +1051,8 @@ void z80sio_channel::device_start() save_item(NAME(m_tx_count)); save_item(NAME(m_tx_phase)); save_item(NAME(m_tx_parity)); - save_item(NAME(m_tx_in_pkt)); - save_item(NAME(m_tx_forced_sync)); + save_item(NAME(m_tx_in_pkt)); // TODO: does this actually function in async mode? save_item(NAME(m_tx_sr)); - save_item(NAME(m_tx_crc)); save_item(NAME(m_tx_hist)); save_item(NAME(m_tx_flags)); save_item(NAME(m_tx_delay)); @@ -1010,6 +1065,41 @@ void z80sio_channel::device_start() save_item(NAME(m_dcd)); save_item(NAME(m_sync)); save_item(NAME(m_cts)); + sync_save_state(); +} + +void z80sio_channel::sync_save_state() +{ + save_item(NAME(m_wr6)); + save_item(NAME(m_wr7)); + save_item(NAME(m_dlyd_rxd)); + save_item(NAME(m_rx_bit_limit)); + save_item(NAME(m_rx_sync_fsm)); + save_item(NAME(m_rx_one_cnt)); + save_item(NAME(m_rx_sync_sr)); + save_item(NAME(m_rx_crc_delay)); + save_item(NAME(m_rx_crc)); + save_item(NAME(m_rx_crc_en)); + //save_item(NAME(m_tx_in_pkt)); + save_item(NAME(m_tx_forced_sync)); + save_item(NAME(m_tx_crc)); +} + +void z80dart_channel::sync_save_state() +{ + // no need to save the above members +} + +void mk68564_channel::device_start() +{ + z80sio_channel::device_start(); + + m_brg_timer = timer_alloc(FUNC(mk68564_channel::brg_timeout), this); + + save_item(NAME(m_tx_auto_enable)); + save_item(NAME(m_brg_tc)); + save_item(NAME(m_brg_control)); + save_item(NAME(m_brg_state)); } @@ -1029,7 +1119,7 @@ void z80sio_channel::device_reset() m_tx_count = 0; m_rr0 &= ~RR0_RX_CHAR_AVAILABLE; m_rr0 |= RR0_SYNC_HUNT; - m_rr1 &= ~(RR1_PARITY_ERROR | RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR); + m_rr1 &= ~(RR1_PARITY_ERROR | RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR | RR1_END_OF_FRAME); // disable receiver m_wr3 &= ~WR3_RX_ENABLE; @@ -1047,7 +1137,9 @@ void z80sio_channel::device_reset() out_txd_cb(1); m_tx_sr = ~0; - // TODO: what happens to WAIT/READY? + // Disable wait & ready + m_wr1 &= ~WR1_WRDY_ENABLE; + update_wait_ready(); // reset external lines out_rts_cb(m_rts = 1); @@ -1057,11 +1149,22 @@ void z80sio_channel::device_reset() m_uart->clear_interrupt(m_index, INT_TRANSMIT); m_uart->clear_interrupt(m_index, INT_RECEIVE); reset_ext_status(); - // FIXME: should this actually reset all the interrtupts, or just the prioritisation (daisy chain) logic? + // FIXME: should this actually reset all the interrupts, or just the prioritisation (daisy chain) logic? if (m_index == z80sio_device::CHANNEL_A) m_uart->reset_interrupts(); } +void mk68564_channel::device_reset() +{ + z80sio_channel::device_reset(); + + m_tx_auto_enable = false; + m_brg_tc = 0; + m_brg_control = 0; + m_brg_state = false; + m_brg_timer->adjust(attotime::never); +} + bool z80sio_channel::is_tx_idle() const { return (m_tx_sr & TX_SR_MASK) == TX_SR_MASK; @@ -1073,6 +1176,8 @@ bool z80sio_channel::is_tx_idle() const //------------------------------------------------- void z80sio_channel::transmit_enable() { + LOGTX("%s\n", FUNCNAME); + if (transmit_allowed()) { if (is_tx_idle()) @@ -1082,8 +1187,7 @@ void z80sio_channel::transmit_enable() LOGTX("Channel %c synchronous transmit enabled - load sync pattern\n", 'A' + m_index); tx_setup_idle(); m_tx_forced_sync = false; - if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX)) - set_ready(true); + update_wait_ready(); } else if (!(m_rr0 & RR0_TX_BUFFER_EMPTY)) async_tx_setup(); @@ -1113,7 +1217,7 @@ void z80sio_channel::transmit_enable() //------------------------------------------------- void z80sio_channel::transmit_complete() { - LOG("%s %s\n",FUNCNAME, tag()); + if (!m_rts) LOGTX("%s %s\n",FUNCNAME, tag()); if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC) sync_tx_sr_empty(); @@ -1131,7 +1235,7 @@ void z80sio_channel::sync_tx_sr_empty() { if (!transmit_allowed()) { - LOGTX("%s() Channel %c Transmitter Disabled m_wr5:%02x\n", FUNCNAME, 'A' + m_index, m_wr5); + if (!m_rts) LOGTX("%s() Channel %c Transmitter Disabled m_wr5:%02x\n", FUNCNAME, 'A' + m_index, m_wr5); m_tx_flags = 0; } else if (m_tx_forced_sync || @@ -1198,6 +1302,11 @@ void z80sio_channel::sync_tx_sr_empty() } } +void z80dart_channel::sync_tx_sr_empty() +{ + LOG("%s (sync mode not supported by DART)\n", FUNCNAME); +} + bool z80sio_channel::get_tx_empty() const { // During CRC transmission, tx buffer is shown as full @@ -1212,19 +1321,13 @@ void z80sio_channel::set_tx_empty(bool prev_state, bool new_state) else m_rr0 &= ~RR0_TX_BUFFER_EMPTY; + update_wait_ready(); + bool curr_tx_empty = get_tx_empty(); - if (!prev_state && curr_tx_empty) + if (!prev_state && curr_tx_empty && (m_wr1 & WR1_TX_INT_ENABLE)) { - if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX)) - set_ready(true); - if (m_wr1 & WR1_TX_INT_ENABLE) - m_uart->trigger_interrupt(m_index, INT_TRANSMIT); - } - else if (prev_state && !curr_tx_empty) - { - if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX)) - set_ready(false); + m_uart->trigger_interrupt(m_index, INT_TRANSMIT); } } @@ -1275,8 +1378,8 @@ void z80sio_channel::async_tx_setup() void z80sio_channel::reset_ext_status() { // this will clear latched external pin state - m_ext_latched = 0; - m_brk_latched = 0; + m_ext_latched = false; + m_brk_latched = false; read_ext(); // Clear any pending External interrupt @@ -1320,7 +1423,7 @@ void z80sio_channel::trigger_ext_int() // update line if (!m_ext_latched) read_ext(); - m_ext_latched = 1; + m_ext_latched = true; // trigger interrupt if enabled if (m_wr1 & WR1_EXT_INT_ENABLE) @@ -1331,7 +1434,7 @@ void z80sio_channel::trigger_ext_int() //------------------------------------------------- // get_clock_mode - get clock divisor //------------------------------------------------- -int z80sio_channel::get_clock_mode() +int z80sio_channel::get_clock_mode() const { //LOG("%s %s\n",FUNCNAME, tag()); int clocks = 1; @@ -1379,7 +1482,7 @@ void z80sio_channel::update_dtr_rts_break() //------------------------------------------------- // get_rx_word_length - get receive word length //------------------------------------------------- -int z80sio_channel::get_rx_word_length() +int z80sio_channel::get_rx_word_length() const { LOG("%s %s\n",FUNCNAME, tag()); int bits = 5; @@ -1420,10 +1523,10 @@ int z80sio_channel::get_tx_word_length() const * Break/Abort latch. */ uint8_t z80sio_channel::do_sioreg_rr0() { - LOGR("%s\n", FUNCNAME); uint8_t tmp = m_rr0 & ~RR0_TX_BUFFER_EMPTY; if (get_tx_empty()) tmp |= RR0_TX_BUFFER_EMPTY; + LOGR("%s: %02x\n", FUNCNAME, tmp); return tmp; } @@ -1507,7 +1610,7 @@ void z80sio_channel::do_sioreg_wr0_resets(uint8_t data) LOGCMD("Z80SIO Channel %c : CRC_RESET_NULL\n", 'A' + m_index); break; case WR0_CRC_RESET_RX: /* In Synchronous mode: all Os (zeros) (CCITT-O CRC-16) */ - LOGCMD("Z80SIO Channel %c : CRC_RESET_RX - not implemented\n", 'A' + m_index); + LOGCMD("Z80SIO Channel %c : CRC_RESET_RX\n", 'A' + m_index); m_rx_crc = ((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_SDLC) ? ~uint16_t(0U) : uint16_t(0U); m_rx_crc_en = false; break; @@ -1520,6 +1623,8 @@ void z80sio_channel::do_sioreg_wr0_resets(uint8_t data) // Command is accepted in active part of packet only if (m_tx_in_pkt) m_rr0 &= ~RR0_TX_UNDERRUN; + else + LOGCMD(" - not accepted as not in active part of packet\n"); break; default: /* Will not happen unless someone messes with the mask */ logerror("Z80SIO Channel %c : %s Wrong CRC reset/init command:%02x\n", 'A' + m_index, FUNCNAME, data & WR0_CRC_RESET_CODE_MASK); @@ -1531,7 +1636,7 @@ void z80sio_channel::do_sioreg_wr0(uint8_t data) m_wr0 = data; if ((data & WR0_COMMAND_MASK) != WR0_NULL) - LOGSETUP(" * %s %c Reg %02x <- %02x \n", owner()->tag(), 'A' + m_index, 0, data); + LOGSETUP("\n * %s %c Reg %02x <- %02x \n", owner()->tag(), 'A' + m_index, 0, data); switch (data & WR0_COMMAND_MASK) { case WR0_NULL: @@ -1563,7 +1668,7 @@ void z80sio_channel::do_sioreg_wr0(uint8_t data) case WR0_ENABLE_INT_NEXT_RX: // enable interrupt on next receive character LOGINT("%s Ch:%c : Enable Interrupt on Next Received Character\n", FUNCNAME, 'A' + m_index); - m_rx_first = 1; + m_rx_first = true; break; case WR0_RESET_TX_INT: LOGCMD("%s Ch:%c : Reset Transmitter Interrupt Pending\n", FUNCNAME, 'A' + m_index); @@ -1573,7 +1678,7 @@ void z80sio_channel::do_sioreg_wr0(uint8_t data) case WR0_ERROR_RESET: // error reset LOGCMD("%s Ch:%c : Error Reset\n", FUNCNAME, 'A' + m_index); - if ((WR1_RX_INT_FIRST == (m_wr1 & WR1_RX_INT_MODE_MASK)) && (m_rr1 & (RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR))) + if ((WR1_RX_INT_FIRST == (m_wr1 & WR1_RX_INT_MODE_MASK)) && (m_rr1 & (RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_END_OF_FRAME))) { // clearing framing and overrun errors advances the FIFO // TODO: Intel 8274 manual doesn't mention this behaviour - is it specific to Z80 SIO? @@ -1583,6 +1688,7 @@ void z80sio_channel::do_sioreg_wr0(uint8_t data) else { m_rr1 &= ~(RR1_END_OF_FRAME | RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR); + update_rx_int(); } break; case WR0_RETURN_FROM_INT: @@ -1601,44 +1707,35 @@ void z80sio_channel::do_sioreg_wr1(uint8_t data) { /* TODO: implement vector modifications when WR1 bit D2 is changed */ m_wr1 = data; - LOG("Z80SIO \"%s\" Channel %c : External Interrupt Enable %u\n", owner()->tag(), 'A' + m_index, (data & WR1_EXT_INT_ENABLE) ? 1 : 0); - LOG("Z80SIO \"%s\" Channel %c : Transmit Interrupt Enable %u\n", owner()->tag(), 'A' + m_index, (data & WR1_TX_INT_ENABLE) ? 1 : 0); - LOG("Z80SIO \"%s\" Channel %c : Status Affects Vector %u\n", owner()->tag(), 'A' + m_index, (data & WR1_STATUS_VECTOR) ? 1 : 0); - LOG("Z80SIO \"%s\" Channel %c : Wait/Ready Enable %u\n", owner()->tag(), 'A' + m_index, (data & WR1_WRDY_ENABLE) ? 1 : 0); - LOG("Z80SIO \"%s\" Channel %c : Wait/Ready Function %s\n", owner()->tag(), 'A' + m_index, (data & WR1_WRDY_FUNCTION) ? "Ready" : "Wait"); - LOG("Z80SIO \"%s\" Channel %c : Wait/Ready on %s\n", owner()->tag(), 'A' + m_index, (data & WR1_WRDY_ON_RX_TX) ? "Receive" : "Transmit"); - - switch (data & WR1_RX_INT_MODE_MASK) - { - case WR1_RX_INT_DISABLE: - LOG("Z80SIO \"%s\" Channel %c : Receiver Interrupt Disabled\n", owner()->tag(), 'A' + m_index); - break; + LOGSETUP("Z80SIO \"%s\" Channel %c :\n", owner()->tag(), 'A' + m_index); + LOGSETUP(" - External Interrupt Enable %u\n", (data & WR1_EXT_INT_ENABLE) ? 1 : 0); + LOGSETUP(" - Transmit Interrupt Enable %u\n", (data & WR1_TX_INT_ENABLE) ? 1 : 0); + LOGSETUP(" - Status Affects Vector %u\n", (data & WR1_STATUS_VECTOR) ? 1 : 0); + LOGSETUP(" - Wait/Ready Enable %u\n", (data & WR1_WRDY_ENABLE) ? 1 : 0); + LOGSETUP(" - Wait/Ready Function %s\n", (data & WR1_WRDY_FUNCTION) ? "Ready" : "Wait"); + LOGSETUP(" - Wait/Ready on %s\n", (data & WR1_WRDY_ON_RX_TX) ? "Rx" : "Tx"); + LOGSETUP(" - Receiver Interrupt %s\n", std::array<char const *, 4> + {{"Disabled", "on First Character", "on All Characters, Parity Affects Vector", "on All Characters"}}[(m_wr2 >> 3) & 0x03]); - case WR1_RX_INT_FIRST: - LOG("Z80SIO \"%s\" Channel %c : Receiver Interrupt on First Character\n", owner()->tag(), 'A' + m_index); - break; - - case WR1_RX_INT_ALL_PARITY: - LOG("Z80SIO \"%s\" Channel %c : Receiver Interrupt on All Characters, Parity Affects Vector\n", owner()->tag(), 'A' + m_index); - break; - - case WR1_RX_INT_ALL: - LOG("Z80SIO \"%s\" Channel %c : Receiver Interrupt on All Characters\n", owner()->tag(), 'A' + m_index); - break; - } - - if (!(data & WR1_WRDY_ENABLE)) - set_ready(false); - else if (data & WR1_WRDY_ON_RX_TX) - set_ready(bool(m_rr0 & RR0_RX_CHAR_AVAILABLE)); - else - set_ready(m_rr0 & RR0_TX_BUFFER_EMPTY); + update_wait_ready(); } void z80sio_channel::do_sioreg_wr2(uint8_t data) { m_wr2 = data; - LOG("Z80SIO \"%s\" Channel %c : Interrupt Vector %02x\n", owner()->tag(), 'A' + m_index, data); + LOGSETUP("Z80SIO \"%s\" Channel %c : ", owner()->tag(), 'A' + m_index); + if (m_index == 0) + { + LOGSETUP(" - INT/DMA priority and mode: %02x\n", m_wr2 & 0x07); + LOGSETUP(" - Interrupt mode: %s\n", std::array<char const *, 4> {{"85-1", "85-2", "85-3", "86"}}[(m_wr2 >> 3) & 0x03]); + LOGSETUP(" - Vector mode: %s\n", (m_wr2 & 0x20) ? "Vectored" : "Non-vectored"); + LOGSETUP(" - Rx INT mask: %d\n", (m_wr2 >> 6) & 0x01 ); + LOGSETUP(" - Pin 10: %s\n", (m_wr2 & 0x80) ? "SYNCB" : "RTSB"); + } + else + { + LOGSETUP("Interrupt Vector %02x\n", m_wr2); + } } void z80sio_channel::do_sioreg_wr3(uint8_t data) @@ -1647,12 +1744,13 @@ void z80sio_channel::do_sioreg_wr3(uint8_t data) LOGSETUP("Z80SIO Channel %c : Sync Character Load Inhibit %u\n", 'A' + m_index, (data & WR3_SYNC_CHAR_LOAD_INHIBIT) ? 1 : 0); LOGSETUP("Z80SIO Channel %c : Receive CRC Enable %u\n", 'A' + m_index, (data & WR3_RX_CRC_ENABLE) ? 1 : 0); LOGSETUP("Z80SIO Channel %c : Auto Enables %u\n", 'A' + m_index, (data & WR3_AUTO_ENABLES) ? 1 : 0); - LOGSETUP("Z80SIO Channel %c : Receiver Bits/Character %u\n", 'A' + m_index, get_rx_word_length()); - if (data & WR3_ENTER_HUNT_PHASE) - LOGCMD("Z80SIO Channel %c : Enter Hunt Phase\n", 'A' + m_index); + LOGSETUP("Z80SIO Channel %c : Enter Hunt Phase %u\n", 'A' + m_index, (data & WR3_ENTER_HUNT_PHASE) ? 1 : 0); + //if (data & WR3_ENTER_HUNT_PHASE) + //LOGCMD("Z80SIO Channel %c : Enter Hunt Phase\n", 'A' + m_index); bool const was_allowed(receive_allowed()); m_wr3 = data; + LOG("Z80SIO Channel %c : Receiver Bits/Character %u\n", 'A' + m_index, get_rx_word_length()); // depends on m_wr3 being updated if (!was_allowed && receive_allowed()) { @@ -1668,25 +1766,27 @@ void z80sio_channel::do_sioreg_wr3(uint8_t data) void z80sio_channel::do_sioreg_wr4(uint8_t data) { m_wr4 = data; - LOG("Z80SIO \"%s\" Channel %c : Parity Enable %u\n", owner()->tag(), 'A' + m_index, (data & WR4_PARITY_ENABLE) ? 1 : 0); - LOG("Z80SIO \"%s\" Channel %c : Parity %s\n", owner()->tag(), 'A' + m_index, (data & WR4_PARITY_EVEN) ? "Even" : "Odd"); + LOGSETUP("Z80SIO \"%s\" Channel %c : Parity Enable %u\n", owner()->tag(), 'A' + m_index, (data & WR4_PARITY_ENABLE) ? 1 : 0); + LOGSETUP("Z80SIO \"%s\" Channel %c : Parity %s\n", owner()->tag(), 'A' + m_index, (data & WR4_PARITY_EVEN) ? "Even" : "Odd"); if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC) - LOG("Z80SIO \"%s\" Channel %c : Synchronous Mode\n", owner()->tag(), 'A' + m_index); + LOGSETUP("Z80SIO \"%s\" Channel %c : Synchronous Mode %s\n", owner()->tag(), 'A' + m_index, + std::array<char const *, 4> {{"Monosync", "Bisync", "HDLC/SDLC", "External"}}[(m_wr4 >> 4) & 0x03]); else - LOG("Z80SIO \"%s\" Channel %c : Stop Bits %g\n", owner()->tag(), 'A' + m_index, (((m_wr4 & WR4_STOP_BITS_MASK) >> 2) + 1) / 2.); - LOG("Z80SIO \"%s\" Channel %c : Clock Mode %uX\n", owner()->tag(), 'A' + m_index, get_clock_mode()); + LOGSETUP("Z80SIO \"%s\" Channel %c : Stop Bits %g\n", owner()->tag(), 'A' + m_index, (((m_wr4 & WR4_STOP_BITS_MASK) >> 2) + 1) / 2.); + LOGSETUP("Z80SIO \"%s\" Channel %c : Clock Mode %uX\n", owner()->tag(), 'A' + m_index, get_clock_mode()); } void z80sio_channel::do_sioreg_wr5(uint8_t data) { m_wr5 = data; - LOG("Z80SIO Channel %c : Transmitter Enable %u\n", 'A' + m_index, (data & WR5_TX_ENABLE) ? 1 : 0); - LOG("Z80SIO Channel %c : Transmitter Bits/Character %u\n", 'A' + m_index, get_tx_word_length()); - LOG("Z80SIO Channel %c : Transmit CRC Enable %u\n", 'A' + m_index, (data & WR5_TX_CRC_ENABLE) ? 1 : 0); - LOG("Z80SIO Channel %c : %s Frame Check Polynomial\n", 'A' + m_index, (data & WR5_CRC16) ? "CRC-16" : "SDLC"); - LOG("Z80SIO Channel %c : Send Break %u\n", 'A' + m_index, (data & WR5_SEND_BREAK) ? 1 : 0); - LOG("Z80SIO Channel %c : Request to Send %u\n", 'A' + m_index, (data & WR5_RTS) ? 1 : 0); - LOG("Z80SIO Channel %c : Data Terminal Ready %u\n", 'A' + m_index, (data & WR5_DTR) ? 1 : 0); + LOGSETUP("Z80SIO Channel %c\n", 'A' + m_index); + LOGSETUP(" - Transmitter Enable %u\n", (data & WR5_TX_ENABLE) ? 1 : 0); + LOGSETUP(" - Transmitter Bits/Character %u\n", get_tx_word_length()); + LOGSETUP(" - Transmit CRC Enable %u\n", (data & WR5_TX_CRC_ENABLE) ? 1 : 0); + LOGSETUP(" - %s Frame Check Polynomial\n", (data & WR5_CRC16) ? "CRC-16" : "SDLC"); + LOGSETUP(" - Send Break %u\n", (data & WR5_SEND_BREAK) ? 1 : 0); + LOGSETUP(" - Request to Send %u\n", (data & WR5_RTS) ? 1 : 0); + LOGSETUP(" - Data Terminal Ready %u\n", (data & WR5_DTR) ? 1 : 0); if (~data & WR5_TX_ENABLE) m_uart->clear_interrupt(m_index, INT_TRANSMIT); @@ -1694,13 +1794,13 @@ void z80sio_channel::do_sioreg_wr5(uint8_t data) void z80sio_channel::do_sioreg_wr6(uint8_t data) { - LOG("Z80SIO \"%s\" Channel %c : Transmit Sync/Sync 1/SDLC Address %02x\n", owner()->tag(), 'A' + m_index, data); + LOGSETUP("Z80SIO \"%s\" Channel %c : Transmit Sync/Sync 1/SDLC Address %02x\n", owner()->tag(), 'A' + m_index, data); m_wr6 = data; } void z80sio_channel::do_sioreg_wr7(uint8_t data) { - LOG("Z80SIO \"%s\" Channel %c : Receive Sync/Sync 2/SDLC Flag %02x\n", owner()->tag(), 'A' + m_index, data); + LOGSETUP("Z80SIO \"%s\" Channel %c : Receive Sync/Sync 2/SDLC Flag %02x\n", owner()->tag(), 'A' + m_index, data); m_wr7 = data; } @@ -1714,7 +1814,7 @@ void z80sio_channel::control_write(uint8_t data) if (reg != 0) { LOGSETUP(" * %s %c Reg %02x <- %02x - %s\n", tag(), 'A' + m_index, reg, data, std::array<char const *, 8> - {{"WR0", "WR1", "WR2", "WR3 - Async Rx setup", "WR4 - Async Clock, Parity and stop bits", "WR5 - Async Tx setup", "WR6", "WR7"}}[reg]); + {{"WR0", "WR1", "WR2", "WR3", "WR4", "WR5", "WR6", "WR7"}}[reg]); // mask out register index m_wr0 &= ~WR0_REGISTER_MASK; } @@ -1771,10 +1871,14 @@ void z80sio_channel::data_write(uint8_t data) m_tx_data = data; set_tx_empty(get_tx_empty() , false); if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC) + { + LOGTX("Z80SIO: WR4_STOP_BITS_SYNC detected\n"); m_tx_in_pkt = true; + } else { // ALL_SENT is only meaningful in async mode, in sync mode it's always 1 + LOGTX("Z80SIO: WR4_STOP_BITS_SYNC *not* detected\n"); m_rr1 &= ~RR1_ALL_SENT; m_all_sent_delay = 0; } @@ -1804,37 +1908,59 @@ void z80sio_channel::advance_rx_fifo() m_rx_error_fifo >>= 8; // load error status from the FIFO - m_rr1 = (m_rr1 & ~m_rr1_auto_reset) | uint8_t(m_rx_error_fifo & 0x000000ffU); - - // if we're in interrupt-on-first mode, clear interrupt if there's no pending error condition - if ((m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_FIRST) - { - for (int i = 0; m_rx_fifo_depth > i; ++i) - { - if (uint8_t(m_rx_error_fifo >> (i * 8)) & (RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR)) - return; - } - m_uart->clear_interrupt(m_index, INT_RECEIVE); - } + m_rr1 = (m_rr1 & ~m_rr1_auto_reset) | uint8_t(m_rx_error_fifo & 0x000000ffU & ~RR1_HIDDEN_1ST_MARKER); } else { // no more characters available in the FIFO m_rr0 &= ~RR0_RX_CHAR_AVAILABLE; - if ((m_wr1 & WR1_WRDY_ENABLE) && (m_wr1 & WR1_WRDY_ON_RX_TX)) - set_ready(false); - m_uart->clear_interrupt(m_index, INT_RECEIVE); + update_wait_ready(); } } + update_rx_int(); } uint8_t z80sio_channel::get_special_rx_mask() const { - return ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC) ? - (RR1_RX_OVERRUN_ERROR | RR1_END_OF_FRAME) : - (RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR); + if ((m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_DISABLE) + { + return 0; + } + else + { + uint8_t mask = ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC) ? + (RR1_RX_OVERRUN_ERROR | RR1_END_OF_FRAME) : + (RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR); + if ((m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL_PARITY) + mask |= RR1_PARITY_ERROR; + return mask; + } } +void z80sio_channel::update_rx_int() +{ + bool state = false; + + auto rx_int_mode = m_wr1 & WR1_RX_INT_MODE_MASK; + if (rx_int_mode != WR1_RX_INT_DISABLE) + { + if (m_rr1 & get_special_rx_mask()) + state = true; + else if (m_rx_fifo_depth) + { + // FIFO not empty + if (rx_int_mode != WR1_RX_INT_FIRST) + state = true; + else if (m_rx_error_fifo & RR1_HIDDEN_1ST_MARKER) + state = true; + } + } + LOGINT("rx %d wr1 %02x rr1 %02x fd %u ref %06x\n", state, m_wr1, m_rr1, m_rx_fifo_depth, m_rx_error_fifo); + if (state) + m_uart->trigger_interrupt(m_index, INT_RECEIVE); + else + m_uart->clear_interrupt(m_index, INT_RECEIVE); +} //------------------------------------------------- // receive_enabled - conditions have changed @@ -1860,6 +1986,11 @@ void z80sio_channel::enter_hunt_mode() } } +void z80dart_channel::enter_hunt_mode() +{ + LOG("%s (sync mode not supported by DART)\n", FUNCNAME); +} + //------------------------------------------------- // sync_receive - synchronous reception handler //------------------------------------------------- @@ -1977,6 +2108,11 @@ void z80sio_channel::sync_receive() m_dlyd_rxd = m_rxd; } +void z80dart_channel::sync_receive() +{ + LOG("%s (sync mode not supported by DART)\n", FUNCNAME); +} + //------------------------------------------------- // sdlc_receive - SDLC reception handler //------------------------------------------------- @@ -2003,7 +2139,7 @@ void z80sio_channel::sdlc_receive() LOGRCV("SDLC Abort detected\n"); m_rr0 |= RR0_BREAK_ABORT; if (!m_brk_latched) { - m_brk_latched = 1; + m_brk_latched = true; trigger_ext_int(); } enter_hunt_mode(); @@ -2023,7 +2159,7 @@ void z80sio_channel::sdlc_receive() { m_rr0 &= ~RR0_BREAK_ABORT; if (!m_brk_latched) { - m_brk_latched = 1; + m_brk_latched = true; trigger_ext_int(); } } @@ -2112,7 +2248,13 @@ void z80sio_channel::sdlc_receive() data |= ~((1U << m_rx_bit_limit) - 1); LOGRCV("SDLC rx data=%02x (%d bits)\n" , data , m_rx_bit_limit); queue_received(data , 0); - m_rx_sync_fsm = SYNC_FSM_IN_FRAME; + if (m_rx_sync_fsm == SYNC_FSM_1ST_CHAR) + { + // reception of 1st char clears END-OF-FRAME + m_rr1 &= ~RR1_END_OF_FRAME; + update_rx_int(); + m_rx_sync_fsm = SYNC_FSM_IN_FRAME; + } } } } @@ -2124,6 +2266,11 @@ void z80sio_channel::sdlc_receive() } } +void z80dart_channel::sdlc_receive() +{ + logerror("%s (sync mode not supported by DART)\n", FUNCNAME); +} + //------------------------------------------------- // receive_data - receive data word //------------------------------------------------- @@ -2138,6 +2285,13 @@ void z80sio_channel::receive_data() void z80sio_channel::queue_received(uint16_t data, uint32_t error) { + if ((m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_FIRST && m_rx_first) + { + // insert a hidden marker for 1st received character + error |= RR1_HIDDEN_1ST_MARKER; + m_rx_first = false; + } + if (3 == m_rx_fifo_depth) { LOG(" Receive FIFO overrun detected\n"); @@ -2155,38 +2309,22 @@ void z80sio_channel::queue_received(uint16_t data, uint32_t error) m_rx_data_fifo |= uint32_t(data & 0x00ffU) << (8 * m_rx_fifo_depth); m_rx_error_fifo |= error << (8 * m_rx_fifo_depth); if (!m_rx_fifo_depth) - m_rr1 = (m_rr1 & ~m_rr1_auto_reset) | uint8_t(error); + m_rr1 = (m_rr1 & ~m_rr1_auto_reset) | uint8_t(error & ~RR1_HIDDEN_1ST_MARKER); ++m_rx_fifo_depth; } m_rr0 |= RR0_RX_CHAR_AVAILABLE; - if ((m_wr1 & WR1_WRDY_ENABLE) && (m_wr1 & WR1_WRDY_ON_RX_TX)) - set_ready(true); + update_wait_ready(); // receive interrupt - switch (m_wr1 & WR1_RX_INT_MODE_MASK) - { - case WR1_RX_INT_FIRST: - if (m_rx_first || (error & get_special_rx_mask())) - m_uart->trigger_interrupt(m_index, INT_RECEIVE); - m_rx_first = 0; - break; - - case WR1_RX_INT_ALL_PARITY: - case WR1_RX_INT_ALL: - m_uart->trigger_interrupt(m_index, INT_RECEIVE); - break; - - default: - LOG("No receive interrupt triggered\n"); - } + update_rx_int(); } //------------------------------------------------- // cts_w - clear to send handler //------------------------------------------------- -WRITE_LINE_MEMBER( z80sio_channel::cts_w ) +void z80sio_channel::cts_w(int state) { if (bool(m_cts) != bool(state)) { @@ -2204,11 +2342,11 @@ WRITE_LINE_MEMBER( z80sio_channel::cts_w ) //------------------------------------------------- // dcd_w - data carrier detected handler //------------------------------------------------- -WRITE_LINE_MEMBER( z80sio_channel::dcd_w ) +void z80sio_channel::dcd_w(int state) { if (bool(m_dcd) != bool(state)) { - LOG("Z80SIO Channel %c : DCD %u\n", 'A' + m_index, state); + LOGDCD("Z80SIO Channel %c : DCD %u\n", 'A' + m_index, state); bool const was_allowed(receive_allowed()); m_dcd = state; @@ -2224,11 +2362,11 @@ WRITE_LINE_MEMBER( z80sio_channel::dcd_w ) //------------------------------------------------- // sh_w - Sync Hunt handler //------------------------------------------------- -WRITE_LINE_MEMBER( z80sio_channel::sync_w ) +void z80sio_channel::sync_w(int state) { if (bool(m_sync) != bool(state)) { - LOG("Z80SIO Channel %c : Sync %u\n", 'A' + m_index, state); + LOGSYNC("Z80SIO Channel %c : Sync %u\n", 'A' + m_index, state); m_sync = state; @@ -2242,7 +2380,7 @@ WRITE_LINE_MEMBER( z80sio_channel::sync_w ) //------------------------------------------------- // rxc_w - receive clock //------------------------------------------------- -WRITE_LINE_MEMBER( z80sio_channel::rxc_w ) +void z80sio_channel::rxc_w(int state) { //LOG("Z80SIO \"%s\" Channel %c : Receiver Clock Pulse\n", owner()->tag(), m_index + 'A'); //if ((receive_allowed() || m_rx_bit != 0) && state && !m_rx_clock) @@ -2275,7 +2413,7 @@ WRITE_LINE_MEMBER( z80sio_channel::rxc_w ) LOGRCV("Break termination detected\n"); m_rr0 &= ~RR0_BREAK_ABORT; if (!m_brk_latched) { - m_brk_latched = 1; + m_brk_latched = true; trigger_ext_int(); } } @@ -2342,7 +2480,7 @@ WRITE_LINE_MEMBER( z80sio_channel::rxc_w ) { LOGRCV("Break detected\n"); m_rr0 |= RR0_BREAK_ABORT; - m_brk_latched = 1; + m_brk_latched = true; trigger_ext_int(); } } @@ -2367,7 +2505,7 @@ WRITE_LINE_MEMBER( z80sio_channel::rxc_w ) //------------------------------------------------- // txc_w - transmit clock //------------------------------------------------- -WRITE_LINE_MEMBER( z80sio_channel::txc_w ) +void z80sio_channel::txc_w(int state) { //LOG("Z80SIO \"%s\" Channel %c : Transmitter Clock Pulse\n", owner()->tag(), m_index + 'A'); if (!state && m_tx_clock) @@ -2389,9 +2527,10 @@ WRITE_LINE_MEMBER( z80sio_channel::txc_w ) m_tx_count = get_clock_mode() / 2; // Send out a delayed half bit bool new_txd = BIT(m_tx_delay , 3); + if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC || !(m_rr1 & RR1_ALL_SENT)) + LOGBIT("%.6f TX %d DLY %x\n" , machine().time().as_double() , new_txd , m_tx_delay & 0xf); if (new_txd != m_txd && !(m_wr5 & WR5_SEND_BREAK)) { - LOGBIT("%.6f TX %d DLY %x\n" , machine().time().as_double() , new_txd , m_tx_delay & 0xf); out_txd_cb(new_txd); } m_txd = new_txd; @@ -2412,9 +2551,11 @@ WRITE_LINE_MEMBER( z80sio_channel::txc_w ) // Generate a new bit bool new_bit = false; if ((m_wr4 & (WR4_SYNC_MODE_MASK | WR4_STOP_BITS_MASK)) == (WR4_SYNC_MODE_SDLC | WR4_STOP_BITS_SYNC) && - !(m_tx_flags & TX_FLAG_FRAMING) && (m_tx_hist & 0x1f) == 0x1f) - // SDLC, not sending framing & 5 ones in a row: do zero insertion + (m_tx_flags & (TX_FLAG_DATA_TX | TX_FLAG_CRC_TX)) && (m_tx_hist & 0x1f) == 0x1f) + { + // SDLC, sending data/CRC & 5 ones in a row: do zero insertion new_bit = false; + } else { bool get_out = false; @@ -2502,10 +2643,10 @@ WRITE_LINE_MEMBER( z80sio_channel::txc_w ) else m_all_sent_delay = 0; } - if (m_tx_flags & TX_FLAG_FRAMING) - m_tx_hist = 0; - else + if (m_tx_flags & (TX_FLAG_DATA_TX | TX_FLAG_CRC_TX)) m_tx_hist = (m_tx_hist << 1) | new_bit; + else + m_tx_hist = 0; // Insert new bit in delay register m_tx_delay = (m_tx_delay & ~1U) | new_bit; } @@ -2513,3 +2654,374 @@ WRITE_LINE_MEMBER( z80sio_channel::txc_w ) } m_tx_clock = state; } + +//************************************************************************** +// MK68564 REGISTER INTERFACE +//************************************************************************** + +//------------------------------------------------- +// cmdreg_r - read from command register +//------------------------------------------------- +uint8_t mk68564_channel::cmdreg_r() +{ + return m_wr0; +} + + +//------------------------------------------------- +// cmdreg_w - write to command register +//------------------------------------------------- +void mk68564_channel::cmdreg_w(uint8_t data) +{ + // TODO: bit 0 sets loop mode (no register select) + // FIXME: no return from interrupt command + do_sioreg_wr0(data); +} + + +//------------------------------------------------- +// modectl_r - read from mode control register +//------------------------------------------------- +uint8_t mk68564_channel::modectl_r() +{ + return m_wr4; +} + + +//------------------------------------------------- +// modectl_w - write to mode control register +//------------------------------------------------- +void mk68564_channel::modectl_w(uint8_t data) +{ + do_sioreg_wr4(data); +} + + +//------------------------------------------------- +// intctl_r - read from interrupt control register +//------------------------------------------------- +uint8_t mk68564_channel::intctl_r() +{ + return m_wr1 | (m_wr5 & WR5_CRC16 ? 0x80 : 0); +} + + +//------------------------------------------------- +// intctl_w - write to interrupt control register +//------------------------------------------------- +void mk68564_channel::intctl_w(uint8_t data) +{ + if (BIT(data, 7)) + m_wr5 |= WR5_CRC16; + else + m_wr5 &= ~WR5_CRC16; + + // TODO: bits 5 and 6 are independent RxRDY and WxRDY enables + do_sioreg_wr1(data & 0x7f); +} + + +//------------------------------------------------- +// sync1_r - read from sync word register 1 +//------------------------------------------------- +uint8_t mk68564_channel::sync1_r() +{ + return m_wr6; +} + + +//------------------------------------------------- +// sync1_w - write to sync word register 1 +//------------------------------------------------- +void mk68564_channel::sync1_w(uint8_t data) +{ + do_sioreg_wr6(data); +} + + +//------------------------------------------------- +// sync2_r - read from sync word register 2 +//------------------------------------------------- +uint8_t mk68564_channel::sync2_r() +{ + return m_wr7; +} + + +//------------------------------------------------- +// sync2_w - write to sync word register 2 +//------------------------------------------------- +void mk68564_channel::sync2_w(uint8_t data) +{ + do_sioreg_wr7(data); +} + + +//------------------------------------------------- +// rcvctl_r - read from receiver control register +//------------------------------------------------- +uint8_t mk68564_channel::rcvctl_r() +{ + return bitswap<8>(m_wr3, 6, 7, 5, 4, 3, 2, 1, 0) & ~WR3_ENTER_HUNT_PHASE; +} + + +//------------------------------------------------- +// rcvctl_w - write to receiver control register +//------------------------------------------------- +void mk68564_channel::rcvctl_w(uint8_t data) +{ + do_sioreg_wr3(bitswap<8>(data, 6, 7, 5, 4, 3, 2, 1, 0)); +} + + +//------------------------------------------------- +// xmtctl_r - read from transmitter control +// register +//------------------------------------------------- +uint8_t mk68564_channel::xmtctl_r() +{ + uint8_t xmtctl = 0; + if (m_wr5 & WR5_TX_ENABLE) + xmtctl |= 0x01; + if (m_wr5 & WR5_RTS) + xmtctl |= 0x02; + if (m_wr5 & WR5_DTR) + xmtctl |= 0x04; + if (m_wr5 & WR5_TX_CRC_ENABLE) + xmtctl |= 0x08; + if (m_wr5 & WR5_SEND_BREAK) + xmtctl |= 0x10; + if (m_tx_auto_enable) + xmtctl |= 0x20; + xmtctl |= (m_wr5 & 0x40) << 1; + xmtctl |= (m_wr5 & 0x80) >> 1; + return xmtctl; +} + + +//------------------------------------------------- +// xmtctl_w - write to transmitter control +// register +//------------------------------------------------- +void mk68564_channel::xmtctl_w(uint8_t data) +{ + uint8_t control = + (BIT(data, 0) ? WR5_TX_ENABLE : 0) | + (BIT(data, 1) ? WR5_RTS : 0) | + (BIT(data, 2) ? WR5_DTR : 0) | + (BIT(data, 3) ? WR5_TX_CRC_ENABLE : 0) | + (BIT(data, 4) ? WR5_SEND_BREAK : 0) | + (data & 0x40) << 1 | + (data & 0x80) >> 1 | + (m_wr5 & WR5_CRC16); + do_sioreg_wr5(control); + + m_tx_auto_enable = BIT(data, 5); +} + + +//------------------------------------------------- +// tcreg_r - read from time constant register +//------------------------------------------------- +uint8_t mk68564_channel::tcreg_r() +{ + return m_brg_tc; +} + + +//------------------------------------------------- +// tcreg_w - write to time constant register +//------------------------------------------------- +void mk68564_channel::tcreg_w(uint8_t data) +{ + m_brg_tc = data; +} + + +//------------------------------------------------- +// brgctl_r - read from baud rate generator +// control register +//------------------------------------------------- +uint8_t mk68564_channel::brgctl_r() +{ + // unused bits are all zero + return m_brg_control & 0x0f; +} + + +//------------------------------------------------- +// brgctl_w - write to baud rate generator +// control register +//------------------------------------------------- +void mk68564_channel::brgctl_w(uint8_t data) +{ + if (BIT(data, 0)) + LOGBRG("%s: BRG enabled, divide by %d, RxC %sternal, TxC %sternal (TC = %d, %.1f Hz)\n", + machine().describe_context(), + BIT(data, 1) ? 64 : 4, + BIT(data, 2) ? "in" : "ex", + BIT(data, 3) ? "in" : "ex", + m_brg_tc, + clocks_to_attotime((m_brg_tc ? m_brg_tc : 256) * (BIT(data, 1) ? 64 : 4)).as_hz()); + else + LOGBRG("%s: BRG disabled\n", machine().describe_context()); + + m_brg_control = data & 0x0f; + m_brg_state = false; + brg_update(); +} + + +//------------------------------------------------- +// vectrg_w - write to the interrupt vector +// register (only one exists) +//------------------------------------------------- +void mk68564_device::vectrg_w(uint8_t data) +{ + m_chanB->do_sioreg_wr2(data); +} + + +//------------------------------------------------- +// read - 68000 compatible bus read +//------------------------------------------------- +uint8_t mk68564_device::read(offs_t offset) +{ + mk68564_channel &channel = downcast<mk68564_channel &>(BIT(offset, 4) ? *m_chanB : *m_chanA); + + switch (offset & 0x0f) + { + case 0x00: + return channel.cmdreg_r(); + + case 0x01: + return channel.modectl_r(); + + case 0x02: + return channel.intctl_r(); + + case 0x03: + return channel.sync1_r(); + + case 0x04: + return channel.sync2_r(); + + case 0x05: + return channel.rcvctl_r(); + + case 0x06: + return channel.xmtctl_r(); + + case 0x07: + return channel.do_sioreg_rr0(); + + case 0x08: + return channel.do_sioreg_rr1(); + + case 0x09: + return channel.data_read(); + + case 0x0a: + return channel.tcreg_r(); + + case 0x0b: + return channel.brgctl_r(); + + case 0x0c: // vector register is addressable through either channel + return read_vector(); + + default: // unused registers read as FF + return 0xff; + } +} + + +//------------------------------------------------- +// write - 68000 compatible bus write +//------------------------------------------------- +void mk68564_device::write(offs_t offset, uint8_t data) +{ + mk68564_channel &channel = downcast<mk68564_channel &>(BIT(offset, 4) ? *m_chanB : *m_chanA); + + switch (offset & 0x0f) + { + case 0x00: + channel.cmdreg_w(data); + break; + + case 0x01: + channel.modectl_w(data); + break; + + case 0x02: + channel.intctl_w(data); + break; + + case 0x03: + channel.sync1_w(data); + break; + + case 0x04: + channel.sync2_w(data); + break; + + case 0x05: + channel.rcvctl_w(data); + break; + + case 0x06: + channel.xmtctl_w(data); + break; + + case 0x09: + channel.data_write(data); + break; + + case 0x0a: + channel.tcreg_w(data); + break; + + case 0x0b: + channel.brgctl_w(data); + break; + + case 0x0c: // vector register is addressable through either channel + vectrg_w(data); + break; + + default: + logerror("Write %02X to unused/read-only register %02X\n", data, offset & 0x1f); + break; + } +} + +//************************************************************************** +// MK68564 BAUD RATE GENERATOR +//************************************************************************** + +void mk68564_device::set_xtal(uint32_t clock) +{ + assert(!configured()); + subdevice<mk68564_channel>(CHANA_TAG)->set_clock(clock); + subdevice<mk68564_channel>(CHANB_TAG)->set_clock(clock); +} + +void mk68564_channel::brg_update() +{ + if (BIT(m_brg_control, 2)) + rxc_w(m_brg_state); + if (BIT(m_brg_control, 3)) + txc_w(m_brg_state); + + if (BIT(m_brg_control, 0)) + m_brg_timer->adjust(clocks_to_attotime((m_brg_tc ? m_brg_tc : 256) * (BIT(m_brg_control, 1) ? 32 : 2))); + else + m_brg_timer->adjust(attotime::never); +} + +TIMER_CALLBACK_MEMBER(mk68564_channel::brg_timeout) +{ + m_brg_state = !m_brg_state; + brg_update(); +} |