#include "unix.h"
#include "extern.h"

#define TIOCGETP 0
#define TIOCSETP 1

#define XTABS	0006000
#define RAW	0000040
#define CRLF    0000020
#define ECHO	0000010
#define CBREAK	0000002
#define COOKED  0000000

struct tty_data {
    char t_ispeed;
    char t_ospeed;
    char t_erase;
    char t_kill;
    int t_flags;
    char t_intr;
    char t_quit;
    char t_start;
    char t_stop;
    char t_eof;
};

struct tty_data ttydata[3];   /* ttydata[0] is not used */

#define CTRL(c)  (c & 0x1f)

struct tty_data ttydflt = {
    0, 0, '\b', CTRL('x'), XTABS|CRLF|ECHO|COOKED,
    CTRL('c'), CTRL('\\'), CTRL('q'), CTRL('s'), CTRL('d')
};


#define TTYSIZ 132

char tbuf1[TTYSIZ];
char tbuf2[TTYSIZ];

struct s_queue ttyinq[3] = {
{
    NULL,	/* ttyinq[0] is never used */
    NULL,
    NULL,
    0,
    0,
    0
},
{
    tbuf1,
    tbuf1,
    tbuf1,
    TTYSIZ,
    0,
    TTYSIZ/2
},
{
    tbuf2,
    tbuf2,
    tbuf2,
    TTYSIZ,
    0,
    TTYSIZ/2
}
};


int stopflag[3];  /* Flag for ^S/^Q */
int flshflag[3];   /* Flag for ^O */

tty_read(minor, rawflag)
int16 minor;
int16 rawflag;
{
    int nread;
    char c;

    /* Minor == 0 means that it is the controlling tty of the process */
    ifnot (minor)
	minor = udata.u_ptab->p_tty;

    if (minor < 1 || minor > 2)
    {
	udata.u_error = ENODEV;
	return (-1);
    }

    nread = 0;
    while (nread < udata.u_count)
    {
	for (;;)
	{
	    if (remq(&ttyinq[minor],&c))
		break;
	    psleep(&ttyinq[minor]);
   	    if (udata.u_cursig || udata.u_ptab->p_pending)  /* messy */
	    {
	        udata.u_error = EINTR;
	        return(-1);
	    }
	}

	if (udata.u_sysio)
	    *udata.u_base = c;
	else
	    uputc(c, udata.u_base);

	if (nread++ == 0 && c == ttydata[minor].t_eof)   /* ^D */
	    return(0);

	if (c == '\n')
	    break;
	++udata.u_base;
    } 
    return(nread);
}



tty_write(minor, rawflag)
int16 minor;
int16 rawflag;
{
    int towrite, c;

    /* Minor == 0 means that it is the controlling tty of the process */
    ifnot (minor)
	minor = udata.u_ptab->p_tty;

    if (minor < 1 || minor > 2)
    {
	udata.u_error = ENODEV;
	return (-1);
    }

    towrite = udata.u_count;

    while (udata.u_count-- != 0)
    {
	for (;;)	/* Wait on the ^S/^Q flag */
	{
	    ifnot (stopflag[minor])	
		break;
	    psleep(&stopflag[minor]);
   	    if (udata.u_cursig || udata.u_ptab->p_pending)  /* messy */
	    {
	        udata.u_error = EINTR;
	        return(-1);
	    }
	}
	
	ifnot (flshflag[minor])
	{
	    if (udata.u_sysio)
		c = *udata.u_base;
	    else
		c = ugetc(udata.u_base);

   	    if (c == '\n' && (ttydata[minor].t_flags & CRLF))
	        _putc(minor, '\r');
	    _putc(minor, c);
	}
	++udata.u_base;
    }
    return(towrite);
}



tty_open(minor)
int minor;
{
    /* Minor == 0 means that it is the controlling tty of the process */
    ifnot (minor)
	minor = udata.u_ptab->p_tty;

    if (minor < 1 || minor > 2)
    {
	udata.u_error = ENODEV;
	return (-1);
    }

    /* If there is no controlling tty for the process, establish it */
    ifnot (udata.u_ptab->p_tty)
	udata.u_ptab->p_tty = minor;

    /* Initialize the ttydata */
    bcopy(&ttydflt, &ttydata[minor], sizeof(struct tty_data));

    return(0);
}


tty_close(minor)
int minor;
{
    /* If we are closing the controlling tty, make note */
    if (minor == udata.u_ptab->p_tty)
	udata.u_ptab->p_tty = 0;

    return(0);
}


tty_ioctl(minor, request, data)
int minor;
int request;
char *data;   /* In user space */
{
   /* Minor == 0 means that it is the controlling tty of the process */
    ifnot (minor)
	minor = udata.u_ptab->p_tty;

    if (minor < 1 || minor > 2)
    {
	udata.u_error = ENODEV;
	return (-1);
    }

    switch (request)
    {
	case TIOCGETP:
	    uput(&ttydata[minor], data, 6);
	    break;
	case TIOCSETP:
	    uget(data, &ttydata[minor], 6);
	    break;
	default:
	    udata.u_error = EINVAL; /* ??? */
	    return(-1);
	    break;
    }
    return (0);
}




/* This tty interrupt routine checks to see if the uart receiver actually
caused the interrupt.  If so it adds the character to the tty input
queue, echoing and processing backspace and carriage return.  If the queue 
contains a full line, it wakes up anything waiting on it.  If it is totally
full, it beeps at the user. */

tinproc(minor, c)
int minor;
int c;
{
    char oc;
    struct tty_data *td;
    int mode;

    td = &ttydata[minor];
    mode = td->t_flags & (RAW|CBREAK);

    if (mode != RAW)
    {
	c &= 0x7f;   /* Strip off parity */
	if (!c)
	    return;
    }

    if (c==0x1a) /* ^Z */
	idump();	/* For debugging */


    if (mode == COOKED)
    {
    	if (c == td->t_erase)
    	{
	    if (uninsq(&ttyinq[minor],&oc))
	    {
		if (oc == '\n')
		    insq(&ttyinq[minor],oc);  /* Don't erase past nl */
	    	else
	     	{
		    echo(minor, '\b');
		    echo(minor, ' ');
		    echo(minor, '\b');
	    	}
	    }
	    return;
	}

	if (c == td->t_kill)
    	{
	    while (uninsq(&ttyinq[minor],&oc))
	    {
		if (oc == '\n')
		{
		    insq(&ttyinq[minor],oc);  /* Don't erase past nl */
		    break;
		}
		echo(minor, '\b');
		echo(minor, ' ');
		echo(minor, '\b');
	    }
	    return;
	}
    }

    if (mode == COOKED || mode == CBREAK)
    {

	if (c == '\r' && (td->t_flags & CRLF))
	    c = '\n';

	if (c == td->t_intr)  /* ^C */
	{
	    sgrpsig(minor, SIGINT);
  	    clrq(&ttyinq[minor]);
	    stopflag[minor] = 
	    flshflag[minor] = 0;
 	    return;
	}

	if (c == td->t_quit)  /* ^\ */
	{
	    sgrpsig(minor, SIGQUIT);
  	    clrq(&ttyinq[minor]);
	    stopflag[minor] = 
	    flshflag[minor] = 0;
	    return;
	}

	if (c == '\017')  /* ^O */
	{
	    flshflag[minor] = !flshflag[minor];
	    return;
	}

	if (c == td->t_stop)   /* ^S */
	{
	    stopflag[minor] = 1;
	    return;
	}

	if (c == td->t_start)  /* ^Q */
	{
	    stopflag[minor] = 0;
	    wakeup(&stopflag[minor]);
	    return;
	}
    }

    /* All modes come here */

    if (c == '\n' && (td->t_flags & CRLF))
	echo(minor, '\r');

    if (insq(&ttyinq[minor],c))
        echo(minor, c);
    else
	_putc(minor, '\007');	/* Beep if no more room */

    if (mode != COOKED || c == '\n' || c == td->t_eof)   /* ^D */
	wakeup(&ttyinq[minor]);

}


echo(minor, c)
int minor;
int c;
{
    if (ttydata[minor].t_flags & ECHO)
	_putc(minor, c);
}




#asm 
; Z280 UART register addresses (in page FE)

CFREG	equ	10h
TCREG	equ	12h
RCREG	equ	14h
RDREG	equ	16h
TDREG	equ	18h
#endasm


_putc(minor, c)
char c;
{
    if (minor == 1)
    {
        /* 6850 chip */
	while(!(in(0x72)&02))
	    ;
	out(c,0x73);
    } else {
	/* Z280 internal UART */
	putuart(c);
    }
}
    

putuart()
{
#asm 8080

	PUSH	B
	CALL	IOPGFE?##
UOTL:
	IN	TCREG
	ANI	01H 		;bit to test for tx ready
	JZ	UOTL

	LDHLSX	4		;get arg
	MOV	A,L
	OUT	TDREG

	CALL	IOPG00?##
	POP	B
#endasm
}


acia_int()
{
#asm 8080
	PUSH	PSW
	PUSH	B
	PUSH	D
	PUSH	H
.Z80
	PUSH	IX
	PUSH	IY
.8080
#endasm
	while ( (in(0x72)&0x81) == 0x81 )
	    tinproc(1, in(0x73));

#asm 8080
	JMP	IRET##
#endasm

}


uart_int()
{
#asm 8080
	PUSH	PSW
	PUSH	B
	PUSH	D
	PUSH	H
.Z80
	PUSH	IX
	PUSH	IY
.8080
	;get char from uart here


uartloop:
	CALL	IOPGFE?##
	IN	RCREG
	ANI	10H 		;bit to test for rcv ready
	JZ	nochar
	IN	RDREG
	CALL	IOPG00?##

	lxi	h,2
	push	h

	mov	l,a
	mvi	h,0
	push	h

	call	tinproc?	; tinproc(2, c);
	pop	h
	pop	h

	jmp	uartloop
nochar:
    	call	iopg00?##

	JMP	IRET##
#endasm

}
