/*********************************************************************

    6551.h

    MOS Technology 6551 Asynchronous Communications Interface Adapter

    A1 A0  Write                    Read
     0  0  Transmit Data Register   Receiver Data Register
     0  1  Programmed Reset         Status Register
     1  0              Command Register
     1  1              Control Register

*********************************************************************/

#include "emu.h"
#include "6551acia.h"


//**************************************************************************
//  DEVICE DEFINITIONS
//**************************************************************************

const device_type ACIA6551 = &device_creator<acia6551_device>;

//-------------------------------------------------
//  acia6551_device - constructor
//-------------------------------------------------

acia6551_device::acia6551_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
    : device_t(mconfig, ACIA6551, "MOS Technology 6551 ACIA", tag, owner, clock),
	  device_serial_interface(mconfig, *this)
{
}

/***************************************************************************
    IMPLEMENTATION
***************************************************************************/

/*-------------------------------------------------
    input_callback - called when other side
    has updated state
-------------------------------------------------*/
void acia6551_device::input_callback(UINT8 state)
{
	m_input_state = state;
}

/*-------------------------------------------------
    timer_callback
-------------------------------------------------*/
void acia6551_device::timer_callback()
{
	/* get bit received from other side and update receive register */
	receive_register_update_bit(get_in_data_bit());

	if (is_receive_register_full())
	{
		receive_register_extract();
		receive_character(get_received_char());
	}

	/* transmit register full? */
	if ((m_status_register & (1<<4))==0)
	{
		/* if transmit reg is empty */
		if (is_transmit_register_empty())
		{
			/* set it up */
			transmit_register_setup(m_transmit_data_register);
			/* acia transmit reg now empty */
			m_status_register |=(1<<4);
			/* and refresh ints */
			refresh_ints();
		}
	}

	/* if transmit is not empty... transmit data */
	if (!is_transmit_register_empty())
	{
	//  logerror("UART6551\n");
		transmit_register_send_bit();
	}
}

static TIMER_CALLBACK(acia_6551_timer_callback)
{
	reinterpret_cast<acia6551_device *>(ptr)->timer_callback();
}

//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void acia6551_device::device_start()
{
	/* transmit data reg is empty */
	m_status_register = (1<<4);
	m_timer = machine().scheduler().timer_alloc(FUNC(acia_6551_timer_callback), (void *) this);

	transmit_register_reset();
	receive_register_reset();
}



/*-------------------------------------------------
    refresh_ints - update interrupt output
-------------------------------------------------*/

void acia6551_device::refresh_ints()
{
	int interrupt_state;

	interrupt_state = 0;

	/* receive interrupts */

	/* receive data register full? */
	if (m_status_register & (1<<3))
	{
		/* receiver interrupt enable? */
		if ((m_command_register & (1<<1))==0)
		{
			/* trigger a interrupt */
			interrupt_state = 1;
		}
	}

	/* set state of irq bit in status register */
	m_status_register &= ~(1<<7);

	if (interrupt_state)
	{
		m_status_register |=(1<<7);
	}

	//if (m_irq_callback != NULL)
        //(*m_irq_callback)(interrupt_state);
}



/*-------------------------------------------------
    receive_character
-------------------------------------------------*/

void acia6551_device::receive_character(UINT8 ch)
{
	/* receive register full? */
	if (m_status_register & (1<<3))
	{
		/* set overrun error */
		m_status_register |= (1<<2);
		return;
	}

	/* set new byte */
	m_receive_data_register = ch;
	/* receive register is full */
	m_status_register |= (1<<3);
	/* update ints */
	refresh_ints();
}



/*-------------------------------------------------
    read
-------------------------------------------------*/

READ8_MEMBER(acia6551_device::read)
{
	unsigned char data;

	data = 0x0ff;
	switch (offset & 0x03)
	{
		case 0:
		{
			/*  clear parity error, framing error and overrun error */
			/* when read of data reigster is done */
			m_status_register &=~((1<<0) | (1<<1) | (1<<2));
			/* clear receive data register full flag */
			m_status_register &=~(1<<3);
			/* return data */
			data = m_receive_data_register;
		}
		break;

	/*
    Status                    cleared by
    b0  Parity error * (1: error)       self clearing **
    b1  Framing error * (1: error)      self clearing **
    b2  Overrun * (1: error)            self clearing **
    b3  Receive Data Register Full (1: full)  Read Receive Data Register
    b4  Transmit Data Reg Empty (1: empty) Write Transmit Data Register
    b5  DCD (0: DCD low, 1: DCD high) Not resettable, reflects DCD state
    b6  DSR (0: DSR low, 1: DCD high) Not resettable, reflects DSR state
    b7  IRQ (0: no int., 1: interrupt)  Read Status Register
    */
		case 1:
        {
            data = m_status_register;
			/* clear interrupt */
			m_status_register &= ~(1<<7);
		}
        break;

        case 2:
			data = m_command_register;
			break;

		case 3:
			data = m_control_register;
			break;

		default:
			break;
	}

	logerror("6551 R %04x %02x\n",offset & 0x03,data);

	return data;
}



/*-------------------------------------------------
    update_data_form
-------------------------------------------------*/

void acia6551_device::update_data_form()
{
	int word_length = 8-((m_control_register>>5) & 0x03);
	int stop_bit_count = (m_control_register>>7)+1;
	int parity = 0;
	if (m_command_register & (1<<5))
	{
		parity = SERIAL_PARITY_ODD;
	}
	else
	{
		parity = SERIAL_PARITY_NONE;
	}

	set_data_frame(word_length, stop_bit_count, parity);
}



/*-------------------------------------------------
    write
-------------------------------------------------*/

WRITE8_MEMBER(acia6551_device::write)
{
	logerror("6551 W %04x %02x\n",offset & 0x03, data);

	switch (offset & 0x03)
	{
		case 0:
		{
			/* clear transmit data register empty */
			m_status_register &= ~(1<<4);
			/* store byte */
			m_transmit_data_register = data;

		}
		break;

		case 1:
		{
			/* telstrat writes 0x07f! */
		}
		break;


		/*
        Command Register:

        b0  Data Terminal Ready
            0 : disable receiver and all interrupts (DTR high)
            1 : enable receiver and all interrupts (DTR low)
        b1  Receiver Interrupt Enable
            0 : IRQ interrupt enabled from bit 3 of status register
            1 : IRQ interrupt disabled
        b3,b2   Transmitter Control
                Transmit Interrupt    RTS level    Transmitter
            00     disabled     high        off
            01     enabled      low     on
            10     disabled     low     on
            11     disabled     low    Transmit BRK
        b4  Normal/Echo Mode for Receiver
            0 : normal
            1 : echo (bits 2 and 3 must be 0)
        b5  Parity Enable
            0 : parity disabled, no parity bit generated or received
            1 : parity enabled
        b7,b6   Parity
            00 : odd parity receiver and transmitter
            01 : even parity receiver and transmitter
            10 : mark parity bit transmitted, parity check disabled
            11 : space parity bit transmitted, parity check disabled
        */

		case 2:
		{
			m_command_register = data;

			/* update state of dtr */
			m_connection_state &=~SERIAL_STATE_DTR;
			if (m_command_register & (1<<0))
			{
				m_connection_state |=SERIAL_STATE_DTR;
			}

			/* update state of rts */
			switch ((m_command_register>>2) & 0x03)
			{
				case 0:
				{
					m_connection_state &=~SERIAL_STATE_RTS;
				}
				break;

				case 1:
				case 2:
				case 3:
				{
					m_connection_state |=SERIAL_STATE_RTS;
				}
				break;
			}

			serial_connection_out();
			update_data_form();
		}
		break;

		/*
        Control register:

        b3-b0   baud rate generator:
            0000 : 16x external clock
            0001 : 50 baud
            0010 : 75 25
            0011 : 110 35
            0100 : 134.5
            0101 : 150
            0110 : 300 150
            0111 : 600 300
            1000 : 1200 600
            1001 : 1800 600
            1010 : 2400 600
            1011 : 3600 1200
            1100 : 4800 1200
            1101 : 7200 2400
            1110 : 9600 2400
            1111 : 19,200 9600
        b4  receiver clock source
            0 : external receiver clock
            1 : baud rate generator
        b6,b5   word length
            00 : 8 bits
            01 : 7
            10 : 6
            11 : 5
        b7  stop bits
            0 : 1 stop bit
            1 : 2 stop bits
                (1 stop bit if parity and word length = 8)
                (1 1/2 stop bits if word length = 5 and no parity)
        */
		case 3:
		{
			unsigned char previous_control_register;

            previous_control_register = m_control_register;

            if (((previous_control_register^data) & 0x07)!=0)
			{
				int rate;

                rate = data & 0x07;

				/* baud rate changed? */
				m_timer->reset();

				if (rate==0)
				{
					/* 16x external clock */
					logerror("6551: external clock not supported!\n");
				}
				else
				{
					int baud_rate;

					switch (rate)
					{

						default:
						case 1:
						{
							baud_rate = 50;
						}
						break;

						case 2:
						{
							baud_rate = 75;
						}
						break;

						case 3:
						{
							baud_rate = 110;
						}
						break;

						case 4:
						{
							baud_rate = 135;
						}
						break;

						case 5:
						{
							baud_rate = 150;
						}
						break;

						case 6:
						{
							baud_rate = 300;
						}
						break;

						case 7:
						{
							baud_rate = 600;
						}
						break;

						case 8:
						{
							baud_rate = 1200;
						}
						break;

						case 9:
						{
							baud_rate = 1800;
						}
						break;

						case 10:
						{
							baud_rate = 2400;
						}
						break;

						case 11:
						{
							baud_rate = 3600;
						}
						break;

						case 12:
						{
							baud_rate = 4800;
						}
						break;

						case 13:
						{
							baud_rate = 7200;
						}
						break;

						case 14:
						{
							baud_rate = 9600;
						}
						break;

						case 15:
						{
							baud_rate = 19200;
						}
						break;
					}

					m_timer->adjust(attotime::zero, 0, attotime::from_hz(baud_rate));
				}
			}

			m_control_register = data;
			update_data_form();
		}
		break;

		default:
			break;
	}

}
