mirror of
https://github.com/holub/mame
synced 2025-06-04 20:06:28 +03:00
388 lines
9.1 KiB
C
388 lines
9.1 KiB
C
/***************************************************************************
|
|
|
|
Serial device interface
|
|
|
|
***************************************************************************/
|
|
|
|
#include "emu.h"
|
|
|
|
|
|
/* 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 */
|
|
#define RECEIVE_REGISTER_WAITING_FOR_START_BIT 0x01
|
|
/* receive is synchronised with data, data bits will be clocked in */
|
|
#define RECEIVE_REGISTER_SYNCHRONISED 0x02
|
|
/* set if receive register has been filled */
|
|
#define RECEIVE_REGISTER_FULL 0x04
|
|
|
|
/* register is empty and ready to be filled with data */
|
|
#define TRANSMIT_REGISTER_EMPTY 0x0001
|
|
|
|
device_serial_interface::device_serial_interface(const machine_config &mconfig, device_t &device)
|
|
: device_interface(device),
|
|
m_other_connection(NULL)
|
|
{
|
|
/* 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::set_data_frame(int num_data_bits, int stop_bit_count, int parity_code)
|
|
{
|
|
m_df_word_length = num_data_bits;
|
|
m_df_stop_bit_count = stop_bit_count;
|
|
m_df_parity = parity_code;
|
|
|
|
m_rcv_bit_count = m_df_word_length + m_df_stop_bit_count;
|
|
|
|
if (m_df_parity != SERIAL_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;
|
|
m_rcv_flags &=~RECEIVE_REGISTER_SYNCHRONISED;
|
|
m_rcv_flags |= RECEIVE_REGISTER_WAITING_FOR_START_BIT;
|
|
}
|
|
|
|
/* 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 & 1;
|
|
|
|
/* 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 & 0xfffe) | bit;
|
|
/* update bit count received */
|
|
m_rcv_bit_count_received++;
|
|
|
|
/* asyncrhonouse 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;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED)
|
|
{
|
|
/* 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;
|
|
//logerror("receive register full\n");
|
|
m_rcv_flags |= RECEIVE_REGISTER_FULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void device_serial_interface::receive_register_extract()
|
|
{
|
|
unsigned long data_shift;
|
|
UINT8 data;
|
|
|
|
receive_register_reset();
|
|
|
|
data_shift = 0;
|
|
|
|
/* if parity is even or odd, there should be a parity bit in the stream! */
|
|
if (m_df_parity!=SERIAL_PARITY_NONE)
|
|
{
|
|
data_shift++;
|
|
}
|
|
|
|
data_shift+=m_df_stop_bit_count;
|
|
|
|
/* strip off stop bits and parity */
|
|
data = m_rcv_register_data>>data_shift;
|
|
|
|
/* mask off other bits so data byte has 0's in unused bits */
|
|
data = data & (0x0ff
|
|
>>
|
|
(8-(m_df_word_length)));
|
|
|
|
m_rcv_byte_received = data;
|
|
|
|
/* parity enable? */
|
|
switch (m_df_parity)
|
|
{
|
|
case SERIAL_PARITY_NONE:
|
|
break;
|
|
|
|
/* check parity */
|
|
case SERIAL_PARITY_ODD:
|
|
case SERIAL_PARITY_EVEN:
|
|
{
|
|
//unsigned char computed_parity;
|
|
//unsigned char parity_received;
|
|
|
|
/* get state of parity bit received */
|
|
//parity_received = (m_rcv_register_data>>m_df_stop_bit_count) & 0x01;
|
|
|
|
/* compute parity for received bits */
|
|
//computed_parity = serial_helper_get_parity(data);
|
|
|
|
if (m_df_parity == SERIAL_PARITY_ODD)
|
|
{
|
|
/* odd parity */
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
/* even parity */
|
|
|
|
|
|
}
|
|
|
|
}
|
|
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(UINT8 data_byte)
|
|
{
|
|
int i;
|
|
unsigned char transmit_data;
|
|
|
|
|
|
m_tra_bit_count_transmitted = 0;
|
|
m_tra_bit_count = 0;
|
|
m_tra_flags &=~TRANSMIT_REGISTER_EMPTY;
|
|
|
|
/* start bit */
|
|
transmit_register_add_bit(0);
|
|
|
|
/* data bits */
|
|
transmit_data = data_byte;
|
|
for (i=0; i<m_df_word_length; i++)
|
|
{
|
|
int databit;
|
|
|
|
/* get bit from data */
|
|
databit = (transmit_data>>(m_df_word_length-1)) & 0x01;
|
|
/* add bit to formatted byte */
|
|
transmit_register_add_bit(databit);
|
|
transmit_data = transmit_data<<1;
|
|
}
|
|
|
|
/* parity */
|
|
if (m_df_parity!=SERIAL_PARITY_NONE)
|
|
{
|
|
/* odd or even parity */
|
|
unsigned char parity;
|
|
|
|
/* 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);
|
|
|
|
transmit_register_add_bit(parity);
|
|
}
|
|
|
|
/* stop bit(s) */
|
|
for (i=0; i<m_df_stop_bit_count; i++)
|
|
{
|
|
transmit_register_add_bit(1);
|
|
}
|
|
}
|
|
|
|
|
|
/* get a bit from the transmit register */
|
|
UINT8 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)) & 0x01;
|
|
|
|
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;
|
|
}
|
|
|
|
void device_serial_interface::transmit_register_send_bit()
|
|
{
|
|
int data;
|
|
|
|
data = transmit_register_get_data_bit();
|
|
|
|
/* set tx data bit */
|
|
m_connection_state &=~SERIAL_STATE_TX_DATA;
|
|
m_connection_state|=(data<<5);
|
|
|
|
/* state out through connection */
|
|
serial_connection_out();
|
|
}
|
|
|
|
|
|
|
|
/* this converts state at this end to a state the other end can accept */
|
|
/* e.g. CTS at this end becomes RTS at other end.
|
|
RTS at this end becomes CTS at other end.
|
|
TX at this end becomes RX at other end.
|
|
RX at this end becomes TX at other end.
|
|
etc
|
|
|
|
The same thing is done inside the serial null-terminal lead */
|
|
|
|
static UINT8 serial_connection_spin_bits(UINT8 input_status)
|
|
{
|
|
return
|
|
/* cts -> rts */
|
|
(((input_status & 0x01)<<1) |
|
|
/* rts -> cts */
|
|
((input_status>>1) & 0x01) |
|
|
/* dsr -> dtr */
|
|
(((input_status>>2) & 0x01)<<3) |
|
|
/* dtr -> dsr */
|
|
(((input_status>>3) & 0x01)<<2) |
|
|
/* rx -> tx */
|
|
(((input_status>>4) & 0x01)<<5) |
|
|
/* tx -> rx */
|
|
(((input_status>>5) & 0x01)<<4));
|
|
}
|
|
|
|
void device_serial_interface::serial_connection_out()
|
|
{
|
|
|
|
if (m_other_connection!=NULL)
|
|
{
|
|
UINT8 state_at_other_end = serial_connection_spin_bits(m_connection_state);
|
|
|
|
m_other_connection->input_callback(state_at_other_end);
|
|
}
|
|
}
|
|
|
|
bool device_serial_interface::is_receive_register_full()
|
|
{
|
|
return m_rcv_flags & RECEIVE_REGISTER_FULL;
|
|
}
|
|
|
|
bool device_serial_interface::is_transmit_register_empty()
|
|
{
|
|
return m_tra_flags & TRANSMIT_REGISTER_EMPTY;
|
|
}
|
|
|
|
void device_serial_interface::set_other_connection(device_serial_interface *other_connection)
|
|
{
|
|
m_other_connection = other_connection;
|
|
}
|
|
|
|
/* join two serial connections together */
|
|
void device_serial_interface::connect(device_serial_interface *other_connection)
|
|
{
|
|
/* both connections should have their in connection setup! */
|
|
/* the in connection is the callback they use to update their state based
|
|
on the output from the other side */
|
|
set_other_connection(other_connection);
|
|
other_connection->set_other_connection(this);
|
|
|
|
/* let b know the state of a */
|
|
serial_connection_out();
|
|
/* let a know the state of b */
|
|
other_connection->serial_connection_out();
|
|
}
|
|
|
|
|
|
|
|
const device_type SERIAL_SOURCE = &device_creator<serial_source_device>;
|
|
|
|
//-------------------------------------------------
|
|
// serial_source_device - constructor
|
|
//-------------------------------------------------
|
|
|
|
serial_source_device::serial_source_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
|
: device_t(mconfig, SERIAL_SOURCE, "Serial source", tag, owner, clock),
|
|
device_serial_interface(mconfig, *this)
|
|
{
|
|
}
|
|
|
|
void serial_source_device::device_start()
|
|
{
|
|
}
|
|
|
|
void serial_source_device::input_callback(UINT8 state)
|
|
{
|
|
m_input_state = state;
|
|
}
|
|
|
|
void serial_source_device::send_bit(UINT8 data)
|
|
{
|
|
set_out_data_bit(data);
|
|
serial_connection_out();
|
|
}
|