1
0
mirror of https://github.com/simh/simh.git synced 2026-04-28 04:44:55 +00:00

simh 3.11

This commit is contained in:
Bob Supnik
2019-11-25 20:19:00 -08:00
committed by Mark Pizzolato
parent cf9ead8d04
commit 43360191c8
99 changed files with 760 additions and 121744 deletions

View File

@@ -1,6 +1,6 @@
/* sim_tmxr.c: Telnet terminal multiplexor library
Copyright (c) 2001-2018, Robert M Supnik
Copyright (c) 2001-2019, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@@ -26,6 +26,10 @@
Based on the original DZ11 simulator by Thord Nilson, as updated by
Arthur Krewat.
19-Mar-19 JDB Added tmxr_read, tmxr_write, tmxr_show, tmxr_close
global hooks and associated local hook routines;
added tmxr_init_line, tmxr_report_connection,
and tmxr_disconnect_line global routines
06-Mar-18 RMS Revised for new IP address format in sim_sock
08-Jul-17 JDB Corrected misleading indentation in tmxr_poll_tx
06-Aug-15 JDB [4.0] Added modem control functions
@@ -81,6 +85,9 @@
tmxr_tqln - number of buffered characters for line
tmxr_set_lnorder - set line connection order
tmxr_show_lnorder - show line connection order
tmxr_init_line - initialize the line data
tmxr_report_connection - report a line connection to the port
tmxr_disconnect_line - disconnect a line
All routines are OS-independent.
*/
@@ -132,12 +139,20 @@
#define TNS_DO 006 /* DO request pending rejection */
void tmxr_rmvrc (TMLN *lp, int32 p);
int32 tmxr_send_buffered_data (TMLN *lp);
TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, TMXR *mp);
extern char sim_name[];
extern uint32 sim_os_msec (void);
static int32 tmxr_local_read (TMLN *lp, int32 length);
static int32 tmxr_local_write (TMLN *lp, int32 length);
static void tmxr_local_show (TMLN *lp, FILE *stream);
static void tmxr_local_close (TMLN *lp);
int32 (*tmxr_read) (TMLN *lp, int32 length) = tmxr_local_read;
int32 (*tmxr_write) (TMLN *lp, int32 length) = tmxr_local_write;
void (*tmxr_show) (TMLN *lp, FILE *stream) = tmxr_local_show;
void (*tmxr_close) (TMLN *lp) = tmxr_local_close;
/* Poll for new connection
Called from unit service routine to test for new connection
@@ -193,33 +208,9 @@ if (newsock != INVALID_SOCKET) { /* got a live one? */
lp->conn = newsock; /* record connection */
lp->ipad = ipaddr; /* ip address */
lp->cnms = sim_os_msec (); /* time of conn */
lp->rxbpr = lp->rxbpi = 0; /* init buf pointers */
lp->txbpr = lp->txbpi = 0;
lp->rxcnt = lp->txcnt = 0; /* init counters */
lp->tsta = 0; /* init telnet state */
lp->xmte = 1; /* enable transmit */
lp->dstb = 0; /* default bin mode */
tmxr_init_line (lp); /* initialize the line */
sim_write_sock (newsock, mantra, sizeof (mantra));
tmxr_linemsg (lp, "\n\r\nConnected to the ");
tmxr_linemsg (lp, sim_name);
tmxr_linemsg (lp, " simulator ");
if (mp->dptr) { /* device defined? */
tmxr_linemsg (lp, sim_dname (mp->dptr)); /* report device name */
tmxr_linemsg (lp, " device");
if (mp->lines > 1) { /* more than one line? */
char line[20];
tmxr_linemsg (lp, ", line "); /* report the line number */
sprintf (line, "%i", i);
tmxr_linemsg (lp, line);
}
}
tmxr_linemsg (lp, "\r\n\n");
tmxr_poll_tx (mp); /* flush output */
tmxr_report_connection (mp, lp, i); /* report the connection */
return i;
}
} /* end if newsock */
@@ -235,12 +226,9 @@ if (lp->txlog) /* dump log */
tmxr_send_buffered_data (lp); /* send buffered data */
free (lp->ipad);
lp->ipad = NULL;
sim_close_sock (lp->conn); /* reset conn */
lp->conn = lp->tsta = 0; /* reset state */
lp->rxbpr = lp->rxbpi = 0;
lp->txbpr = lp->txbpi = 0;
lp->xmte = 1;
lp->dstb = 0;
tmxr_close (lp); /* reset the connection */
tmxr_init_line (lp); /* initialize the line */
lp->conn = 0; /* and clear the connection */
return;
}
@@ -339,8 +327,10 @@ if (lp->conn && lp->rcve) { /* conn & enb? */
if (j) { /* any? */
tmp = lp->rxb[lp->rxbpr]; /* get char */
val = TMXR_VALID | (tmp & 0377); /* valid + chr */
if (lp->rbr[lp->rxbpr]) /* break? */
if (lp->rbr[lp->rxbpr]) { /* break? */
lp->rbr[lp->rxbpr] = 0; /* clear status */
val = val | SCPE_BREAK;
}
lp->rxbpr = lp->rxbpr + 1; /* adv pointer */
}
} /* end if conn */
@@ -368,21 +358,23 @@ for (i = 0; i < mp->lines; i++) { /* loop thru lines */
nbytes = 0;
if (lp->rxbpi == 0) /* need input? */
nbytes = sim_read_sock (lp->conn, /* yes, read */
&(lp->rxb[lp->rxbpi]), /* leave spc for */
TMXR_MAXBUF - TMXR_GUARD); /* Telnet cruft */
nbytes = tmxr_read (lp, /* yes, read */
TMXR_MAXBUF - TMXR_GUARD); /* leave spc for Telnet cruft */
else if (lp->tsta) /* in Telnet seq? */
nbytes = sim_read_sock (lp->conn, /* yes, read to end */
&(lp->rxb[lp->rxbpi]),
nbytes = tmxr_read (lp, /* yes, read to end */
TMXR_MAXBUF - lp->rxbpi);
if (nbytes < 0) /* closed? reset ln */
tmxr_reset_ln (lp);
else if (nbytes > 0) { /* if data rcvd */
j = lp->rxbpi; /* start of data */
memset (&lp->rbr[j], 0, nbytes); /* clear status */
lp->rxbpi = lp->rxbpi + nbytes; /* adv pointers */
lp->rxcnt = lp->rxcnt + nbytes;
if (lp->exptr != NULL) /* if the line is extended */
continue; /* then skip the Telnet processing */
memset (&lp->rbr[j], 0, nbytes); /* clear status */
/* Examine new data, remove TELNET cruft before making input available */
for (; j < lp->rxbpi; ) { /* loop thru char */
@@ -512,6 +504,7 @@ for ( ; p < lp->rxbpi; p++) {
lp->rxb[p] = lp->rxb[p + 1];
lp->rbr[p] = lp->rbr[p + 1];
}
lp->rbr[p] = 0; /* clear potential break from vacated slot */
lp->rxbpi = lp->rxbpi - 1;
return;
}
@@ -589,11 +582,11 @@ int32 nbytes, sbytes;
nbytes = tmxr_tqln(lp); /* avail bytes */
if (nbytes) { /* >0? write */
if (lp->txbpr < lp->txbpi) /* no wrap? */
sbytes = sim_write_sock (lp->conn, /* write all data */
&(lp->txb[lp->txbpr]), nbytes);
else sbytes = sim_write_sock (lp->conn, /* write to end buf */
&(lp->txb[lp->txbpr]), TMXR_MAXBUF - lp->txbpr);
if (sbytes != SOCKET_ERROR) { /* ok? */
sbytes = tmxr_write (lp, nbytes); /* write all data */
else
sbytes = tmxr_write (lp, TMXR_MAXBUF - lp->txbpr); /* write to end buf */
if (sbytes > 0) { /* ok? */
lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */
if (lp->txbpr >= TMXR_MAXBUF) /* wrap? */
lp->txbpr = 0;
@@ -601,8 +594,8 @@ if (nbytes) { /* >0? write */
nbytes = nbytes - sbytes;
}
if (nbytes && (lp->txbpr == 0)) { /* more data and wrap? */
sbytes = sim_write_sock (lp->conn, lp->txb, nbytes);
if (sbytes != SOCKET_ERROR) { /* ok */
sbytes = tmxr_write (lp, nbytes);
if (sbytes > 0) { /* ok */
lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */
if (lp->txbpr >= TMXR_MAXBUF) /* wrap? */
lp->txbpr = 0;
@@ -641,12 +634,11 @@ mp->port = port; /* save port */
mp->master = sock; /* save master socket */
for (i = 0; i < mp->lines; i++) { /* initialize lines */
lp = mp->ldsc + i;
lp->conn = lp->tsta = 0;
lp->rxbpi = lp->rxbpr = 0;
lp->txbpi = lp->txbpr = 0;
lp->rxcnt = lp->txcnt = 0;
lp->xmte = 1;
lp->dstb = 0;
if (lp->exptr == NULL) { /* if the line is not extended */
tmxr_init_line (lp); /* then initialize the line */
lp->conn = 0; /* and clear the connection */
}
}
return SCPE_OK;
}
@@ -685,12 +677,9 @@ TMLN *lp;
for (i = 0; i < mp->lines; i++) { /* loop thru conn */
lp = mp->ldsc + i;
if (lp->conn) {
tmxr_linemsg (lp, "\r\nDisconnected from the ");
tmxr_linemsg (lp, sim_name);
tmxr_linemsg (lp, " simulator\r\n\n");
tmxr_reset_ln (lp);
} /* end if conn */
if (lp->exptr == NULL && lp->conn) /* if the line is not extended but is connected */
tmxr_disconnect_line (lp); /* then disconnect it */
} /* end for */
sim_close_sock (mp->master); /* close master socket */
mp->master = 0;
@@ -754,7 +743,7 @@ if (lp->conn) {
hr = ctime / 3600;
mn = (ctime / 60) % 60;
sc = ctime % 60;
fprintf (st, "IP address %s", lp->ipad);
tmxr_show (lp, st); /* display the port connection */
if (ctime)
fprintf (st, ", connected %02d:%02d:%02d\n", hr, mn, sc);
}
@@ -1122,3 +1111,166 @@ fprintf (st, "lines=%d", mp->lines);
return SCPE_OK;
}
/* Global utility routines */
/* Initialize the line state.
Reset the line state to represent an idle line. Note that we do not clear
all of the line structure members, so a connected line remains connected
after this call.
Because a line break is represented by a flag in the "receive break status"
array, we must zero that array in order to clear any leftover break
indications.
*/
void tmxr_init_line (TMLN *lp)
{
lp->tsta = 0; /* clear the Telnet negotiation state */
lp->xmte = 1; /* enable transmission */
lp->dstb = 0; /* default to binary data mode */
lp->rxbpr = lp->rxbpi = lp->rxcnt = 0; /* clear the receive indexes */
lp->txbpr = lp->txbpi = lp->txcnt = 0; /* clear the transmit indexes */
memset (lp->rbr, 0, TMXR_MAXBUF); /* clear the break status array */
return;
}
/* Report a connection to a line.
This routine sends a notification of the form:
Connected to the <sim> simulator <dev> device, line <n>
...to the line number specified by the "line" parameter of the multiplexer
associated with the line descriptor pointer "lp" and the mux descriptor
pointer "mp". If the device has only one line, the "line <n>" part is
omitted. If the device has not been defined, the "<dev> device" part is
omitted.
*/
void tmxr_report_connection (TMXR *mp, TMLN *lp, int32 line)
{
int32 buffer_count;
char line_number [20];
tmxr_linemsg (lp, "\n\r\nConnected to the "); /* report the connection */
tmxr_linemsg (lp, sim_name); /* to the simulator */
tmxr_linemsg (lp, " simulator ");
if (mp->dptr) { /* if the device pointer has been set */
tmxr_linemsg (lp, sim_dname (mp->dptr)); /* then report the name */
tmxr_linemsg (lp, " device"); /* of the connected device */
if (mp->lines > 1) { /* if the multiplexer has more than one line */
tmxr_linemsg (lp, ", line "); /* then report the line number */
sprintf (line_number, "%i", line); /* of the connection */
tmxr_linemsg (lp, line_number);
}
}
tmxr_linemsg (lp, "\r\n\n");
buffer_count = tmxr_send_buffered_data (lp); /* write the message */
if (buffer_count == 0) /* if the write buffer is now empty */
lp->xmte = 1; /* then reenable transmission if it was paused */
return;
}
/* Report a disconnection from a line.
This routine sends a notification of the form:
Disconnected from the <sim> simulator
...to the line number associated with the line descriptor pointer "lp" and
then disconnects the line.
Implementation notes:
1. We do not write the buffer here, because the disconnect routine will do
that for us.
*/
void tmxr_disconnect_line (TMLN *lp)
{
tmxr_linemsg (lp, "\r\nDisconnected from the "); /* report the disconnection */
tmxr_linemsg (lp, sim_name); /* from the simulator */
tmxr_linemsg (lp, " simulator\r\n\n");
tmxr_reset_ln (lp); /* disconnect the line */
return;
}
/* Local hook routines */
/* Read from a line.
This routine reads up to "length" bytes into the buffer associated with line
"lp". The actual number of bytes read is returned. If no bytes are
available, 0 is returned. If an error occurred while reading, -1 is
returned.
*/
static int32 tmxr_local_read (TMLN *lp, int32 length)
{
return sim_read_sock (lp->conn, &(lp->rxb [lp->rxbpi]), length);
}
/* Write to a line.
This routine writes up to "length" bytes from the buffer associated with
"lp". The actual number of bytes written is returned. If an error occurred
while writing, -1 is returned.
*/
static int32 tmxr_local_write (TMLN *lp, int32 length)
{
int32 written;
written = sim_write_sock (lp->conn, &(lp->txb [lp->txbpr]), length);
if (written == SOCKET_ERROR) /* did an error occur? */
return -1; /* return error indication */
else
return written;
}
/* Show a line.
This routine writes the port description to the file indicated by the
"stream" parameter.
*/
static void tmxr_local_show (TMLN *lp, FILE *stream)
{
fprintf (stream, "IP address %s", lp->ipad);
return;
}
/* Close a line.
This routine closes the line indicated by the "lp" parameter.
*/
static void tmxr_local_close (TMLN *lp)
{
sim_close_sock (lp->conn); /* reset conn */
return;
}