/* altair_sio: MITS Altair serial I/O card

	These functions support a simulated MITS 2SIO interface card.
    The card had two physical I/O ports which could be connected
    to any serial I/O device that would connect to a current loop,
    RS232, or TTY interface.  Available baud rates were jumper
    selectable for each port from 110 to 9600.

    All I/O is via programmed I/O.  Each each has a status port
    and a data port.  A write to the status port can select
    some options for the device (0x03 will reset the port).
    A read of the status port gets the port status:

    +---+---+---+---+---+---+---+---+
    | X	  X   X   X   X   X   O   I |
    +---+---+---+---+---+---+---+---+

	I - A 1 in this bit position means a character has been received
    	on the data port and is ready to be read.
    O - A 1 in this bit means the port is ready to receive a character
    	on the data port and transmit it out over the serial line.

    A read to the data port gets the buffered character, a write
    to the data port writes the character to the device.
*/

#include <stdio.h>

#include "altair_defs.h"

#define	UNIT_V_ANSI	(UNIT_V_UF + 0)			/* ANSI mode */
#define UNIT_ANSI	(1 << UNIT_V_ANSI)

t_stat sio_svc (UNIT *uptr);
t_stat sio_reset (DEVICE *dptr);
t_stat ptr_svc (UNIT *uptr);
t_stat ptr_reset (DEVICE *dptr);
t_stat ptp_svc (UNIT *uptr);
t_stat ptp_reset (DEVICE *dptr);

int32 ptr_stopioe = 0, ptp_stopioe = 0;			/* stop on error */

extern t_stat sim_activate (UNIT *uptr, int32 interval);
extern t_stat sim_cancel (UNIT *uptr);
extern t_stat sim_poll_kbd (void);
extern t_stat sim_putchar (int32 out);

/* 2SIO Standard I/O Data Structures */

UNIT sio_unit = { UDATA (&sio_svc, 0, 0),
			 KBD_POLL_WAIT };

REG sio_reg[] = {
	{ ORDATA (DATA, sio_unit.buf, 8) },
	{ ORDATA (STAT, sio_unit.u3, 8) },
	{ NULL }  };

MTAB sio_mod[] = {
	{ UNIT_ANSI, 0, "TTY", "TTY", NULL },
	{ UNIT_ANSI, UNIT_ANSI, "ANSI", "ANSI", NULL },
	{ 0 }  };

DEVICE sio_dev = {
	"2SIO", &sio_unit, sio_reg, sio_mod,
	1, 10, 31, 1, 8, 8,
	NULL, NULL, &sio_reset,
	NULL, NULL, NULL };

UNIT ptr_unit = { UDATA (&ptr_svc, UNIT_SEQ + UNIT_ATTABLE, 0),
			 KBD_POLL_WAIT };

REG ptr_reg[] = {
	{ ORDATA (DATA, ptr_unit.buf, 8) },
	{ ORDATA (STAT, ptr_unit.u3, 8) },
	{ ORDATA (POS, ptr_unit.pos, 31) },
	{ NULL }  };

DEVICE ptr_dev = {
	"PTR", &ptr_unit, ptr_reg, NULL,
	1, 10, 31, 1, 8, 8,
	NULL, NULL, &ptr_reset,
	NULL, NULL, NULL };

UNIT ptp_unit = { UDATA (&ptp_svc, UNIT_SEQ + UNIT_ATTABLE, 0),
			 KBD_POLL_WAIT };

REG ptp_reg[] = {
	{ ORDATA (DATA, ptp_unit.buf, 8) },
	{ ORDATA (STAT, ptp_unit.u3, 8) },
	{ ORDATA (POS, ptp_unit.pos, 31) },
	{ NULL }  };

DEVICE ptp_dev = {
	"PTP", &ptp_unit, ptp_reg, NULL,
	1, 10, 31, 1, 8, 8,
	NULL, NULL, &ptp_reset,
	NULL, NULL, NULL };

/*  Service routines to handle simulator functions */

/* service routine - actually gets char & places in buffer */

int32 sio_svc (UNIT *uptr)
{
	int32 temp;

	sim_activate (&sio_unit, sio_unit.wait);		/* continue poll */
	if ((temp = sim_poll_kbd ()) < SCPE_KFLAG)
		return temp;	/* no char or error? */
	sio_unit.buf = temp & 0377;		/* Save char */
	sio_unit.u3 |= 0x01;            /* Set status */

	/* Do any special character handling here */

	sio_unit.pos++;
	return SCPE_OK;
}


int32 ptr_svc (UNIT *uptr)
{
    return SCPE_OK;
}

int32 ptp_svc (UNIT *uptr)
{
	return SCPE_OK;
}


/* Reset routine */

int32 sio_reset (DEVICE *dptr)
{
	sio_unit.buf = 0;    /* Data */
	sio_unit.u3 = 0x02;  /* Status */
	sim_activate (&sio_unit, sio_unit.wait);		/* activate unit */
	return SCPE_OK;
}


int32 ptr_reset (DEVICE *dptr)
{
	ptr_unit.buf = 0;
	ptr_unit.u3 = 0x02;
	sim_cancel (&ptr_unit);					/* deactivate unit */
	return SCPE_OK;
}

int32 ptp_reset (DEVICE *dptr)
{
	ptp_unit.buf = 0;
	ptp_unit.u3 = 0x02;
	sim_cancel (&ptp_unit);					/* deactivate unit */
	return SCPE_OK;
}


/* 	I/O instruction handlers, called from the CPU module when an
   	IN or OUT instruction is issued.

   	Each function is passed an 'io' flag, where 0 means a read from
   	the port, and 1 means a write to the port.  On input, the actual
    input is passed as the return value, on output, 'data' is written
    to the device.
*/

int32 sio0s(int32 io, int32 data)
{
	if (io == 0) {
        return (sio_unit.u3);
    } else {
    	if (data == 0x03) {		/* reset port! */
        	sio_unit.u3 = 0x02;
            sio_unit.buf = 0;
            sio_unit.pos = 0;
        }
    	return (0);
    }
}

int32 sio0d(int32 io, int32 data)
{
	if (io == 0) {
        sio_unit.u3 = sio_unit.u3 & 0xFE;
        return (sio_unit.buf);
    } else {
    	sim_putchar(data);
    }
}

/* Port 2 controls the PTR/PTP devices */

int32 sio1s(int32 io, int32 data)
{
	if (io == 0) {
    	if ((ptr_unit.flags & UNIT_ATT) == 0)	/* attached? */
        	return 0x02;
        if (ptr_unit.u3 != 0)                   /* No more data? */
        	return 0x02;
        return (0x03);							/* ready to read/write */
    } else {
    	if (data == 0x03) {
        	ptr_unit.u3 = 0;
            ptr_unit.buf = 0;
            ptr_unit.pos = 0;
        	ptp_unit.u3 = 0;
            ptp_unit.buf = 0;
            ptp_unit.pos = 0;
        }
    	return (0);
    }
}

int32 sio1d(int32 io, int32 data)
{
	int32 temp;
    UNIT *uptr;

	if (io == 0) {
        if ((ptr_unit.flags & UNIT_ATT) == 0)			/* attached? */
            return 0;
        if (ptr_unit.u3 != 0)
        	return 0;
        uptr = ptr_dev.units;
        if ((temp = getc(uptr -> fileref)) == EOF) {	/* end of file? */
            ptr_unit.u3 = 0x01;
            return 0;
        }
		ptr_unit.pos++;
        return (temp & 0xFF);
    } else {
    	uptr = ptp_dev.units;
    	putc(data, uptr -> fileref);
		ptp_unit.pos++;
    }
}

