/* 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, 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) | |
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) */ | |
{ 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, 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 %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, 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) */ | |
r = sim_parse_addr (cptr, host, sizeof(host), NULL, port, sizeof(port), SCA_DEFAULT_PORT); | |
if (r != SCPE_OK) | |
return r; | |
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; | |
r = sim_parse_addr (cptr, host, sizeof(host), NULL, port, sizeof(port), SCA_DEFAULT_PORT); | |
if (r != SCPE_OK) | |
return r; | |
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, 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) | |
{ | |
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); | |
} | |
} |