1
0
mirror of https://github.com/prirun/p50em.git synced 2026-03-07 11:07:01 +00:00

6 Commits

Author SHA1 Message Date
Dennis Boone
d6d01cd05c Workaround for locked up AMLC lines
As it was in the real world, especially with dialup ports, it is possible
for an emulator AMLC line to get blocked by stray flow control characters.
The blocked port may appear dead to the next user to connect, if they are
not "serial savvy" enough to try sending an XON character.

A large number of vulnerability scanners have found and have been hitting
the public emulators, injecting HTTP transactions or worse into the AMLC
ports and often locking all of them daily.

This workaround injects an XON (DC1, 0221) and a line kill character as
extracted from the DEFKIL field in FIGCOM, into the AMLC line when a new
connection is received.

There is a -dolineclear command line switch to enable the behavior.
2021-02-07 00:37:15 -05:00
Dennis Boone
ea2edd89de Fix terminal i/o issue in d_hlt
Due to the configuration of the tty, the getchar() call in d_hlt
returns nothing.  Work around this in the same way the SOC console
code does, by opening it on a separate unit, and reading from that
instead.  Without this fix, the code which looks for certain halt
cases and offers "continue or exit" just spins, printing its message
and getting no input.
2021-02-06 17:11:55 -05:00
Dennis Boone
3d71fd85f0 Update copyrights. 2021-02-03 18:51:34 -05:00
Dennis Boone
5d119bb2c5 Update README.md
Github discussions.
2021-01-21 14:24:49 -05:00
Dennis Boone
aa8095217f Merge pull request #5 from kej715/master
Add support for RJE MASTER mode in MDLC/HSSMLC controller, and use TRACE system instead of separate debug tracing.
2020-08-07 10:19:32 -04:00
Kevin Jordan
89043d09a4 Add support for RJE MASTER mode in MDLC/HSSMLC controller, and use
TRACE macro in the controller implementation.
2020-08-06 23:43:50 -04:00
5 changed files with 381 additions and 231 deletions

View File

@@ -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 Coming soon, we swear! There is a unix man page included in this
repository. repository.
Github discussions are now enabled for this repository, and can be
used to ask for help.
## Public Systems ## Public Systems
There are a set of emulators available for public use. These may be There are a set of emulators available for public use. These may be

View File

@@ -262,6 +262,7 @@ int devamlc (int class, int func, int device) {
unsigned short ctinterrupt; /* 1 bit per line */ unsigned short ctinterrupt; /* 1 bit per line */
unsigned short dss; /* 1 bit per line */ unsigned short dss; /* 1 bit per line */
unsigned short connected; /* 1 bit per line */ unsigned short connected; /* 1 bit per line */
unsigned short dolineclear; /* 1 bit per line */
unsigned short serial; /* true if any CT_SERIAL lines */ unsigned short serial; /* true if any CT_SERIAL lines */
unsigned short dsstime; /* countdown to dss poll */ unsigned short dsstime; /* countdown to dss poll */
short fd[16]; /* Unix fd, 1 per line */ short fd[16]; /* Unix fd, 1 per line */
@@ -329,6 +330,7 @@ int devamlc (int class, int func, int device) {
for (dx=0; dx<MAXBOARDS; dx++) { for (dx=0; dx<MAXBOARDS; dx++) {
dc[dx].deviceid = 0; dc[dx].deviceid = 0;
dc[dx].connected = 0; dc[dx].connected = 0;
dc[dx].dolineclear = 0;
dc[dx].serial = 0; dc[dx].serial = 0;
for (lx = 0; lx < 16; lx++) { for (lx = 0; lx < 16; lx++) {
dc[dx].fd[lx] = -1; dc[dx].fd[lx] = -1;
@@ -885,6 +887,7 @@ int devamlc (int class, int func, int device) {
close(dc[i].fd[lx]); close(dc[i].fd[lx]);
dc[i].dss |= BITMASK16(lx+1); dc[i].dss |= BITMASK16(lx+1);
dc[i].connected |= BITMASK16(lx+1); dc[i].connected |= BITMASK16(lx+1);
dc[i].dolineclear |= BITMASK16(lx+1);
dc[i].fd[lx] = fd; dc[i].fd[lx] = fd;
dc[i].tstate[lx] = TS_DATA; dc[i].tstate[lx] = TS_DATA;
//printf("em: AMLC connection, fd=%d, device='%o, line=%d\n", fd, dc[i].deviceid, lx); //printf("em: AMLC connection, fd=%d, device='%o, line=%d\n", fd, dc[i].deviceid, lx);
@@ -1140,6 +1143,53 @@ dorecv:
if (dc[dx].deviceid == 0 || dc[dx].connected == 0 || dc[dx].eor) if (dc[dx].deviceid == 0 || dc[dx].connected == 0 || dc[dx].eor)
continue; continue;
/* Inject xon / kill if dolineclear is true */
if (dolinecleararg)
for (lx = 0; lx < 16; lx++)
if (dc[dx].dolineclear & BITMASK16(lx+1))
{
unsigned char ch;
unsigned short utemp;
int dmcpair, lcount;
ea_t dmcea, dmcbufbegea, dmcbufendea;
unsigned short dmcnw;
if (dc[dx].bufnum)
dmcea = dc[dx].dmcchan + 2;
else
dmcea = dc[dx].dmcchan;
dmcpair = get32io(dmcea);
dmcbufbegea = dmcpair>>16;
dmcbufendea = dmcpair & 0xffff;
dmcnw = dmcbufendea - dmcbufbegea + 1;
if (dmcnw < 2)
continue;
utemp = lx<<12 | 0x0200 | 0221; /* dc1/xon */
put16io(utemp, dmcbufbegea);
dmcbufbegea = INCVA(dmcbufbegea, 1);
ch = (unsigned char)get16(MAKEVA(014, 0705));
utemp = lx<<12 | 0x0200 | ch; /* kill */
put16io(utemp, dmcbufbegea);
dmcbufbegea = INCVA(dmcbufbegea, 1);
dc[dx].recvlx = lx;
if (dmcbufbegea-1 > dmcbufendea)
fatal("AMLC tumble table overflowed?");
put16io(dmcbufbegea, dmcea);
if (dmcbufbegea > dmcbufendea) { /* end of range has occurred */
dc[dx].bufnum = 1-dc[dx].bufnum;
dc[dx].eor = 1;
neweor = 1;
anyeor = 1;
}
dc[dx].dolineclear &= ~BITMASK16(lx+1);
}
/* select to see which lines have data to be read */ /* select to see which lines have data to be read */
FD_ZERO(&readfds); FD_ZERO(&readfds);

504
devsmlc.h
View File

@@ -1,4 +1,3 @@
#define DEBUG 0
/* /*
Implements the SMLC subsystem for Primos. Implements the SMLC subsystem for Primos.
@@ -48,8 +47,10 @@
*/ */
#include <ctype.h>
#include <stdbool.h> #include <stdbool.h>
#include <time.h> #include <time.h>
#include <arpa/inet.h>
#define SMLC_DEVICEID 0050 /* HSSMLC/MDLC is '50, SMLC is '56 */ #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_ACK0_STATUS (007<<4) /* encoded ACK0 status */
#define SMLC_NAK_STATUS (013<<4) /* encoded NAK status */ #define SMLC_NAK_STATUS (013<<4) /* encoded NAK status */
#define SMLC_ENQ_STATUS (014<<4) /* encoded soh-ENQ 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 */ /* seconds between attempts to make TCP connections */
#define SMLC_CONNECTINTERVAL 15 #define SMLC_CONNECTINTERVAL 15
#if DEBUG
#define HexColumn(x) (3 * (x) + 4) #define HexColumn(x) (3 * (x) + 4)
#define AsciiColumn(x) (HexColumn(16) + 2 + (x)) #define AsciiColumn(x) (HexColumn(16) + 2 + (x))
#define LogLineLength (AsciiColumn(16)) #define LogLineLength (AsciiColumn(16))
@@ -124,11 +125,9 @@
static void smlclogbytes(unsigned char *bytes, int len); static void smlclogbytes(unsigned char *bytes, int len);
static void smlclogflush(void); static void smlclogflush(void);
static FILE *smlclog = NULL;
static char smlclogbuf[LogLineLength + 1]; static char smlclogbuf[LogLineLength + 1];
static int smlclogbytescol = 0; static int smlclogbytescol = 0;
static char smlctimestamp[20]; static char smlctimestamp[20];
#endif
/* connection states */ /* connection states */
@@ -144,7 +143,9 @@ typedef enum {
typedef enum { typedef enum {
SMLC_STATERCVSYN = 0, SMLC_STATERCVSYN = 0,
SMLC_STATERCVDLE, SMLC_STATERCVDLE,
SMLC_STATERCVCHAR SMLC_STATERCVCHAR,
SMLC_STATERCVSOH,
SMLC_STATERCVENQ
} SmlcReceiveState; } SmlcReceiveState;
/* controller interrupt states */ /* controller interrupt states */
@@ -155,20 +156,18 @@ typedef enum {
SMLC_INTERRUPTPENDING SMLC_INTERRUPTPENDING
} SmlcInterruptState; } SmlcInterruptState;
#if DEBUG
static char *intstates[] = { static char *intstates[] = {
"not interrupting", "not interrupting",
"interrupting", "interrupting",
"interrupt pending" "interrupt pending"
}; };
#endif
/* send/receive buffer */ /* send/receive buffer */
typedef struct smlcbuffer { typedef struct smlcbuffer {
unsigned short in; unsigned short in;
unsigned short out; unsigned short out;
char data[SMLC_BUFSIZE]; unsigned char data[SMLC_BUFSIZE];
} SmlcBuffer; } SmlcBuffer;
/* Simplex Line Control Block. */ /* Simplex Line Control Block. */
@@ -195,7 +194,7 @@ typedef struct plcb { /* physical line control block */
SmlcReceiveState recvstate; /* receive state */ SmlcReceiveState recvstate; /* receive state */
bool starting; /* TRUE if waiting for response to SOH-ENQ */ bool starting; /* TRUE if waiting for response to SOH-ENQ */
bool naksent; /* TRUE if last frame sent was a NAK */ 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 */ char *remoteID; /* remote TCP endpoint identifier */
uint32_t host; /* TCP host */ uint32_t host; /* TCP host */
unsigned short port; /* TCP port */ unsigned short port; /* TCP port */
@@ -225,19 +224,12 @@ static void smlcaddstatus(SmlcDCB *dcbp, int line, unsigned short status) {
dmcbufbegea = dmcpair >> 16; dmcbufbegea = dmcpair >> 16;
dmcbufendea = dmcpair & 0xffff; dmcbufendea = dmcpair & 0xffff;
if (dmcbufendea >= dmcbufbegea) { if (dmcbufendea >= dmcbufbegea) {
#if DEBUG TRACE(T_SMLC, "%s Interrupt status '%06o added for line %d on device '%02o, next '%06o, last '%06o\n",
fprintf(smlclog, "%s Interrupt status '%06o added for line %d on device '%02o, next '%06o, last '%06o\n",
smlctimestamp, status, line, dcbp->deviceid, dmcbufbegea, dmcbufendea); smlctimestamp, status, line, dcbp->deviceid, dmcbufbegea, dmcbufendea);
#endif
put16io(status + (line * 2), dmcbufbegea); put16io(status + (line * 2), dmcbufbegea);
dmcbufbegea = INCVA(dmcbufbegea, 1); dmcbufbegea = INCVA(dmcbufbegea, 1);
put16io(dmcbufbegea, dcbp->dmcstatus); put16io(dmcbufbegea, dcbp->dmcstatus);
dcbp->intstate = SMLC_INTERRUPTPENDING; 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 int optenable = 1;
static struct timeval timeout = {0, 0}; static struct timeval timeout = {0, 0};
struct sockaddr_in addr;
socklen_t addrlen;
char *bp; char *bp;
char buf[SMLC_BUFSIZE]; char buf[SMLC_BUFSIZE];
int bufidx; int bufidx;
@@ -259,11 +253,12 @@ int devsmlc (int class, int func, int device) {
short dmcnw; short dmcnw;
unsigned int dmcpair; unsigned int dmcpair;
int dx; int dx;
char *ep; unsigned char *ep;
int fd; int fd;
int flags;
uint32_t hostaddr; uint32_t hostaddr;
int i; int i;
char *ip; unsigned char *ip;
PLCB *lp; PLCB *lp;
bool isetb; bool isetb;
int lx; int lx;
@@ -271,11 +266,10 @@ int devsmlc (int class, int func, int device) {
int maxfd; int maxfd;
int n; int n;
int nbytes; int nbytes;
char *np; unsigned char *np;
char *op; unsigned char *op;
socklen_t optlen; socklen_t optlen;
int optval; int optval;
struct sockaddr_in raddr;
fd_set readfds; fd_set readfds;
int readycount; int readycount;
int rc; int rc;
@@ -284,16 +278,14 @@ int devsmlc (int class, int func, int device) {
int sx; int sx;
unsigned short word; unsigned short word;
fd_set writefds; fd_set writefds;
#if DEBUG
struct tm *tp; struct tm *tp;
struct timespec sts; struct timespec sts;
#endif
currenttime = time(0); currenttime = time(0);
#if DEBUG #ifndef NOTRACE
tp = localtime(&currenttime); tp = localtime(&currenttime);
clock_gettime(CLOCK_REALTIME, &sts); 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 #endif
switch (device) { switch (device) {
@@ -320,20 +312,13 @@ int devsmlc (int class, int func, int device) {
if (!inited) { if (!inited) {
FILE *cfgfile; FILE *cfgfile;
char devname[32];
int lc;
int tempport;
struct hostent* host; struct hostent* host;
int lc;
char *p; char *p;
char tcpaddr[MAXHOSTLEN + 1];
int tempport;
#if DEBUG smlclogflush(); // initialize log buffer for BSC frames
smlclog = fopen("smlc.log", "wt");
if (smlclog == NULL) {
fprintf(stderr, "Failed to create smlclog.txt - aborting\n");
fatal(NULL);
}
smlclogflush(); // initialize log buffer
#endif
/* initially, we don't know about any SMLC boards */ /* 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) { while (fgets(buf, sizeof(buf), cfgfile) != NULL) {
int n; int n;
lc++; 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 */ buf[strlen(buf) - 1] = 0; /* remove trailing newline */
if (buf[0] == '\0' || buf[0] == ';' || buf[0] == '#') if (buf[0] == '\0' || buf[0] == ';' || buf[0] == '#')
continue; continue;
n = sscanf(buf, "%d %s", &i, devname); n = sscanf(buf, "%d %s", &i, tcpaddr);
if (n != 2) { 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; continue;
} }
if (i < 0 || i >= SMLC_MAXLINES) { 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; 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; dx = i / SMLC_LINESPERBOARD;
lx = i % SMLC_LINESPERBOARD; lx = i % SMLC_LINESPERBOARD;
if (strlen(devname) > MAXHOSTLEN) { if (strlen(tcpaddr) > MAXHOSTLEN) {
fprintf(stderr, "Line %d of smlc.cfg ignored: IP address too long\n", lc); fprintf(stderr, "smlc.cfg[%d] IP address too long: %s\n", lc, buf);
continue; continue;
} }
tempport = 0; tempport = 0;
if ((p=strtok(devname, PDELIM)) != NULL) { host = NULL;
host = gethostbyname(p); p = index(tcpaddr, ':');
if (p != NULL) {
*p++ = '\0';
host = gethostbyname(tcpaddr);
if (host == NULL) { 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; continue;
} }
if ((p=strtok(NULL, DELIM)) != NULL) { tempport = atoi(p);
tempport = atoi(p); } else {
if (tempport < 1 || tempport > 65000) { tempport = atoi(tcpaddr);
fprintf(stderr, "Line %d of smlc.cfg ignored: port number %d out of range 1-65000\n", tempport, lc);
continue;
}
}
} }
if (tempport == 0) { 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; continue;
} }
dc[dx].plines[lx].remoteID = (char *)malloc(32); 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, sprintf(dc[dx].plines[lx].remoteID, "%d.%d.%d.%d:%d", (hostaddr >> 24) & 0xff, (hostaddr >> 16) & 0xff,
(hostaddr >> 8) & 0xff, hostaddr & 0xff, tempport); (hostaddr >> 8) & 0xff, hostaddr & 0xff, tempport);
dc[dx].plines[lx].host = hostaddr; dc[dx].plines[lx].host = hostaddr;
dc[dx].plines[lx].port = tempport; dc[dx].plines[lx].port = tempport;
#if DEBUG TRACE(T_SMLC, "%s smlc.cfg[%d] controller '%02o, line %d, TCP address %s\n", smlctimestamp, lc, dx + 050, lx,
fprintf(smlclog, "%s TCP address, host=%x, port=%d, controller=%d, line=%d\n", smlctimestamp, dc[dx].plines[lx].host, dc[dx].plines[lx].remoteID);
tempport, dx, lx);
#endif
} }
fclose(cfgfile); fclose(cfgfile);
} }
@@ -445,40 +427,30 @@ int devsmlc (int class, int func, int device) {
switch (func) { switch (func) {
case 000: // enable high-speed SMLC clock case 000: // enable high-speed SMLC clock
#if DEBUG TRACE(T_SMLC, "%s OCP '%02o%02o enable high-speed clock\n", smlctimestamp, func, device);
fprintf(smlclog, "%s OCP '%02o%02o enable high-speed clock\n", smlctimestamp, func, device);
#endif
IOSKIP; IOSKIP;
break; break;
case 013: // acknowledge and clear interrupt case 013: // acknowledge and clear interrupt
#if DEBUG TRACE(T_SMLC, "%s OCP '%02o%02o acknowledge and clear interrupt\n", smlctimestamp, func, device);
fprintf(smlclog, "%s OCP '%02o%02o acknowledge and clear interrupt\n", smlctimestamp, func, device);
#endif
dc[dx].intstate = SMLC_NOTINTERRUPTING; dc[dx].intstate = SMLC_NOTINTERRUPTING;
IOSKIP; IOSKIP;
break; break;
case 015: // enable interrupts case 015: // enable interrupts
#if DEBUG TRACE(T_SMLC, "%s OCP '%02o%02o enable interrupts\n", smlctimestamp, func, device);
fprintf(smlclog, "%s OCP '%02o%02o enable interrupts\n", smlctimestamp, func, device);
#endif
dc[dx].intenabled = 1; dc[dx].intenabled = 1;
IOSKIP; IOSKIP;
break; break;
case 016: // disable interrupts case 016: // disable interrupts
#if DEBUG TRACE(T_SMLC, "%s OCP '%02o%02o disable interrupts\n", smlctimestamp, func, device);
fprintf(smlclog, "%s OCP '%02o%02o disable interrupts\n", smlctimestamp, func, device);
#endif
dc[dx].intenabled = 0; dc[dx].intenabled = 0;
IOSKIP; IOSKIP;
break; break;
case 017: // initialize controller case 017: // initialize controller
#if DEBUG TRACE(T_SMLC, "%s OCP '%02o%02o initialize controller\n", smlctimestamp, func, device);
fprintf(smlclog, "%s OCP '%02o%02o initialize controller\n", smlctimestamp, func, device);
#endif
dc[dx].intvector = 0; dc[dx].intvector = 0;
dc[dx].intenabled = 0; dc[dx].intenabled = 0;
dc[dx].intstate = SMLC_NOTINTERRUPTING; 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); TRACE(T_INST, " SKS '%02o%02o\n", func, device);
if (func == 004) { /* skip if not interrupting */ if (func == 004) { /* skip if not interrupting */
#if DEBUG TRACE(T_SMLC, "%s SKS '02%02o skip if not interrupting, state is %s\n", smlctimestamp, device, intstates[dc[dx].intstate]);
fprintf(smlclog, "%s SKS '02%02o skip if not interrupting, state is %s\n", smlctimestamp, device, intstates[dc[dx].intstate]);
#endif
if (dc[dx].intstate == SMLC_INTERRUPTING) { if (dc[dx].intstate == SMLC_INTERRUPTING) {
IOSKIP; IOSKIP;
} }
@@ -564,16 +534,12 @@ int devsmlc (int class, int func, int device) {
} }
} }
putcrs16(A, data); putcrs16(A, data);
#if DEBUG TRACE(T_SMLC, "%s INA '00%02o input status returns 0x%04x\n", smlctimestamp, device, data);
fprintf(smlclog, "%s INA '00%02o input status returns 0x%04x\n", smlctimestamp, device, data);
#endif
IOSKIP; IOSKIP;
} else if (func == 011) { /* report device ID */ } else if (func == 011) { /* report device ID */
putcrs16(A, SMLC_DEVICEID); putcrs16(A, SMLC_DEVICEID);
#if DEBUG TRACE(T_SMLC, "%s INA '11%02o report device ID returns 0x%04x\n", smlctimestamp, device, getcrs16(A));
fprintf(smlclog, "%s INA '11%02o report device ID returns 0x%04x\n", smlctimestamp, device, getcrs16(A));
#endif
IOSKIP; IOSKIP;
} else { } else {
@@ -595,9 +561,7 @@ int devsmlc (int class, int func, int device) {
data = getcrs16(A); data = getcrs16(A);
dc[dx].fncode = data >> 8; dc[dx].fncode = data >> 8;
dc[dx].lineno = data & 0377; dc[dx].lineno = data & 0377;
#if DEBUG TRACE(T_SMLC, "%s OTA '00%02o set function/line '%03o/'%03o\n", smlctimestamp, device, dc[dx].fncode, dc[dx].lineno);
fprintf(smlclog, "%s OTA '00%02o set function/line '%03o/'%03o\n", smlctimestamp, device, dc[dx].fncode, dc[dx].lineno);
#endif
switch (dc[dx].fncode) { switch (dc[dx].fncode) {
case 000: // set modem controls case 000: // set modem controls
@@ -627,15 +591,11 @@ int devsmlc (int class, int func, int device) {
switch (dc[dx].fncode) { switch (dc[dx].fncode) {
case 000: // set modem controls case 000: // set modem controls
#if DEBUG TRACE(T_SMLC, "%s OTA '01%02o line '%02o set modem controls '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
fprintf(smlclog, "%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);
fprintf(smlclog, "%s DTR: %d, RTS: %d\n", smlctimestamp, data & 1, (data & 2) == 1);
#endif
sp->dtr = data & 1; sp->dtr = data & 1;
if ((data & 1) == 0 && lp->fd != -1) { if ((data & 1) == 0 && lp->fd != -1) {
#if DEBUG TRACE(T_SMLC, "%s close connection to %s\n", smlctimestamp, lp->remoteID);
fprintf(smlclog, "%s close connection to %s\n", smlctimestamp, lp->remoteID);
#endif
close(lp->fd); close(lp->fd);
lp->fd = -1; lp->fd = -1;
lp->connstate = SMLC_STATEDISCONNECTED; lp->connstate = SMLC_STATEDISCONNECTED;
@@ -647,9 +607,7 @@ int devsmlc (int class, int func, int device) {
case 010: // set special character case 010: // set special character
i = (data >> 8) & 0xff; i = (data >> 8) & 0xff;
ch = data & 0xff; ch = data & 0xff;
#if DEBUG TRACE(T_SMLC, "%s OTA '01%02o line '%02o set special character %d <%02x>\n", smlctimestamp, device, dc[dx].lineno, i, ch);
fprintf(smlclog, "%s OTA '01%02o line '%02o set special character %d <%02x>\n", smlctimestamp, device, dc[dx].lineno, i, ch);
#endif
if (i < SMLC_MAXSPCHARS) { if (i < SMLC_MAXSPCHARS) {
sp->spchars[i] = ch; sp->spchars[i] = ch;
} else { } else {
@@ -660,30 +618,26 @@ int devsmlc (int class, int func, int device) {
break; break;
case 012: // set configuration word case 012: // set configuration word
#if DEBUG TRACE(T_SMLC, "%s OTA '01%02o line '%02o set configuration word 0x%04x\n", smlctimestamp, device, dc[dx].lineno, data);
fprintf(smlclog, "%s OTA '01%02o line '%02o set configuration word 0x%04x\n", smlctimestamp, device, dc[dx].lineno, data);
#endif
sp->configword = data; sp->configword = data;
IOSKIP; IOSKIP;
break; break;
case 014: // set primary I/O channel case 014: // set primary I/O channel
#if DEBUG TRACE(T_SMLC, "%s OTA '01%02o line '%02o set primary I/O channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
fprintf(smlclog, "%s OTA '01%02o line '%02o set primary I/O channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
#endif
sp->dmcprimarycount = (data >> 12) + 1; sp->dmcprimarycount = (data >> 12) + 1;
sp->dmcprimaryidx = 0; sp->dmcprimaryidx = 0;
sp->dmcprimary = data & 0x7fe; sp->dmcprimary = data & 0x7fe;
if (data != 0) { if (data != 0) {
if ((data & 04000) == 0) fatal("Can't run SMLC in DMA mode!"); if ((data & 04000) == 0) fatal("Can't run SMLC in DMA mode!");
#if DEBUG #ifndef NOTRACE
fprintf(smlclog, "%s %d buffers\n", smlctimestamp, sp->dmcprimarycount); TRACE(T_SMLC, "%s %d buffers\n", smlctimestamp, sp->dmcprimarycount);
for (i = 0; i < sp->dmcprimarycount; i++) { for (i = 0; i < sp->dmcprimarycount; i++) {
dmcpair = get32io ((data & 0x7fe) + (i * 2)); dmcpair = get32io ((data & 0x7fe) + (i * 2));
dmcbufbegea = dmcpair >> 16; dmcbufbegea = dmcpair >> 16;
dmcbufendea = dmcpair & 0xffff; dmcbufendea = dmcpair & 0xffff;
dmcnw = (dmcbufendea - dmcbufbegea) + 1; 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 #endif
} }
@@ -691,10 +645,8 @@ int devsmlc (int class, int func, int device) {
break; break;
case 015: // enable case 015: // enable
#if DEBUG TRACE(T_SMLC, "%s OTA '01%02o line '%02o enable '%06o (%s %s)\n", smlctimestamp, device, dc[dx].lineno, data,
fprintf(smlclog, "%s OTA '01%02o line '%02o enable '%06o (%s %s)\n", smlctimestamp, device, dc[dx].lineno, data,
sx ? "xmit" : "recv", (data & 1) ? "on" : "off"); sx ? "xmit" : "recv", (data & 1) ? "on" : "off");
#endif
sp->enabled = data & 1; sp->enabled = data & 1;
if (sp->enabled) devpoll[device] = 1; if (sp->enabled) devpoll[device] = 1;
IOSKIP; 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 * this OTA function. Consequently, this emulation module does not currently implement any logic to alternate
* storing recceived data between the primary and backup channels. * storing recceived data between the primary and backup channels.
*/ */
#if DEBUG TRACE(T_SMLC, "%s OTA '01%02o line '%02o set backup I/O channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
fprintf(smlclog, "%s OTA '01%02o line '%02o set backup I/O channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
#endif
sp->dmcbackupcount = (data >> 12) + 1; sp->dmcbackupcount = (data >> 12) + 1;
sp->dmcbackupidx = 0; sp->dmcbackupidx = 0;
sp->dmcbackup = data & 0x7fe; sp->dmcbackup = data & 0x7fe;
if (data != 0) { if (data != 0) {
if ((data & 04000) == 0) fatal("Can't run SMLC in DMA mode!"); if ((data & 04000) == 0) fatal("Can't run SMLC in DMA mode!");
#if DEBUG #ifndef NOTRACE
fprintf(smlclog, "%s %d buffers\n", smlctimestamp, sp->dmcbackupcount); TRACE(T_SMLC, "%s %d buffers\n", smlctimestamp, sp->dmcbackupcount);
for (i = 0; i < sp->dmcbackupcount; i++) { for (i = 0; i < sp->dmcbackupcount; i++) {
dmcpair = get32io ((data & 0x7fe) + (i * 2)); dmcpair = get32io ((data & 0x7fe) + (i * 2));
dmcbufbegea = dmcpair >> 16; dmcbufbegea = dmcpair >> 16;
dmcbufendea = dmcpair & 0xffff; dmcbufendea = dmcpair & 0xffff;
dmcnw = (dmcbufendea - dmcbufbegea) + 1; 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 #endif
} }
@@ -729,9 +679,7 @@ int devsmlc (int class, int func, int device) {
break; break;
case 017: // set status channel case 017: // set status channel
#if DEBUG TRACE(T_SMLC, "%s OTA '01%02o line '%02o set status channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
fprintf(smlclog, "%s OTA '01%02o line '%02o set status channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
#endif
if (!(data & 04000) && data != 0) if (!(data & 04000) && data != 0)
fatal("Can't run SMLC in DMA mode!"); fatal("Can't run SMLC in DMA mode!");
if (lx != 0 || sx != 0) { if (lx != 0 || sx != 0) {
@@ -740,9 +688,7 @@ int devsmlc (int class, int func, int device) {
fatal(NULL); fatal(NULL);
} }
dc[dx].dmcstatus = data & 0x7fe; dc[dx].dmcstatus = data & 0x7fe;
#if DEBUG TRACE(T_SMLC, "%s next %06o, last %06o\n", smlctimestamp, get32io(dc[dx].dmcstatus) >> 16, get32io(dc[dx].dmcstatus) & 0xffff);
fprintf(smlclog, "%s next %06o, last %06o\n", smlctimestamp, get32io(dc[dx].dmcstatus) >> 16, get32io(dc[dx].dmcstatus) & 0xffff);
#endif
IOSKIP; IOSKIP;
break; break;
@@ -754,9 +700,7 @@ int devsmlc (int class, int func, int device) {
break; break;
case 016: // set interrupt vector case 016: // set interrupt vector
#if DEBUG TRACE(T_SMLC, "%s OTA '16%02o set interrupt vector '%06o\n", smlctimestamp, device, getcrs16(A));
fprintf(smlclog, "%s OTA '16%02o set interrupt vector '%06o\n", smlctimestamp, device, getcrs16(A));
#endif
dc[dx].intvector = getcrs16(A); dc[dx].intvector = getcrs16(A);
IOSKIP; IOSKIP;
break; break;
@@ -785,9 +729,7 @@ int devsmlc (int class, int func, int device) {
} else { } else {
gv.intvec = dc[dx].intvector; gv.intvec = dc[dx].intvector;
dc[dx].intstate = SMLC_INTERRUPTING; dc[dx].intstate = SMLC_INTERRUPTING;
#if DEBUG TRACE(T_SMLC, "%s Raise interrupt on device '%02o\n", smlctimestamp, device);
fprintf(smlclog, "%s Raise interrupt on device '%02o\n", smlctimestamp, device);
#endif
} }
return 0; return 0;
} }
@@ -798,23 +740,41 @@ int devsmlc (int class, int func, int device) {
for (lx = 0; lx < SMLC_LINESPERBOARD; lx++) { for (lx = 0; lx < SMLC_LINESPERBOARD; lx++) {
lp = &dc[dx].plines[lx]; lp = &dc[dx].plines[lx];
if (lp->host == 0) continue; if (lp->port == 0) continue;
switch (lp->connstate) { switch (lp->connstate) {
case SMLC_STATEDISCONNECTED: 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); fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) { if (fd < 0) {
fprintf(stderr, "Failed to create socket for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx); fprintf(stderr, "Failed to create socket for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL); fatal(NULL);
} }
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optenable, sizeof(optenable)); if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optenable, sizeof(optenable)) == -1) {
fcntl(fd, F_SETFL, O_NONBLOCK); fprintf(stderr, "Failed to set socket option SO_KEEPALICE for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
bzero((char *) &raddr, sizeof(raddr)); fatal(NULL);
raddr.sin_family = AF_INET; }
raddr.sin_addr.s_addr = htonl(dc[dx].plines[lx].host); if ((flags = fcntl(fd, F_GETFL)) == -1) {
raddr.sin_port = htons(dc[dx].plines[lx].port); fprintf(stderr, "Failed to get flags for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
rc = connect(fd, (struct sockaddr *)&raddr, sizeof(raddr)); 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; lp->connstate = SMLC_STATECONNECTING;
if (rc < 0 && errno != EINPROGRESS) { if (rc < 0 && errno != EINPROGRESS) {
fprintf(stderr, "Failed to create connection to %s for SMLC line %d\n", lp->remoteID, 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; lp->connstate = SMLC_STATEDISCONNECTED;
break; break;
} else { // connection in progress } else { // connection in progress
#if DEBUG TRACE(T_SMLC, "%s Connection initiated to %s for SMLC line %d\n", smlctimestamp, lp->remoteID,
fprintf(smlclog, "%s Connection initiated to %s for SMLC line %d\n", smlctimestamp, lp->remoteID,
(dx * SMLC_LINESPERBOARD) + lx); (dx * SMLC_LINESPERBOARD) + lx);
#endif
lp->fd = fd; 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: case SMLC_STATECONNECTING:
FD_SET(lp->fd, &writefds); if (lp->host != 0) {
if (lp->fd > maxfd) maxfd = lp->fd; //
// 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; break;
case SMLC_STATECONNECTED: case SMLC_STATECONNECTED:
@@ -863,11 +872,9 @@ int devsmlc (int class, int func, int device) {
if (dmcnw <= 0) { if (dmcnw <= 0) {
sp->dmcprimaryidx += 1; sp->dmcprimaryidx += 1;
} else { } else {
#if DEBUG TRACE(T_SMLC, "%s Line %d on device '%02o send to %s from channel '%06o\n", smlctimestamp, lx, device, lp->remoteID,
fprintf(smlclog, "%s Line %d on device '%02o send to %s from channel '%06o\n", smlctimestamp, lx, device, lp->remoteID,
sp->dmcprimary); sp->dmcprimary);
fprintf(smlclog, "%s next '%06o, last '%06o, words %d\n", smlctimestamp, dmcbufbegea, dmcbufendea, dmcnw); TRACE(T_SMLC, "%s next '%06o, last '%06o, words %d\n", smlctimestamp, dmcbufbegea, dmcbufendea, dmcnw);
#endif
n = 0; n = 0;
while (n < dmcnw && np + 1 < ep) { while (n < dmcnw && np + 1 < ep) {
word = get16io(dmcbufbegea); word = get16io(dmcbufbegea);
@@ -876,10 +883,8 @@ int devsmlc (int class, int func, int device) {
*np++ = word & 0377; *np++ = word & 0377;
n += 1; n += 1;
} }
#if DEBUG TRACE(T_SMLC, "%s %d words transferred to output buffer\n", smlctimestamp, n);
fprintf(smlclog, "%s %d words transferred to output buffer\n", smlctimestamp, n); TRACE(T_SMLC, "%s next '%06o, last '%06o\n", smlctimestamp, dmcbufbegea, dmcbufendea);
fprintf(smlclog, "%s next '%06o, last '%06o\n", smlctimestamp, dmcbufbegea, dmcbufendea);
#endif
put16io(dmcbufbegea, sp->dmcprimary + (sp->dmcprimaryidx * 2)); put16io(dmcbufbegea, sp->dmcprimary + (sp->dmcprimaryidx * 2));
if (n == dmcnw) { if (n == dmcnw) {
status = SMLC_EOR | SMLC_XMIT; status = SMLC_EOR | SMLC_XMIT;
@@ -925,7 +930,7 @@ int devsmlc (int class, int func, int device) {
for (lx = 0; lx < SMLC_LINESPERBOARD; lx++) { for (lx = 0; lx < SMLC_LINESPERBOARD; lx++) {
lp = &dc[dx].plines[lx]; lp = &dc[dx].plines[lx];
if (lp->host == 0) continue; if (lp->port == 0) continue;
switch (lp->connstate) { switch (lp->connstate) {
case SMLC_STATECONNECTING: 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); rc = getsockopt(lp->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen);
if (rc < 0) { if (rc < 0) {
fprintf(stderr, "Failed to get socket status for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx); fprintf(stderr, "Failed to get socket status for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
close(lp->fd); fatal(NULL);
lp->fd = -1;
lp->nextconntime = currenttime + SMLC_CONNECTINTERVAL;
lp->connstate = SMLC_STATEDISCONNECTED;
} else if (optval != 0) { // connection failed } 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); fprintf(stderr, "Failed to create connection to %s for SMLC line %d\n", lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx);
close(lp->fd); close(lp->fd);
@@ -945,47 +947,72 @@ int devsmlc (int class, int func, int device) {
lp->nextconntime = currenttime + SMLC_CONNECTINTERVAL; lp->nextconntime = currenttime + SMLC_CONNECTINTERVAL;
lp->connstate = SMLC_STATEDISCONNECTED; lp->connstate = SMLC_STATEDISCONNECTED;
} else { } else {
#if DEBUG TRACE(T_SMLC, "%s Connection created to %s for SMLC line %d\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx);
fprintf(smlclog, "%s Connection created to %s for SMLC line %d\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx);
#endif
lp->connstate = SMLC_STATECONNECTED; lp->connstate = SMLC_STATECONNECTED;
lp->recvstate = SMLC_STATERCVSYN; lp->recvstate = SMLC_STATERCVSYN;
lp->starting = 0; }
lp->naksent = 0; } else if (FD_ISSET(lp->fd, &readfds)) {
lp->slines[SMLC_RECVIX].buf.in = 0; addrlen = sizeof(addr);
lp->slines[SMLC_RECVIX].buf.out = 0; fd = accept(lp->fd, (struct sockaddr *)&addr, &addrlen);
lp->slines[SMLC_XMITIX].buf.in = 0; if (fd >= 0) {
lp->slines[SMLC_XMITIX].buf.out = 0; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optenable, sizeof(optenable)) == -1) {
smlcaddstatus(&dc[dx], lx, SMLC_DSS_BIT | SMLC_DSR_BIT | SMLC_CTS_BIT | SMLC_DCD_BIT | SMLC_SQ_BIT); 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) if (lp->connstate == SMLC_STATECONNECTED) {
break; 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: case SMLC_STATECONNECTED:
sp = &lp->slines[SMLC_RECVIX]; sp = &lp->slines[SMLC_RECVIX];
if (FD_ISSET(lp->fd, &readfds)) { if (FD_ISSET(lp->fd, &readfds)) {
n = read(lp->fd, &sp->buf.data[sp->buf.in], sizeof(sp->buf.data) - sp->buf.in); n = read(lp->fd, &sp->buf.data[sp->buf.in], sizeof(sp->buf.data) - sp->buf.in);
if (n > 0) { if (n > 0) {
#if DEBUG #ifndef NOTRACE
fprintf(smlclog, "%s Line %d on device '%02o received %d bytes from %s:\n", smlctimestamp, lx, device, n, lp->remoteID); 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); smlclogbytes(&sp->buf.data[sp->buf.in], n);
smlclogflush(); smlclogflush();
#endif #endif
sp->buf.in += n; sp->buf.in += n;
} else if (n < 0 && (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)) { } else if (n < 0 && (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)) {
n = 0; n = 0;
#if DEBUG TRACE(T_SMLC, "%s read from %s for line %d returned errno %d (ignored)\n",
fprintf(smlclog, "%s recv from %s for line %d returned errno %d (ignored)\n",
smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx, errno); smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx, errno);
#endif
} else { } else {
#if DEBUG #ifndef NOTRACE
if (n < 0) { 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); smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx, errno);
} else { } 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 #endif
lp->connstate = SMLC_STATEDISCONNECTING; lp->connstate = SMLC_STATEDISCONNECTING;
@@ -1007,17 +1034,15 @@ int devsmlc (int class, int func, int device) {
sp->dmcprimaryidx += 1; sp->dmcprimaryidx += 1;
continue; continue;
} }
#if DEBUG TRACE(T_SMLC, "%s Line %d on device '%02o receive from %s to channel '%06o\n", smlctimestamp, lx, device, lp->remoteID,
fprintf(smlclog, "%s Line %d on device '%02o receive from %s to channel '%06o\n", smlctimestamp, lx, device, lp->remoteID,
sp->dmcprimary); sp->dmcprimary);
fprintf(smlclog, "%s next '%06o, last '%06o, words %d\n", smlctimestamp, dmcbufbegea, dmcbufendea, dmcnw); TRACE(T_SMLC, "%s next '%06o, last '%06o, words %d\n", smlctimestamp, dmcbufbegea, dmcbufendea, dmcnw);
#endif
maxbytes = dmcnw * 2; maxbytes = dmcnw * 2;
n = 0; n = 0;
dmcnw = 0; dmcnw = 0;
status = 0; status = 0;
isetb = 0; isetb = 0;
while (n < maxbytes && np < ip) { while (n < maxbytes && np < ip && status == 0) {
ch = *np++; ch = *np++;
if (lp->recvstate == SMLC_STATERCVSYN) { if (lp->recvstate == SMLC_STATERCVSYN) {
if (ch == SMLC_SYN) { if (ch == SMLC_SYN) {
@@ -1030,10 +1055,8 @@ int devsmlc (int class, int func, int device) {
lp->recvstate = SMLC_STATERCVSYN; lp->recvstate = SMLC_STATERCVSYN;
break; break;
} else { } else {
#if DEBUG TRACE(T_SMLC, "%s NAK received after sending NAK to %s for line %d\n", smlctimestamp, lp->remoteID,
fprintf(smlclog, "%s NAK received after sending NAK to %s for line %d\n", smlctimestamp, lp->remoteID,
(dx * SMLC_LINESPERBOARD) + lx); (dx * SMLC_LINESPERBOARD) + lx);
#endif
lp->connstate = SMLC_STATEDISCONNECTING; lp->connstate = SMLC_STATEDISCONNECTING;
smlcaddstatus(&dc[dx], lx, SMLC_DSS_BIT); smlcaddstatus(&dc[dx], lx, SMLC_DSS_BIT);
devpoll[device] = 1; /* return ASAP */ devpoll[device] = 1; /* return ASAP */
@@ -1052,29 +1075,64 @@ int devsmlc (int class, int func, int device) {
word = ch << 8; word = ch << 8;
} }
n += 1; n += 1;
if (lp->recvstate == SMLC_STATERCVDLE) { switch (lp->recvstate) {
case SMLC_STATERCVDLE:
lp->recvstate = SMLC_STATERCVCHAR; lp->recvstate = SMLC_STATERCVCHAR;
if (ch == SMLC_ACK0) { switch (ch) {
case SMLC_ACK0:
status |= SMLC_ENCODE_BIT | SMLC_ACK0_STATUS; status |= SMLC_ENCODE_BIT | SMLC_ACK0_STATUS;
lp->recvstate = SMLC_STATERCVSYN; lp->recvstate = SMLC_STATERCVSYN;
break; break;
} else if (ch == SMLC_STX) { case SMLC_STX:
status |= SMLC_ENCODE_BIT | SMLC_STX_STATUS; status |= SMLC_ENCODE_BIT | SMLC_STX_STATUS;
break; break;
} else if (ch == SMLC_ETB) { case SMLC_ETB:
status |= SMLC_ENCODE_BIT | SMLC_ETB_STATUS; status |= SMLC_ENCODE_BIT | SMLC_ETB_STATUS;
lp->recvstate = SMLC_STATERCVSYN; lp->recvstate = SMLC_STATERCVSYN;
isetb = 1; isetb = 1;
break; break;
default:
// do nothing
break;
} }
} else if (ch == SMLC_DLE) { break;
lp->recvstate = SMLC_STATERCVDLE; 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 & 1) != 0) { // odd number of bytes processed, so store "incomplete" word, or back up one byte
if (n < maxbytes if (n < maxbytes
&& ((n > 1 && *(np - 1) == SMLC_ETB && *(np - 2) == SMLC_DLE) && (status & SMLC_ENCODE_BIT) != 0
|| (n == 1 && *(np - 1) == SMLC_NAK))) { && ( (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); put16io(word, dmcbufbegea);
dmcbufbegea = INCVA(dmcbufbegea, 1); dmcbufbegea = INCVA(dmcbufbegea, 1);
dmcnw += 1; dmcnw += 1;
@@ -1085,10 +1143,8 @@ int devsmlc (int class, int func, int device) {
} }
put16io(dmcbufbegea, sp->dmcprimary + (sp->dmcprimaryidx * 2)); put16io(dmcbufbegea, sp->dmcprimary + (sp->dmcprimaryidx * 2));
sp->dmcprimaryidx += 1; sp->dmcprimaryidx += 1;
#if DEBUG TRACE(T_SMLC, "%s %d words transferred from input buffer\n", smlctimestamp, dmcnw);
fprintf(smlclog, "%s %d words transferred from input buffer\n", smlctimestamp, dmcnw); TRACE(T_SMLC, "%s next '%06o, last '%06o\n", smlctimestamp, dmcbufbegea, dmcbufendea);
fprintf(smlclog, "%s next '%06o, last '%06o\n", smlctimestamp, dmcbufbegea, dmcbufendea);
#endif
if (n == maxbytes) { if (n == maxbytes) {
status |= SMLC_EOR; status |= SMLC_EOR;
} else if (n > maxbytes) { } 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; lp->naksent = nbytes >= 5 && sp->buf.data[sp->buf.out + 4] == SMLC_NAK;
n = write(lp->fd, &sp->buf.data[sp->buf.out], nbytes); n = write(lp->fd, &sp->buf.data[sp->buf.out], nbytes);
if (n >= 0) { if (n >= 0) {
#if DEBUG #ifndef NOTRACE
fprintf(smlclog, "%s Line %d on device '%02o sent %d bytes to %s:\n", smlctimestamp, lx, device, n, lp->remoteID); 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); smlclogbytes(&sp->buf.data[sp->buf.out], n);
smlclogflush(); smlclogflush();
#endif #endif
@@ -1128,10 +1184,8 @@ int devsmlc (int class, int func, int device) {
smlcaddstatus(&dc[dx], lx, SMLC_LCT | SMLC_XMIT); smlcaddstatus(&dc[dx], lx, SMLC_LCT | SMLC_XMIT);
} }
} else if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) { } else if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) {
#if DEBUG TRACE(T_SMLC, "%s Connection failed to %s for line %d with errno %d\n", smlctimestamp, lp->remoteID,
fprintf(smlclog, "%s Connection failed to %s for line %d with errno %d\n", smlctimestamp, lp->remoteID,
(dx * SMLC_LINESPERBOARD) + lx, errno); (dx * SMLC_LINESPERBOARD) + lx, errno);
#endif
lp->connstate = SMLC_STATEDISCONNECTING; lp->connstate = SMLC_STATEDISCONNECTING;
smlcaddstatus(&dc[dx], lx, SMLC_DSS_BIT); smlcaddstatus(&dc[dx], lx, SMLC_DSS_BIT);
devpoll[device] = 1; /* return ASAP */ devpoll[device] = 1; /* return ASAP */
@@ -1155,7 +1209,6 @@ int devsmlc (int class, int func, int device) {
return 0; return 0;
} }
#if DEBUG
static unsigned char ebcdicToAscii[256] = { static unsigned char ebcdicToAscii[256] = {
/* 00-07 */ 0x00, 0x01, 0x02, 0x03, 0x1a, 0x09, 0x1a, 0x7f, /* 00-07 */ 0x00, 0x01, 0x02, 0x03, 0x1a, 0x09, 0x1a, 0x7f,
/* 08-0F */ 0x1a, 0x1a, 0x1a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08-0F */ 0x1a, 0x1a, 0x1a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
@@ -1193,14 +1246,16 @@ static unsigned char ebcdicToAscii[256] = {
}; };
static void smlclogflush(void) { static void smlclogflush(void) {
#ifndef NOTRACE
if (smlclogbytescol > 0) { if (smlclogbytescol > 0) {
fputs(smlclogbuf, smlclog); fputs(smlclogbuf, gv.tracefile);
fputc('\n', smlclog); fputc('\n', gv.tracefile);
fflush(smlclog); fflush(gv.tracefile);
} }
smlclogbytescol = 0; smlclogbytescol = 0;
memset(smlclogbuf, ' ', LogLineLength); memset(smlclogbuf, ' ', LogLineLength);
smlclogbuf[LogLineLength] = '\0'; smlclogbuf[LogLineLength] = '\0';
#endif
} }
static void smlclogbytes(unsigned char *bytes, int len) { static void smlclogbytes(unsigned char *bytes, int len) {
@@ -1211,24 +1266,27 @@ static void smlclogbytes(unsigned char *bytes, int len) {
int hexCol; int hexCol;
int i; int i;
ascCol = AsciiColumn(smlclogbytescol); #ifndef NOTRACE
hexCol = HexColumn(smlclogbytescol); if (gv.traceflags & T_SMLC) {
ascCol = AsciiColumn(smlclogbytescol);
hexCol = HexColumn(smlclogbytescol);
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
b = bytes[i]; b = bytes[i];
ac = ebcdicToAscii[b]; ac = ebcdicToAscii[b];
if (ac < 0x20 || ac >= 0x7f) { if (ac < 0x20 || ac >= 0x7f) {
ac = '.'; ac = '.';
} }
sprintf(hex, "%02x", b); sprintf(hex, "%02x", b);
memcpy(smlclogbuf + hexCol, hex, 2); memcpy(smlclogbuf + hexCol, hex, 2);
hexCol += 3; hexCol += 3;
smlclogbuf[ascCol++] = ac; smlclogbuf[ascCol++] = ac;
if (++smlclogbytescol >= 16) { if (++smlclogbytescol >= 16) {
smlclogflush(); smlclogflush();
ascCol = AsciiColumn(smlclogbytescol); ascCol = AsciiColumn(smlclogbytescol);
hexCol = HexColumn(smlclogbytescol); hexCol = HexColumn(smlclogbytescol);
}
} }
} }
}
#endif #endif
}

22
em.1
View File

@@ -145,6 +145,15 @@ on the host. Multiple trace types may be listed, separated by
spaces. Tracing may be initially turned off by including the spaces. Tracing may be initially turned off by including the
.I off .I off
trace type. trace type.
.PP
\fB-dolineclear\fR
.IP
Inject an XON (DC1, 0221) character and a line kill character into the
input buffers each time an AMLC line receives an incoming connection.
The system's configured line kill character is fetched from the
DEFKIL field of FIGCOM, at address 14(0)/705. This behavior helps
counteract lines which have been blocked by internet vulnerability
scanners injecting non-telnet data streams.
.\" --------------------------------------------------------------------------- .\" ---------------------------------------------------------------------------
.SH FILES .SH FILES
.TP .TP
@@ -166,13 +175,15 @@ Example:
.EE .EE
.TP .TP
smlc.cfg smlc.cfg
Used to associate sync serial (MDLC or HSSMLC) lines with a destination Used to associate outbound sync serial (MDLC or HSSMLC) lines with destination
IP address and port number. Optional. Comments work the same as IP addresses and port numbers, or to associate inbound sync serial lines
amlc.cfg. Example: 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 .EX
0 10.1.1.3:2554 0 10.1.1.3:2554 # Connect to TCP port 2554 of host 10.1.1.3 for outbound line 0
1 127.0.0.1:2554 1 9951 # Listen on TCP port 9951 for inbound line 1
.EE .EE
.TP .TP
console.log console.log
@@ -286,6 +297,7 @@ off|Start with tracing disabled
all|Everything all|Everything
flush|Flush trace file after each write flush|Flush trace file after each write
tlb|STLB and IOTLB changes tlb|STLB and IOTLB changes
smlc|MDLC/HSSMLC device I/O
OWNERL|Execution of this PCB OWNERL|Execution of this PCB
#instruction count|Begin after specified number of instructions #instruction count|Begin after specified number of instructions
|(the leading # is literal) |(the leading # is literal)

33
em.c
View File

@@ -1,5 +1,5 @@
/* Pr1me Computer emulator, Jim Wilcoxson (prirun@gmail.com), April 4, 2005 /* 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: Emulates a Prime Computer system by:
- booting from a Prime disk image (normal usage) - 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_PX Process exchange
T_LM License manager T_LM License manager
T_TLB STLB and IOTLB changes T_TLB STLB and IOTLB changes
T_SMLC SMLC device I/O
*/ */
#define T_EAR 0x00000001 #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_GET 0x00020000
#define T_EAS 0x00040000 #define T_EAS 0x00040000
#define T_TLB 0x00080000 #define T_TLB 0x00080000
#define T_SMLC 0x00100000
#define BITMASK16(b) (0x8000 >> ((b)-1)) #define BITMASK16(b) (0x8000 >> ((b)-1))
#define BITMASK32(b) ((unsigned int)(0x80000000) >> ((b)-1)) #define BITMASK32(b) ((unsigned int)(0x80000000) >> ((b)-1))
@@ -656,6 +658,7 @@ static unsigned short *physmem = NULL; /* system's physical memory */
//static ea_t tnoua_ea=0, tnou_ea=0, tsrc_ea=0; //static ea_t tnoua_ea=0, tnou_ea=0, tsrc_ea=0;
static int domemdump; /* -memdump arg */ static int domemdump; /* -memdump arg */
static int dolinecleararg; /* -dolineclear arg */
static int tport; /* -tport option (incoming terminals) */ static int tport; /* -tport option (incoming terminals) */
static int nport; /* -nport option (PNC/Ringnet) */ static int nport; /* -nport option (PNC/Ringnet) */
@@ -4366,7 +4369,7 @@ int main (int argc, char **argv) {
#define XRBRACE 0375 #define XRBRACE 0375
printf("[Prime Emulator ver %s %s]\n", REV, __DATE__); 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)) { if (argc > 1 && (strcmp(argv[1],"--version") == 0)) {
exit(0); exit(0);
} }
@@ -4449,6 +4452,7 @@ int main (int argc, char **argv) {
#include "dispatch.h" #include "dispatch.h"
domemdump = 0; domemdump = 0;
dolinecleararg = 0;
bootarg = NULL; bootarg = NULL;
bootfile[0] = 0; bootfile[0] = 0;
gv.pmap32bits = 0; gv.pmap32bits = 0;
@@ -4468,6 +4472,9 @@ int main (int argc, char **argv) {
} else if (strcmp(argv[i],"-memdump") == 0) { } else if (strcmp(argv[i],"-memdump") == 0) {
domemdump = 1; domemdump = 1;
} else if (strcmp(argv[i],"-dolineclear") == 0) {
dolinecleararg = 1;
} else if (strcmp(argv[i],"-ss") == 0) { } else if (strcmp(argv[i],"-ss") == 0) {
if (i+1 < argc && argv[i+1][0] != '-') { if (i+1 < argc && argv[i+1][0] != '-') {
sscanf(argv[++i],"%o", &templ); sscanf(argv[++i],"%o", &templ);
@@ -4606,6 +4613,8 @@ int main (int argc, char **argv) {
setlinebuf(gv.tracefile); setlinebuf(gv.tracefile);
else if (strcmp(argv[i],"tlb") == 0) else if (strcmp(argv[i],"tlb") == 0)
gv.traceflags |= T_TLB; 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) 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 # */ gv.traceuser = 0100000 | (templ<<6); /* form OWNERL for user # */
else if (strlen(argv[i]) == 6 && sscanf(argv[i],"%o", &templ) == 1) else if (strlen(argv[i]) == 6 && sscanf(argv[i],"%o", &templ) == 1)
@@ -6293,13 +6302,31 @@ d_hlt: /* 000000 */
if (bootarg) { 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)); 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) { 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... "); 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"); printf("\n");
if (utempa == '\r' || utempa == '\n') if (utempa == '\r' || utempa == '\n')
{
close(ttydev);
goto fetch; goto fetch;
}
if (utempa == 'h') if (utempa == 'h')
{
close(ttydev);
break; break;
}
} }
} }
fatal("CPU halt"); fatal("CPU halt");