// license:BSD-3-Clause // copyright-holders:Carl, Miodrag Milanovic /*************************************************************************** Serial device interface ***************************************************************************/ #include "emu.h" #include "diserial.h" device_serial_interface::device_serial_interface(const machine_config &mconfig, device_t &device) : device_interface(device, "serial"), m_start_bit_hack_for_external_clocks(false), m_df_start_bit_count(0), m_df_word_length(0), m_df_parity(PARITY_NONE), m_df_stop_bit_count(STOP_BITS_0), m_rcv_register_data(0x8000), m_rcv_flags(0), m_rcv_bit_count_received(0), m_rcv_bit_count(0), m_rcv_byte_received(0), m_rcv_framing_error(false), m_rcv_parity_error(false), m_tra_register_data(0), m_tra_flags(TRANSMIT_REGISTER_EMPTY), m_tra_bit_count_transmitted(0), m_tra_bit_count(0), m_rcv_clock(nullptr), m_tra_clock(nullptr), m_rcv_rate(attotime::never), m_tra_rate(attotime::never), m_rcv_line(0), m_tra_clock_state(false), m_rcv_clock_state(false) { /* if sum of all bits in the byte is even, then the data has even parity, otherwise it has odd parity */ for (int i=0; i<256; i++) { int sum = 0; int data = i; for (int b=0; b<8; b++) { sum+=data & 0x01; data = data>>1; } m_serial_parity_table[i] = sum & 0x01; } } device_serial_interface::~device_serial_interface() { } void device_serial_interface::interface_pre_start() { if (!m_rcv_clock) m_rcv_clock = device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(device_serial_interface::rcv_clock), this)); if (!m_tra_clock) m_tra_clock = device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(device_serial_interface::tra_clock), this)); m_rcv_clock_state = false; m_tra_clock_state = false; } void device_serial_interface::interface_post_start() { device().save_item(NAME(m_df_start_bit_count)); device().save_item(NAME(m_df_word_length)); device().save_item(NAME(m_df_parity)); device().save_item(NAME(m_df_stop_bit_count)); device().save_item(NAME(m_rcv_register_data)); device().save_item(NAME(m_rcv_flags)); device().save_item(NAME(m_rcv_bit_count_received)); device().save_item(NAME(m_rcv_bit_count)); device().save_item(NAME(m_rcv_byte_received)); device().save_item(NAME(m_rcv_framing_error)); device().save_item(NAME(m_rcv_parity_error)); device().save_item(NAME(m_tra_register_data)); device().save_item(NAME(m_tra_flags)); device().save_item(NAME(m_tra_bit_count_transmitted)); device().save_item(NAME(m_tra_bit_count)); device().save_item(NAME(m_rcv_rate)); device().save_item(NAME(m_tra_rate)); device().save_item(NAME(m_rcv_line)); device().save_item(NAME(m_tra_clock_state)); device().save_item(NAME(m_rcv_clock_state)); } void device_serial_interface::set_rcv_rate(const attotime &rate) { m_rcv_rate = rate/2; receive_register_reset(); m_rcv_clock->adjust(attotime::never); } void device_serial_interface::set_tra_rate(const attotime &rate) { m_tra_rate = rate/2; transmit_register_reset(); m_tra_clock->adjust(attotime::never); } void device_serial_interface::tra_edge() { if (!is_transmit_register_empty()) { tra_callback(); if (is_transmit_register_empty()) tra_complete(); } if (is_transmit_register_empty() && !m_tra_rate.is_never()) { m_tra_clock->adjust(attotime::never); } } void device_serial_interface::rcv_edge() { rcv_callback(); if(is_receive_register_full()) { m_rcv_clock->adjust(attotime::never); rcv_complete(); } } WRITE_LINE_MEMBER(device_serial_interface::tx_clock_w) { if(state != m_tra_clock_state) { m_tra_clock_state = state; if(m_tra_clock_state) tra_edge(); } } WRITE_LINE_MEMBER(device_serial_interface::rx_clock_w) { if(state != m_rcv_clock_state) { m_rcv_clock_state = state; if(!m_rcv_clock_state) rcv_edge(); } } WRITE_LINE_MEMBER(device_serial_interface::clock_w) { tx_clock_w(state); rx_clock_w(state); } void device_serial_interface::set_data_frame(int start_bit_count, int data_bit_count, parity_t parity, stop_bits_t stop_bits) { //device().logerror("Start bits: %d; Data bits: %d; Parity: %s; Stop bits: %s\n", start_bit_count, data_bit_count, parity_tostring(parity), stop_bits_tostring(stop_bits)); m_df_word_length = data_bit_count; switch (stop_bits) { case STOP_BITS_0: default: m_df_stop_bit_count = 0; break; case STOP_BITS_1: m_df_stop_bit_count = 1; break; case STOP_BITS_1_5: m_df_stop_bit_count = 2; // TODO: support 1.5 stop bits break; case STOP_BITS_2: m_df_stop_bit_count = 2; break; } m_df_parity = parity; m_df_start_bit_count = start_bit_count; m_rcv_bit_count = m_df_word_length + m_df_stop_bit_count; if (m_df_parity != PARITY_NONE) { m_rcv_bit_count++; } } void device_serial_interface::receive_register_reset() { m_rcv_bit_count_received = 0; m_rcv_flags &=~RECEIVE_REGISTER_FULL; if (m_df_start_bit_count == 0) { m_rcv_flags |= RECEIVE_REGISTER_SYNCHRONISED; m_rcv_flags &=~RECEIVE_REGISTER_WAITING_FOR_START_BIT; } else { m_rcv_flags &=~RECEIVE_REGISTER_SYNCHRONISED; m_rcv_flags |= RECEIVE_REGISTER_WAITING_FOR_START_BIT; } } WRITE_LINE_MEMBER(device_serial_interface::rx_w) { m_rcv_line = state; if(m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED) return; receive_register_update_bit(state); if(m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED) { //device().logerror("Receiver is synchronized\n"); if(m_rcv_clock && !(m_rcv_rate.is_never())) // make start delay just a bit longer to make sure we are called after the sender m_rcv_clock->adjust(((m_rcv_rate*3)/2), 0, m_rcv_rate); else if(m_start_bit_hack_for_external_clocks) m_rcv_bit_count_received--; } return; } /* this is generic code to be used in serial chip implementations */ /* the majority of serial chips work in the same way and this code will work */ /* for them */ /* receive a bit */ void device_serial_interface::receive_register_update_bit(int bit) { int previous_bit; //LOG(("receive register receive bit: %1x\n",bit)); previous_bit = (m_rcv_register_data & 0x8000) ? 1 : 0; /* shift previous bit 7 out */ m_rcv_register_data = m_rcv_register_data>>1; /* shift new bit in */ m_rcv_register_data = (m_rcv_register_data & 0x7fff) | (bit<<15); /* update bit count received */ /* asynchronous mode */ if (m_rcv_flags & RECEIVE_REGISTER_WAITING_FOR_START_BIT) { /* the previous bit is stored in uart.receive char bit 0 */ /* has the bit state changed? */ if (((previous_bit ^ bit) & 0x01)!=0) { /* yes */ if (bit==0) { //logerror("receive register saw start bit\n"); /* seen start bit! */ /* not waiting for start bit now! */ m_rcv_flags &=~RECEIVE_REGISTER_WAITING_FOR_START_BIT; m_rcv_flags |=RECEIVE_REGISTER_SYNCHRONISED; /* reset bit count received */ m_rcv_bit_count_received = 0; m_rcv_framing_error = false; m_rcv_parity_error = false; } } } else if (m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED) { //device().logerror("Received bit %d\n", m_rcv_bit_count_received); m_rcv_bit_count_received++; if (!bit && (m_rcv_bit_count_received > (m_rcv_bit_count - m_df_stop_bit_count))) { m_rcv_framing_error = true; } /* received all bits? */ if (m_rcv_bit_count_received==m_rcv_bit_count) { m_rcv_bit_count_received = 0; m_rcv_flags &=~RECEIVE_REGISTER_SYNCHRONISED; m_rcv_flags |= RECEIVE_REGISTER_WAITING_FOR_START_BIT; //device().logerror("Receive register full\n"); m_rcv_flags |= RECEIVE_REGISTER_FULL; } } } void device_serial_interface::receive_register_extract() { u8 data; receive_register_reset(); /* strip off stop bits and parity */ assert(m_rcv_bit_count >0 && m_rcv_bit_count <= 16); data = m_rcv_register_data>>(16-m_rcv_bit_count); /* mask off other bits so data byte has 0's in unused bits */ data &= ~(0xff<> (16 - m_rcv_bit_count + m_df_word_length)) & 0x01; /* parity enable? */ switch (m_df_parity) { case PARITY_ODD: if (parity_received == serial_helper_get_parity(data)) m_rcv_parity_error = true; break; case PARITY_EVEN: if (parity_received != serial_helper_get_parity(data)) m_rcv_parity_error = true; break; case PARITY_MARK: if (!parity_received) m_rcv_parity_error = true; break; case PARITY_SPACE: if (parity_received) m_rcv_parity_error = true; break; } } /***** TRANSMIT REGISTER *****/ void device_serial_interface::transmit_register_reset() { m_tra_flags |=TRANSMIT_REGISTER_EMPTY; } /* used to construct data in stream format */ void device_serial_interface::transmit_register_add_bit(int bit) { /* combine bit */ m_tra_register_data = m_tra_register_data<<1; m_tra_register_data &=~1; m_tra_register_data|=(bit & 0x01); m_tra_bit_count++; } /* generate data in stream format ready for transfer */ void device_serial_interface::transmit_register_setup(u8 data_byte) { int i; u8 transmit_data; if(m_tra_clock && !m_tra_rate.is_never()) m_tra_clock->adjust(m_tra_rate, 0, m_tra_rate); m_tra_bit_count_transmitted = 0; m_tra_bit_count = 0; m_tra_flags &=~TRANSMIT_REGISTER_EMPTY; /* start bit */ for (i=0; i>1; } /* parity */ if (m_df_parity!=PARITY_NONE) { /* odd or even parity */ u8 parity = 0; switch (m_df_parity) { case PARITY_ODD: /* get parity */ /* if parity = 0, data has even parity - i.e. there is an even number of one bits in the data */ /* if parity = 1, data has odd parity - i.e. there is an odd number of one bits in the data */ parity = serial_helper_get_parity(data_byte) ^ 1; break; case PARITY_EVEN: parity = serial_helper_get_parity(data_byte); break; case PARITY_MARK: parity = 1; break; case PARITY_SPACE: parity = 0; break; } transmit_register_add_bit(parity); } /* stop bit(s) + 1 extra bit as delay between bytes, needed to get 1 stop bit to work. */ if (m_df_stop_bit_count) // no stop bits for synchronous for (i=0; i<=m_df_stop_bit_count; i++) // ToDo - see if the hack on this line is still needed (was added 2016-04-10) transmit_register_add_bit(1); } /* get a bit from the transmit register */ u8 device_serial_interface::transmit_register_get_data_bit() { int bit; bit = (m_tra_register_data>>(m_tra_bit_count-1-m_tra_bit_count_transmitted))&1; m_tra_bit_count_transmitted++; //device().logerror("%d bits transmitted\n", m_tra_bit_count_transmitted); /* have all bits of this stream formatted byte been sent? */ if (m_tra_bit_count_transmitted==m_tra_bit_count) { /* yes - generate a new byte to send */ m_tra_flags |= TRANSMIT_REGISTER_EMPTY; } return bit; } const char *device_serial_interface::parity_tostring(parity_t parity) { switch (parity) { case PARITY_NONE: return "NONE"; case PARITY_ODD: return "ODD"; case PARITY_EVEN: return "EVEN"; case PARITY_MARK: return "MARK"; case PARITY_SPACE: return "SPACE"; default: return "UNKNOWN"; } } const char *device_serial_interface::stop_bits_tostring(stop_bits_t stop_bits) { switch (stop_bits) { case STOP_BITS_0: return "0"; case STOP_BITS_1: return "1"; case STOP_BITS_1_5: return "1.5"; case STOP_BITS_2: return "2"; default: return "UNKNOWN"; } }