From 519b14e33c614b7065c2bc561728394253c08db1 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Mon, 30 Oct 2017 13:41:43 +1100 Subject: z80sio: Made async receive behave more like real device. * Check that start bit persists for half a clock interval. * Sample data bits mid-interval. * Handle invalid stop bit as described in Zilog manual. * Check parity and latch overrun and parity errors. (nw) This fixes the issues with corrupt characters being typed on the Kaypro. Synchronous modes are still broken. The channels are still using device_serial_interface to transmit frames, but receiving is handled entirely in the device class itself. Overruns still aren't handled properly. --- src/devices/machine/z80sio.cpp | 179 ++++++++++++++++++++++------------------- src/devices/machine/z80sio.h | 37 +++------ src/mame/drivers/apricot.cpp | 5 +- src/mame/machine/kay_kbd.cpp | 29 ++++++- src/mame/machine/kay_kbd.h | 3 +- 5 files changed, 138 insertions(+), 115 deletions(-) diff --git a/src/devices/machine/z80sio.cpp b/src/devices/machine/z80sio.cpp index 8cf9169c6b2..e17d7c83fa9 100644 --- a/src/devices/machine/z80sio.cpp +++ b/src/devices/machine/z80sio.cpp @@ -122,10 +122,6 @@ z80sio_device::z80sio_device(const machine_config &mconfig, device_type type, co device_z80daisy_interface(mconfig, *this), m_chanA(*this, CHANA_TAG), m_chanB(*this, CHANB_TAG), - m_rxca(0), - m_txca(0), - m_rxcb(0), - m_txcb(0), m_out_txda_cb(*this), m_out_dtra_cb(*this), m_out_rtsa_cb(*this), @@ -195,14 +191,6 @@ void z80sio_device::device_start() { LOG("%s\n", FUNCNAME); - // configure channel A - m_chanA->m_rxc = m_rxca; - m_chanA->m_txc = m_txca; - - // configure channel B - m_chanB->m_rxc = m_rxcb; - m_chanB->m_txc = m_txcb; - // state saving save_item(NAME(m_int_state)); save_item(NAME(m_int_source)); @@ -523,8 +511,10 @@ WRITE8_MEMBER( z80sio_device::ba_cd_w ) z80sio_channel::z80sio_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, Z80SIO_CHANNEL, tag, owner, clock) , device_serial_interface(mconfig, *this) - , m_rx_error(0) , m_rx_clock(0) + , m_rx_count(0) + , m_rx_bit(0) + , m_rx_sr(0) , m_rx_first(0) , m_rx_break(0) , m_rx_rr0_latch(0) @@ -576,8 +566,10 @@ void z80sio_channel::device_start() save_item(NAME(m_wr5)); save_item(NAME(m_wr6)); save_item(NAME(m_wr7)); - save_item(NAME(m_rx_error)); save_item(NAME(m_rx_clock)); + save_item(NAME(m_rx_count)); + save_item(NAME(m_rx_bit)); + save_item(NAME(m_rx_sr)); save_item(NAME(m_rx_first)); save_item(NAME(m_rx_break)); save_item(NAME(m_rx_rr0_latch)); @@ -601,7 +593,7 @@ void z80sio_channel::device_reset() LOG("%s\n", FUNCNAME); // Reset RS232 emulation - receive_register_reset(); + receive_reset(); transmit_register_reset(); // disable receiver @@ -716,33 +708,6 @@ void z80sio_channel::tra_complete() } -//------------------------------------------------- -// rcv_callback - -//------------------------------------------------- -void z80sio_channel::rcv_callback() -{ - if (m_wr3 & WR3_RX_ENABLE) - { - LOGBIT("%s() \"%s \"Channel %c Received Data Bit %d\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_rxd); - receive_register_update_bit(m_rxd); - } -} - - -//------------------------------------------------- -// rcv_complete - -//------------------------------------------------- -void z80sio_channel::rcv_complete() -{ - uint8_t data; - - receive_register_extract(); - data = get_received_char(); - LOGRCV("%s() \"%s \"Channel %c Received Data %02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, data); - receive_data(data); -} - - //------------------------------------------------- // get_clock_mode - get clock divisor //------------------------------------------------- @@ -1205,7 +1170,7 @@ uint8_t z80sio_channel::data_read() data = m_rx_data_fifo.dequeue(); // load error status from the FIFO - m_rr1 = (m_rr1 & ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR)) | m_rx_error_fifo.dequeue(); + m_rr1 = (m_rr1 & ~RR1_CRC_FRAMING_ERROR) | m_rx_error_fifo.dequeue(); if (m_rx_data_fifo.empty()) { @@ -1249,18 +1214,29 @@ void z80sio_channel::data_write(uint8_t data) } +//------------------------------------------------- +// receive_reset - reset receive state +//------------------------------------------------- +void z80sio_channel::receive_reset() +{ + m_rx_count = (get_clock_mode() - 1) / 2; + m_rx_bit = 0; +} + //------------------------------------------------- // receive_data - receive data word //------------------------------------------------- -void z80sio_channel::receive_data(uint8_t data) +void z80sio_channel::receive_data() { - LOGRCV("%s(%02x) %s:%c\n",FUNCNAME, data, tag(), 'A' + m_index); + LOGRCV("%s(%04x) %s:%c\n",FUNCNAME, m_rx_sr, tag(), 'A' + m_index); if (m_rx_data_fifo.full()) { + // FIXME: this should overwrite the tail of the FIFO and store the overrun error + LOG(" Overrun detected\n"); // receive overrun error detected - m_rx_error |= RR1_RX_OVERRUN_ERROR; + //uint8_t rx_error |= RR1_RX_OVERRUN_ERROR; switch (m_wr1 & WR1_RX_INT_MODE_MASK) { @@ -1279,9 +1255,24 @@ void z80sio_channel::receive_data(uint8_t data) } else { + // check for parity and framing errors + int const word_length = get_rx_word_length(); + bool const parity = 0U != (m_wr4 & WR4_PARITY_ENABLE); + uint16_t const stop_bit = uint16_t(1U) << (word_length + (parity ? 1 : 0)); + uint8_t rx_error = (m_rx_sr & stop_bit) ? 0U : RR1_CRC_FRAMING_ERROR; + m_rx_sr |= stop_bit; + if (parity) + { + uint16_t par(m_rx_sr); + for (int i = 1; word_length >= i; ++i) + par ^= BIT(par, i); + if (bool(BIT(par, 0)) == bool(m_wr4 & WR4_PARITY_EVEN)) + rx_error |= RR1_PARITY_ERROR; + } + // store received character and error status into FIFO - m_rx_data_fifo.enqueue(data); - m_rx_error_fifo.enqueue(m_rx_error); + m_rx_data_fifo.enqueue(uint8_t(m_rx_sr & 0xffU)); + m_rx_error_fifo.enqueue(rx_error); } m_rr0 |= RR0_RX_CHAR_AVAILABLE; @@ -1301,9 +1292,9 @@ void z80sio_channel::receive_data(uint8_t data) case WR1_RX_INT_ALL: m_uart->trigger_interrupt(m_index, INT_RECEIVE); break; + default: LOG("No interrupt triggered\n"); - } } @@ -1421,18 +1412,62 @@ WRITE_LINE_MEMBER( z80sio_channel::sync_w ) WRITE_LINE_MEMBER( z80sio_channel::rxc_w ) { //LOG("Z80SIO \"%s\" Channel %c : Receiver Clock Pulse\n", owner()->tag(), m_index + 'A'); - int clocks = get_clock_mode(); - if (clocks == 1) - rx_clock_w(state); - else if(state) + if ((m_wr3 & WR3_RX_ENABLE) && state && !m_rx_clock) { - rx_clock_w(m_rx_clock < clocks/2); + // RxD sampled on rising edge + int const clocks = get_clock_mode() - 1; - m_rx_clock++; - if (m_rx_clock == clocks) - m_rx_clock = 0; + if (!m_rx_bit) + { + // look for start bit + if (m_rxd) + { + // line idle + m_rx_count = (std::max)(m_rx_count, (clocks / 2) + 1) - 1; + } + else if (!m_rx_count) + { + // half a bit period expired, start shifting bits + m_rx_count = clocks; + ++m_rx_bit; + m_rx_sr = ~uint16_t(0U); + } + else + { + // ensure start bit lasts long enough + --m_rx_count; + } + } + else if (!m_rx_count) + { + // sample a data/parity/stop bit + if (!m_rxd) + m_rx_sr &= ~uint16_t(1U << (m_rx_bit - 1)); + + if ((get_rx_word_length() + ((m_wr4 & WR4_PARITY_ENABLE) ? 1 : 0) + 1) == m_rx_bit) + { + // this is the stop bit - framing error adds a half bit period + LOGBIT("%s() \"%s \"Channel %c Received Data Bit %d\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_rxd); + m_rx_count = m_rxd ? 0 : clocks; + m_rx_bit = 0; + LOGRCV("%s() \"%s \"Channel %c Received Data %02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_rx_sr & 0xff); + receive_data(); + } + else + { + // wait a whole bit period for the next bit + m_rx_count = clocks; + ++m_rx_bit; + } + } + else + { + // bit period hasn't expired + --m_rx_count; + } } + m_rx_clock = state; } @@ -1462,8 +1497,8 @@ WRITE_LINE_MEMBER( z80sio_channel::txc_w ) //------------------------------------------------- void z80sio_channel::update_serial() { - int data_bit_count = get_rx_word_length(); - stop_bits_t stop_bits = get_stop_bits(); + int const data_bit_count = get_rx_word_length(); + stop_bits_t const stop_bits = get_stop_bits(); parity_t parity; LOG("%s\n", FUNCNAME); @@ -1481,20 +1516,7 @@ void z80sio_channel::update_serial() set_data_frame(1, data_bit_count, parity, stop_bits); - int clocks = get_clock_mode(); - - if (m_rxc > 0) - { - LOG("- RxC:%d/%d = %d\n", m_rxc, clocks, m_rxc / clocks); - set_rcv_rate(m_rxc / clocks); - } - - if (m_txc > 0) - { - LOG("- TxC:%d/%d = %d\n", m_txc, clocks, m_txc / clocks); - set_tra_rate(m_txc / clocks); - } - receive_register_reset(); // if stop bits is changed from 0, receive register has to be reset + receive_reset(); // if stop bits is changed from 0, receive register has to be reset } @@ -1511,14 +1533,3 @@ void z80sio_channel::set_dtr(int state) else m_uart->m_out_dtrb_cb(m_dtr); } - -//------------------------------------------------- -// write_rx - -//------------------------------------------------- -WRITE_LINE_MEMBER(z80sio_channel::write_rx) -{ - m_rxd = state; - //only use rx_w when self-clocked - if(m_rxc) - device_serial_interface::rx_w(state); -} diff --git a/src/devices/machine/z80sio.h b/src/devices/machine/z80sio.h index 55803b7e427..dfe33c4068b 100644 --- a/src/devices/machine/z80sio.h +++ b/src/devices/machine/z80sio.h @@ -67,9 +67,6 @@ #define SIO_CHANB_TAG "chb" /* Generic macros */ -#define MCFG_Z80SIO_OFFSETS(_rxa, _txa, _rxb, _txb) \ - z80sio_device::configure_channels(*device, _rxa, _txa, _rxb, _txb); - #define MCFG_Z80SIO_OUT_INT_CB(_devcb) \ devcb = &z80sio_device::set_out_int_callback(*device, DEVCB_##_devcb); @@ -129,8 +126,7 @@ class z80sio_device; -class z80sio_channel : public device_t, - public device_serial_interface +class z80sio_channel : public device_t, public device_serial_interface { friend class z80sio_device; @@ -140,8 +136,6 @@ public: // device_serial_interface overrides virtual void tra_callback() override; virtual void tra_complete() override; - virtual void rcv_callback() override; - virtual void rcv_complete() override; // read register handlers uint8_t do_sioreg_rr0(); @@ -165,18 +159,16 @@ public: uint8_t data_read(); void data_write(uint8_t data); - void receive_data(uint8_t data); + void receive_reset(); + void receive_data(); - DECLARE_WRITE_LINE_MEMBER( write_rx ); + DECLARE_WRITE_LINE_MEMBER( write_rx ) { m_rxd = state; } DECLARE_WRITE_LINE_MEMBER( cts_w ); DECLARE_WRITE_LINE_MEMBER( dcd_w ); DECLARE_WRITE_LINE_MEMBER( rxc_w ); DECLARE_WRITE_LINE_MEMBER( txc_w ); DECLARE_WRITE_LINE_MEMBER( sync_w ); - int m_rxc; - int m_txc; - // Register state // read registers enum uint8_t m_rr0; // REG_RR0_STATUS @@ -380,9 +372,12 @@ protected: // receiver state util::fifo m_rx_data_fifo; util::fifo m_rx_error_fifo; - uint8_t m_rx_error; // current receive error - int m_rx_clock; // receive clock pulse count + int m_rx_clock; // receive clock line state + int m_rx_count; // clocks until next sample + int m_rx_bit; // receive data bit (0 = start bit, 1 = LSB, etc.) + uint16_t m_rx_sr; // receive shift register + int m_rx_first; // first character received int m_rx_break; // receive break condition uint8_t m_rx_rr0_latch; // read register 0 latched @@ -440,15 +435,6 @@ public: dev.m_cputag = tag; } - static void configure_channels(device_t &device, int rxa, int txa, int rxb, int txb) - { - z80sio_device &dev = downcast(device); - dev.m_rxca = rxa; - dev.m_txca = txa; - dev.m_rxcb = rxb; - dev.m_txcb = txb; - } - DECLARE_READ8_MEMBER( cd_ba_r ); DECLARE_WRITE8_MEMBER( cd_ba_w ); DECLARE_READ8_MEMBER( ba_cd_r ); @@ -521,11 +507,6 @@ protected: required_device m_chanB; // internal state - int m_rxca; - int m_txca; - int m_rxcb; - int m_txcb; - devcb_write_line m_out_txda_cb; devcb_write_line m_out_dtra_cb; devcb_write_line m_out_rtsa_cb; diff --git a/src/mame/drivers/apricot.cpp b/src/mame/drivers/apricot.cpp index 964b2484555..7a2b1d67716 100644 --- a/src/mame/drivers/apricot.cpp +++ b/src/mame/drivers/apricot.cpp @@ -20,6 +20,7 @@ #include "cpu/i8089/i8089.h" #include "formats/apridisk.h" #include "imagedev/flopdrv.h" +#include "machine/clock.h" #include "machine/ram.h" #include "machine/74153.h" #include "machine/i8255.h" @@ -412,8 +413,10 @@ static MACHINE_CONFIG_START( apricot ) MCFG_TTL153_ZA_CB(DEVWRITELINE("ic15", z80sio_device, rxca_w)) MCFG_TTL153_ZB_CB(DEVWRITELINE("ic15", z80sio_device, txca_w)) + MCFG_CLOCK_ADD("ic15_rxtxcb", XTAL_4MHz / 16) + MCFG_CLOCK_SIGNAL_HANDLER(DEVWRITELINE("ic15", z80sio_device, rxtxcb_w)) + MCFG_DEVICE_ADD("ic15", Z80SIO, XTAL_15MHz / 6) - MCFG_Z80SIO_OFFSETS(0, 0, XTAL_4MHz / 16, XTAL_4MHz / 16) MCFG_Z80SIO_CPU("ic91") MCFG_Z80SIO_OUT_TXDA_CB(DEVWRITELINE("rs232", rs232_port_device, write_txd)) MCFG_Z80SIO_OUT_DTRA_CB(DEVWRITELINE("rs232", rs232_port_device, write_dtr)) diff --git a/src/mame/machine/kay_kbd.cpp b/src/mame/machine/kay_kbd.cpp index de2ee0935fc..29baed6184e 100644 --- a/src/mame/machine/kay_kbd.cpp +++ b/src/mame/machine/kay_kbd.cpp @@ -11,6 +11,14 @@ #include "emu.h" #include "machine/kay_kbd.h" +#define LOG_GENERAL (1U << 0) +#define LOG_TXD (1U << 1) + +//#define VERBOSE (LOG_GENERAL | LOG_TXD) +#include "logmacro.h" + +#define LOGTXD(...) LOGMASKED(LOG_TXD, __VA_ARGS__) + /* * The KAYPRO keyboard has roughly the following layout: @@ -323,8 +331,9 @@ kaypro_10_keyboard_device::kaypro_10_keyboard_device( machine_config const &mconfig, char const *tag, device_t *owner, - uint32_t clock) + std::uint32_t clock) : device_t(mconfig, KAYPRO_10_KEYBOARD, tag, owner, clock) + , m_mcu(*this, "mcu") , m_bell(*this, "bell") , m_matrix(*this, "ROW.%X", 0) , m_modifiers(*this, "MOD") @@ -379,6 +388,24 @@ READ8_MEMBER(kaypro_10_keyboard_device::p2_r) WRITE8_MEMBER(kaypro_10_keyboard_device::p2_w) { + if ((VERBOSE & LOG_TXD) && (0x0014U >= static_cast(m_mcu)->safe_pc())) + { + auto const suppressor(machine().disable_side_effect()); + address_space &mcu_ram(m_mcu->space(AS_DATA)); + std::uint8_t const txd_time(mcu_ram.read_byte(0x1cU)); + std::uint8_t const serial_flags(mcu_ram.read_byte(0x1eU)); + bool const txd_active(BIT(serial_flags, 6)); + if ((21U == txd_time) && txd_active) + { + std::uint8_t const txd_bit(mcu_ram.read_byte(0x1bU)); + LOGTXD( + (9U == txd_bit) ? "serial transmit: start bit = %1$u (cycles = %3$u)\n" : "serial transmit: bit %2$u = %1$u (cycles = %3$u)\n", + BIT(data, 7), + 8U - txd_bit, + m_mcu->total_cycles()); + } + } + m_bell->level_w(BIT(data, 6)); m_rxd_cb(BIT(data, 7)); } diff --git a/src/mame/machine/kay_kbd.h b/src/mame/machine/kay_kbd.h index 22edbaf207b..9bed953824d 100644 --- a/src/mame/machine/kay_kbd.h +++ b/src/mame/machine/kay_kbd.h @@ -19,7 +19,7 @@ public: machine_config const &mconfig, char const *tag, device_t *owner, - uint32_t clock); + std::uint32_t clock); template static devcb_base &set_rxd_cb(device_t &device, Object &&cb) { return downcast(device).m_rxd_cb.set_callback(std::forward(cb)); } @@ -40,6 +40,7 @@ protected: DECLARE_WRITE8_MEMBER(bus_w); private: + required_device m_mcu; required_device m_bell; required_ioport_array<16> m_matrix; required_ioport m_modifiers; -- cgit v1.2.3