diff --git a/README.md b/README.md index 19a7603..016f01b 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ the host processor having a large set of general-purpose registers. Coming soon, we swear! There is a unix man page included in this repository. +Github discussions are now enabled for this repository, and can be +used to ask for help. + ## Public Systems There are a set of emulators available for public use. These may be diff --git a/devsmlc.h b/devsmlc.h index 527dc4a..632b793 100644 --- a/devsmlc.h +++ b/devsmlc.h @@ -1,4 +1,3 @@ -#define DEBUG 0 /* Implements the SMLC subsystem for Primos. @@ -48,8 +47,10 @@ */ +#include #include #include +#include #define SMLC_DEVICEID 0050 /* HSSMLC/MDLC is '50, SMLC is '56 */ @@ -111,12 +112,12 @@ #define SMLC_ACK0_STATUS (007<<4) /* encoded ACK0 status */ #define SMLC_NAK_STATUS (013<<4) /* encoded NAK status */ #define SMLC_ENQ_STATUS (014<<4) /* encoded soh-ENQ status */ +#define SMLC_MASK_STATUS (017<<4) /* encoded status mask */ /* seconds between attempts to make TCP connections */ #define SMLC_CONNECTINTERVAL 15 -#if DEBUG #define HexColumn(x) (3 * (x) + 4) #define AsciiColumn(x) (HexColumn(16) + 2 + (x)) #define LogLineLength (AsciiColumn(16)) @@ -124,11 +125,9 @@ static void smlclogbytes(unsigned char *bytes, int len); static void smlclogflush(void); -static FILE *smlclog = NULL; static char smlclogbuf[LogLineLength + 1]; static int smlclogbytescol = 0; static char smlctimestamp[20]; -#endif /* connection states */ @@ -144,7 +143,9 @@ typedef enum { typedef enum { SMLC_STATERCVSYN = 0, SMLC_STATERCVDLE, - SMLC_STATERCVCHAR + SMLC_STATERCVCHAR, + SMLC_STATERCVSOH, + SMLC_STATERCVENQ } SmlcReceiveState; /* controller interrupt states */ @@ -155,20 +156,18 @@ typedef enum { SMLC_INTERRUPTPENDING } SmlcInterruptState; -#if DEBUG static char *intstates[] = { "not interrupting", "interrupting", "interrupt pending" }; -#endif /* send/receive buffer */ typedef struct smlcbuffer { unsigned short in; unsigned short out; - char data[SMLC_BUFSIZE]; + unsigned char data[SMLC_BUFSIZE]; } SmlcBuffer; /* Simplex Line Control Block. */ @@ -195,7 +194,7 @@ typedef struct plcb { /* physical line control block */ SmlcReceiveState recvstate; /* receive state */ bool starting; /* TRUE if waiting for response to SOH-ENQ */ bool naksent; /* TRUE if last frame sent was a NAK */ - int fd; /* Unix fd */ + int fd; /* Unix file descriptor of socket */ char *remoteID; /* remote TCP endpoint identifier */ uint32_t host; /* TCP host */ unsigned short port; /* TCP port */ @@ -225,19 +224,12 @@ static void smlcaddstatus(SmlcDCB *dcbp, int line, unsigned short status) { dmcbufbegea = dmcpair >> 16; dmcbufendea = dmcpair & 0xffff; if (dmcbufendea >= dmcbufbegea) { -#if DEBUG - fprintf(smlclog, "%s Interrupt status '%06o added for line %d on device '%02o, next '%06o, last '%06o\n", + TRACE(T_SMLC, "%s Interrupt status '%06o added for line %d on device '%02o, next '%06o, last '%06o\n", smlctimestamp, status, line, dcbp->deviceid, dmcbufbegea, dmcbufendea); -#endif put16io(status + (line * 2), dmcbufbegea); dmcbufbegea = INCVA(dmcbufbegea, 1); put16io(dmcbufbegea, dcbp->dmcstatus); dcbp->intstate = SMLC_INTERRUPTPENDING; -#if DEBUG - } else { - fprintf(smlclog, "%s Interrupt status '%06o -NOT- added for line %d on device '%02o, next '%06o, last '%06o\n", - smlctimestamp, status, line, dcbp->deviceid, dmcbufbegea, dmcbufendea); -#endif } } @@ -248,6 +240,8 @@ int devsmlc (int class, int func, int device) { static int optenable = 1; static struct timeval timeout = {0, 0}; + struct sockaddr_in addr; + socklen_t addrlen; char *bp; char buf[SMLC_BUFSIZE]; int bufidx; @@ -259,11 +253,12 @@ int devsmlc (int class, int func, int device) { short dmcnw; unsigned int dmcpair; int dx; - char *ep; + unsigned char *ep; int fd; + int flags; uint32_t hostaddr; int i; - char *ip; + unsigned char *ip; PLCB *lp; bool isetb; int lx; @@ -271,11 +266,10 @@ int devsmlc (int class, int func, int device) { int maxfd; int n; int nbytes; - char *np; - char *op; + unsigned char *np; + unsigned char *op; socklen_t optlen; int optval; - struct sockaddr_in raddr; fd_set readfds; int readycount; int rc; @@ -284,16 +278,14 @@ int devsmlc (int class, int func, int device) { int sx; unsigned short word; fd_set writefds; -#if DEBUG struct tm *tp; struct timespec sts; -#endif currenttime = time(0); -#if DEBUG +#ifndef NOTRACE tp = localtime(¤ttime); clock_gettime(CLOCK_REALTIME, &sts); - sprintf(smlctimestamp, "%02d:%02d:%02d.%09d", tp->tm_hour, tp->tm_min, tp->tm_sec, sts.tv_nsec); + sprintf(smlctimestamp, "%02d:%02d:%02d.%09ld", tp->tm_hour, tp->tm_min, tp->tm_sec, sts.tv_nsec); #endif switch (device) { @@ -320,20 +312,13 @@ int devsmlc (int class, int func, int device) { if (!inited) { FILE *cfgfile; - char devname[32]; - int lc; - int tempport; struct hostent* host; + int lc; char *p; + char tcpaddr[MAXHOSTLEN + 1]; + int tempport; -#if DEBUG - smlclog = fopen("smlc.log", "wt"); - if (smlclog == NULL) { - fprintf(stderr, "Failed to create smlclog.txt - aborting\n"); - fatal(NULL); - } - smlclogflush(); // initialize log buffer -#endif + smlclogflush(); // initialize log buffer for BSC frames /* initially, we don't know about any SMLC boards */ @@ -371,57 +356,54 @@ int devsmlc (int class, int func, int device) { while (fgets(buf, sizeof(buf), cfgfile) != NULL) { int n; lc++; - buf[sizeof(devname)] = 0; /* don't let sscanf overwrite anything */ + buf[sizeof(tcpaddr)] = 0; /* don't let sscanf overwrite anything */ buf[strlen(buf) - 1] = 0; /* remove trailing newline */ if (buf[0] == '\0' || buf[0] == ';' || buf[0] == '#') continue; - n = sscanf(buf, "%d %s", &i, devname); + n = sscanf(buf, "%d %s", &i, tcpaddr); if (n != 2) { - fprintf(stderr, "Can't parse smlc.cfg line #%d: %s\n", lc, buf); + fprintf(stderr, "smlc.cfg[%d] Can't parse: %s\n", lc, buf); continue; } if (i < 0 || i >= SMLC_MAXLINES) { - fprintf(stderr, "Line # %d out of range in smlc.cfg at line #%d: %s\n", i, lc, buf); + fprintf(stderr, "smlc.cfg[%d] SMLC line # %d out of range: %s\n", lc, i, buf); continue; } -#if DEBUG - fprintf(smlclog, "%s Line %d, SMLC line %d set to device %s\n", smlctimestamp, lc, i, devname); -#endif dx = i / SMLC_LINESPERBOARD; lx = i % SMLC_LINESPERBOARD; - if (strlen(devname) > MAXHOSTLEN) { - fprintf(stderr, "Line %d of smlc.cfg ignored: IP address too long\n", lc); + if (strlen(tcpaddr) > MAXHOSTLEN) { + fprintf(stderr, "smlc.cfg[%d] IP address too long: %s\n", lc, buf); continue; } tempport = 0; - if ((p=strtok(devname, PDELIM)) != NULL) { - host = gethostbyname(p); + host = NULL; + p = index(tcpaddr, ':'); + if (p != NULL) { + *p++ = '\0'; + host = gethostbyname(tcpaddr); if (host == NULL) { - fprintf(stderr, "Line %d of smlc.cfg ignored: can't resolve IP address %s\n", lc, p); + fprintf(stderr, "smlc.cfg[%d] Can't resolve IP address of %s\n", lc, tcpaddr); continue; } - if ((p=strtok(NULL, DELIM)) != NULL) { - tempport = atoi(p); - if (tempport < 1 || tempport > 65000) { - fprintf(stderr, "Line %d of smlc.cfg ignored: port number %d out of range 1-65000\n", tempport, lc); - continue; - } - } + tempport = atoi(p); + } else { + tempport = atoi(tcpaddr); } if (tempport == 0) { - fprintf(stderr, "Line %d of smlc.cfg ignored: no IP address or port number specified\n", lc); + fprintf(stderr, "smlc.cfg[%d] No IP address or port number specified\n", lc); + continue; + } else if (tempport < 1 || tempport > 65535) { + fprintf(stderr, "smlc.cfg[%d] Port number %d out of range 1-65535\n", lc, tempport); continue; } dc[dx].plines[lx].remoteID = (char *)malloc(32); - hostaddr = ntohl(*(unsigned int *)host->h_addr); + hostaddr = (host != NULL) ? ntohl(*(unsigned int *)host->h_addr) : 0; sprintf(dc[dx].plines[lx].remoteID, "%d.%d.%d.%d:%d", (hostaddr >> 24) & 0xff, (hostaddr >> 16) & 0xff, (hostaddr >> 8) & 0xff, hostaddr & 0xff, tempport); dc[dx].plines[lx].host = hostaddr; dc[dx].plines[lx].port = tempport; -#if DEBUG - fprintf(smlclog, "%s TCP address, host=%x, port=%d, controller=%d, line=%d\n", smlctimestamp, dc[dx].plines[lx].host, - tempport, dx, lx); -#endif + TRACE(T_SMLC, "%s smlc.cfg[%d] controller '%02o, line %d, TCP address %s\n", smlctimestamp, lc, dx + 050, lx, + dc[dx].plines[lx].remoteID); } fclose(cfgfile); } @@ -445,40 +427,30 @@ int devsmlc (int class, int func, int device) { switch (func) { case 000: // enable high-speed SMLC clock -#if DEBUG - fprintf(smlclog, "%s OCP '%02o%02o enable high-speed clock\n", smlctimestamp, func, device); -#endif + TRACE(T_SMLC, "%s OCP '%02o%02o enable high-speed clock\n", smlctimestamp, func, device); IOSKIP; break; case 013: // acknowledge and clear interrupt -#if DEBUG - fprintf(smlclog, "%s OCP '%02o%02o acknowledge and clear interrupt\n", smlctimestamp, func, device); -#endif + TRACE(T_SMLC, "%s OCP '%02o%02o acknowledge and clear interrupt\n", smlctimestamp, func, device); dc[dx].intstate = SMLC_NOTINTERRUPTING; IOSKIP; break; case 015: // enable interrupts -#if DEBUG - fprintf(smlclog, "%s OCP '%02o%02o enable interrupts\n", smlctimestamp, func, device); -#endif + TRACE(T_SMLC, "%s OCP '%02o%02o enable interrupts\n", smlctimestamp, func, device); dc[dx].intenabled = 1; IOSKIP; break; case 016: // disable interrupts -#if DEBUG - fprintf(smlclog, "%s OCP '%02o%02o disable interrupts\n", smlctimestamp, func, device); -#endif + TRACE(T_SMLC, "%s OCP '%02o%02o disable interrupts\n", smlctimestamp, func, device); dc[dx].intenabled = 0; IOSKIP; break; case 017: // initialize controller -#if DEBUG - fprintf(smlclog, "%s OCP '%02o%02o initialize controller\n", smlctimestamp, func, device); -#endif + TRACE(T_SMLC, "%s OCP '%02o%02o initialize controller\n", smlctimestamp, func, device); dc[dx].intvector = 0; dc[dx].intenabled = 0; dc[dx].intstate = SMLC_NOTINTERRUPTING; @@ -534,9 +506,7 @@ int devsmlc (int class, int func, int device) { TRACE(T_INST, " SKS '%02o%02o\n", func, device); if (func == 004) { /* skip if not interrupting */ -#if DEBUG - fprintf(smlclog, "%s SKS '02%02o skip if not interrupting, state is %s\n", smlctimestamp, device, intstates[dc[dx].intstate]); -#endif + TRACE(T_SMLC, "%s SKS '02%02o skip if not interrupting, state is %s\n", smlctimestamp, device, intstates[dc[dx].intstate]); if (dc[dx].intstate == SMLC_INTERRUPTING) { IOSKIP; } @@ -564,16 +534,12 @@ int devsmlc (int class, int func, int device) { } } putcrs16(A, data); -#if DEBUG - fprintf(smlclog, "%s INA '00%02o input status returns 0x%04x\n", smlctimestamp, device, data); -#endif + TRACE(T_SMLC, "%s INA '00%02o input status returns 0x%04x\n", smlctimestamp, device, data); IOSKIP; } else if (func == 011) { /* report device ID */ putcrs16(A, SMLC_DEVICEID); -#if DEBUG - fprintf(smlclog, "%s INA '11%02o report device ID returns 0x%04x\n", smlctimestamp, device, getcrs16(A)); -#endif + TRACE(T_SMLC, "%s INA '11%02o report device ID returns 0x%04x\n", smlctimestamp, device, getcrs16(A)); IOSKIP; } else { @@ -595,9 +561,7 @@ int devsmlc (int class, int func, int device) { data = getcrs16(A); dc[dx].fncode = data >> 8; dc[dx].lineno = data & 0377; -#if DEBUG - fprintf(smlclog, "%s OTA '00%02o set function/line '%03o/'%03o\n", smlctimestamp, device, dc[dx].fncode, dc[dx].lineno); -#endif + TRACE(T_SMLC, "%s OTA '00%02o set function/line '%03o/'%03o\n", smlctimestamp, device, dc[dx].fncode, dc[dx].lineno); switch (dc[dx].fncode) { case 000: // set modem controls @@ -627,15 +591,11 @@ int devsmlc (int class, int func, int device) { switch (dc[dx].fncode) { case 000: // set modem controls -#if DEBUG - fprintf(smlclog, "%s OTA '01%02o line '%02o set modem controls '%06o\n", smlctimestamp, device, dc[dx].lineno, data); - fprintf(smlclog, "%s DTR: %d, RTS: %d\n", smlctimestamp, data & 1, (data & 2) == 1); -#endif + TRACE(T_SMLC, "%s OTA '01%02o line '%02o set modem controls '%06o\n", smlctimestamp, device, dc[dx].lineno, data); + TRACE(T_SMLC, "%s DTR: %d, RTS: %d\n", smlctimestamp, data & 1, (data & 2) == 1); sp->dtr = data & 1; if ((data & 1) == 0 && lp->fd != -1) { -#if DEBUG - fprintf(smlclog, "%s close connection to %s\n", smlctimestamp, lp->remoteID); -#endif + TRACE(T_SMLC, "%s close connection to %s\n", smlctimestamp, lp->remoteID); close(lp->fd); lp->fd = -1; lp->connstate = SMLC_STATEDISCONNECTED; @@ -647,9 +607,7 @@ int devsmlc (int class, int func, int device) { case 010: // set special character i = (data >> 8) & 0xff; ch = data & 0xff; -#if DEBUG - fprintf(smlclog, "%s OTA '01%02o line '%02o set special character %d <%02x>\n", smlctimestamp, device, dc[dx].lineno, i, ch); -#endif + TRACE(T_SMLC, "%s OTA '01%02o line '%02o set special character %d <%02x>\n", smlctimestamp, device, dc[dx].lineno, i, ch); if (i < SMLC_MAXSPCHARS) { sp->spchars[i] = ch; } else { @@ -660,30 +618,26 @@ int devsmlc (int class, int func, int device) { break; case 012: // set configuration word -#if DEBUG - fprintf(smlclog, "%s OTA '01%02o line '%02o set configuration word 0x%04x\n", smlctimestamp, device, dc[dx].lineno, data); -#endif + TRACE(T_SMLC, "%s OTA '01%02o line '%02o set configuration word 0x%04x\n", smlctimestamp, device, dc[dx].lineno, data); sp->configword = data; IOSKIP; break; case 014: // set primary I/O channel -#if DEBUG - fprintf(smlclog, "%s OTA '01%02o line '%02o set primary I/O channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data); -#endif + TRACE(T_SMLC, "%s OTA '01%02o line '%02o set primary I/O channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data); sp->dmcprimarycount = (data >> 12) + 1; sp->dmcprimaryidx = 0; sp->dmcprimary = data & 0x7fe; if (data != 0) { if ((data & 04000) == 0) fatal("Can't run SMLC in DMA mode!"); -#if DEBUG - fprintf(smlclog, "%s %d buffers\n", smlctimestamp, sp->dmcprimarycount); +#ifndef NOTRACE + TRACE(T_SMLC, "%s %d buffers\n", smlctimestamp, sp->dmcprimarycount); for (i = 0; i < sp->dmcprimarycount; i++) { dmcpair = get32io ((data & 0x7fe) + (i * 2)); dmcbufbegea = dmcpair >> 16; dmcbufendea = dmcpair & 0xffff; dmcnw = (dmcbufendea - dmcbufbegea) + 1; - fprintf(smlclog, "%s %d: next '%06o, last '%06o, words %d\n", smlctimestamp, i, dmcbufbegea, dmcbufendea, dmcnw); + TRACE(T_SMLC, "%s %d: next '%06o, last '%06o, words %d\n", smlctimestamp, i, dmcbufbegea, dmcbufendea, dmcnw); } #endif } @@ -691,10 +645,8 @@ int devsmlc (int class, int func, int device) { break; case 015: // enable -#if DEBUG - fprintf(smlclog, "%s OTA '01%02o line '%02o enable '%06o (%s %s)\n", smlctimestamp, device, dc[dx].lineno, data, + TRACE(T_SMLC, "%s OTA '01%02o line '%02o enable '%06o (%s %s)\n", smlctimestamp, device, dc[dx].lineno, data, sx ? "xmit" : "recv", (data & 1) ? "on" : "off"); -#endif sp->enabled = data & 1; if (sp->enabled) devpoll[device] = 1; IOSKIP; @@ -706,22 +658,20 @@ int devsmlc (int class, int func, int device) { * this OTA function. Consequently, this emulation module does not currently implement any logic to alternate * storing recceived data between the primary and backup channels. */ -#if DEBUG - fprintf(smlclog, "%s OTA '01%02o line '%02o set backup I/O channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data); -#endif + TRACE(T_SMLC, "%s OTA '01%02o line '%02o set backup I/O channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data); sp->dmcbackupcount = (data >> 12) + 1; sp->dmcbackupidx = 0; sp->dmcbackup = data & 0x7fe; if (data != 0) { if ((data & 04000) == 0) fatal("Can't run SMLC in DMA mode!"); -#if DEBUG - fprintf(smlclog, "%s %d buffers\n", smlctimestamp, sp->dmcbackupcount); +#ifndef NOTRACE + TRACE(T_SMLC, "%s %d buffers\n", smlctimestamp, sp->dmcbackupcount); for (i = 0; i < sp->dmcbackupcount; i++) { dmcpair = get32io ((data & 0x7fe) + (i * 2)); dmcbufbegea = dmcpair >> 16; dmcbufendea = dmcpair & 0xffff; dmcnw = (dmcbufendea - dmcbufbegea) + 1; - fprintf(smlclog, "%s %d: next '%06o, last '%06o, words %d\n", smlctimestamp, i, dmcbufbegea, dmcbufendea, dmcnw); + TRACE(T_SMLC, "%s %d: next '%06o, last '%06o, words %d\n", smlctimestamp, i, dmcbufbegea, dmcbufendea, dmcnw); } #endif } @@ -729,9 +679,7 @@ int devsmlc (int class, int func, int device) { break; case 017: // set status channel -#if DEBUG - fprintf(smlclog, "%s OTA '01%02o line '%02o set status channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data); -#endif + TRACE(T_SMLC, "%s OTA '01%02o line '%02o set status channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data); if (!(data & 04000) && data != 0) fatal("Can't run SMLC in DMA mode!"); if (lx != 0 || sx != 0) { @@ -740,9 +688,7 @@ int devsmlc (int class, int func, int device) { fatal(NULL); } dc[dx].dmcstatus = data & 0x7fe; -#if DEBUG - fprintf(smlclog, "%s next %06o, last %06o\n", smlctimestamp, get32io(dc[dx].dmcstatus) >> 16, get32io(dc[dx].dmcstatus) & 0xffff); -#endif + TRACE(T_SMLC, "%s next %06o, last %06o\n", smlctimestamp, get32io(dc[dx].dmcstatus) >> 16, get32io(dc[dx].dmcstatus) & 0xffff); IOSKIP; break; @@ -754,9 +700,7 @@ int devsmlc (int class, int func, int device) { break; case 016: // set interrupt vector -#if DEBUG - fprintf(smlclog, "%s OTA '16%02o set interrupt vector '%06o\n", smlctimestamp, device, getcrs16(A)); -#endif + TRACE(T_SMLC, "%s OTA '16%02o set interrupt vector '%06o\n", smlctimestamp, device, getcrs16(A)); dc[dx].intvector = getcrs16(A); IOSKIP; break; @@ -785,9 +729,7 @@ int devsmlc (int class, int func, int device) { } else { gv.intvec = dc[dx].intvector; dc[dx].intstate = SMLC_INTERRUPTING; -#if DEBUG - fprintf(smlclog, "%s Raise interrupt on device '%02o\n", smlctimestamp, device); -#endif + TRACE(T_SMLC, "%s Raise interrupt on device '%02o\n", smlctimestamp, device); } return 0; } @@ -798,23 +740,41 @@ int devsmlc (int class, int func, int device) { for (lx = 0; lx < SMLC_LINESPERBOARD; lx++) { lp = &dc[dx].plines[lx]; - if (lp->host == 0) continue; + if (lp->port == 0) continue; switch (lp->connstate) { case SMLC_STATEDISCONNECTED: - if (lp->nextconntime <= currenttime && lp->slines[SMLC_RECVIX].dtr != 0) { + if (lp->host != 0) { + // + // Workstation mode. + // + // Periodically attempt to create a connection to the remote host, when + // the DTR modem signal is up. + // + if (lp->slines[SMLC_RECVIX].dtr == 0 || lp->nextconntime > currenttime) continue; fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (fd < 0) { fprintf(stderr, "Failed to create socket for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx); fatal(NULL); } - setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optenable, sizeof(optenable)); - fcntl(fd, F_SETFL, O_NONBLOCK); - bzero((char *) &raddr, sizeof(raddr)); - raddr.sin_family = AF_INET; - raddr.sin_addr.s_addr = htonl(dc[dx].plines[lx].host); - raddr.sin_port = htons(dc[dx].plines[lx].port); - rc = connect(fd, (struct sockaddr *)&raddr, sizeof(raddr)); + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optenable, sizeof(optenable)) == -1) { + fprintf(stderr, "Failed to set socket option SO_KEEPALICE for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx); + fatal(NULL); + } + if ((flags = fcntl(fd, F_GETFL)) == -1) { + fprintf(stderr, "Failed to get flags for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx); + fatal(NULL); + } + flags |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) == -1) { + fprintf(stderr, "Failed to set flags for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx); + fatal(NULL); + } + bzero((char *) &addr, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(lp->host); + addr.sin_port = htons(lp->port); + rc = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); lp->connstate = SMLC_STATECONNECTING; if (rc < 0 && errno != EINPROGRESS) { fprintf(stderr, "Failed to create connection to %s for SMLC line %d\n", lp->remoteID, @@ -824,18 +784,67 @@ int devsmlc (int class, int func, int device) { lp->connstate = SMLC_STATEDISCONNECTED; break; } else { // connection in progress -#if DEBUG - fprintf(smlclog, "%s Connection initiated to %s for SMLC line %d\n", smlctimestamp, lp->remoteID, + TRACE(T_SMLC, "%s Connection initiated to %s for SMLC line %d\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx); -#endif lp->fd = fd; } + } else { + // + // Host mode. + // + // Begin listening for connections. + // + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + fprintf(stderr, "Failed to create socket for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx); + fatal(NULL); + } + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optenable, sizeof(optenable)) == -1) { + fprintf(stderr, "Failed to set socket option SO_REUSEADDR for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx); + fatal(NULL); + } + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(lp->port); + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + fprintf(stderr, "Failed to bind to port %d for SMLC line %d\n", lp->port, (dx * SMLC_LINESPERBOARD) + lx); + fatal(NULL); + } + if (listen(fd, 1) == -1) { + fprintf(stderr, "Failed to listen on port %d for SMLC line %d\n", lp->port, (dx * SMLC_LINESPERBOARD) + lx); + fatal(NULL); + } + if ((flags = fcntl(fd, F_GETFL)) == -1) { + fprintf(stderr, "Failed to get flags for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx); + fatal(NULL); + } + flags |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) == -1) { + fprintf(stderr, "Failed to set flags for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx); + fatal(NULL); + } + lp->fd = fd; + lp->connstate = SMLC_STATECONNECTING; + TRACE(T_SMLC, "%s Listening for connections on %s for SMLC line %d\n", smlctimestamp, lp->remoteID, + (dx * SMLC_LINESPERBOARD) + lx); } - /* fall through when no error has occurred */ + if (lp->connstate != SMLC_STATECONNECTING) break; case SMLC_STATECONNECTING: - FD_SET(lp->fd, &writefds); - if (lp->fd > maxfd) maxfd = lp->fd; + if (lp->host != 0) { + // + // Workstation mode. Connection is complete when socket is writable. + // + FD_SET(lp->fd, &writefds); + if (lp->fd > maxfd) maxfd = lp->fd; + } else if (lp->slines[SMLC_RECVIX].dtr != 0) { + // + // Host mode. Connection request received when socket is readable. + // However, test for requests only when DTR modem signal is up. + // + FD_SET(lp->fd, &readfds); + if (lp->fd > maxfd) maxfd = lp->fd; + } break; case SMLC_STATECONNECTED: @@ -863,11 +872,9 @@ int devsmlc (int class, int func, int device) { if (dmcnw <= 0) { sp->dmcprimaryidx += 1; } else { -#if DEBUG - fprintf(smlclog, "%s Line %d on device '%02o send to %s from channel '%06o\n", smlctimestamp, lx, device, lp->remoteID, + TRACE(T_SMLC, "%s Line %d on device '%02o send to %s from channel '%06o\n", smlctimestamp, lx, device, lp->remoteID, sp->dmcprimary); - fprintf(smlclog, "%s next '%06o, last '%06o, words %d\n", smlctimestamp, dmcbufbegea, dmcbufendea, dmcnw); -#endif + TRACE(T_SMLC, "%s next '%06o, last '%06o, words %d\n", smlctimestamp, dmcbufbegea, dmcbufendea, dmcnw); n = 0; while (n < dmcnw && np + 1 < ep) { word = get16io(dmcbufbegea); @@ -876,10 +883,8 @@ int devsmlc (int class, int func, int device) { *np++ = word & 0377; n += 1; } -#if DEBUG - fprintf(smlclog, "%s %d words transferred to output buffer\n", smlctimestamp, n); - fprintf(smlclog, "%s next '%06o, last '%06o\n", smlctimestamp, dmcbufbegea, dmcbufendea); -#endif + TRACE(T_SMLC, "%s %d words transferred to output buffer\n", smlctimestamp, n); + TRACE(T_SMLC, "%s next '%06o, last '%06o\n", smlctimestamp, dmcbufbegea, dmcbufendea); put16io(dmcbufbegea, sp->dmcprimary + (sp->dmcprimaryidx * 2)); if (n == dmcnw) { status = SMLC_EOR | SMLC_XMIT; @@ -925,7 +930,7 @@ int devsmlc (int class, int func, int device) { for (lx = 0; lx < SMLC_LINESPERBOARD; lx++) { lp = &dc[dx].plines[lx]; - if (lp->host == 0) continue; + if (lp->port == 0) continue; switch (lp->connstate) { case SMLC_STATECONNECTING: @@ -934,10 +939,7 @@ int devsmlc (int class, int func, int device) { rc = getsockopt(lp->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen); if (rc < 0) { fprintf(stderr, "Failed to get socket status for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx); - close(lp->fd); - lp->fd = -1; - lp->nextconntime = currenttime + SMLC_CONNECTINTERVAL; - lp->connstate = SMLC_STATEDISCONNECTED; + fatal(NULL); } else if (optval != 0) { // connection failed fprintf(stderr, "Failed to create connection to %s for SMLC line %d\n", lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx); close(lp->fd); @@ -945,47 +947,72 @@ int devsmlc (int class, int func, int device) { lp->nextconntime = currenttime + SMLC_CONNECTINTERVAL; lp->connstate = SMLC_STATEDISCONNECTED; } else { -#if DEBUG - fprintf(smlclog, "%s Connection created to %s for SMLC line %d\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx); -#endif + TRACE(T_SMLC, "%s Connection created to %s for SMLC line %d\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx); lp->connstate = SMLC_STATECONNECTED; lp->recvstate = SMLC_STATERCVSYN; - lp->starting = 0; - lp->naksent = 0; - lp->slines[SMLC_RECVIX].buf.in = 0; - lp->slines[SMLC_RECVIX].buf.out = 0; - lp->slines[SMLC_XMITIX].buf.in = 0; - lp->slines[SMLC_XMITIX].buf.out = 0; - smlcaddstatus(&dc[dx], lx, SMLC_DSS_BIT | SMLC_DSR_BIT | SMLC_CTS_BIT | SMLC_DCD_BIT | SMLC_SQ_BIT); + } + } else if (FD_ISSET(lp->fd, &readfds)) { + addrlen = sizeof(addr); + fd = accept(lp->fd, (struct sockaddr *)&addr, &addrlen); + if (fd >= 0) { + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optenable, sizeof(optenable)) == -1) { + fprintf(stderr, "Failed to set socket option SO_KEEPALICE for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx); + fatal(NULL); + } + if ((flags = fcntl(fd, F_GETFL)) == -1) { + fprintf(stderr, "Failed to get flags for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx); + fatal(NULL); + } + flags |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) == -1) { + fprintf(stderr, "Failed to set flags for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx); + fatal(NULL); + } + close(lp->fd); + lp->fd = fd; + lp->connstate = SMLC_STATECONNECTED; + lp->recvstate = SMLC_STATERCVSOH; + strcpy(lp->remoteID, inet_ntoa(addr.sin_addr)); + TRACE(T_SMLC, "%s Connection accepted from %s for SMLC line %d\n", smlctimestamp, lp->remoteID, + (dx * SMLC_LINESPERBOARD) + lx); + } + else { + TRACE(T_SMLC, "%s Spurious connection attempt on SMLC line %d\n", smlctimestamp, (dx * SMLC_LINESPERBOARD) + lx); } } - if (lp->connstate != SMLC_STATECONNECTED) - break; + if (lp->connstate == SMLC_STATECONNECTED) { + lp->starting = 0; + lp->naksent = 0; + lp->slines[SMLC_RECVIX].buf.in = 0; + lp->slines[SMLC_RECVIX].buf.out = 0; + lp->slines[SMLC_XMITIX].buf.in = 0; + lp->slines[SMLC_XMITIX].buf.out = 0; + smlcaddstatus(&dc[dx], lx, SMLC_DSS_BIT | SMLC_DSR_BIT | SMLC_CTS_BIT | SMLC_DCD_BIT | SMLC_SQ_BIT); + } + break; case SMLC_STATECONNECTED: sp = &lp->slines[SMLC_RECVIX]; if (FD_ISSET(lp->fd, &readfds)) { n = read(lp->fd, &sp->buf.data[sp->buf.in], sizeof(sp->buf.data) - sp->buf.in); if (n > 0) { -#if DEBUG - fprintf(smlclog, "%s Line %d on device '%02o received %d bytes from %s:\n", smlctimestamp, lx, device, n, lp->remoteID); +#ifndef NOTRACE + TRACE(T_SMLC, "%s Line %d on device '%02o received %d bytes from %s:\n", smlctimestamp, lx, device, n, lp->remoteID); smlclogbytes(&sp->buf.data[sp->buf.in], n); smlclogflush(); #endif sp->buf.in += n; } else if (n < 0 && (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)) { n = 0; -#if DEBUG - fprintf(smlclog, "%s recv from %s for line %d returned errno %d (ignored)\n", + TRACE(T_SMLC, "%s read from %s for line %d returned errno %d (ignored)\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx, errno); -#endif } else { -#if DEBUG +#ifndef NOTRACE if (n < 0) { - fprintf(smlclog, "%s recv from %s for line %d failed with errno %d\n", + TRACE(T_SMLC, "%s read from %s for line %d failed with errno %d\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx, errno); } else { - fprintf(smlclog, "%s recv from %s for line %d returned EOF\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx); + TRACE(T_SMLC, "%s read from %s for line %d returned EOF\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx); } #endif lp->connstate = SMLC_STATEDISCONNECTING; @@ -1007,17 +1034,15 @@ int devsmlc (int class, int func, int device) { sp->dmcprimaryidx += 1; continue; } -#if DEBUG - fprintf(smlclog, "%s Line %d on device '%02o receive from %s to channel '%06o\n", smlctimestamp, lx, device, lp->remoteID, + TRACE(T_SMLC, "%s Line %d on device '%02o receive from %s to channel '%06o\n", smlctimestamp, lx, device, lp->remoteID, sp->dmcprimary); - fprintf(smlclog, "%s next '%06o, last '%06o, words %d\n", smlctimestamp, dmcbufbegea, dmcbufendea, dmcnw); -#endif + TRACE(T_SMLC, "%s next '%06o, last '%06o, words %d\n", smlctimestamp, dmcbufbegea, dmcbufendea, dmcnw); maxbytes = dmcnw * 2; n = 0; dmcnw = 0; status = 0; isetb = 0; - while (n < maxbytes && np < ip) { + while (n < maxbytes && np < ip && status == 0) { ch = *np++; if (lp->recvstate == SMLC_STATERCVSYN) { if (ch == SMLC_SYN) { @@ -1030,10 +1055,8 @@ int devsmlc (int class, int func, int device) { lp->recvstate = SMLC_STATERCVSYN; break; } else { -#if DEBUG - fprintf(smlclog, "%s NAK received after sending NAK to %s for line %d\n", smlctimestamp, lp->remoteID, + TRACE(T_SMLC, "%s NAK received after sending NAK to %s for line %d\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx); -#endif lp->connstate = SMLC_STATEDISCONNECTING; smlcaddstatus(&dc[dx], lx, SMLC_DSS_BIT); devpoll[device] = 1; /* return ASAP */ @@ -1052,29 +1075,64 @@ int devsmlc (int class, int func, int device) { word = ch << 8; } n += 1; - if (lp->recvstate == SMLC_STATERCVDLE) { + switch (lp->recvstate) { + case SMLC_STATERCVDLE: lp->recvstate = SMLC_STATERCVCHAR; - if (ch == SMLC_ACK0) { + switch (ch) { + case SMLC_ACK0: status |= SMLC_ENCODE_BIT | SMLC_ACK0_STATUS; lp->recvstate = SMLC_STATERCVSYN; break; - } else if (ch == SMLC_STX) { + case SMLC_STX: status |= SMLC_ENCODE_BIT | SMLC_STX_STATUS; break; - } else if (ch == SMLC_ETB) { + case SMLC_ETB: status |= SMLC_ENCODE_BIT | SMLC_ETB_STATUS; lp->recvstate = SMLC_STATERCVSYN; isetb = 1; break; + default: + // do nothing + break; } - } else if (ch == SMLC_DLE) { - lp->recvstate = SMLC_STATERCVDLE; + break; + case SMLC_STATERCVSOH: + if (ch == SMLC_SOH) { + status |= SMLC_ENCODE_BIT | SMLC_SOH_STATUS; + lp->recvstate = SMLC_STATERCVENQ; + } else { + n = 0; + } + break; + case SMLC_STATERCVENQ: + if (ch == SMLC_ENQ) { + status |= SMLC_ENCODE_BIT | SMLC_ENQ_STATUS; + lp->recvstate = SMLC_STATERCVSYN; + } else if (ch == SMLC_SOH) { + status |= SMLC_ENCODE_BIT | SMLC_SOH_STATUS; + } else { + n = 0; + lp->recvstate = SMLC_STATERCVSOH; + } + break; + case SMLC_STATERCVCHAR: + if (ch == SMLC_DLE) { + lp->recvstate = SMLC_STATERCVDLE; + } + break; + default: + fprintf(stderr, "Invalid recv state %d for SMLC line %d\n", lp->recvstate, (dx * SMLC_LINESPERBOARD) + lx); + fatal(NULL); + break; } } if ((n & 1) != 0) { // odd number of bytes processed, so store "incomplete" word, or back up one byte if (n < maxbytes - && ((n > 1 && *(np - 1) == SMLC_ETB && *(np - 2) == SMLC_DLE) - || (n == 1 && *(np - 1) == SMLC_NAK))) { + && (status & SMLC_ENCODE_BIT) != 0 + && ( (status & SMLC_MASK_STATUS) == SMLC_ETB_STATUS + || (status & SMLC_MASK_STATUS) == SMLC_SOH_STATUS + || (status & SMLC_MASK_STATUS) == SMLC_ENQ_STATUS + || (status & SMLC_MASK_STATUS) == SMLC_NAK_STATUS)) { put16io(word, dmcbufbegea); dmcbufbegea = INCVA(dmcbufbegea, 1); dmcnw += 1; @@ -1085,10 +1143,8 @@ int devsmlc (int class, int func, int device) { } put16io(dmcbufbegea, sp->dmcprimary + (sp->dmcprimaryidx * 2)); sp->dmcprimaryidx += 1; -#if DEBUG - fprintf(smlclog, "%s %d words transferred from input buffer\n", smlctimestamp, dmcnw); - fprintf(smlclog, "%s next '%06o, last '%06o\n", smlctimestamp, dmcbufbegea, dmcbufendea); -#endif + TRACE(T_SMLC, "%s %d words transferred from input buffer\n", smlctimestamp, dmcnw); + TRACE(T_SMLC, "%s next '%06o, last '%06o\n", smlctimestamp, dmcbufbegea, dmcbufendea); if (n == maxbytes) { status |= SMLC_EOR; } else if (n > maxbytes) { @@ -1117,8 +1173,8 @@ int devsmlc (int class, int func, int device) { lp->naksent = nbytes >= 5 && sp->buf.data[sp->buf.out + 4] == SMLC_NAK; n = write(lp->fd, &sp->buf.data[sp->buf.out], nbytes); if (n >= 0) { -#if DEBUG - fprintf(smlclog, "%s Line %d on device '%02o sent %d bytes to %s:\n", smlctimestamp, lx, device, n, lp->remoteID); +#ifndef NOTRACE + TRACE(T_SMLC, "%s Line %d on device '%02o sent %d bytes to %s:\n", smlctimestamp, lx, device, n, lp->remoteID); smlclogbytes(&sp->buf.data[sp->buf.out], n); smlclogflush(); #endif @@ -1128,10 +1184,8 @@ int devsmlc (int class, int func, int device) { smlcaddstatus(&dc[dx], lx, SMLC_LCT | SMLC_XMIT); } } else if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) { -#if DEBUG - fprintf(smlclog, "%s Connection failed to %s for line %d with errno %d\n", smlctimestamp, lp->remoteID, + TRACE(T_SMLC, "%s Connection failed to %s for line %d with errno %d\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx, errno); -#endif lp->connstate = SMLC_STATEDISCONNECTING; smlcaddstatus(&dc[dx], lx, SMLC_DSS_BIT); devpoll[device] = 1; /* return ASAP */ @@ -1155,7 +1209,6 @@ int devsmlc (int class, int func, int device) { return 0; } -#if DEBUG static unsigned char ebcdicToAscii[256] = { /* 00-07 */ 0x00, 0x01, 0x02, 0x03, 0x1a, 0x09, 0x1a, 0x7f, /* 08-0F */ 0x1a, 0x1a, 0x1a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, @@ -1193,14 +1246,16 @@ static unsigned char ebcdicToAscii[256] = { }; static void smlclogflush(void) { +#ifndef NOTRACE if (smlclogbytescol > 0) { - fputs(smlclogbuf, smlclog); - fputc('\n', smlclog); - fflush(smlclog); + fputs(smlclogbuf, gv.tracefile); + fputc('\n', gv.tracefile); + fflush(gv.tracefile); } smlclogbytescol = 0; memset(smlclogbuf, ' ', LogLineLength); smlclogbuf[LogLineLength] = '\0'; +#endif } static void smlclogbytes(unsigned char *bytes, int len) { @@ -1211,24 +1266,27 @@ static void smlclogbytes(unsigned char *bytes, int len) { int hexCol; int i; - ascCol = AsciiColumn(smlclogbytescol); - hexCol = HexColumn(smlclogbytescol); +#ifndef NOTRACE + if (gv.traceflags & T_SMLC) { + ascCol = AsciiColumn(smlclogbytescol); + hexCol = HexColumn(smlclogbytescol); - for (i = 0; i < len; i++) { - b = bytes[i]; - ac = ebcdicToAscii[b]; - if (ac < 0x20 || ac >= 0x7f) { - ac = '.'; - } - sprintf(hex, "%02x", b); - memcpy(smlclogbuf + hexCol, hex, 2); - hexCol += 3; - smlclogbuf[ascCol++] = ac; - if (++smlclogbytescol >= 16) { - smlclogflush(); - ascCol = AsciiColumn(smlclogbytescol); - hexCol = HexColumn(smlclogbytescol); + for (i = 0; i < len; i++) { + b = bytes[i]; + ac = ebcdicToAscii[b]; + if (ac < 0x20 || ac >= 0x7f) { + ac = '.'; + } + sprintf(hex, "%02x", b); + memcpy(smlclogbuf + hexCol, hex, 2); + hexCol += 3; + smlclogbuf[ascCol++] = ac; + if (++smlclogbytescol >= 16) { + smlclogflush(); + ascCol = AsciiColumn(smlclogbytescol); + hexCol = HexColumn(smlclogbytescol); + } } } -} #endif +} diff --git a/em.1 b/em.1 index b1f2ae8..37cd052 100644 --- a/em.1 +++ b/em.1 @@ -166,13 +166,15 @@ Example: .EE .TP smlc.cfg -Used to associate sync serial (MDLC or HSSMLC) lines with a destination -IP address and port number. Optional. Comments work the same as -amlc.cfg. Example: +Used to associate outbound sync serial (MDLC or HSSMLC) lines with destination +IP addresses and port numbers, or to associate inbound sync serial lines +with port numbers. An outbound line corresponds to an RJE site defined as a +SLAVE site, and an inbound line corresponds to an RJE site defined as a MASTER +site. Optional. Comments work the same as amlc.cfg. Example: .EX -0 10.1.1.3:2554 -1 127.0.0.1:2554 +0 10.1.1.3:2554 # Connect to TCP port 2554 of host 10.1.1.3 for outbound line 0 +1 9951 # Listen on TCP port 9951 for inbound line 1 .EE .TP console.log @@ -286,6 +288,7 @@ off|Start with tracing disabled all|Everything flush|Flush trace file after each write tlb|STLB and IOTLB changes +smlc|MDLC/HSSMLC device I/O OWNERL|Execution of this PCB #instruction count|Begin after specified number of instructions |(the leading # is literal) diff --git a/em.c b/em.c index d3b1976..f87f681 100644 --- a/em.c +++ b/em.c @@ -1,5 +1,5 @@ /* Pr1me Computer emulator, Jim Wilcoxson (prirun@gmail.com), April 4, 2005 - Copyright (C) 2005-2019, Jim Wilcoxson. All Rights Reserved. + Copyright (C) 2005-2021, Jim Wilcoxson. All Rights Reserved. Emulates a Prime Computer system by: - booting from a Prime disk image (normal usage) @@ -287,6 +287,7 @@ static void macheck (unsigned short p300vec, unsigned short chkvec, unsigned int T_PX Process exchange T_LM License manager T_TLB STLB and IOTLB changes + T_SMLC SMLC device I/O */ #define T_EAR 0x00000001 @@ -309,6 +310,7 @@ static void macheck (unsigned short p300vec, unsigned short chkvec, unsigned int #define T_GET 0x00020000 #define T_EAS 0x00040000 #define T_TLB 0x00080000 +#define T_SMLC 0x00100000 #define BITMASK16(b) (0x8000 >> ((b)-1)) #define BITMASK32(b) ((unsigned int)(0x80000000) >> ((b)-1)) @@ -4366,7 +4368,7 @@ int main (int argc, char **argv) { #define XRBRACE 0375 printf("[Prime Emulator ver %s %s]\n", REV, __DATE__); - printf("[Copyright (C) 2005-2019 Jim Wilcoxson prirun@gmail.com]\n"); + printf("[Copyright (C) 2005-2021 Jim Wilcoxson prirun@gmail.com]\n"); if (argc > 1 && (strcmp(argv[1],"--version") == 0)) { exit(0); } @@ -4606,6 +4608,8 @@ int main (int argc, char **argv) { setlinebuf(gv.tracefile); else if (strcmp(argv[i],"tlb") == 0) gv.traceflags |= T_TLB; + else if (strcmp(argv[i],"smlc") == 0) + gv.traceflags |= T_SMLC; else if (isdigit(argv[i][0]) && strlen(argv[i]) <= 3 && sscanf(argv[i],"%d", &templ) == 1) gv.traceuser = 0100000 | (templ<<6); /* form OWNERL for user # */ else if (strlen(argv[i]) == 6 && sscanf(argv[i],"%o", &templ) == 1) @@ -6293,13 +6297,31 @@ d_hlt: /* 000000 */ if (bootarg) { printf("\nCPU halt, instruction #%u at %o/%o %s: %o %o ^%06o^\nA='%o/%d B='%o/%d L='%o/%d X=%o/%d", gv.instcount, RPH, RPL, searchloadmap(gv.prevpc,' '), get16t(gv.prevpc), get16t(gv.prevpc+1), lights, getcrs16(A), getcrs16s(A), getcrs16(B), getcrs16s(B), getcrs32(A), getcrs32s(A), getcrs16(X), getcrs16s(X)); while (1) { + int n; + static int ttydev; + ttydev = open("/dev/tty", O_RDWR, 0); + if (ttydev < 0) { + perror(" error opening /dev/tty"); + fatal(NULL); + } printf("\nPress Enter to continue, h to halt... "); - utempa = getchar(); + fflush(stdout); + utempa = ' '; + n = 0; + while (n == 0) + n = read(ttydev, &utempa, 1); +/* utempa = getchar(); */ printf("\n"); if (utempa == '\r' || utempa == '\n') + { + close(ttydev); goto fetch; + } if (utempa == 'h') + { + close(ttydev); break; + } } } fatal("CPU halt");