#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 */
	SERIAL_PARITY_MARK,		/* one parity */
	SERIAL_PARITY_SPACE		/* zero 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 set_rcv_rate(int baud);
	void set_tra_rate(int baud);

	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_other_connection(device_serial_interface *other_connection);

	void connect(device_serial_interface *other_connection);
	UINT8 check_for_start(UINT8 bit);
protected:
	UINT8 m_input_state;
	UINT8 m_connection_state;
	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();
private:
	void tra_timer(void *ptr, int param);
	void rcv_timer(void *ptr, int param);

	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;

	emu_timer *m_rcv_clock;
	emu_timer *m_tra_clock;
	int m_rcv_baud;
	int m_tra_baud;
	UINT8 m_rcv_line;

	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__ */
