blob: 03d46ef0572e943bc9eea86b7086d93af55e19a5 [file] [log] [blame] [raw]
/* 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-2010, 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>
#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, CONST 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 char sca_port[CBUFSIZE]; /* listening port */
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)
t_stat sca_set_baud (UNIT *uptr, int32 value, CONST 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) */
{ BRDATA (SCASOCKET, sca_port, 8, 8, sizeof(sca_port)) }, /* 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, CONST 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 (const char *str)
{
int len;
char *m;
len = strlen(str)+1;
if ((m = (char *)malloc(len)) == NULL) {
printf("Out of memory!");
return (char *)"?"; /* 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_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 %s)", 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, (char *) 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, CONST char *cptr)
{
char host[CBUFSIZE], port[CBUFSIZE];
t_bool do_listen;
char name[CBUFSIZE];
t_stat r;
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 port number (only; otherwise it's a dummy argument) */
if (sim_parse_addr (cptr, host, sizeof(host), NULL, port, sizeof(port), SCA_DEFAULT_PORT, NULL))
return SCPE_ARG;
if ((0 == strcmp(port, cptr)) && (0 == strcmp(port, "dummy")))
strcpy(port, SCA_DEFAULT_PORT);
sprintf(sca_port, "%s%s%s:%s", strchr(host, ':') ? "[" : "", host, strchr(host, ':') ? "]" : "", 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". */
sca_lsock = sim_master_sock(sca_port, &r);
if (r != SCPE_OK)
return r;
if (sca_lsock == INVALID_SOCKET)
return SCPE_OPENERR;
SETBIT(sca_unit.flags, UNIT_LISTEN); /* note that we are listening, not yet connected */
sprintf(name, "(Listening on port %s)", sca_port);
sca_unit.filename = mstring(name);
printf("%s\n", sca_unit.filename);
}
else {
while (*cptr && *cptr <= ' ')
cptr++;
if (! *cptr)
return SCPE_2FARG;
if (sim_parse_addr (cptr, host, sizeof(host), NULL, port, sizeof(port), SCA_DEFAULT_PORT, NULL))
return SCPE_ARG;
if ((0 == strcmp(cptr, port)) && (0 == strcmp(host, ""))) {
strcpy(host, port);
strcpy(port, SCA_DEFAULT_PORT);
}
sprintf(sca_port, "%s%s%s:%s", strchr(host, ':') ? "[" : "", host, strchr(host, ':') ? "]" : "", port);
if ((sca_sock = sim_connect_sock(sca_port, NULL, NULL)) == 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 "sim_check_conn" to wait and find out which way it goes...
*/
while (0 == sim_check_conn(sca_sock, 0))/* wait for connection to complete or fail */
sim_os_ms_sleep(1000);
if (1 == sim_check_conn(sca_sock, 0)) { /* sca_sock appears in "writable" set -- connect completed */
sprintf(name, "%s%s%s:%s", strchr(host, ':') ? "[" : "", host, strchr(host, ':') ? "]" : "", port);
sca_unit.filename = mstring(name);
SETBIT(sca_dsw, SCA_DSW_READY);
}
else { /* sca_sock appears in "error" set -- connect failed */
sim_close_sock(sca_sock);
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_sock = INVALID_SOCKET;
}
if (sca_lsock != INVALID_SOCKET) { /* close listening socket */
sim_close_sock(sca_lsock);
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)
{
char *connectaddress;
if ((sca_sock = sim_accept_conn(sca_lsock, &connectaddress)) == INVALID_SOCKET)
return;
printf("(SCA connection from %s)\n", connectaddress);
if (sca_unit.filename != NULL)
free(sca_unit.filename);
sca_unit.filename = connectaddress;
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, (char *) 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);
}
}