diff --git a/sim_console.c b/sim_console.c index d1b0dd2d..bcba790f 100644 --- a/sim_console.c +++ b/sim_console.c @@ -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 */ diff --git a/sim_console.h b/sim_console.h index 2c72676f..a1ff8f62 100644 --- a/sim_console.h +++ b/sim_console.h @@ -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); diff --git a/sim_tmxr.c b/sim_tmxr.c index 745eab16..a19c8043 100644 --- a/sim_tmxr.c +++ b/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); diff --git a/sim_tmxr.h b/sim_tmxr.h index 3bcd3d12..8d24994c 100644 --- a/sim_tmxr.h +++ b/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