1
0
mirror of https://github.com/prirun/p50em.git synced 2026-01-22 02:05:24 +00:00

Remove "room left in input buffer" concept, remove DMT support

This commit is contained in:
Jim 2011-09-05 16:17:48 -04:00
parent 20e43665bd
commit 023edffb04

248
devamlc.h
View File

@ -74,6 +74,25 @@
OCP '15xx/'16xx
- enable/disable interrupts
- emulator stores in a structure
AMLC status word (from AMLCT5):
BITS DESCRIPTION
1 END OF RANGE INTERRUPT
2 CLOCK RUNNING
3-6 LINE # OF LINE WHOSE DATA SET STATUS HAS CHANGED
(VALID ONLY WHEN BIT 7 IS SET)
7 AMLC INTERRUPTING BECAUSE DATA SET STATUS HAS CHANGED
8 0 = AMLC RECEIVING INTO FIRST BUFFER
1 = AMLC RECEIVING INTO SECOND BUFFER
9 CHARACTER TIME INTRP INDICATION #1
10 MULTIPLE LINES HAD CHAR TIME INTERRUPT
11 INTERRUPTS ENABLED
12 0 = CONTROLLER IN DMT MODE
1 = CONTROLLER IN DMQ MODE
13-16 LINE # OF LINE CAUSING CHARACTER TIME INTRP.
*/
/* this macro closes an AMLC connection - used in several places */
@ -97,7 +116,7 @@ int devamlc (int class, int func, int device) {
#define MAXLINES 128
#define MAXBOARDS 8
#define MAXROOM 1024
#define MAXREAD 64
/* AMLC poll rate (ms). Max data rate = queue size*1000/AMLCPOLL
The max AMLC output queue size is 1023 (octal 2000), so a poll
@ -106,6 +125,7 @@ int devamlc (int class, int func, int device) {
there are DMQ buffers with 255 or more characters. */
#define AMLCPOLL 100
#define AMLCPOLL 50
/* DSSCOUNTDOWN is the number of carrier status requests that should
occur before polling real serial devices. Primos does a carrier
@ -114,12 +134,6 @@ int devamlc (int class, int func, int device) {
#define DSSCOUNTDOWN 25
#if 1
#define QAMLC 020000 /* this is to enable QAMLC/DMQ functionality */
#else
#define QAMLC 0
#endif
/* connection types for each line. This _doesn't_ imply the line is
actually connected, ie, an AMLC line may be tied to a specific
serial device (like a USB->serial gizmo), but the USB device may
@ -178,7 +192,6 @@ int devamlc (int class, int func, int device) {
unsigned short dsstime; /* countdown to dss poll */
short fd[16]; /* Unix fd, 1 per line */
unsigned short tstate[16]; /* telnet state */
unsigned short room[16]; /* room (chars) left in input buffer */
unsigned short lconf[16]; /* line configuration word */
unsigned short ctype[16]; /* connection type for each line */
unsigned int obhost[16]; /* outbound telnet host */
@ -186,7 +199,6 @@ int devamlc (int class, int func, int device) {
unsigned short modemstate[16]; /* Unix modem state bits (serial) */
unsigned short recvlx; /* next line to check for recv data */
unsigned short pclock; /* programmable clock */
char dmqmode; /* 0=DMT, 1=DMQ */
char bufnum; /* 0=1st input buffer, 1=2nd */
char eor; /* 1=End of Range on input */
} dc[MAXBOARDS];
@ -202,7 +214,7 @@ int devamlc (int class, int func, int device) {
struct sockaddr_in addr;
int fd;
unsigned int addrlen;
unsigned char buf[1024]; /* max size of DMQ buffer */
char buf[1024]; /* max size of DMQ buffer */
int i, j, n, maxn, n2, nw;
fd_set fds;
struct timeval timeout;
@ -213,7 +225,6 @@ int devamlc (int class, int func, int device) {
unsigned short qtop, qbot, qtemp;
unsigned short qseg, qmask, qents;
ea_t qentea;
char line[100];
int lc;
FILE *cfgfile;
char devname[32];
@ -272,7 +283,6 @@ int devamlc (int class, int func, int device) {
for (lx = 0; lx < 16; lx++) {
dc[dx2].fd[lx] = -1;
dc[dx2].tstate[lx] = TS_DATA;
dc[dx2].room[lx] = 64;
dc[dx2].lconf[lx] = 0;
dc[dx2].ctype[lx] = CT_SOCKET;
dc[dx2].modemstate[lx] = 0;
@ -314,22 +324,22 @@ int devamlc (int class, int func, int device) {
printf("em: error opening amlc config file: %s", strerror(errno));
} else {
lc = 0;
while (fgets(line, sizeof(line), cfgfile) != NULL) {
while (fgets(buf, sizeof(buf), cfgfile) != NULL) {
lc++;
line[sizeof(devname)] = 0; /* don't let sscanf overwrite anything */
line[strlen(line)-1] = 0; /* remove trailing nl */
if (strcmp(line,"") == 0 || line[0] == ';')
buf[sizeof(devname)] = 0; /* don't let sscanf overwrite anything */
buf[strlen(buf)-1] = 0; /* remove trailing nl */
if (strcmp(buf,"") == 0 || buf[0] == ';')
continue;
if (line[0] == '0')
n = sscanf(line, "%o %s", &i, devname);
if (buf[0] == '0')
n = sscanf(buf, "%o %s", &i, devname);
else
n = sscanf(line, "%d %s", &i, devname);
n = sscanf(buf, "%d %s", &i, devname);
if (n != 2) {
printf("em: Can't parse amlc config file line #%d: %s\n", lc, line);
printf("em: Can't parse amlc config file buf #%d: %s\n", lc, buf);
continue;
}
if (i < 0 || i >= MAXLINES) {
printf("em: amlc line # '%o (%d) out of range in amlc config file at line #%d: %s\n", i, i, lc, line);
printf("em: amlc line # '%o (%d) out of range in amlc config file at line #%d: %s\n", i, i, lc, buf);
continue;
}
//printf("devamlc: lc=%d, line '%o (%d) set to device %s\n", lc, i, i, devname);
@ -434,10 +444,10 @@ int devamlc (int class, int func, int device) {
//printf(" OCP '%02o%02o\n", func, device);
if (func == 012) { /* set normal (DMT) mode */
dc[dx].dmqmode = 0;
fatal("AMLC DMT mode not supported");
} else if (func == 013 && QAMLC) { /* set diagnostic (DMQ) mode */
dc[dx].dmqmode = 1;
} else if (func == 013) { /* set diagnostic (DMQ) mode */
;
} else if (func == 015) { /* enable interrupts */
dc[dx].intenable = 1;
@ -458,7 +468,6 @@ int devamlc (int class, int func, int device) {
dc[dx].dss = 0; /* NOTE: 1=asserted in emulator, 0=asserted on Prime */
dc[dx].dsstime = DSSCOUNTDOWN;
dc[dx].pclock = 0;
dc[dx].dmqmode = 0;
dc[dx].bufnum = 0;
dc[dx].eor = 0;
@ -514,7 +523,7 @@ int devamlc (int class, int func, int device) {
IOSKIP;
} else if (func == 07) { /* input AMLC status */
crs[A] = 040000 | (dc[dx].bufnum<<8) | (dc[dx].intenable<<5) | (dc[dx].dmqmode<<4);
crs[A] = 040000 | (dc[dx].bufnum<<8) | (dc[dx].intenable<<5) | (1<<4);
if (dc[dx].eor) {
crs[A] |= 0100000;
dc[dx].eor = 0;
@ -529,7 +538,7 @@ int devamlc (int class, int func, int device) {
IOSKIP;
} else if (func == 011) { /* input ID */
crs[A] = QAMLC | 054;
crs[A] = 020000 | 054; /* 020000 = QAMLC */
IOSKIP;
} else {
@ -706,24 +715,17 @@ int devamlc (int class, int func, int device) {
AMLC_SET_POLL;
IOSKIP;
} else if (func == 03) { /* set room in input buffer */
lx = (crs[A]>>12);
dc[dx].room[lx] = crs[A] & 0xFFF;
//printf("OTA '03%02o: AMLC line %d, room=%d, A=0x%04x\n", device, lx, dc[dx].room[lx], crs[A]);
IOSKIP;
} else if (func == 014) { /* set DMA/C channel (for input) */
dc[dx].dmcchan = crs[A] & 0x7ff;
//printf("OTA '14%02o: AMLC chan = %o\n", device, dc[dx].dmcchan);
if (!(crs[A] & 0x800))
fatal("Can't run AMLC in DMA mode!");
#if 0
dmcea = dc[dx].dmcchan;
dmcpair = get32io(dmcea);
dmcbufbegea = dmcpair>>16;
dmcbufendea = dmcpair & 0xffff;
dmcnw = dmcbufendea - dmcbufbegea + 1;
printf("AMLC: dmcnw=%d\n", dmcnw);
#if 1
dmcea = dc[dx].dmcchan;
dmcpair = get32io(dmcea);
dmcbufbegea = dmcpair>>16;
dmcbufendea = dmcpair & 0xffff;
dmcnw = dmcbufendea - dmcbufbegea + 1;
printf("OTA '14%02o: AMLC chan=%o, DMC begin=%o, end=%o, nw=%d\n", device, dc[dx].dmcchan, dmcbufbegea, dmcbufendea, dmcnw);
#endif
IOSKIP;
@ -755,10 +757,9 @@ int devamlc (int class, int func, int device) {
/* check for 1 new telnet connection on each AMLC poll */
addrlen = sizeof(addr);
while ((fd = accept(tsfd, (struct sockaddr *)&addr, &addrlen)) == -1 && errno == EINTR)
;
fd = accept(tsfd, (struct sockaddr *)&addr, &addrlen);
if (fd == -1) {
if (errno != EWOULDBLOCK) {
if (errno != EWOULDBLOCK && errno != EINTR) {
perror("accept error for AMLC");
}
} else {
@ -789,7 +790,6 @@ int devamlc (int class, int func, int device) {
dc[i].connected |= BITMASK16(lx+1);
dc[i].fd[lx] = fd;
dc[i].tstate[lx] = TS_DATA;
dc[i].room[lx] = MAXROOM;
//printf("em: AMLC connection, fd=%d, device='%o, line=%d\n", fd, dc[i].deviceid, lx);
goto endconnect;
}
@ -863,55 +863,41 @@ endconnect:
if (dc[dx].xmitenabled & BITMASK16(lx+1)) {
n = 0;
if (dc[dx].dmqmode) {
qcbea = dc[dx].baseaddr + lx*4;
if (dc[dx].connected & BITMASK16(lx+1)) {
qcbea = dc[dx].baseaddr + lx*4;
if (dc[dx].connected & BITMASK16(lx+1)) {
/* this line is connected, determine max chars to write
XXX: maxn should scale, depending on the actual line
throughput and AMLC poll rate */
/* this line is connected, determine max chars to write
XXX: maxn should scale, depending on the actual line
throughput and AMLC poll rate */
qtop = get16io(qcbea);
qbot = get16io(qcbea+1);
if (qtop == qbot)
continue; /* queue is empty, try next line */
qseg = get16io(qcbea+2);
qmask = get16io(qcbea+3);
qents = (qbot-qtop) & qmask;
maxn = sizeof(buf);
/* XXX: for FTDI USB->serial chip, optimal request size
is a multiple of 62 bytes, ie, 992 bytes, not 1K */
if (qents < maxn)
maxn = qents;
qentea = MAKEVA(qseg & 0xfff, qtop);
qtop = get16io(qcbea);
qbot = get16io(qcbea+1);
if (qtop == qbot)
continue; /* queue is empty, try next line */
qseg = get16io(qcbea+2);
qmask = get16io(qcbea+3);
qents = (qbot-qtop) & qmask;
maxn = sizeof(buf);
/* XXX: for FTDI USB->serial chip, optimal request size
is a multiple of 62 bytes, ie, 992 bytes, not 1K */
if (qents < maxn)
maxn = qents;
qentea = MAKEVA(qseg & 0xfff, qtop);
/* pack DMQ characters into a buffer & fix parity
XXX: turning off the high bit at this low level
precludes the use of TTY8BIT mode... */
/* pack DMQ characters into a buffer & fix parity
XXX: turning off the high bit at this low level
precludes the use of TTY8BIT mode... */
n = 0;
for (i=0; i < maxn; i++) {
utempa = MEM[qentea];
qentea = (qentea & ~qmask) | ((qentea+1) & qmask);
//printf("Device %o, line %d, entry=%o (%c)\n", device, lx, utempa, utempa & 0x7f);
buf[n++] = utempa & 0x7F;
}
} else { /* no line connected, just drain queue */
//printf("Draining output queue on line %d\n", lx);
put16io(get16io(qcbea), qcbea+1);
n = 0;
for (i=0; i < maxn; i++) {
utempa = MEM[qentea];
qentea = (qentea & ~qmask) | ((qentea+1) & qmask);
//printf("Device %o, line %d, entry=%o (%c)\n", device, lx, utempa, utempa & 0x7f);
buf[n++] = utempa & 0x7F;
}
} else { /* DMT */
utempa = get16io(dc[dx].baseaddr + lx);
if (utempa != 0) {
if ((utempa & 0x8000) && (dc[dx].connected & BITMASK16(lx+1))) {
//printf("Device %o, line %d, entry=%o (%c)\n", device, lx, utempa, utempa & 0x7f);
buf[n++] = utempa & 0x7F;
}
}
/* would need to setup DMT xmit poll here, and/or look for
char time interrupt. In practice, DMT isn't used when
the AMLC device is configured as a QAMLC */
} else { /* no line connected, just drain queue */
//printf("Draining output queue on line %d\n", lx);
put16io(get16io(qcbea), qcbea+1);
}
/* n chars have been packed into buf; see how many we can send */
@ -923,8 +909,7 @@ endconnect:
if (nw > 0) {
/* nw chars were sent; for DMQ, update the queue head
top to reflect nw dequeued entries. For DMT, clear
the dedicated cell (only writes 1 char at a time).
top to reflect nw dequeued entries.
XXX: Might be good to keep write stats here, to
decide how many chars to dequeue above and/or how
@ -932,11 +917,8 @@ endconnect:
buffers are used and Unix buffers get full so writes
can't complete */
if (dc[dx].dmqmode) {
qtop = (qtop & ~qmask) | ((qtop+nw) & qmask);
put16io(qtop, qcbea);
} else
put16io(0, dc[dx].baseaddr + lx);
qtop = (qtop & ~qmask) | ((qtop+nw) & qmask);
put16io(qtop, qcbea);
if (nw > maxxmit)
maxxmit = nw;
} else if (nw == -1)
@ -965,6 +947,31 @@ endconnect:
}
}
/* the largest DMQ buffer size is 1023 chars. If any line's DMQ
buffer is getting filled completely, then we need to poll
faster to increase throughput. If the max queue size falls
below 256, then decrease the interrupt rate. Anywhere between
256-1022, leave the poll rate alone.
XXX NOTE: polling faster only causes AMLDIM to fill buffers
faster for the last AMLC board (with ctinterrupt set).
*/
#if 1
if (maxxmit >= 1023) {
if (pollspeedup < 8) {
pollspeedup++;
//printf("%d ", pollspeedup);
//fflush(stdout);
}
} else if (pollspeedup > 1 && maxxmit < 256) {
pollspeedup--;
//printf("%d ", pollspeedup);
//fflush(stdout);
}
#endif
/* process input, but only as much as will fit into the DMC
buffer.
@ -977,31 +984,21 @@ endconnect:
The AMLC tumble tables should never overflow, because we only
read as many characters from the socket buffers as will fit in
the tumble tables. However, the tty line buffers may overflow,
causing data from the terminal to be dropped. To help avoid
this, a new OTA "set room left" has been implemented. If there
is no room left in the tty input buffer, don't read any more
characters from the socket for that line. In case Primos has
not been modified to use the room left feature, it is
initialized to MAXROOM for each line, so that at most MAXROOM
characters are read from a line during a poll. If MAXROOM is
small, like 64, this also makes overflow less likely if the
line input buffer is set to '200 or so with AMLBUF. To
optimize transfers to the Prime over AMLC lines, MAXROOM is set
much higher, to 1024 */
causing data from the terminal to be dropped. */
if (!dc[dx].eor) {
if (dc[dx].bufnum)
dmcea = dc[dx].dmcchan ^ 2;
dmcea = dc[dx].dmcchan + 2;
else
dmcea = dc[dx].dmcchan;
dmcpair = get32io(dmcea);
dmcbufbegea = dmcpair>>16;
dmcbufendea = dmcpair & 0xffff;
dmcnw = dmcbufendea - dmcbufbegea + 1;
//printf("AMLC: dmcnw=%d\n", dmcnw);
lx = dc[dx].recvlx;
for (lcount = 0; lcount < 16 && dmcnw > 0; lcount++) {
if ((dc[dx].connected & dc[dx].recvenabled & BITMASK16(lx+1))
&& dc[dx].room[lx] >= 8) {
if ((dc[dx].connected & dc[dx].recvenabled & BITMASK16(lx+1))) {
/* dmcnw is the # of characters left in the dmc buffer, but
there may be further size/space restrictions for this line */
@ -1009,10 +1006,8 @@ endconnect:
n2 = dmcnw;
if (n2 > sizeof(buf))
n2 = sizeof(buf);
if (n2 > dc[dx].room[lx])
n2 = dc[dx].room[lx];
if (n2 > MAXROOM) /* don't let 1 line hog the resource */
n2 = MAXROOM;
if (n2 > MAXREAD) /* don't let 1 line hog the resource */
n2 = MAXREAD;
while ((n = read(dc[dx].fd[lx], buf, n2)) == -1 && errno == EINTR)
;
@ -1164,31 +1159,6 @@ endconnect:
(the last board), so it will always be polling and checking
for new incoming connections */
/* the largest DMQ buffer size is 1023 chars. If any line's DMQ
buffer is getting filled completely, then we need to poll
faster to increase throughput. If the max queue size falls
below 256, then decrease the interrupt rate. Anywhere between
256-1022, leave the poll rate alone.
XXX NOTE: polling faster only causes AMLDIM to fill buffers
faster for the last AMLC board (with ctinterrupt set).
*/
#if 1
if (maxxmit >= 1023) {
if (pollspeedup < 8) {
pollspeedup++;
//printf("%d ", pollspeedup);
//fflush(stdout);
}
} else if (pollspeedup > 1 && maxxmit < 256) {
pollspeedup--;
//printf("%d ", pollspeedup);
//fflush(stdout);
}
#endif
AMLC_SET_POLL;
break;
}