/* ibm1130_sca.c: IBM 1130 synchronous communications adapter emulation

   Based on the SIMH simulator package written by Robert M Supnik

   Brian Knittel
   Revision History

   2005.03.08 - Started

 * (C) Copyright 2005, Brian Knittel.
 * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN
 * RISK basis, there is no warranty of fitness for any purpose, and the rest of the
 * usual yada-yada. Please keep this notice and the copyright in any distributions
 * or modifications.
 *
 * This is not a supported product, but I welcome bug reports and fixes.
 * Mail to simh@ibm1130.org
 */

/******************************************************************************************************************
 * NOTES:
 *			This module sends raw bisync data over a standard TCP port. It's meant to be
 *			used with the emulated 2703 device in Hercules.
 *
 * Attach command:
 *
 * to establish an outgoing connection:
 *
 *		attach sca host					connect to named host on default port (initial default port is 2703); or
 *		attach sca host:###				connect to named host on port ###. ### is also set as new default port number.
 *										>> The simulator waits until the connection is established
 *
 * or to set up for incoming connections:
 *
 *		attach sca -l dummy				listen for a connection on default port (initially 2703); Nonnumeric "dummy" argument is ignored; or
 *		attach sca -l ###				listen for a connection on the port ###. ### is also set as the new default port
 *										>> The simulator proceeds. When another simulator connects, the READY bit is set in the DSW.
 *
 *										If the SCA's autoanswer-enable bit has been set, an incoming connection causes an interrupt (untested)
 * Configuration commands:
 *
 *		set sca bsc						set bisync mode (default)
 *		set sca str						set synchronous transmit/recieve mode (NOT IMPLEMENTED)
 *
 *  	set sca ###						set simulated baud rate to ###, where ### is 600, 1200, 2000, 2400 or 4800 (4800 is default)
 *
 *  	set sca half					set simulated half-duplex mode
 *  	set sca full					set simulated full-duplex mode (note: 1130's SCA can't actually send and receive at the same time!)
 *
 *  	deposit sca keepalive ###		send SYN packets every ### msec when suppressing SYN's, default is 0 (no keepalives)
 *
 * STR/BSC mode is selected by a toggle switch on the 1130, with the SET SCA BSC or SET SET STR command here.
 * Testable with in_bsc_mode() or in_str_mode() in this module. If necessary, the SET command can be configured
 * to call a routine when the mode is changed; or, we can just required the user to reboot the simulated 1130
 * when switching modes.
 *
 * STR MODE IS NOT IMPLEMENTED!
 *
 * The SCA adapter appears to know nothing of the protocols used by STR and BSC. It does handle the sync/idle
 * character specially, and between BSC and STR mode the timers are used differently. Also in STR mode it
 * can be set to a sychronization mode where it sends SYN's without program intervention.
 *
 * See definition of SCA_STATE for defintion of simulator states.
 *
 * Rather than trying to simulate the actual baud rates, we try to keep the character service interrupts
 * coming at about the same number of instruction intervals -- thus existing 1130 code should work correctly
 * but the effective data transfer rate will be much higher. The "timers" however are written to run on real wall clock
 * time, This may or may not work. If necessary they could be set to time out based on the number of calls to sca_svc
 * which occurs once per character send/receive time; For example, at 4800 baud and an 8 bit frame, we get
 * 600 characters/second, so the 3 second timer would expire in 1800 sca_svc calls. Well, that's something to
 * think about.
 *
 * To void blowing zillions of SYN characters across the network when the system is running but idle, we suppress
 * them. If 100 consecutive SYN's are sent, we flush the output buffer and stop sending SYN's
 * until some other character is sent, OR the line is turned around (e.g. INITR, INITW or an end-operation
 * CONTROL is issued), or the number of msec set by DEPOSIT SCS KEEPALIVE has passed, if a value has
 * been set. By default no keepalives are sent.
 *
 * Timer operations are not debugged. First, do timers automatically reset and re-interrupt if
 * left alone after they timeout the first time? Does XIO_SENSE_DEV really restart all running timers?
 * Does it touch the timer trigger (program timer?) How do 3 and 1.25 second timers really work
 * in BSC mode? Hard to tell from the FC manual.
 ******************************************************************************************************************/

#include "ibm1130_defs.h"
#include "sim_sock.h"										/* include path must include main simh directory */
#include <ctype.h>

extern SOCKET sim_create_sock (void);						/* missing from sim_sock.h */

#define DEBUG_SCA_FLUSH			0x0001						/* debugging options */
#define DEBUG_SCA_TRANSMIT		0x0002
#define DEBUG_SCA_CHECK_INDATA	0x0004
#define DEBUG_SCA_RECEIVE_SYNC	0x0008
#define DEBUG_SCA_RECEIVE_DATA	0x0010
#define DEBUG_SCA_XIO_READ		0x0020
#define DEBUG_SCA_XIO_WRITE		0x0040
#define DEBUG_SCA_XIO_CONTROL	0x0080
#define DEBUG_SCA_XIO_INITW		0x0100
#define DEBUG_SCA_XIO_INITR		0x0200
#define DEBUG_SCA_XIO_SENSE_DEV	0x0400
#define DEBUG_SCA_TIMERS		0x0800
#define DEBUG_SCA_ALL			0xFFFF

/* #define DEBUG_SCA			(DEBUG_SCA_TIMERS|DEBUG_SCA_FLUSH|DEBUG_SCA_TRANSMIT|DEBUG_SCA_CHECK_INDATA|DEBUG_SCA_RECEIVE_SYNC|DEBUG_SCA_RECEIVE_DATA|DEBUG_SCA_XIO_INITR|DEBUG_SCA_XIO_INITW) */
#define DEBUG_SCA			(DEBUG_SCA_TIMERS|DEBUG_SCA_FLUSH|DEBUG_SCA_CHECK_INDATA|DEBUG_SCA_XIO_INITR|DEBUG_SCA_XIO_INITW)

#define SCA_DEFAULT_PORT		2703						/* default socket, This is the number of the IBM 360's BSC device */

#define MAX_SYNS				 100						/* number of consecutive syn's after which we stop buffering them */

/***************************************************************************************
 *  SCA
 ***************************************************************************************/

#define SCA_DSW_READ_RESPONSE					0x8000		/* receive buffer full interrupt */
#define SCA_DSW_WRITE_RESPONSE					0x4000		/* transmitter buffer empty interrupt */
#define SCA_DSW_CHECK							0x2000		/* data overrun or character gap error */
#define SCA_DSW_TIMEOUT							0x1000		/* timer interrupt, mode specific */
#define SCA_DSW_AUTOANSWER_REQUEST				0x0800		/* dataset is ringing and autoanswer is enabled */
#define SCA_DSW_BUSY							0x0400		/* adapter is in either receive or transmit mode */
#define SCA_DSW_AUTOANSWER_ENABLED				0x0200		/* 1 when autoanswer mode has been enabled */
#define SCA_DSW_READY							0x0100		/* Carrier detect? Connected and ready to rcv, xmit or sync */
#define SCA_DSW_RECEIVE_RUN						0x0080		/* used in two-wire half-duplex STR mode only. "Slave" mode (?) */

#define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT)

typedef enum {				/*  ms     m = mode (0 = idle, 1 = send, 2 = receive), s = substate */
	SCA_STATE_IDLE			= 0x00,	/* nothing happening */
	SCA_STATE_TURN_SEND		= 0x10,	/* line turning around to the send state */
	SCA_STATE_SEND_SYNC		= 0x11,	/* sca is sending syncs */
	SCA_STATE_SEND1			= 0x12,	/* have issued write response, waiting for write command */
	SCA_STATE_SEND2			= 0x13,	/* write command issued, "sending" byte */
	SCA_STATE_TURN_RECEIVE	= 0x20,	/* line turnaround to the receive state */
	SCA_STATE_RECEIVE_SYNC	= 0x21,	/* sca is receiving syncs */
	SCA_STATE_RECEIVE_SYNC2	= 0x22,	/* bsc mode, waiting for 2nd SYN */
	SCA_STATE_RECEIVE_SYNC3	= 0x23,	/* bsc mode, waiting for 1st non-SYN */
	SCA_STATE_RECEIVE1		= 0x24,	/* "receiving" a byte */
	SCA_STATE_RECEIVE2		= 0x25,	/* read response issued, "receiving" next byte */
} SCA_STATE;

#define in_send_state()    (sca_state & 0x10)
#define in_receive_state() (sca_state & 0x20)

static t_stat sca_svc			(UNIT *uptr);				/* prototypes */
static t_stat sca_reset			(DEVICE *dptr);
static t_stat sca_attach		(UNIT *uptr, char *cptr);
static t_stat sca_detach		(UNIT *uptr);
static void sca_start_timer		(int n, int msec_now);
static void sca_halt_timer		(int n);
static void sca_toggle_timer	(int n, int msec_now);
															/* timer states, chosen so any_timer_running can be calculated by oring states of all 3 timers */
typedef enum {SCA_TIMER_INACTIVE = 0, SCA_TIMER_RUNNING = 1, SCA_TIMER_INHIBITED = 2, SCA_TIMER_TIMEDOUT = 4} SCA_TIMER_STATE;

#define TIMER_3S		0		/* 3 second timer	 		  index into sca_timer_xxx arrays */
#define TIMER_125S		1		/* 1.25 second timer */
#define TIMER_035S		2		/* 0.35 second timer */

static uint16 sca_dsw    = 0;								/* device status word */
static uint32 sca_cwait  = 275;								/* inter-character wait */
static uint32 sca_iwait	 = 2750;							/* idle wait */
static uint32 sca_state  = SCA_STATE_IDLE;
static uint8  sichar     = 0;								/* sync/idle character */
static uint8  rcvd_char  = 0;								/* most recently received character */
static uint8  sca_frame  = 8;
static uint16 sca_port   = SCA_DEFAULT_PORT;				/* listening port number */
static int32  sca_keepalive = 0;							/* keepalive SYN packet period in msec, default = 0 (disabled) */
static SCA_TIMER_STATE sca_timer_state[3];					/* current timer state */
static int    sca_timer_endtime[3];							/* clocktime when timeout is to occur if state is RUNNING */
static int    sca_timer_timeleft[3];						/* time left in msec if state is INHIBITED */
static t_bool any_timer_running = FALSE;					/* TRUE if at least one timer is running */
static int    sca_timer_msec[3] = {3000, 1250, 350};		/* timebase in msec for the three timers: 3 sec, 1.25 sec, 0.35 sec */
static t_bool sca_timer_trigger;							/* if TRUE, the "timer trigger" is set, the 0.35s timer is running and the 3 sec and 1.25 sec timers are inhibited */
static int	  sca_nsyns  = 0;								/* number of consecutively sent SYN's */
static int    idles_since_last_write = 0;					/* used to detect when software has ceased sending data */
static SOCKET sca_lsock = INVALID_SOCKET;
static SOCKET sca_sock = INVALID_SOCKET;

#define SCA_SENDBUF_SIZE	145								/* maximum number of bytes to buffer for transmission */
#define SCA_RCVBUF_SIZE		256								/* max number of bytes to read from socket at a time */
#define SCA_SEND_THRESHHOLD	140								/* number of bytes to buffer before initiating packet send */
#define SCA_IDLE_THRESHHOLD   3								/* maximum number of unintentional idles to buffer before initiating send */

static uint8  sca_sendbuf[SCA_SENDBUF_SIZE];				/* bytes pending to write to socket */
static uint8  sca_rcvbuf[SCA_RCVBUF_SIZE];					/* bytes received from socket, to be given to SCA */
static int    sca_n2send = 0;								/* number of bytes queued for transmission */
static int	  sca_nrcvd = 0;								/* number of received bytes in buffer */
static int    sca_rcvptr = 0;								/* index of next byte to take from rcvbuf */

#define UNIT_V_BISYNC     (UNIT_V_UF + 0)					/* BSC (bisync) mode */
#define UNIT_V_BAUD		  (UNIT_V_UF + 1)					/* 3 bits for baud rate encoding */
#define UNIT_V_FULLDUPLEX (UNIT_V_UF + 4)
#define UNIT_V_AUTOANSWER (UNIT_V_UF + 5)
#define UNIT_V_LISTEN	  (UNIT_V_UF + 6)					/* listen socket mode */

#define UNIT_BISYNC	 	  (1u << UNIT_V_BISYNC)
#define UNIT_BAUDMASK     (7u << UNIT_V_BAUD)
#define UNIT_BAUD600	  (0u << UNIT_V_BAUD)
#define UNIT_BAUD1200	  (1u << UNIT_V_BAUD)
#define UNIT_BAUD2000	  (2u << UNIT_V_BAUD)
#define UNIT_BAUD2400	  (3u << UNIT_V_BAUD)
#define UNIT_BAUD4800	  (4u << UNIT_V_BAUD)
#define UNIT_FULLDUPLEX   (1u << UNIT_V_FULLDUPLEX)
#define UNIT_AUTOANSWER	  (1u << UNIT_V_AUTOANSWER)
#define UNIT_LISTEN		  (1u << UNIT_V_LISTEN)

extern int sim_switches;									/* variable that gets bits set for -x switches on command lines */

t_stat sca_set_baud (UNIT *uptr, int32 value, char *cptr, void *desc);

UNIT sca_unit = {														/* default settings */
	UDATA (sca_svc, UNIT_ATTABLE|UNIT_BISYNC|UNIT_BAUD4800|UNIT_FULLDUPLEX, 0),
};

REG sca_reg[] = {														/* DEVICE STATE/SETTABLE PARAMETERS: */
	{ HRDATA (SCADSW,    sca_dsw,    16) },								/* device status word */
	{ DRDATA (SICHAR,    sichar,      8), PV_LEFT },					/* sync/idle character */
	{ DRDATA (RCVDCHAR,  rcvd_char,   8), PV_LEFT },					/* most recently received character */
	{ DRDATA (FRAME,     sca_frame,   8), PV_LEFT },					/* frame bits (6, 7 or 8)
	{ DRDATA (SCASTATE,  sca_state,  32), PV_LEFT },					/* current state */
	{ DRDATA (CTIME,     sca_cwait,  32), PV_LEFT },					/* inter-character wait */
	{ DRDATA (ITIME,     sca_iwait,  32), PV_LEFT },					/* idle wait (polling interval for socket connects) */
	{ DRDATA (SCASOCKET, sca_port,   16), PV_LEFT },					/* listening port number */
	{ DRDATA (KEEPALIVE, sca_keepalive, 32), PV_LEFT },					/* keepalive packet period in msec */
	{ NULL }  };

MTAB sca_mod[] = {														/* DEVICE OPTIONS */
	{ UNIT_BISYNC, 		0,             		"STR",  "STR",  NULL },		/* mode option */
	{ UNIT_BISYNC, 		UNIT_BISYNC,   		"BSC",  "BSC",  NULL },
	{ UNIT_BAUDMASK,	UNIT_BAUD600,  		"600",  "600",  sca_set_baud },		/* data rate option */
	{ UNIT_BAUDMASK,	UNIT_BAUD1200, 		"1200", "1200", sca_set_baud },
	{ UNIT_BAUDMASK,	UNIT_BAUD2000, 		"2000", "2000", sca_set_baud },
	{ UNIT_BAUDMASK,	UNIT_BAUD2400, 		"2400", "2400", sca_set_baud },
	{ UNIT_BAUDMASK,	UNIT_BAUD4800,		"4800", "4800", sca_set_baud },
	{ UNIT_FULLDUPLEX, 	0, 					"HALF", "HALF", NULL },		/* duplex option (does this matter?) */
	{ UNIT_FULLDUPLEX, 	UNIT_FULLDUPLEX,	"FULL", "FULL", NULL },
	{ 0 }  };

DEVICE sca_dev = {
	"SCA", &sca_unit, sca_reg, sca_mod,
	1, 16, 16, 1, 16, 16,
	NULL, NULL, sca_reset,
	NULL, sca_attach, sca_detach
};

/*********************************************************************************************
 * sca_set_baud - set baud rate handler (SET SCA.BAUD nnn)
 *********************************************************************************************/

t_stat sca_set_baud (UNIT *uptr, int32 value, char *cptr, void *desc)
{
	uint32 newbits;

	switch (value) {
		case 600:		newbits = UNIT_BAUD600;		break;
		case 1200:		newbits = UNIT_BAUD1200;	break;
		case 2000:		newbits = UNIT_BAUD2000;	break;
		case 2400:		newbits = UNIT_BAUD2400;	break;
		case 4800:		newbits = UNIT_BAUD4800;	break;
		default:		return SCPE_ARG;
	}

	CLRBIT(sca_unit.flags,  UNIT_BAUDMASK);
	SETBIT(sca_unit.flags, newbits);

	sca_cwait = 1320000 / value;			/* intercharacter wait time in instructions (roughly) */

	return SCPE_OK;
}

/*********************************************************************************************
 * HANDY MACROS 
 *********************************************************************************************/

#define in_bsc_mode()		    (sca_unit.flags  & UNIT_BISYNC)					/* TRUE if user selected BSC mode */
#define in_str_mode()		    ((sca_unit.flags & UNIT_BISYNC) == 0)			/* TRUE if user selected STR mode */

/*********************************************************************************************
 * mstring - allocate a copy of a string
 *********************************************************************************************/

char *mstring (char *str)
{
	int len;
	char *m;

	len = strlen(str)+1;
	if ((m = malloc(len)) == NULL) {
		printf("Out of memory!");
		return "?";				/* this will of course cause trouble if it's subsequently freed */
	}
	strcpy(m, str);
	return m;
}

/*********************************************************************************************
 * sca_socket_error - call when there is an error reading from or writing to socket
 *********************************************************************************************/

static void sca_socket_error (void)
{
	char name[100];

	/* print diagnostic? */
	printf("SCA socket error, closing connection\n");

	/* tell 1130 that connection was lost */
	CLRBIT(sca_dsw, SCA_DSW_READY);

	if (sca_sock != INVALID_SOCKET) {
		/* close socket, prepare to listen again if in listen mode. It's a "master" socket if it was an outgoing connection */
		sim_close_sock(sca_sock, (sca_unit.flags & UNIT_LISTEN) == 0);
		sca_sock = INVALID_SOCKET;

		if (sca_unit.filename != NULL)								/* reset filename string in unit record */
			free(sca_unit.filename);

		if (sca_unit.flags & UNIT_LISTEN) {
			sprintf(name, "(Listening on port %d)", sca_port);
			sca_unit.filename = mstring(name);
			printf("%s\n", name);
		}
		else
			sca_unit.filename = mstring("(connection failed)");
	}

	/* clear buffers */
	sca_nrcvd = sca_rcvptr = sca_n2send = sca_nsyns = 0;
}

/*********************************************************************************************
 * sca_transmit_byte, sca_flush - send data buffering mechanism
 *********************************************************************************************/

static void sca_flush (void)
{
	int nbytes;

	if (sca_n2send > 0) {
#if (DEBUG_SCA & DEBUG_SCA_FLUSH)
		printf("* SCA_FLUSH %d byte%s\n", sca_n2send, (sca_n2send == 1) ? "" : "s");
#endif

		if (sca_sock != INVALID_SOCKET) {
			nbytes = sim_write_sock(sca_sock, sca_sendbuf, sca_n2send);

			if (nbytes == SOCKET_ERROR)
				sca_socket_error();
			else if (nbytes != sca_n2send)
				printf("SOCKET BLOCKED -- NEED TO REWRITE IBM1130_SCA.C");

			/* I'm going to assume that SCA communications on the 1130 will consist entirely */
			/* of back and forth exchanges of short records, and so we should never stuff the pipe so full that */
			/* it blocks. If it does ever block, we'll have to come up with an asychronous buffering mechanism. */
		}

		sca_n2send = 0;						/* mark buffer cleared */
	}
}

/*********************************************************************************************
 * sca_transmit_byte - buffer a byte to be send to the socket
 *********************************************************************************************/

static void sca_transmit_byte (uint8 b)
{
	uint32 curtime;
	static uint32 last_syn_time, next_syn_time;

#if (DEBUG_SCA & DEBUG_SCA_TRANSMIT)
	printf("* SCA_TRANSMIT: %02x\n", b);
#endif

	/* write a byte to the socket. Let's assume an 8 bit frame in all cases.
	 * We buffer them up, then send the packet when (a) it fills up to a certain point
	 * and/or (b) some time has passed? We handle (b) by:
	 *		checking in sva_svc if several sca_svc calls are made w/o any XIO_WRITES, and
	 *		flushing send buffer on line turnaround, timeouts, or any other significant state change
	 */

	/* on socket error, call sca_socket_error(); */

	if (b == sichar) {
		if (sca_nsyns >= MAX_SYNS) {			/* we're suppressing SYN's */
			if (sca_keepalive > 0) {			/* we're sending keepalives, though... check to see if it's time */
				curtime = sim_os_msec();
				if (curtime >= next_syn_time || curtime < last_syn_time) {		/*  check for < last_syn_time because sim_os_msec() can wrap when OS has been running a long time */
					sca_sendbuf[sca_n2send++] = b;
					sca_sendbuf[sca_n2send++] = b;								/* send 2 of them */
					sca_flush();
					last_syn_time = curtime;
					next_syn_time = last_syn_time + sca_keepalive;
				}
			}
			return;
		}

		if (++sca_nsyns == MAX_SYNS) {			/* we've sent a bunch of SYN's, time to stop sending them */
			sca_sendbuf[sca_n2send] = b;		/* send last one */
			sca_flush();
			last_syn_time = sim_os_msec();		/* remember time, and note time to send next one */
			next_syn_time = last_syn_time + sca_keepalive;
			return;
		}
	}
	else
		sca_nsyns = 0;

	sca_sendbuf[sca_n2send] = b;				/* store character */

	if (++sca_n2send >= SCA_SEND_THRESHHOLD)
		sca_flush();							/* buffer is full, send it immediately */
}

/*********************************************************************************************
 * sca_interrupt (utility routine) - set a bit in the device status word and initiate an interrupt
 *********************************************************************************************/

static void sca_interrupt (int bit)
{
	sca_dsw |= bit;						/* set device status word bit(s) */
	SETBIT(ILSW[1], ILSW_1_SCA);		/* set interrupt level status word bit */

	calc_ints();						/* udpate simulator interrupt status (not really needed if within xio handler, since ibm1130_cpu calls it after xio handler) */
}

/*********************************************************************************************
 * sca_reset - reset the SCA device
 *********************************************************************************************/

static t_stat sca_reset (DEVICE *dptr)
{
	/* flush any pending data */
	sca_flush();
	sca_nrcvd = sca_rcvptr = sca_n2send = sca_nsyns = 0;

	/* reset sca activity */
	sca_state = SCA_STATE_IDLE;
	CLRBIT(sca_dsw, SCA_DSW_BUSY | SCA_DSW_AUTOANSWER_ENABLED | SCA_DSW_RECEIVE_RUN | SCA_DSW_READ_RESPONSE | SCA_DSW_WRITE_RESPONSE | SCA_DSW_CHECK | SCA_DSW_TIMEOUT | SCA_DSW_AUTOANSWER_REQUEST);
	sca_timer_state[0] = sca_timer_state[1] = sca_timer_state[2] = SCA_TIMER_INACTIVE;
	any_timer_running = FALSE;
	sca_timer_trigger = FALSE;

	if (sca_unit.flags & UNIT_ATT)					/* if unit is attached (or listening) */
		sim_activate(&sca_unit, sca_iwait);			/* poll for service. Must do this here as BOOT clears activity queue before resetting all devices */

	return SCPE_OK;
}

/*********************************************************************************************
 * sca_attach - attach the SCA device
 *********************************************************************************************/

static t_stat sca_attach (UNIT *uptr, char *cptr)
{
	t_bool do_listen;
    char *colon;
	uint32 ipaddr;
	int32 port;
	struct hostent *he;
	char name[256];
	static SOCKET sdummy = INVALID_SOCKET;
	fd_set wr_set, err_set;

	do_listen = sim_switches & SWMASK('L');		/* -l means listen mode */

	if (sca_unit.flags & UNIT_ATT)				/* if already attached, detach */
		detach_unit(&sca_unit);

	if (do_listen) {							/* if listen mode, string specifies socket number (only; otherwise it's a dummy argument) */
		if (isdigit(*cptr)) {					/* if digits specified, extract port number */
			port = atoi(cptr);
			if (port <= 0 || port > 65535)
				return SCPE_ARG;
			else
				sca_port = port;
		}
		/* else if nondigits specified, ignore... but the command has to have something there otherwise the core scp */
		/* attach_cmd() routine complains "too few arguments". */

		if ((sca_lsock = sim_master_sock(sca_port)) == INVALID_SOCKET)
			return SCPE_OPENERR;
		
		SETBIT(sca_unit.flags, UNIT_LISTEN);	/* note that we are listening, not yet connected */

		sprintf(name, "(Listening on port %d)", sca_port);
		sca_unit.filename = mstring(name);
		printf("%s\n", name);

	}
	else {
		while (*cptr && *cptr <= ' ')
			cptr++;

		if (! *cptr)
			return SCPE_2FARG;

		if ((colon = strchr(cptr, ':')) != NULL) {
			*colon++ = '\0';					/* clip hostname at colon */

			port = atoi(colon);					/* extract port number that follows it */
			if (port <= 0 || port > 65535)
				return SCPE_ARG;
			else
				sca_port = port;
		}

		if (sdummy == INVALID_SOCKET)
			if ((sdummy = sim_create_sock()) == INVALID_SOCKET)		/* create and keep a socket, to force initialization */
				return SCPE_IERR;									/* of socket library (e.g on Win32 call WSAStartup), else gethostbyname fails */

		if (get_ipaddr(cptr, &ipaddr, NULL) != SCPE_OK) {			/* try to parse hostname as dotted decimal nnn.nnn.nnn.nnn */
			if ((he = gethostbyname(cptr)) == NULL)					/* if not decimal, look up name through DNS */
				return SCPE_OPENERR;
		
			if ((ipaddr = * (unsigned long *) he->h_addr_list[0]) == INADDR_NONE)
				return SCPE_OPENERR;

			ipaddr = ntohl(ipaddr);									/* convert to host byte order; gethostbyname() gives us network order */
		}

		if ((sca_sock = sim_connect_sock(ipaddr, sca_port)) == INVALID_SOCKET)
			return SCPE_OPENERR;

		/* sim_connect_sock() sets socket to nonblocking before initiating the connect, so
		 * the connect is pending when it returns. For outgoing connections, the attach command should wait
		 * until the connection succeeds or fails. We use "accept" to wait and find out which way it goes...
		 */

		FD_ZERO(&wr_set);								/* we are only interested in info for sca_sock */
		FD_ZERO(&err_set);
		FD_SET(sca_sock, &wr_set);
		FD_SET(sca_sock, &err_set);

		select(3, NULL, &wr_set, &err_set, NULL);		/* wait for connection to complete or fail */

		if (FD_ISSET(sca_sock, &wr_set)) {				/* sca_sock appears in "writable" set -- connect completed */
			sprintf(name, "%s:%d", cptr, sca_port);
			sca_unit.filename = mstring(name);
			SETBIT(sca_dsw, SCA_DSW_READY);
		}
		else if (FD_ISSET(sca_sock, &err_set)) {		/* sca_sock appears in "error" set -- connect failed */
			sim_close_sock(sca_sock, TRUE);
			sca_sock = INVALID_SOCKET;
			return SCPE_OPENERR;
		}
		else {											/* if we get here my assumption about how select works is wrong */
			printf("SCA_SOCK NOT FOUND IN WR_SET -OR- ERR_SET, CODING IN IBM1130_SCA IS WRONG\n");
			sim_close_sock(sca_sock, TRUE);
			sca_sock = INVALID_SOCKET;
			return SCPE_OPENERR;
		}
	}
	
	/* set up socket connect or listen. on success, set UNIT_ATT.
	 * If listen mode, set UNIT_LISTEN. sca_svc will poll for connection
	 * If connect mode, set dsw SCA_DSW_READY to indicate connection is up
	 */
	
	SETBIT(sca_unit.flags, UNIT_ATT);					/* record successful socket binding */

	sca_state = SCA_STATE_IDLE;
	sim_activate(&sca_unit, sca_iwait);					/* start polling for service */

	sca_n2send = 0;										/* clear buffers */
	sca_nrcvd  = 0;
	sca_rcvptr = 0;
	sca_nsyns  = 0;

	return SCPE_OK;
}

/*********************************************************************************************
 * sca_detach - detach the SCA device
 *********************************************************************************************/

static t_stat sca_detach (UNIT *uptr)
{
	if ((sca_unit.flags & UNIT_ATT) == 0)
		return SCPE_OK;

	sca_flush();

	sca_state = SCA_STATE_IDLE;					/* stop processing during service calls */
	sim_cancel(&sca_unit);						/* stop polling for service */

	CLRBIT(sca_dsw, SCA_DSW_READY);				/* indicate offline */

	if (sca_sock != INVALID_SOCKET) {			/* close connected socket */
		sim_close_sock(sca_sock, (sca_unit.flags & UNIT_LISTEN) == 0);
		sca_sock = INVALID_SOCKET;
	}
	if (sca_lsock != INVALID_SOCKET) {			/* close listening socket */
		sim_close_sock(sca_lsock, TRUE);
		sca_lsock = INVALID_SOCKET;
	}
	
	free(sca_unit.filename);
	sca_unit.filename = NULL;

	CLRBIT(sca_unit.flags, UNIT_ATT|UNIT_LISTEN);

	return SCPE_OK;
}

/*********************************************************************************************
 * sca_check_connect - see if an incoming socket connection has com
 *********************************************************************************************/

static void sca_check_connect (void)
{
	uint32 ipaddr;
	char name[100];

	if ((sca_sock = sim_accept_conn(sca_lsock, &ipaddr)) == INVALID_SOCKET)
		return;

	ipaddr = htonl(ipaddr);								/* convert to network order so we can print it */

	sprintf(name, "%d.%d.%d.%d", ipaddr & 0xFF, (ipaddr >> 8) & 0xFF, (ipaddr >> 16) & 0xFF, (ipaddr >> 24) & 0xFF);

	printf("(SCA connection from %s)\n", name);

	if (sca_unit.filename != NULL)
		free(sca_unit.filename);

	sca_unit.filename = mstring(name);

	SETBIT(sca_dsw, SCA_DSW_READY);						/* indicate active connection  */

	if (sca_dsw & SCA_DSW_AUTOANSWER_ENABLED)			/* if autoanswer was enabled, I guess we should give them an interrupt. Untested. */
		sca_interrupt(SCA_DSW_AUTOANSWER_REQUEST);
}

/*********************************************************************************************
 *  sca_check_indata - try to fill receive buffer from socket
 *********************************************************************************************/

static void sca_check_indata (void)
{
	int nbytes;

	sca_rcvptr = 0;								/* reset pointers and count */
	sca_nrcvd  = 0;

#ifdef FAKE_SCA

	nbytes = 5;									/* FAKE: receive SYN SYN SYN SYN DLE ACK0 */
	sca_rcvbuf[0] = 0x32;
	sca_rcvbuf[1] = 0x32;
	sca_rcvbuf[2] = 0x32;
	sca_rcvbuf[3] = 0x10;
	sca_rcvbuf[4] = 0x70;

#else
												/* read socket; 0 is returned if no data is available */
	nbytes = sim_read_sock(sca_sock, sca_rcvbuf, SCA_RCVBUF_SIZE);

#endif

	if (nbytes < 0)
		sca_socket_error();
	else										/* zero or more */
		sca_nrcvd = nbytes;

#if (DEBUG_SCA & DEBUG_SCA_CHECK_INDATA)
	if (sca_nrcvd > 0)
		printf("* SCA_CHECK_INDATA %d byte%s\n", sca_nrcvd, (sca_nrcvd == 1) ? "" : "s");
#endif
}

/*********************************************************************************************
 * sca_svc - handled scheduled event. This will presumably be scheduled frequently, and can check
 * for incoming data, reasonableness of initiating a write interrupt, timeouts etc.
 *********************************************************************************************/

static t_stat sca_svc (UNIT *uptr)
{
	t_bool timeout;
	int msec_now;
	int i;

	/* if not connected, and if in wait-for-connection mode, check for connection attempt */
	if ((sca_unit.flags & UNIT_LISTEN) && ! (sca_dsw & SCA_DSW_READY))
		sca_check_connect();

	if (any_timer_running) {
		msec_now = sim_os_msec();

		timeout = FALSE;
		for (i = 0; i < 3; i++) {
			if (sca_timer_state[i] == SCA_TIMER_RUNNING && msec_now >= sca_timer_endtime[i]) {
				timeout = TRUE;
				sca_timer_state[i] = SCA_TIMER_TIMEDOUT;
#if (DEBUG_SCA & DEBUG_SCA_TIMERS)
				printf("+ SCA_TIMER %d timed out\n", i);
#endif

				if (i == TIMER_035S && sca_timer_trigger) {
					sca_timer_trigger = FALSE;										/* uninhibit the other two timers */
					sca_toggle_timer(TIMER_3S,   msec_now);
					sca_toggle_timer(TIMER_125S, msec_now);
				}
			}
		}

		if (timeout)
			sca_interrupt(SCA_DSW_TIMEOUT);

		any_timer_running = (sca_timer_state[0]| sca_timer_state[1] | sca_timer_state[2]) & SCA_TIMER_RUNNING;
	}

	if (sca_dsw & SCA_DSW_READY) {				/* if connected */

		/* if rcvd data buffer is empty, and if in one of the receive states, checÄk for arrival of received data */
		if (in_receive_state() && sca_rcvptr >= sca_nrcvd)
			sca_check_indata();

		switch (sca_state) {
			case SCA_STATE_IDLE:
				break;

			case SCA_STATE_TURN_SEND:
				/* has enough time gone by yet? if so... */
				sca_state = SCA_STATE_SEND1;
				sca_interrupt(SCA_DSW_WRITE_RESPONSE);
				break;

			case SCA_STATE_SEND_SYNC:
				sca_transmit_byte(sichar);
				break;

			case SCA_STATE_SEND1:
				sca_transmit_byte(sichar);			/* character interval has passed with no character written? character gap check */
				sca_interrupt(SCA_DSW_CHECK);		/* send an idle character (maybe? for socket IO maybe send nothing) and interrupt */

				if (idles_since_last_write >= 0) {
					if (++idles_since_last_write >= SCA_IDLE_THRESHHOLD) {
						sca_flush();
						idles_since_last_write = -1;	/* don't send a short packet again until real data gets written again */
						sca_nsyns = 0;					/* resume sending syns if output starts up again */
					}
				}
				break;

			case SCA_STATE_SEND2:
				sca_state = SCA_STATE_SEND1;			/* character has been sent. Schedule another transmit */
				sca_interrupt(SCA_DSW_WRITE_RESPONSE);
				break;

			case SCA_STATE_TURN_RECEIVE:
				/* has enough time gone by yet? if so... */
				sca_state = SCA_STATE_RECEIVE_SYNC;		/* assume a character is coming in */
				break;

			case SCA_STATE_RECEIVE_SYNC:
				if (sca_rcvptr < sca_nrcvd) {
					rcvd_char = sca_rcvbuf[sca_rcvptr++];
#if (DEBUG_SCA & DEBUG_SCA_RECEIVE_SYNC)
					printf("* SCA rcvd %02x %s\n", rcvd_char, (rcvd_char == sichar) ? "sync1" : "ignored");
#endif
					if (in_bsc_mode()) {
						if (rcvd_char == sichar) 			/* count the SYN but don't interrupt */
							sca_state = SCA_STATE_RECEIVE_SYNC2;
					}
				}
				break;

			case SCA_STATE_RECEIVE_SYNC2:
				if (sca_rcvptr < sca_nrcvd) {
					rcvd_char = sca_rcvbuf[sca_rcvptr++];
#if (DEBUG_SCA & DEBUG_SCA_RECEIVE_SYNC)
					printf("* SCA rcvd %02x %s\n", rcvd_char, (rcvd_char == sichar) ? "sync2" : "ignored");
#endif
					if (in_bsc_mode()) {
						if (rcvd_char == sichar)			/* count the SYN but don't interrupt */
							sca_state = SCA_STATE_RECEIVE_SYNC3;
					}
				}
				break;

			case SCA_STATE_RECEIVE_SYNC3:
			case SCA_STATE_RECEIVE1:
				if (sca_rcvptr < sca_nrcvd) {
					rcvd_char = sca_rcvbuf[sca_rcvptr++];

					if (sca_state == SCA_STATE_RECEIVE_SYNC3 && rcvd_char == sichar) {
						/* we're ready for data, but we're still seeing SYNs */
#if (DEBUG_SCA & DEBUG_SCA_RECEIVE_SYNC)
						printf("* SCA rcvd %02x extra sync\n", rcvd_char);
#endif
					}
					else {
#if (DEBUG_SCA & DEBUG_SCA_RECEIVE_DATA)
						printf("* SCA rcvd %02x\n", rcvd_char);
#endif
						sca_interrupt(SCA_DSW_READ_RESPONSE);
						sca_state = SCA_STATE_RECEIVE2;
					}
				}
				/* otherwise remain in state until data becomes available */
				break;

			case SCA_STATE_RECEIVE2:					/* if we are still in this state when another service interval has passed */
				if (sca_rcvptr < sca_nrcvd) {
					rcvd_char = sca_rcvbuf[sca_rcvptr++];

					sca_interrupt(SCA_DSW_CHECK);		/* overrun error */
					sca_state = SCA_STATE_RECEIVE1;		/* another character will come soon */
				}
				break;

			default:
				printf("Simulator error: unknown state %d in sca_svc\n", sca_state);
				sca_state = SCA_STATE_IDLE;
				break;
		}
	}
												/* schedule service again */
	sim_activate(&sca_unit, (sca_state == SCA_STATE_IDLE) ? sca_iwait : sca_cwait);

	return SCPE_OK;
}

/*********************************************************************************************
 * sca_toggle_timer - toggle a given timer's running/inhibited state for XIO_CONTROL
 *********************************************************************************************/

static void sca_toggle_timer (int n, int msec_now)
{
	if (sca_timer_state[n] == SCA_TIMER_RUNNING && sca_timer_trigger) {
		sca_timer_state[n] = SCA_TIMER_INHIBITED;
		sca_timer_timeleft[n] = sca_timer_endtime[n] - msec_now;		/* save time left */
#if (DEBUG_SCA & DEBUG_SCA_TIMERS)
		printf("+ SCA_TIMER %d inhibited\n", n);
#endif
	}
	else if (sca_timer_state[n] == SCA_TIMER_INHIBITED && ! sca_timer_trigger) {
		sca_timer_state[n] = SCA_TIMER_RUNNING;
		sca_timer_endtime[n] = sca_timer_timeleft[n] + msec_now;		/* compute new endtime */
#if (DEBUG_SCA & DEBUG_SCA_TIMERS)
		printf("+ SCA_TIMER %d uninhibited\n", n);
#endif
	}
}

static void sca_start_timer (int n, int msec_now)
{
	sca_timer_state[n]   = SCA_TIMER_RUNNING;
	sca_timer_endtime[n] = sca_timer_msec[n] + msec_now;
	any_timer_running    = TRUE;
#if (DEBUG_SCA & DEBUG_SCA_TIMERS)
	printf("+ SCA_TIMER %d started\n", n);
#endif
}

static void sca_halt_timer (int n)
{
#if (DEBUG_SCA & DEBUG_SCA_TIMERS)
	if (sca_timer_state[n] != SCA_TIMER_INACTIVE)
		printf("+ SCA_TIMER %d stopped\n", n);
#endif

	sca_timer_state[n] = SCA_TIMER_INACTIVE;
}

/*********************************************************************************************
 * sca_start_transmit - initiate transmit mode, from XIO_INITR or XIO_CONTROL (sync mode)
 *********************************************************************************************/

void sca_start_transmit (int32 iocc_addr, int32 modify)
{
	sca_flush();
	sca_nsyns = 0;							/* reset SYN suppression */

	/* Address bits are used to reset DSW conditions. */

	if (modify & 0x40)						/* bit 9. If set, reset all conditions */
		iocc_addr = 0xD800;

	iocc_addr &= 0xD800;					/* look at just bits 0, 1, 3 and 4 */
	if (iocc_addr) {						/* if set, these bits clear DSW conditions */
		CLRBIT(sca_dsw, iocc_addr);
		CLRBIT(ILSW[1], ILSW_1_SCA);		/* and I assume clear the interrupt condition too? (Seems unlikely that INITW would */
	}										/* be used in an interrupt service routine before SENSE, but who knows?) */

	if (! in_send_state()) {
		sca_state = SCA_STATE_TURN_SEND;	/* begin line turnaround */
	}
	else {
		sca_state = SCA_STATE_SEND1;		/* line is  */
		sca_interrupt(SCA_DSW_WRITE_RESPONSE);
	}

	SETBIT(sca_dsw, SCA_DSW_BUSY);			/* SCA is now busy, in transmit mode */

	sim_cancel(&sca_unit);
	sim_activate(&sca_unit, sca_cwait);		/* start polling frequently */
}

/*********************************************************************************************
 * xio_sca - handle SCA XIO command
 *********************************************************************************************/

void xio_sca (int32 iocc_addr, int32 func, int32 modify)
{
	char msg[80];
	int i, msec_now;

	switch (func) {
		case XIO_READ:								/* ***** XIO_READ - store last-received character to memory */
#if (DEBUG_SCA & DEBUG_SCA_XIO_READ)
			printf("SCA RD  addr %04x mod %02x rcvd_char %02x\n", iocc_addr, modify, rcvd_char);
#endif
			if (modify & 0x03) {					/* bits 14 and 15 */
#if (DEBUG_SCA & DEBUG_SCA_XIO_READ)
				printf("(rd diag)\n");
#endif
				/* if either of low two modifier bits is set, reads diagnostic words. whatever that is */
			}
			else {
				WriteW(iocc_addr, rcvd_char << 8);	/* always return last received character */

				/* note: in read mode, read w/o interrupt (or two reads after an interrupt) causes a check
				 * so here we have to check the current state and possibly cause an interrupt
				 * Failure to have read before another arrives (overrun) also causes a check.
				 */

				if (sca_state == SCA_STATE_RECEIVE2)/* XIO_READ should only be done (and only once) after a character interrupt */
					sca_state = SCA_STATE_RECEIVE1;	/* assume another character is coming in -- wait for it */
				else
					sca_interrupt(SCA_DSW_CHECK);
			}
			break;

		case XIO_WRITE:								/* ***** XIO_WRITE - transfer character from memory to output shift register */
#if (DEBUG_SCA & DEBUG_SCA_XIO_WRITE)
			printf("SCA WRT addr %04x (%04x) mod %02x\n", iocc_addr, M[iocc_addr & mem_mask], modify);
#endif
			if (modify & 0x01) {					/* bit 15 */
 				/* clear audible alarm trigger */
#if (DEBUG_SCA & DEBUG_SCA_XIO_WRITE)
				printf("(clr audible alarm trigger)\n");
#endif
			}
			/* else? or can they all operate in parallel? */
			if (modify & 0x02) {					/* bit 14 */
				/* set audible alarm trigger */
#if (DEBUG_SCA & DEBUG_SCA_XIO_WRITE)
				printf("(set audible alarm trigger)\n");
#endif
			}
			/* else? */
			if (modify & 0x04) {					/* bit 13 */
#if (DEBUG_SCA & DEBUG_SCA_XIO_WRITE)
				printf("(set SYN)\n");
#endif
				/* set sync/idle character */
				sichar = (uint8) (ReadW(iocc_addr) >> 8);
				sca_nsyns = 0;						/* reset SYN suppression */
			}
			/* else? does presence of mod bit preclude sending a character? */ 
			if ((modify & 0x07) == 0) {				/* no modifiers */
				/* transmit character --
				 * note: in write mode, failure to write soon enough after a write response interrupt causes a check
				 * Also, writing w/o interrupt (or two writes after an interrupt) causes a check
				 * so again, here we have to check the state to be sure that a write is appropriate
				 *
				 * note that in this simulator, we transmit the character immediately on XIO_WRITE. Therefore,
				 * there is no need to delay an end-operation function (XIO_CONTROL bit 13) until after a character time
				 */

				idles_since_last_write = 0;

				switch (sca_state) {
					case SCA_STATE_SEND_SYNC:
					case SCA_STATE_SEND1:
						sca_transmit_byte((uint8) (M[iocc_addr & mem_mask] >> 8));
						sca_state = SCA_STATE_SEND2;
						sim_cancel(&sca_unit);
						sim_activate(&sca_unit, sca_cwait);		/* schedule service after character write time */
						break;

					case SCA_STATE_SEND2:
						sca_interrupt(SCA_DSW_CHECK);			/* write issued while a character is in progress out? write overrun */
						break;

					case SCA_STATE_IDLE:						/* wrong time to issue a write, incorrect sca state */
					default:
						sca_flush();
						sca_interrupt(SCA_DSW_CHECK);			/* ??? or does this just perform a line turnaround and start transmission? */
						break;
				}

			}
			break;

		case XIO_CONTROL:							/* ***** XIO_CONTROL - manipulate interface state */
#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL)
			printf("SCA CTL addr %04x mod %02x\n", iocc_addr, modify);
#endif
			if (modify & 0x80) {					/* bit 8 */
				/* enable auto answer */
#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL)
				printf("(enable autoanswer)\n");
#endif
				SETBIT(sca_unit.flags, UNIT_AUTOANSWER);
				SETBIT(sca_dsw, SCA_DSW_AUTOANSWER_ENABLED);
			}

			if (modify & 0x40) {					/* bit 9 */
				/* disable auto answer */
#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL)
				printf("(disable autoanswer)\n");
#endif
				CLRBIT(sca_unit.flags, UNIT_AUTOANSWER);
				CLRBIT(sca_dsw, SCA_DSW_AUTOANSWER_ENABLED);
			}

			if (modify & 0x20) {					/* bit 10 */
				/* toggle timers, inhibit->run or run->inhibit */
#if (DEBUG_SCA & (DEBUG_SCA_XIO_CONTROL|DEBUG_SCA_TIMERS))
				printf("(toggle timers)\n");
#endif
				msec_now = sim_os_msec();

				if (in_bsc_mode())
					sca_timer_trigger = ! sca_timer_trigger;	/* toggle the timer trigger */
					if (sca_timer_trigger)						/* if we've just set it, we're stopping the other timers and  */
						sca_start_timer(TIMER_035S, msec_now);	/* starting the 0.35 sec timer */
					else
						sca_halt_timer(TIMER_035S);

				sca_toggle_timer(TIMER_3S, msec_now);			/* toggle the 3 sec and 1.35 sec timers accordingly */
				sca_toggle_timer(TIMER_125S, msec_now);

				any_timer_running = (sca_timer_state[0]| sca_timer_state[1] | sca_timer_state[2]) & SCA_TIMER_RUNNING;
			}

			if (modify & 0x10) {					/* bit 11 */
				/* enable sync mode. See references to this in FC manual
				 * In STR mode only, sends a constant stream of SYN's without any CPU intervention
				 * In BSC mode, appears to start the 1.25 second timer and otherwise act like INITW?
				 */
#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL)
				printf("(enable sync mode)\n");
#endif

				if (in_bsc_mode()) {				/* in bsc mode start the 1.25 second timer. not sure what resets it?!? */
					if (! in_send_state())			/* also may cause a line turnaround */
						sca_start_transmit(iocc_addr, 0);

				 	sca_start_timer(TIMER_125S, sim_os_msec());
				}
			}

			if (modify & 0x08) {					/* bit 12 */
				/* diagnostic mode. What does this do?!? */
#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL)
				printf("(diag mode)\n");
#endif
			}

			if (modify & 0x04) {					/* bit 13 */
				/* end operation (reset adapter. See references to this in FC manual). In transmit mode the real adapter delays this
				 * function until current character has been sent. We don't need to do that as the character got buffered for transmission
				 * immediately on XIO_WRITE.
				 */

#if (DEBUG_SCA & (DEBUG_SCA_XIO_CONTROL|DEBUG_SCA_XIO_INITR|DEBUG_SCA_XIO_INITW))
				printf("(end operation)\n");
#endif
				sca_state = SCA_STATE_IDLE;
				sca_timer_state[0] = sca_timer_state[1] = sca_timer_state[2] = SCA_TIMER_INACTIVE;
				any_timer_running = FALSE;
				sca_timer_trigger = FALSE;
				sca_nsyns = 0;						/* reset SYN suppression */
				CLRBIT(sca_dsw, SCA_DSW_BUSY);
			}

			if (modify & 0x02) {					/* bit 14 */
#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL)
				printf("(6 bit frame)\n");
#endif
				/* set six bit character frame. This is reset to 8 bits at every line turnaround */
				sca_frame = 6;
			}

			if (modify & 0x01) {					/* bit 15 */
#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL)
				printf("(7 bit frame)\n");
#endif
				/* set seven bit character frame. This is reset to 8 bits at every line turnaround */
				sca_frame = 7;
			}

			sca_flush();
			break;

		case XIO_INITW:								/* ***** XIO_INITW - put SCA in transmit mode */
#if (DEBUG_SCA & DEBUG_SCA_XIO_INITW)
			printf("SCA INITW addr %04x mod %02x\n", iocc_addr, modify);
#endif
			/* enter transmit mode. Causes line turnaround. Resets frame to 8 bits. */
			/* (may cause syncing, may involve a fixed timeout?) */
			sca_frame = 8;
			sca_start_transmit(iocc_addr, modify);	/* this code pulled out to a subroutine cuz transmit can be started from XIO_CONTROL too */
			break;

		case XIO_INITR:								/* ***** XIO_INITR - put SCA in receive mode */
#if (DEBUG_SCA & DEBUG_SCA_XIO_INITR)
			printf("SCA INITR addr %04x mod %02x\n", iocc_addr, modify);
#endif
			sca_flush();

			sca_nrcvd = sca_rcvptr = 0;				/* discard any data previously read! */
			sca_nsyns = 0;							/* reset SYN suppression */

			/* enter receive mode. Causes line turnaround (e.g. resets to 8 bit frame). Modifier bits are used here too */
			/* (may cause syncing, may involve a fixed timeout?) */

			sca_frame = 8;
			if (! in_receive_state())
				sca_state = SCA_STATE_TURN_RECEIVE;	/* begin line turnaround */
			else
				sca_state = SCA_STATE_RECEIVE_SYNC;

			SETBIT(sca_dsw, SCA_DSW_BUSY);			/* SCA is now busy, in receive mode */

			if (in_bsc_mode())						/* in BSC mode, start the 3 second timer when we enter receive mode */
				sca_start_timer(TIMER_3S, sim_os_msec());

			break;

		case XIO_SENSE_DEV:							/* ***** XIO_SENSE_DEV - read device status word */
#if (DEBUG_SCA & DEBUG_SCA_XIO_SENSE_DEV)
			printf("SCA SNS mod %02x dsw %04x\n", modify, sca_dsw);
#endif
			ACC = sca_dsw;							/* return DSW in accumulator */

			if (modify & 0x01) {					/* bit 15: reset interrupts */
#if (DEBUG_SCA & DEBUG_SCA_XIO_SENSE_DEV)
				printf("(reset interrupts)\n");
#endif
				CLRBIT(sca_dsw, SCA_DSW_READ_RESPONSE | SCA_DSW_WRITE_RESPONSE | SCA_DSW_CHECK | SCA_DSW_TIMEOUT | SCA_DSW_AUTOANSWER_REQUEST);
				CLRBIT(ILSW[1], ILSW_1_SCA);
			}

			if (modify & 0x02) {					/* bit 14: restart running timers */
#if (DEBUG_SCA & (DEBUG_SCA_XIO_SENSE_DEV|DEBUG_SCA_TIMERS))
				printf("(restart timers)\n");
#endif
				msec_now = sim_os_msec();			/* restart "any running timer?" All three, really? */
				for (i = 0; i < 3; i++)
					if (sca_timer_state[i] == SCA_TIMER_RUNNING || sca_timer_state[i] == SCA_TIMER_TIMEDOUT)
						sca_start_timer(i, msec_now);
			}
			break;

		default:
			sprintf(msg, "Invalid SCA XIO function %x", func);
			xio_error(msg);
	}
}
