TMXR, CONSOLE: Add optional ability to rate limit input data to approximate bps values

A SET CONSOLE SPEED=nnn, where legal values for nnn are common serial
port rates.  The speed value will attempt to limit the input data rates to a
simulator to approximately the specified bits per second.
This commit is contained in:
Mark Pizzolato 2015-11-18 10:44:19 -08:00
parent 099dd40657
commit 53796ba961
4 changed files with 170 additions and 7 deletions

View file

@ -259,6 +259,7 @@ static CTAB set_con_tab[] = {
{ "BRK", &sim_set_kmap, KMAP_BRK },
{ "DEL", &sim_set_kmap, KMAP_DEL |KMAP_NZ },
{ "PCHAR", &sim_set_pchar, 0 },
{ "SPEED", &sim_set_cons_speed, 0 },
{ "TELNET", &sim_set_telnet, 0 },
{ "NOTELNET", &sim_set_notelnet, 0 },
{ "SERIAL", &sim_set_serial, 0 },
@ -291,6 +292,7 @@ static SHTAB show_con_tab[] = {
{ "BRK", &sim_show_kmap, KMAP_BRK },
{ "DEL", &sim_show_kmap, KMAP_DEL },
{ "PCHAR", &sim_show_pchar, 0 },
{ "SPEED", &sim_show_cons_speed, 0 },
{ "LOG", &sim_show_cons_log, 0 },
{ "TELNET", &sim_show_telnet, 0 },
{ "DEBUG", &sim_show_cons_debug, 0 },
@ -1322,6 +1324,20 @@ else fprintf (st, "pchar mask = %o\n", sim_tt_pchar);
return SCPE_OK;
}
/* Set input speed (bps) */
t_stat sim_set_cons_speed (int32 flag, char *cptr)
{
return tmxr_set_line_speed (&sim_con_ldsc, cptr);
}
t_stat sim_show_cons_speed (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
{
if (sim_con_ldsc.rxbps)
fprintf (st, "Speed = %dbps\n", sim_con_ldsc.rxbps);
return SCPE_OK;
}
/* Set log routine */
t_stat sim_set_logon (int32 flag, char *cptr)
@ -1906,14 +1922,20 @@ t_stat c;
if (sim_send_poll_data (&sim_con_send, &c)) /* injected input characters available? */
return c;
if (!sim_rem_master_mode) {
if ((sim_con_ldsc.rxbps) && /* rate limiting && */
((sim_con_ldsc.rxlasttime + (sim_con_ldsc.rxdelta + 500)/1000) > sim_os_msec ())) /* too soon? */
return SCPE_OK; /* not yet */
c = sim_os_poll_kbd (); /* get character */
if (c == SCPE_STOP) { /* ^E */
stop_cpu = 1; /* Force a stop (which is picked up by sim_process_event */
return SCPE_OK;
}
if ((sim_con_tmxr.master == 0) && /* not Telnet? */
(sim_con_ldsc.serport == 0)) /* and not serial? */
(sim_con_ldsc.serport == 0)) { /* and not serial? */
if (c && sim_con_ldsc.rxbps) /* got something && rate limiting? */
sim_con_ldsc.rxlasttime = sim_os_msec (); /* save last input time */
return c; /* in-window */
}
if (!sim_con_ldsc.conn) { /* no telnet or serial connection? */
if (!sim_con_ldsc.txbfd) /* unbuffered? */
return SCPE_LOST; /* connection lost */

View file

@ -88,6 +88,7 @@ t_stat sim_set_cons_expect (int32 flg, char *cptr);
t_stat sim_set_cons_noexpect (int32 flg, char *cptr);
t_stat sim_debug_flush (void);
t_stat sim_set_pchar (int32 flag, char *cptr);
t_stat sim_set_cons_speed (int32 flag, char *cptr);
t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
t_stat sim_show_remote_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
t_stat sim_show_kmap (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
@ -95,6 +96,7 @@ t_stat sim_show_telnet (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cp
t_stat sim_show_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
t_stat sim_show_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
t_stat sim_show_pchar (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
t_stat sim_show_cons_speed (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
t_stat sim_show_cons_buff (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
t_stat sim_show_cons_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
t_stat sim_show_cons_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);

View file

@ -1503,16 +1503,18 @@ t_stat tmxr_set_config_line (TMLN *lp, const char *config)
t_stat r;
tmxr_debug_trace_line (lp, "tmxr_set_config_line()");
if (!lp->modem_control) /* This API ONLY works on modem_control enabled multiplexer lines */
return SCPE_IERR;
if (lp->serport)
r = sim_config_serial (lp->serport, config);
else {
lp->serconfig = (char *)realloc (lp->serconfig, 1 + strlen (config));
strcpy (lp->serconfig, config);
r = SCPE_OK;
r = tmxr_set_line_speed (lp, lp->serconfig);;
if (r != SCPE_OK) {
free (lp->serconfig);
lp->serconfig = NULL;
}
if (r == SCPE_OK) /* Record port state for proper restore */
}
if ((r == SCPE_OK) && (lp->mp) && (lp->mp->uptr)) /* Record port state for proper restore */
lp->mp->uptr->filename = tmxr_mux_attach_string (lp->mp->uptr->filename, lp->mp);
return r;
}
@ -1544,7 +1546,9 @@ t_stat val = 0;
uint32 tmp;
tmxr_debug_trace_line (lp, "tmxr_getc_ln()");
if (lp->conn && lp->rcve) { /* conn & enb? */
if ((lp->conn && lp->rcve) && /* conn & enb & */
((!lp->rxbps) || /* (!rate limited | enough time passed)? */
((lp->rxlasttime + (lp->rxdelta+500)/1000) <= sim_os_msec ()))) {
if (!sim_send_poll_data (&lp->send, &val)) { /* injected input characters available? */
j = lp->rxbpi - lp->rxbpr; /* # input chrs */
if (j) { /* any? */
@ -1560,6 +1564,8 @@ if (lp->conn && lp->rcve) { /* conn & enb? */
} /* end if conn */
if (lp->rxbpi == lp->rxbpr) /* empty? zero ptrs */
lp->rxbpi = lp->rxbpr = 0;
if (val && lp->rxbps)
lp->rxlasttime = sim_os_msec ();
tmxr_debug_return(lp, val);
return val;
}
@ -2151,6 +2157,72 @@ if (uptr && uptr->filename) {
return SCPE_OK;
}
static int32 _tmln_speed_delta (const char *cptr)
{
struct {
const char *bps;
int32 delta;
} *spd, speeds[] = {
{"50", TMLN_SPD_50_BPS},
{"75", TMLN_SPD_75_BPS},
{"110", TMLN_SPD_110_BPS},
{"134", TMLN_SPD_134_BPS},
{"150", TMLN_SPD_150_BPS},
{"300", TMLN_SPD_300_BPS},
{"600", TMLN_SPD_600_BPS},
{"1200", TMLN_SPD_1200_BPS},
{"1800", TMLN_SPD_1800_BPS},
{"2000", TMLN_SPD_2000_BPS},
{"2400", TMLN_SPD_2400_BPS},
{"3600", TMLN_SPD_3600_BPS},
{"4800", TMLN_SPD_4800_BPS},
{"7200", TMLN_SPD_7200_BPS},
{"9600", TMLN_SPD_9600_BPS},
{"19200", TMLN_SPD_19200_BPS},
{"38400", TMLN_SPD_38400_BPS},
{"57600", TMLN_SPD_57600_BPS},
{"76800", TMLN_SPD_76800_BPS},
{"115200", TMLN_SPD_115200_BPS},
{"0", 0}}; /* End of List, last valid value */
int nspeed;
char speed[20];
nspeed = (uint32)strtotv (cptr, &cptr, 10);
if ((*cptr != '\0') && (*cptr != '-'))
return -1;
sprintf (speed, "%d", nspeed);
spd = speeds;
while (1) {
if (0 == strcmp(spd->bps, speed))
return spd->delta;
if (spd->delta == 0)
break;
++spd;
}
return -1;
}
t_stat tmxr_set_line_speed (TMLN *lp, const char *speed)
{
UNIT *uptr;
if (!speed || !*speed)
return SCPE_2FARG;
if (_tmln_speed_delta (speed) < 0)
return SCPE_ARG;
lp->rxbps = atoi (speed);
lp->rxdelta = _tmln_speed_delta (speed);
lp->rxlasttime = 0;
uptr = lp->uptr;
if ((!uptr) && (lp->mp))
uptr = lp->mp->uptr;
if (uptr)
uptr->wait = lp->rxdelta;
return SCPE_OK;
}
/* Open a master listening socket (and all of the other variances of connections).
A listening socket for the port number described by "cptr" is opened for the
@ -2168,7 +2240,7 @@ t_stat tmxr_open_master (TMXR *mp, char *cptr)
int32 i, line, nextline = -1;
char tbuf[CBUFSIZE], listen[CBUFSIZE], destination[CBUFSIZE],
logfiletmpl[CBUFSIZE], buffered[CBUFSIZE], hostport[CBUFSIZE],
port[CBUFSIZE], option[CBUFSIZE];
port[CBUFSIZE], option[CBUFSIZE], speed[CBUFSIZE];
SOCKET sock;
SERHANDLE serport;
char *tptr = cptr;
@ -2192,6 +2264,7 @@ while (*tptr) {
memset(buffered, '\0', sizeof(buffered));
memset(port, '\0', sizeof(port));
memset(option, '\0', sizeof(option));
memset(speed, '\0', sizeof(speed));
nolog = notelnet = listennotelnet = loopback = FALSE;
datagram = mp->datagram;
packet = mp->packet;
@ -2290,6 +2363,13 @@ while (*tptr) {
strcpy (destination, cptr);
continue;
}
if (0 == MATCH_CMD (gbuf, "SPEED")) {
if ((NULL == cptr) || ('\0' == *cptr) ||
(_tmln_speed_delta (cptr) < 0))
return SCPE_ARG;
strcpy (speed, cptr);
continue;
}
cptr = get_glyph (gbuf, port, ';');
if (sim_parse_addr (port, NULL, 0, NULL, NULL, 0, NULL, NULL))
return SCPE_ARG;
@ -2442,6 +2522,10 @@ while (*tptr) {
free (lp->serconfig);
lp->serconfig = NULL;
}
else {
if (speed[0])
tmxr_set_line_speed (lp, speed);
}
tmxr_init_line (lp); /* initialize line state */
lp->sock = 0; /* clear the socket */
}
@ -2453,6 +2537,8 @@ while (*tptr) {
for (i = 0; i < mp->lines; i++) {
lp = mp->ldsc + i;
tmxr_set_line_loopback (lp, loopback);
if (speed[0])
tmxr_set_line_speed (lp, speed);
}
}
if (destination[0]) {
@ -2509,6 +2595,8 @@ while (*tptr) {
sim_close_sock (sock);
lp->notelnet = notelnet;
tmxr_init_line (lp); /* init the line state */
if (speed[0] && (!datagram))
tmxr_set_line_speed (lp, speed);
return SCPE_OK;
}
else
@ -2624,6 +2712,8 @@ while (*tptr) {
sim_printf ("Line %d operating in loopback mode\n", line);
}
lp->modem_control = modem_control;
if (speed[0] && (!datagram) && (!lp->serport))
tmxr_set_line_speed (lp, speed);
r = SCPE_OK;
}
}
@ -3490,8 +3580,13 @@ else {
fprintf(st, ", ModemControl=%s", lp->modem_control ? "enabled" : "disabled");
if (lp->loopback)
fprintf(st, ", Loopback");
if (lp->rxbps)
fprintf(st, ", Speed=%dbps", lp->rxbps);
fprintf (st, "\n");
}
else
if (lp->rxbps)
fprintf(st, ", Speed=%dbps", lp->rxbps);
if ((!lp->sock) && (!lp->connecting) && (!lp->serport) && (!lp->master)) {
if (lp->modem_control)
tmxr_fconns (st, lp, -1);
@ -3625,6 +3720,20 @@ return _sim_activate_after (uptr, usecs_walltime);
#endif
}
t_stat tmxr_activate_after_abs (UNIT *uptr, int32 usecs_walltime)
{
#if defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_MUX)
if ((!(uptr->dynflags & UNIT_TM_POLL)) ||
(!sim_asynch_enabled)) {
return _sim_activate_after_abs (uptr, usecs_walltime);
}
return SCPE_OK;
#else
return _sim_activate_after_abs (uptr, usecs_walltime);
#endif
}
t_stat tmxr_clock_coschedule (UNIT *uptr, int32 interval)
{
return tmxr_clock_coschedule_tmr (uptr, 0, interval);

View file

@ -96,6 +96,30 @@ typedef int SERHANDLE;
/* statically in a unit's flag field */
/* This will disable the unit from */
/* supporting asynchronmous mux behaviors */
/* Receive line speed limits */
#define TMLN_SPD_50_BPS 200000 /* usec per character */
#define TMLN_SPD_75_BPS 133333 /* usec per character */
#define TMLN_SPD_110_BPS 90909 /* usec per character */
#define TMLN_SPD_134_BPS 74626 /* usec per character */
#define TMLN_SPD_150_BPS 66666 /* usec per character */
#define TMLN_SPD_300_BPS 33333 /* usec per character */
#define TMLN_SPD_600_BPS 16666 /* usec per character */
#define TMLN_SPD_1200_BPS 8333 /* usec per character */
#define TMLN_SPD_1800_BPS 5555 /* usec per character */
#define TMLN_SPD_2000_BPS 5000 /* usec per character */
#define TMLN_SPD_2400_BPS 4166 /* usec per character */
#define TMLN_SPD_3600_BPS 2777 /* usec per character */
#define TMLN_SPD_4800_BPS 2083 /* usec per character */
#define TMLN_SPD_7200_BPS 1388 /* usec per character */
#define TMLN_SPD_9600_BPS 1041 /* usec per character */
#define TMLN_SPD_19200_BPS 520 /* usec per character */
#define TMLN_SPD_38400_BPS 260 /* usec per character */
#define TMLN_SPD_57600_BPS 173 /* usec per character */
#define TMLN_SPD_76800_BPS 130 /* usec per character */
#define TMLN_SPD_115200_BPS 86 /* usec per character */
typedef struct tmln TMLN;
typedef struct tmxr TMXR;
@ -142,6 +166,9 @@ struct tmln {
uint8 *rxpb; /* rcv packet buffer */
uint32 rxpbsize; /* rcv packet buffer size */
uint32 rxpboffset; /* rcv packet buffer offset */
uint32 rxbps; /* rcv bps speed (0 - unlimited) */
uint32 rxdelta; /* rcv inter character min time (ms) */
uint32 rxlasttime; /* time last received character was read */
uint8 *txpb; /* xmt packet buffer */
uint32 txpbsize; /* xmt packet buffer size */
uint32 txppsize; /* xmt packet packet size */
@ -215,6 +242,7 @@ t_stat tmxr_set_line_loopback (TMLN *lp, t_bool enable_loopback);
t_bool tmxr_get_line_loopback (TMLN *lp);
t_stat tmxr_set_line_halfduplex (TMLN *lp, t_bool enable_loopback);
t_bool tmxr_get_line_halfduplex (TMLN *lp);
t_stat tmxr_set_line_speed (TMLN *lp, const char *speed);
t_stat tmxr_set_config_line (TMLN *lp, const char *config);
t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll);
t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll);
@ -243,6 +271,7 @@ t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, char* desc);
t_stat tmxr_activate (UNIT *uptr, int32 interval);
t_stat tmxr_activate_after (UNIT *uptr, int32 usecs_walltime);
t_stat tmxr_activate_after_abs (UNIT *uptr, int32 usecs_walltime);
t_stat tmxr_clock_coschedule (UNIT *uptr, int32 interval);
t_stat tmxr_clock_coschedule_tmr (UNIT *uptr, int32 tmr, int32 interval);
t_stat tmxr_change_async (void);
@ -266,6 +295,7 @@ void _tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsiz
#if (!defined(NOT_MUX_USING_CODE))
#define sim_activate tmxr_activate
#define sim_activate_after tmxr_activate_after
#define sim_activate_after_abs tmxr_activate_after_abs
#define sim_clock_coschedule tmxr_clock_coschedule
#define sim_clock_coschedule_tmr tmxr_clock_coschedule_tmr
#endif