/*******************************************************************************
**	tserial.c -- Tattletale Model-7 TPU UART Functions in C
**
**	Copyright (C) 1992 ONSET Computer Corp.  --jhg		All rights reserved.
**	
**	Monday, May 25, 1992
**	
**	modified Thursday, October 22, 1992 to place CHANPRIOR request below
**	HOSTSERVREQ in TSerOpen() and TSerClose() routines.
*******************************************************************************/

#include	<stdio.h>
#include	<stdlib.h>
#include	<serio.h>

#include	<tt7.h>
#include	<tpu.h>
#include	<sim.h>
#include	<timing.h>
#include	<excepts.h>

#include	"tserial.h"

#pragma options(mc68020)

/* UART SELECTORS */
#define		CHARACTER		0
#define		BITCELL			1
#define		BITS			2

#define		UART			12
#define		DEFAULT9600		417


typedef struct
	{
	ExcCFrame	framebuf;	/* you don't do anything with this */
	short		chan;		/* TPU channel 0 .. 13 */
	long		baud;		/* at current clock and TC1 setting */
	char		isOutput;	/* 0 = input channel, non-zero = output */
	char		parity;		/* 'E' = even, 'O' = odd, anything else = none */
	char		stopbits;	/* 0 = 1, 1 = 1, 2 = 2, ... */
	char		databits;	/* 0 = 8, anything else is used directly */
	char		overrun;	/* non-zero is overrun error */
	char		framerr;	/* non-zero is framing error (break) */
	ushort		head;		/* interrupt buffer head pointer */
	ushort		tail;		/* interrupt buffer tail pointer */
	ushort		size;		/* interrupt buffer size */
	char		sending;	/* true if sending, false if need priming */
	uchar		buffer[1];	/* interrupt buffer pointer */
	}	TSerRec, *TSerRecPtr;

static TSerRecPtr	tsrs[14];


/*********************************************
**	F U N C T I O N   P R O T O T Y P E S	**
*********************************************/
static void TSerInputRupt(void);
static void TSerOutputRupt(void);


/*******************************************************************************
**	TSerOpen		Open TPU channel for asynchronous serial input or output.
**	
**	return zero if no errors
*******************************************************************************/
int TSerOpen(int chan, int outflag, long qsize,
	long baud, int parity, int databits, int stopbits)
	{
	TSerRecPtr 		tsrp;

	if (tsrs[chan])
		return (tsAlreadyOpen);
	
	if ((tsrs[chan] = tsrp = calloc(sizeof(TSerRec) + qsize, 1)) == NULL)
		return (tsMemError);

	tsrp->size = qsize ? qsize : 1;
	tsrp->baud = baud;
	tsrp->isOutput = outflag;

/* FIX THESE SOMEDAY */
	tsrp->parity = 0;	/*parity*/
	tsrp->databits = 8;	/*databits*/
	tsrp->stopbits = 0;	/*stopbits*/
	
	CHANPRIOR(chan, Disabled);		/* stop microcode execution */
	*CIER &= ~(1 << chan);			/* force interrupts off */
	*CISR ^= (1 << chan);			/* clear old interrupt */

	tsrp->chan = chan;
	tsrp->head = tsrp->tail = 0;
	tsrp->overrun = tsrp->framerr = 0;

	InstallHandler(tsrp->isOutput ? TSerOutputRupt : TSerInputRupt,
		TPU_INT_VECTOR + chan, OS_VECTOR_BASE, &tsrp->framebuf);

	FUNSEL(chan, UART);
	PRAM[chan][BITCELL] = TPUGetTCR1() / tsrp->baud;
	PRAM[chan][BITS] = tsrp->databits;
	PRAM[chan][CHARACTER] = 0xff;

	HOSTSERVREQ(chan, tsrp->isOutput ? 2 : 3);
	CHANPRIOR(chan, LowPrior);
	
	while(HOSTSERVSTAT(chan) & 3)
		;
	*CISR ^= (1 << chan);						/* clear old interrupt */
	*CIER |= (1 << chan);

	return (tsOK);

	}	/* TSerOpen() */


/*******************************************************************************
**	TSerClose		Close TPU asynchronous channel.
**	
**	return zero if no errors
*******************************************************************************/
int TSerClose(int chan)
	{
	vfptr GenericHandler = (vfptr) ((long *) OS_VECTOR_BASE+13);
	
	CHANPRIOR(chan, Disabled);		/* stop microcode execution */
	*CIER &= ~(1 << chan);			/* force interrupts off */
	*CISR ^= (1 << chan);			/* clear old interrupt */

	InstallHandler(GenericHandler, TPU_INT_VECTOR + chan,
		OS_VECTOR_BASE, NULL);

	free(tsrs[chan]);
	tsrs[chan] = NULL;

	return (tsOK);

	}	/* TSerClose() */


/*******************************************************************************
**	TSerResetBaud	Reset TPU asynchronous channel baud rate.
**	
**	Return actual rate (may be different than requested.
*******************************************************************************/
long TSerResetBaud(int chan, long baud)
	{
	TSerRecPtr 		tsrp = tsrs[chan];
	
	while(HOSTSERVSTAT(chan))	/* wait for any remaining transmission */
		;

	PRAM[chan][BITCELL] = TPUGetTCR1() / tsrp->baud;

	return (TPUGetTCR1() / PRAM[chan][BITCELL]);

	}	/* TSerResetBaud() */


/*******************************************************************************
**	TSerByteAvail		Input Serial Port Status
**	
**	Return TRUE if byte(s) available from serial port
*******************************************************************************/
int TSerByteAvail(int chan)
	{
	TSerRecPtr 		tsrp = tsrs[chan];

	return (tsrp->tail != tsrp->head);
	
	}	/* TSerByteAvail() */


/*******************************************************************************
**	TSerInFlush			Flush any remaining characters from input buffer.
**	
**	
*******************************************************************************/
void TSerInFlush(int chan)
	{
	TSerRecPtr 		tsrp = tsrs[chan];

	tsrp->head = tsrp->tail = 0;
	
	}	/* TSerInFlush() */


/*******************************************************************************
**	TSerGetByte		Input Byte Routine
**	
**	Return next serial input byte
*******************************************************************************/
int TSerGetByte(int chan)
	{
	uchar			data;
	TSerRecPtr 		tsrp = tsrs[chan];

							#ifdef DEBUG
								printf("\n tsrp->buffer = %lX, tsrp->head = %d, tsrp->tail = %d, tsrp->size = %d \n",
									tsrp->buffer, tsrp->head, tsrp->tail, tsrp->size);
							#endif
	while (! TSerByteAvail(chan))
		;


	data = tsrp->buffer[tsrp->tail];
	tsrp->tail = (tsrp->tail + 1) % tsrp->size;
	return (data);

	}	/* TSerGetByte() */

/*******************************************************************************
**	TSerGetByteTimed		Timed Input Byte Routine
**
**	Return next serial input byte or -1 if timeout
*******************************************************************************/

int TSerGetByteTimed(unsigned long mS) {

  uchar			data;
  TSerRecPtr 		tsrp = tsrs[13];
  unsigned long		starttime;

  starttime = MilliSecs();
  for(;;) {
    if( labs(MilliSecs() - starttime) > mS ) return -1;
    else if(TSerByteAvail(13)) break;
  }

  data = tsrp->buffer[tsrp->tail];
  tsrp->tail = (tsrp->tail + 1) % tsrp->size;
  return (data);
}

/**************************************
      TGets

Input: Pointer to input 80 character buffer
Returns: 0 on success, -1 on error

**************************************/
int TGets(char *str) {
  char ch;
  int t;

  for(t=0; t<80; ++t) {
    ch = (char)TSerGetByte(13);
    switch(ch) {
      case '\n':
	str[t] = '\0';
	return 0;

      case '\r':
	str[t] = '\0';
	return 0;

      default:
	str[t] = ch;
    }
  }
  str[80] = '\0';
  return 0;
}

/**************************************
      TGetsTimed

Input: Timeout time in mS
       Pointer to input buffer
Returns: 0 on success, -1 on timeout

**************************************/
int TGetsTimed(unsigned long mS, char *str) {
  char inchar;

  for(;;) {
    inchar = TSerGetByteTimed(mS);
    if(inchar == -1) return -1;
    else if(inchar == '\n') {
      *str = '\0';
      return 0;
    }
    else *str++ = inchar;
  }
}

/*******************************************************************************
**	TSerPutByte		Output Byte Routine
**	
**	Send byte to serial port.
*******************************************************************************/
void TSerPutByte(int chan, int data)
	{
	TSerRecPtr 		tsrp = tsrs[chan];
	volatile short	head, next;

	tsrp = tsrs[chan];							/* work with pointer */
	head = tsrp->head;
	next = (head + 1) % tsrp->size;

	while (next == tsrp->tail)		/* wait if full */
		;

	tsrp->buffer[head] = data;
	tsrp->head = next;

	if (! tsrp->sending)		/* needs priming */
		{
		PRAM[chan][CHARACTER] = tsrp->buffer[tsrp->tail];
		tsrp->tail = (tsrp->tail + 1) % tsrp->size;
		HOSTSERVREQ(chan, 2);
		tsrp->sending = TRUE;
		}

	}	/* TSerPutByte() */

/**********************************************************/

void TPuts(char *str) {
  while(*str) TSerPutByte(12, (int)*str++);
  return;
}

/*******************************************************************************
**	TSerInputRupt			Handle TPU asynchronous serial input interrupts.
**	
**	
*******************************************************************************/
static void TSerInputRupt(void)
	{
	ExcStackFrame	*pESF;				/* we want to examine exception stack */
	int				chan;
	TSerRecPtr 		tsrp;
	short			head;
	short			next;
	ptr				bp;
	
/*
**	DETERMINE INTERRUPTING CHANNEL FROM VECTOR STORED IN STACK
*/	
	GetFramePtr(pESF);		/* get pointer to exception stack */
	chan = ((pESF->vectofs & 0xFFF) >> 2) - TPU_INT_VECTOR;	/* calc TPU channel */
												#ifdef DEBUG
													printf("\n pESF->eSR = %X, pESF->ePC = %lX, pESF->vectofs = %X \n",
														pESF->eSR, pESF->ePC, pESF->vectofs);
												#endif
	tsrp = tsrs[chan];							/* work with pointer */
	head = tsrp->head;
	next = (head + 1) % tsrp->size;
												#ifdef DEBUG
													printf("\n pESF->vectofs = %X, chan = %d, tsrp = %lX \n",
														pESF->vectofs, chan, tsrp);
												#endif

	if (next == tsrp->tail)		/* OVERRUN */
		{
		tsrp->overrun = TRUE;
		tsrp->head = tsrp->tail = 0;
		}
	else
		{
/*	68020 code gen error	tsrp->buffer[head] = PRAM[chan][CHARACTER];	*//* get char from TPU *//**/
		bp = (ptr) tsrp->buffer + head;
		*bp = PRAM[chan][CHARACTER];
		tsrp->head = next;				/* adjust pointer for next */
		}

	*CISR ^= (1 << chan);								/* clear the interrupt */

	}	/* TSerInputRupt() */


/*******************************************************************************
**	TSerOutputRupt			Handle TPU asynchronous serial output interrupts.
**	
**	
*******************************************************************************/
static void TSerOutputRupt(void)
	{
	ExcStackFrame	*pESF;				/* we want to examine exception stack */
	int				chan;
	TSerRecPtr 		tsrp;
	ptr				bp;

/*
**	DETERMINE INTERRUPTING CHANNEL FROM VECTOR STORED IN STACK
*/	
	GetFramePtr(pESF);		/* get pointer to exception stack */

	chan = ((pESF->vectofs & 0xFFF) >> 2) - TPU_INT_VECTOR;	/* calc TPU channel */
												#ifdef DEBUG
													printf("\n pESF->eSR = %X, pESF->ePC = %lX, pESF->vectofs = %X \n",
														pESF->eSR, pESF->ePC, pESF->vectofs);
												#endif

	tsrp = tsrs[chan];							/* work with pointer */
	*CISR ^= (1 << chan);						/* clear the interrupt */
	if (tsrp->head != tsrp->tail)		/* send next buffered byte */
		{
/*	68020 code gen error	PRAM[chan][CHARACTER] = tsrp->buffer[tsrp->tail];*/
		bp = (ptr) tsrp->buffer + tsrp->tail;
		PRAM[chan][CHARACTER] = *bp;
		tsrp->tail = (tsrp->tail + 1) % tsrp->size;
		HOSTSERVREQ(chan, 2);
		}
	else
		tsrp->sending = FALSE;	/* 92/09/01 */

	}	/* TSerOutputRupt() */
