diff --git a/.gitattributes b/.gitattributes index f451f79a5a1..1fcaad0707a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -661,6 +661,8 @@ src/emu/dinvram.c svneol=native#text/plain src/emu/dinvram.h svneol=native#text/plain src/emu/dirtc.c svneol=native#text/plain src/emu/dirtc.h svneol=native#text/plain +src/emu/diserial.c svneol=native#text/plain +src/emu/diserial.h svneol=native#text/plain src/emu/dislot.c svneol=native#text/plain src/emu/dislot.h svneol=native#text/plain src/emu/disound.c svneol=native#text/plain diff --git a/src/emu/diserial.c b/src/emu/diserial.c new file mode 100644 index 00000000000..d24be920b43 --- /dev/null +++ b/src/emu/diserial.c @@ -0,0 +1,387 @@ +/*************************************************************************** + + 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-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_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_callback(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_callback(other_connection); + other_connection->set_callback(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 - 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(); +} diff --git a/src/emu/diserial.h b/src/emu/diserial.h new file mode 100644 index 00000000000..fa8b6fc932d --- /dev/null +++ b/src/emu/diserial.h @@ -0,0 +1,174 @@ +#pragma once + +#ifndef __EMU_H__ +#error Dont include this file directly; include emu.h instead. +#endif + +#ifndef __DISERIAL_H__ +#define __DISERIAL_H__ + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** +/* parity selections */ +/* if all the bits are added in a byte, if the result is: + even -> parity is even + odd -> parity is odd +*/ +enum +{ + SERIAL_PARITY_NONE, /* no parity. a parity bit will not be in the transmitted/received data */ + SERIAL_PARITY_ODD, /* odd parity */ + SERIAL_PARITY_EVEN /* even parity */ +}; + +/* + CTS = Clear to Send. (INPUT) + Other end of connection is ready to accept data + + + NOTE: + + This output is active low on serial chips (e.g. 0 is CTS is set), + but here it is active high! +*/ +#define SERIAL_STATE_CTS 0x0001 + +/* + RTS = Request to Send. (OUTPUT) + This end is ready to send data, and requests if the other + end is ready to accept it + + NOTE: + + This output is active low on serial chips (e.g. 0 is RTS is set), + but here it is active high! +*/ +#define SERIAL_STATE_RTS 0x0002 + +/* + DSR = Data Set ready. (INPUT) + Other end of connection has data + + + NOTE: + + This output is active low on serial chips (e.g. 0 is DSR is set), + but here it is active high! +*/ +#define SERIAL_STATE_DSR 0x0004 + +/* + DTR = Data terminal Ready. (OUTPUT) + TX contains new data. + + NOTE: + + This output is active low on serial chips (e.g. 0 is DTR is set), + but here it is active high! +*/ +#define SERIAL_STATE_DTR 0x0008 +/* RX = Recieve data. (INPUT) */ +#define SERIAL_STATE_RX_DATA 0x00010 +/* TX = Transmit data. (OUTPUT) */ +#define SERIAL_STATE_TX_DATA 0x00020 + + +// ======================> device_serial_interface +class device_serial_interface : public device_interface +{ +public: + // construction/destruction + device_serial_interface(const machine_config &mconfig, device_t &device); + virtual ~device_serial_interface(); + + virtual void input_callback(UINT8 state) = 0; + + void set_data_frame(int num_data_bits, int stop_bit_count, int parity_code); + + void receive_register_reset(); + void receive_register_update_bit(int bit); + void receive_register_extract(); + + + void transmit_register_reset(); + void transmit_register_add_bit(int bit); + void transmit_register_setup(UINT8 data_byte); + UINT8 transmit_register_get_data_bit(); + void transmit_register_send_bit(); + + UINT8 serial_helper_get_parity(UINT8 data) { return m_serial_parity_table[data]; } + + UINT8 get_in_data_bit() { return ((m_input_state & SERIAL_STATE_RX_DATA)>>4) & 1; } + void set_out_data_bit(UINT8 data) { m_connection_state &=~SERIAL_STATE_TX_DATA; m_connection_state |=(data<<5); } + + void serial_connection_out(); + + bool is_receive_register_full(); + bool is_transmit_register_empty(); + + UINT8 get_received_char() { return m_rcv_byte_received; } + + void set_callback(device_serial_interface *other_connection); + + void connect(device_serial_interface *other_connection); +protected: + UINT8 m_input_state; + UINT8 m_connection_state; +private: + UINT8 m_serial_parity_table[256]; + + // Data frame + // length of word in bits + UINT8 m_df_word_length; + // parity state + UINT8 m_df_parity; + // number of stop bits + UINT8 m_df_stop_bit_count; + + // Receive register + /* data */ + UINT16 m_rcv_register_data; + /* flags */ + UINT8 m_rcv_flags; + /* bit count received */ + UINT8 m_rcv_bit_count_received; + /* length of data to receive - includes data bits, parity bit and stop bit */ + UINT8 m_rcv_bit_count; + /* the byte of data received */ + UINT8 m_rcv_byte_received; + + // Transmit register + /* data */ + UINT16 m_tra_register_data; + /* flags */ + UINT8 m_tra_flags; + /* number of bits transmitted */ + UINT8 m_tra_bit_count_transmitted; + /* length of data to send */ + UINT8 m_tra_bit_count; + + device_serial_interface *m_other_connection; +}; + + +class serial_source_device : public device_t, + public device_serial_interface +{ +public: + // construction/destruction + serial_source_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual void input_callback(UINT8 state); + void send_bit(UINT8 data); +protected: + // device-level overrides + virtual void device_start(); +}; + +extern const device_type SERIAL_SOURCE; + +#define MCFG_SERIAL_SOURCE_ADD(_tag) \ + MCFG_DEVICE_ADD((_tag), SERIAL_SOURCE, 0) + +#endif /* __DISERIAL_H__ */ diff --git a/src/emu/emu.h b/src/emu/emu.h index 80c36b36026..6f3222c4910 100644 --- a/src/emu/emu.h +++ b/src/emu/emu.h @@ -92,6 +92,7 @@ typedef device_t * (*machine_config_constructor)(machine_config &config, device_ #include "diexec.h" #include "opresolv.h" #include "diimage.h" +#include "diserial.h" #include "dislot.h" #include "disound.h" #include "dinvram.h" diff --git a/src/emu/emu.mak b/src/emu/emu.mak index c61202b4e26..37ec4d7858f 100644 --- a/src/emu/emu.mak +++ b/src/emu/emu.mak @@ -66,6 +66,7 @@ EMUOBJS = \ $(EMUOBJ)/dimemory.o \ $(EMUOBJ)/dinvram.o \ $(EMUOBJ)/dirtc.o \ + $(EMUOBJ)/diserial.o \ $(EMUOBJ)/dislot.o \ $(EMUOBJ)/disound.o \ $(EMUOBJ)/distate.o \