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:
parent
099dd40657
commit
53796ba961
4 changed files with 170 additions and 7 deletions
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
121
sim_tmxr.c
121
sim_tmxr.c
|
@ -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);
|
||||
|
|
30
sim_tmxr.h
30
sim_tmxr.h
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue