// license:BSD-3-Clause // copyright-holders:Carl, Miodrag Milanovic, Vas Crabb #ifndef MAME_EMU_DISERIAL_H #define MAME_EMU_DISERIAL_H #pragma once #include // Windows headers are crap, let me count the ways #undef PARITY_NONE #undef PARITY_ODD #undef PARITY_EVEN #undef PARITY_MARK #undef PARITY_SPACE // ======================> device_serial_interface class device_serial_interface : public device_interface { public: enum { /* receive is waiting for start bit. The transition from high-low indicates start of start bit. This is used to synchronise with the data being transferred */ RECEIVE_REGISTER_WAITING_FOR_START_BIT = 0x01, /* receive is synchronised with data, data bits will be clocked in */ RECEIVE_REGISTER_SYNCHRONISED = 0x02, /* set if receive register has been filled */ RECEIVE_REGISTER_FULL = 0x04 }; enum { /* register is empty and ready to be filled with data */ TRANSMIT_REGISTER_EMPTY = 0x0001 }; /* parity selections */ /* if all the bits are added in a byte, if the result is: even -> parity is even odd -> parity is odd */ enum parity_t { PARITY_NONE, /* no parity. a parity bit will not be in the transmitted/received data */ PARITY_ODD, /* odd parity */ PARITY_EVEN, /* even parity */ PARITY_MARK, /* one parity */ PARITY_SPACE /* zero parity */ }; enum stop_bits_t { STOP_BITS_0, STOP_BITS_1 = 1, STOP_BITS_1_5 = 2, STOP_BITS_2 = 3 }; /* Communication lines. Beware, everything is active high */ enum { CTS = 0x0001, /* Clear to Send. (INPUT) Other end of connection is ready to accept data */ RTS = 0x0002, /* Request to Send. (OUTPUT) This end is ready to send data, and requests if the other */ /* end is ready to accept it */ DSR = 0x0004, /* Data Set ready. (INPUT) Other end of connection has data */ DTR = 0x0008, /* Data terminal Ready. (OUTPUT) TX contains new data. */ RX = 0x0010, /* Receive data. (INPUT) */ TX = 0x0020 /* TX = Transmit data. (OUTPUT) */ }; // construction/destruction device_serial_interface(const machine_config &mconfig, device_t &device); virtual ~device_serial_interface(); DECLARE_WRITE_LINE_MEMBER(rx_w); DECLARE_WRITE_LINE_MEMBER(tx_clock_w); DECLARE_WRITE_LINE_MEMBER(rx_clock_w); DECLARE_WRITE_LINE_MEMBER(clock_w); protected: void set_data_frame(int start_bit_count, int data_bit_count, parity_t parity, stop_bits_t stop_bits); void receive_register_reset(); void receive_register_update_bit(int bit); void receive_register_extract(); void set_rcv_rate(const attotime &rate); void set_tra_rate(const attotime &rate); void set_rcv_rate(u32 clock, int div) { set_rcv_rate((clock && div) ? (attotime::from_hz(clock) * div) : attotime::never); } void set_tra_rate(u32 clock, int div) { set_tra_rate((clock && div) ? (attotime::from_hz(clock) * div) : attotime::never); } void set_rcv_rate(int baud) { set_rcv_rate(baud ? attotime::from_hz(baud) : attotime::never); } void set_tra_rate(int baud) { set_tra_rate(baud ? attotime::from_hz(baud) : attotime::never); } void set_rate(const attotime &rate) { set_rcv_rate(rate); set_tra_rate(rate); } void set_rate(u32 clock, int div) { set_rcv_rate(clock, div); set_tra_rate(clock, div); } void set_rate(int baud) { set_rcv_rate(baud); set_tra_rate(baud); } void transmit_register_reset(); void transmit_register_add_bit(int bit); void transmit_register_setup(u8 data_byte); u8 transmit_register_get_data_bit(); u8 serial_helper_get_parity(u8 data) { return m_serial_parity_table[data]; } bool is_receive_register_full() const { return m_rcv_flags & RECEIVE_REGISTER_FULL; } bool is_transmit_register_empty() const { return m_tra_flags & TRANSMIT_REGISTER_EMPTY; } bool is_receive_register_synchronized() const { return m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED; } bool is_receive_register_shifting() const { return m_rcv_bit_count_received > 0; } bool is_receive_framing_error() const { return m_rcv_framing_error; } bool is_receive_parity_error() const { return m_rcv_parity_error; } u8 get_received_char() const { return m_rcv_byte_received; } virtual void tra_callback() { } virtual void rcv_callback() { receive_register_update_bit(m_rcv_line); } virtual void tra_complete() { } virtual void rcv_complete() { } // interface-level overrides virtual void interface_pre_start() override; virtual void interface_post_start() override; bool m_start_bit_hack_for_external_clocks; const char *parity_tostring(parity_t stop_bits); const char *stop_bits_tostring(stop_bits_t stop_bits); private: TIMER_CALLBACK_MEMBER(rcv_clock) { rx_clock_w(!m_rcv_clock_state); } TIMER_CALLBACK_MEMBER(tra_clock) { tx_clock_w(!m_tra_clock_state); } u8 m_serial_parity_table[256]; // Data frame // number of start bits int m_df_start_bit_count; // length of word in bits u8 m_df_word_length; // parity state u8 m_df_parity; // number of stop bits u8 m_df_stop_bit_count; // Receive register /* data */ u16 m_rcv_register_data; /* flags */ u8 m_rcv_flags; /* bit count received */ u8 m_rcv_bit_count_received; /* length of data to receive - includes data bits, parity bit and stop bit */ u8 m_rcv_bit_count; /* the byte of data received */ u8 m_rcv_byte_received; bool m_rcv_framing_error; bool m_rcv_parity_error; // Transmit register /* data */ u16 m_tra_register_data; /* flags */ u8 m_tra_flags; /* number of bits transmitted */ u8 m_tra_bit_count_transmitted; /* length of data to send */ u8 m_tra_bit_count; emu_timer *m_rcv_clock; emu_timer *m_tra_clock; attotime m_rcv_rate; attotime m_tra_rate; u8 m_rcv_line; int m_tra_clock_state, m_rcv_clock_state; void tra_edge(); void rcv_edge(); }; template class device_buffered_serial_interface : public device_serial_interface { protected: using device_serial_interface::device_serial_interface; void interface_post_start() override { device_serial_interface::interface_post_start(); device().save_item(NAME(m_fifo)); device().save_item(NAME(m_head)); device().save_item(NAME(m_tail)); device().save_item(NAME(m_empty)); } virtual void tra_complete() override { assert(!m_empty || (m_head == m_tail)); assert(m_head < ARRAY_LENGTH(m_fifo)); assert(m_tail < ARRAY_LENGTH(m_fifo)); if (!m_empty) { transmit_register_setup(m_fifo[m_head]); m_head = (m_head + 1U) % FIFO_LENGTH; m_empty = (m_head == m_tail) ? 1U : 0U; } } virtual void rcv_complete() override { receive_register_extract(); received_byte(get_received_char()); } void clear_fifo() { m_head = m_tail = 0U; m_empty = 1U; } void transmit_byte(u8 byte) { assert(!m_empty || (m_head == m_tail)); assert(m_head < ARRAY_LENGTH(m_fifo)); assert(m_tail < ARRAY_LENGTH(m_fifo)); if (m_empty && is_transmit_register_empty()) { transmit_register_setup(byte); } else if (m_empty || (m_head != m_tail)) { m_fifo[m_tail] = byte; m_tail = (m_tail + 1U) % FIFO_LENGTH; m_empty = 0U; } else { device().logerror("FIFO overrun (byte = 0x%02x)", byte); } } bool fifo_empty() const { return m_empty; } bool fifo_full() const { return !m_empty && (m_head == m_tail); } private: virtual void received_byte(u8 byte) = 0; u8 m_fifo[FIFO_LENGTH]; u32 m_head = 0U, m_tail = 0U; u8 m_empty = 1U; }; #endif // MAME_EMU_DISERIAL_H