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

	Motorola 6850 ACIA interface and emulation

	This function is a simple emulation of up to 4 MC6850
	Asynchronous Communications Interface Adapter.

	Todo:
		Handle interrupts.
		Handle state changes.
**********************************************************************/

#include "driver.h"
#include "mc6850.h"

struct acia6850
{
	const struct acia6850_interface *intf;
};

static struct acia6850 acia[ACIA_6850_MAX];

void acia6850_unconfig (void)
{
	memset (&acia, 0, sizeof (acia));
}

void acia6850_config (int which, const struct acia6850_interface *intf)
{
	if (which >= ACIA_6850_MAX) return;
	acia[which].intf = intf;
}

void acia6850_reset (void)
{
}

int acia6850_read (int which, int offset)
{
	struct acia6850 *currptr = acia + which;
	int	val = 0;

	switch (offset)
	{
		case ACIA_6850_CTRL:
			if ((*(*currptr).intf).in_status_func)
							val = (*(*currptr).intf).in_status_func(0);
			break;
		case ACIA_6850_DATA:
			if ((*(*currptr).intf).in_recv_func)
							val = (*(*currptr).intf).in_recv_func(0);
			break;
	}
	return (val);
}

void acia6850_write (int which, int offset, int data)
{
	struct acia6850 *currptr = acia + which;

	switch (offset)
	{
		case ACIA_6850_CTRL:
			if ((*(*currptr).intf).out_status_func)
							(*(*currptr).intf).out_status_func(0, data);
			break;
		case ACIA_6850_DATA:
			if ((*(*currptr).intf).out_tran_func)
							(*(*currptr).intf).out_tran_func(0, data);
			break;
	}
}

 READ8_HANDLER( acia6850_0_r ) { return acia6850_read (0, offset); }
 READ8_HANDLER( acia6850_1_r ) { return acia6850_read (1, offset); }
 READ8_HANDLER( acia6850_2_r ) { return acia6850_read (2, offset); }
 READ8_HANDLER( acia6850_3_r ) { return acia6850_read (3, offset); }

WRITE8_HANDLER( acia6850_0_w ) { acia6850_write (0, offset, data); }
WRITE8_HANDLER( acia6850_1_w ) { acia6850_write (1, offset, data); }
WRITE8_HANDLER( acia6850_2_w ) { acia6850_write (2, offset, data); }
WRITE8_HANDLER( acia6850_3_w ) { acia6850_write (3, offset, data); }






/************************************************************************
	MC6850

	MESS Driver By:

 	Gordon Jefferyes
 	mess_bbc@gjeffery.dircon.co.uk

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


static int TDR=0;  // Transmit Data Register
static int RDR=0;  // Recieve Data Register
static int STR=0;  // Status Register
static int CTR=0;  // Control Register


static int RSR=0;  // Receive Shift Register
static int RSC=0;  // Receive Shift Counter

static int dcd_count=0;

static int IRQ=0;

// Bits 0,1 of control Register

static int divide_by_lookup[4]={1,16,64,0};
static int divide_by;


// Bits 2,3,4 of control Register

static int word_length_lookup[8]={7,7,7,7,8,8,8,8};
static int word_length;

// Parity Settings
// 2 = even
// 1 = odd
// 0 = no parity bit
static int parity_lookup[8]={2,1,2,1,0,0,2,1};
static int parity;

static int stop_bits_lookup[8]={2,2,1,1,2,1,1,1};
static int stop_bits;

// Bits 5,6 of control Register

static int RTS_lookup[4]={0,0,1,0};
static int RTS;

static int break_level_lookup[4]={0,0,0,1};
static int break_level;

// transmitting Interrupt 0 = Disabled  1 = Enabled
static int transmit_interrupt_enabled_lookup[4]={0,1,0,0};
static int transmit_interrupt_enabled;

// Bit 7 of control Register
static int receive_interrupt_enabled;

static int bytelength=10;


/* ******* call back function stuff ******* */


//Local copy of the MC6850 external procedure calls
static struct MC6850_interface
MC6850_calls= {
	0,// Transmit data ouput
	0,// Request to Send output
	0,// Interupt Request output
};

void MC6850_config(const struct MC6850_interface *intf)
{
	MC6850_calls.out_transmit_data_func=*intf->out_transmit_data_func;
	MC6850_calls.out_RTS_func          =*intf->out_RTS_func;
	MC6850_calls.out_IRQ_func          =*intf->out_IRQ_func;
}

/* ******* Data bus interface ******* */

void MC6850_data_w(int offset, int data)
{

	//logerror("MC6850 write to output buffer %02x\n",data);
	STR=STR&0xfd;
	TDR=data;
}

int MC6850_data_r(int offset)
{
	//logerror("MC6850 Read from RDR value %02x and clear flags\n",RDR);
	STR=STR&0x7e;
	// clear the IRQ
	if ((MC6850_calls.out_IRQ_func) && (IRQ==1))
	{
		(MC6850_calls.out_IRQ_func)(0);
		IRQ=0;
	}
	return RDR;
}


void MC6850_control_w(int offset, int data)
{
	int CTRT; // Temp bits from CTR
	CTR=data;

	//logerror("MC6850 set control flags to %02x\n",CTR);
	divide_by=divide_by_lookup[CTR&0x03];

	CTRT=(CTR>>2)&0x07;
	word_length=word_length_lookup[CTRT];
	parity     =parity_lookup[CTRT];
	stop_bits  =stop_bits_lookup[CTRT];

	CTRT=(CTR>>5)&0x03;
	RTS=RTS_lookup[CTRT];
	/* *** need to call the RTS output function here ***/
	break_level=break_level_lookup[CTRT]; // Don't know what this does
	transmit_interrupt_enabled=transmit_interrupt_enabled_lookup[CTRT];

	receive_interrupt_enabled=(CTR>>7)&0x01;


	bytelength=1;					// start bit
	bytelength+=word_length;		// byte
	if (parity)	bytelength+=1;		// parity bit
	bytelength+=stop_bits;			// stop bit
}


// Bit 0  Receive Data Register Full (RDRF)
// Bit 1  Transmit Data Register Empty (TDRE)
// Bit 2  Data Carrier Detect
// Bit 3  Clear-to-Send
// Bit 4  Framing Error
// Bit 5  Receiver Overrun
// Bit 6  Parity Error
// Bit 7  Interrupt Request


int MC6850_status_r(int offset)
{

	//logerror("MC6850 read status flags %02x\n",STR);
	return STR;
}




/* ******* other input lines ******* */

void MC6850_DCD(int offset, int data)
{
	STR=(STR&0xfb)|((data&1)<<2);
	// check to see if an interrupt should be made.
}

void MC6850_CTS(int offset, int data)
{
	STR=(STR&0xf7)|((data&1)<<3);
	// this also sets the Transmit Controller
}

void MC6850_Receive_Clock(int Receive_Data)
{

	// Only start clocking in the data when a start bit is sent
	if ((RSC>0) || (Receive_Data==0))
	{

		RSR=(RSR>>1)|((Receive_Data&1)<<8);
		RSC+=1;
		// 10 is the default bit size for the cassette input
		if (RSC==bytelength)
		{
			//logerror("MC6850 Full byte recieved $%02x\n",(RSR&0xff));
			if (~(STR&0x01))
			{
				RDR=(RSR&0xff);
				STR=STR|0x81; // set the receive Data Register full and set the Interrupt Request
				//logerror("MC6850 OK to pass on byte to RDR\n");
				// set the IRQ
				// only do interrupt if bit 7 of CTR is high
				if ((MC6850_calls.out_IRQ_func) && (receive_interrupt_enabled) && (IRQ==0))
				{
					(MC6850_calls.out_IRQ_func)(1);
					IRQ=1;
				};
		 	}

			 RSC=0;
			 RSR=0;
			 dcd_count=0;
		};
	} else {
		logerror("Data bit 1 not used\n");
		dcd_count+=1;
		if (dcd_count==8)
		{
			logerror("MC6850 set DCD");
			STR=STR|0x84;
			if ((MC6850_calls.out_IRQ_func) && (receive_interrupt_enabled) && (IRQ==0))
			{
				(MC6850_calls.out_IRQ_func)(1);
					IRQ=1;
			};
		} else {
			if (dcd_count==20) {
				STR=STR&0xfb;
				logerror("MC6850 clear DCD");
			}
		}
	}

}

int MC6850_Transmit_Clock(void)
{
	// clock out the data and return a Trasmit Data bit

	/* lots of stuff to do in here
	   if we get to outputing data too */
	return 0;
}


/* ******* other output lines ******* */

int MC6850_transmit_data(int offset)
{
	// return the last transmitted bit
	return 0;
}

int MC6850_RTS(int offset)
{
	// is this correct ????
	return RTS;
}

/*
interrupt Request is the only other output
and this does not need to be read
*/


