// license:BSD-3-Clause // copyright-holders:Carl, Miodrag Milanovic, Vas Crabb #pragma once #ifndef __EMU_H__ #error Dont include this file directly; include emu.h instead. #endif #ifndef MAME_EMU_DISERIAL_H #define MAME_EMU_DISERIAL_H // 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 transfered */ 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(uint32_t clock, int div) { set_rcv_rate((clock && div) ? (attotime::from_hz(clock) * div) : attotime::never); } void set_tra_rate(uint32_t 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(uint32_t 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(uint8_t data_byte); uint8_t transmit_register_get_data_bit(); uint8_t serial_helper_get_parity(uint8_t data) { return m_serial_parity_table[data]; } bool is_receive_register_full(); bool is_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; } uint8_t 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; // Must be called from device_timer in the underlying device virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); 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); void register_save_state(save_manager &save, device_t *device); private: enum { TRA_TIMER_ID = 10000, RCV_TIMER_ID }; uint8_t m_serial_parity_table[256]; // Data frame // number of start bits int m_df_start_bit_count; // length of word in bits uint8_t m_df_word_length; // parity state uint8_t m_df_parity; // number of stop bits uint8_t m_df_stop_bit_count; // Receive register /* data */ uint16_t m_rcv_register_data; /* flags */ uint8_t m_rcv_flags; /* bit count received */ uint8_t m_rcv_bit_count_received; /* length of data to receive - includes data bits, parity bit and stop bit */ uint8_t m_rcv_bit_count; /* the byte of data received */ uint8_t m_rcv_byte_received; bool m_rcv_framing_error; bool m_rcv_parity_error; // Transmit register /* data */ uint16_t m_tra_register_data; /* flags */ uint8_t m_tra_flags; /* number of bits transmitted */ uint8_t m_tra_bit_count_transmitted; /* length of data to send */ uint8_t m_tra_bit_count; emu_timer *m_rcv_clock; emu_timer *m_tra_clock; attotime m_rcv_rate; attotime m_tra_rate; uint8_t 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; 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(uint8_t 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_full() const { return !m_empty && (m_head == m_tail); } void register_save_state(save_manager &save, device_t *device) { device_serial_interface::register_save_state(save, device); char const *const module(device->name()); char const *const tag(device->tag()); save.save_item(device, module, tag, 0, NAME(m_fifo)); save.save_item(device, module, tag, 0, NAME(m_head)); save.save_item(device, module, tag, 0, NAME(m_tail)); save.save_item(device, module, tag, 0, NAME(m_empty)); } private: virtual void received_byte(uint8_t byte) = 0; uint8_t m_fifo[FIFO_LENGTH]; uint32_t m_head = 0U, m_tail = 0U; uint8_t m_empty = 1U; }; #endif // MAME_EMU_DISERIAL_H