diff --git a/README.md b/README.md index 4f6c9cc7..15ff04d4 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ Simulator binaries for x86 Linus, x86 macOS, and Windows for all recent changes - Simulator THROTTLING can only be enabled once per simulator run. This avoids potential errant behaviors when arbitrarily switching throttling settings. - EXAMINE memory commands will now produce minimal output that summarizes multiple successive locations with the same contents and may be aborted by SIGINT (Control-C). - Ethernet enhancements on all platforms and and much simplified platform support on macOS. +- Almost all existing simulators have useful SET CONSOLE SPEED=nnn behaviors in both the bare console session as well as TELNET console sessions. #### All simulators build cleanly under OpenVMS on ia64 systems. diff --git a/doc/simh.doc b/doc/simh.doc index 091d7fe7..e26e17f2 100644 Binary files a/doc/simh.doc and b/doc/simh.doc differ diff --git a/sim_console.c b/sim_console.c index a2cffda2..d0813576 100644 --- a/sim_console.c +++ b/sim_console.c @@ -113,7 +113,7 @@ sim_ttclose called once before the simulator exits sim_ttisatty called to determine if running interactively sim_os_poll_kbd poll for keyboard input - sim_os_putchar output character to console + _sim_os_putchar output character to console sim_set_noconsole_port Enable automatic WRU console polling sim_set_stable_registers_state Declare that all registers are always stable @@ -148,7 +148,6 @@ int setenv(const char *envname, const char *envval, int overwrite); /* Forward Declarations of Platform specific routines */ static t_stat sim_os_poll_kbd (void); -static t_stat sim_os_putchar (int32 out); static t_stat sim_os_ttinit (void); static t_stat sim_os_ttrun (void); static t_stat sim_os_ttcmd (void); @@ -2259,10 +2258,13 @@ return SCPE_OK; t_stat sim_set_cons_speed (int32 flag, CONST char *cptr) { -t_stat r = tmxr_set_line_speed (&sim_con_ldsc, cptr); +t_stat r; -if ((r == SCPE_OK) && (sim_con_ldsc.uptr != NULL)) - sim_con_ldsc.uptr->wait = sim_con_ldsc.rxdeltausecs; +if (sim_con_ldsc.o_uptr == NULL) + return sim_messagef (SCPE_TTOERR, "Can't set port speed. Console Output unit missing.\n"); +r = tmxr_set_line_speed (&sim_con_ldsc, cptr); +if (r == SCPE_OK) + sim_con_ldsc.o_uptr->wait = 0; return r; } @@ -2549,8 +2551,10 @@ while (*cptr != 0) { /* do all mods */ else { if (cvptr) /* if we removed a = sign */ *(--cvptr) = '='; /* restore it */ - if (sim_con_tmxr.master) /* already open? */ - sim_set_notelnet (0, NULL); /* close first */ + if ((sim_con_tmxr.master) || /* already open? */ + (sim_con_ldsc.serport) || + (sim_con_ldsc.console)) + tmxr_close_master (&sim_con_tmxr); /* close first */ r = tmxr_attach (&sim_con_tmxr, &sim_con_unit, gbuf);/* open master socket */ if (r == SCPE_OK) sim_activate_after(&sim_con_unit, 1000000); /* check for connection in 1 second */ @@ -2567,9 +2571,8 @@ t_stat sim_set_notelnet (int32 flag, CONST char *cptr) { if (cptr && (*cptr != 0)) /* too many arguments? */ return SCPE_2MARG; -if (sim_con_tmxr.master == 0) /* ignore if already closed */ - return SCPE_OK; -return tmxr_close_master (&sim_con_tmxr); /* close master socket */ +tmxr_close_master (&sim_con_tmxr); /* close master socket, if open */ +return tmxr_attach (&sim_con_tmxr, &sim_con_unit, "CONSOLE"); } /* Show console Telnet status */ @@ -3049,21 +3052,18 @@ return SCPE_OK; t_stat sim_putchar (int32 c) { -sim_exp_check (&sim_con_expect, c); -if ((sim_con_tmxr.master == 0) && /* not Telnet? */ - (sim_con_ldsc.serport == 0)) { /* and not serial port */ - ++sim_con_pos; /* bookkeeping */ - if (sim_log) /* log file? */ - fputc (c, sim_log); - sim_debug (DBG_XMT, &sim_con_telnet, "sim_putchar('%c' (0x%02X)\n", sim_isprint (c) ? c : '.', c); - return sim_os_putchar (c); /* in-window version */ - } -if (!sim_con_ldsc.conn) { /* no Telnet or serial connection? */ - if (!sim_con_ldsc.txbfd) /* unbuffered? */ - return SCPE_LOST; /* connection lost */ +if (!sim_con_ldsc.console && /* Non Console */ + !sim_con_ldsc.serport && /* no serial connection */ + ((sim_con_tmxr.master != 0) && /* Telnet but not connected */ + !sim_con_ldsc.conn)) { + if (!sim_con_ldsc.txbfd) /* non-buffered Telnet connection? */ + return SCPE_LOST; /* lost */ if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */ sim_con_ldsc.rcve = 1; /* rcv enabled */ } +if (sim_log) /* log file? */ + fputc (c, sim_log); +sim_debug (DBG_XMT, &sim_con_telnet, "sim_putchar('%c' (0x%02X)\n", sim_isprint (c) ? c : '.', c); tmxr_putc_ln (&sim_con_ldsc, c); /* output char */ ++sim_con_pos; /* bookkeeping */ tmxr_poll_tx (&sim_con_tmxr); /* poll xmt */ @@ -3074,25 +3074,34 @@ t_stat sim_putchar_s (int32 c) { t_stat r; -sim_exp_check (&sim_con_expect, c); -if ((sim_con_tmxr.master == 0) && /* not Telnet? */ - (sim_con_ldsc.serport == 0)) { /* and not serial port */ - ++sim_con_pos; /* bookkeeping */ - if (sim_log) /* log file? */ - fputc (c, sim_log); - sim_debug (DBG_XMT, &sim_con_telnet, "sim_putchar('%c' (0x%02X)\n", sim_isprint (c) ? c : '.', c); - return sim_os_putchar (c); /* in-window version */ - } -if (!sim_con_ldsc.conn) { /* no Telnet or serial connection? */ +if (!sim_con_ldsc.console && /* Non Console */ + !sim_con_ldsc.serport && /* no serial connection */ + ((sim_con_tmxr.master != 0) && /* Telnet but not connected */ + !sim_con_ldsc.conn)) { if (!sim_con_ldsc.txbfd) /* non-buffered Telnet connection? */ return SCPE_LOST; /* lost */ if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */ sim_con_ldsc.rcve = 1; /* rcv enabled */ } -r = tmxr_putc_ln (&sim_con_ldsc, c); /* Telnet output */ +if (tmxr_txdone_ln (&sim_con_ldsc) == 0) { + if (sim_con_ldsc.txbps) /* rate limiting? */ + sim_con_ldsc.o_uptr->wait = /* Long poll to allow proper scheduling*/ + (int32)((2 * TMLN_SPD_50_BPS * sim_timer_inst_per_sec ()) / USECS_PER_SECOND); + else + sim_con_ldsc.o_uptr->wait = SERIAL_OUT_WAIT; /* "standard" output wait */ + return SCPE_STALL; + } +if (sim_log) /* log file? */ + fputc (c, sim_log); +sim_debug (DBG_XMT, &sim_con_telnet, "sim_putchar_s('%c' (0x%02X)\n", sim_isprint (c) ? c : '.', c); +r = tmxr_putc_ln (&sim_con_ldsc, c); /* Telnet & Console output */ if (r == SCPE_OK) ++sim_con_pos; /* bookkeeping */ tmxr_poll_tx (&sim_con_tmxr); /* poll xmt */ +if (sim_con_ldsc.txbps) /* rate limiting? */ + sim_con_ldsc.o_uptr->wait = 0; /* next one 0 wait */ +else + sim_con_ldsc.o_uptr->wait = SERIAL_OUT_WAIT; /* "standard" output wait */ return r; /* return status */ } @@ -3247,8 +3256,10 @@ return SCPE_OK; t_stat sim_ttinit (void) { sim_con_tmxr.ldsc->mp = &sim_con_tmxr; +sim_con_tmxr.ldsc->expect = &sim_con_expect; sim_register_internal_device (&sim_con_telnet); tmxr_startup (); +sim_set_notelnet (0, NULL); return sim_os_ttinit (); } @@ -3427,7 +3438,7 @@ if (response = buffered_character) { return sim_os_poll_kbd_data (); } -static t_stat sim_os_putchar (int32 out) +t_stat _sim_os_putchar (int32 out) { unsigned int status; char c; @@ -3680,7 +3691,7 @@ return SCPE_OK; #define out_hold_unit sim_con_units[1] -static t_stat sim_os_putchar (int32 c) +t_stat _sim_os_putchar (int32 c) { uint32 now; static uint32 last_bell_time; @@ -3880,7 +3891,7 @@ if (sim_int_char && (buf[0] == sim_int_char)) return (buf[0] | SCPE_KFLAG); } -static t_stat sim_os_putchar (int32 out) +t_stat _sim_os_putchar (int32 out) { char c; @@ -4047,7 +4058,7 @@ if (sim_int_char && (buf[0] == sim_int_char)) return (buf[0] | SCPE_KFLAG); } -static t_stat sim_os_putchar (int32 out) +t_stat _sim_os_putchar (int32 out) { char c; diff --git a/sim_scp_private.h b/sim_scp_private.h index f84d61cd..e7fce015 100644 --- a/sim_scp_private.h +++ b/sim_scp_private.h @@ -404,6 +404,11 @@ struct SEND { int32 extoff; /* extra offset */ }; +/* Private SCP only APIs */ + +t_stat _sim_os_putchar (int32 out); + + #endif /* defined(SIM_SCP_PRIVATE_DONT_REPEAT) */ #ifdef __cplusplus diff --git a/sim_tmxr.c b/sim_tmxr.c index 89280085..95ee87a6 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -771,11 +771,15 @@ else { } } else { - if ((lp->conn == TMXR_LINE_DISABLED) || - ((lp->conn == 0) && lp->txbfd)){ - written = length; /* Count here output timing is correct */ - if (lp->conn == TMXR_LINE_DISABLED) - lp->txdrp += length; /* Record as having been dropped on the floor */ + if (lp->console) + written = (SCPE_OK == _sim_os_putchar (lp->txb[i])) ? 1 : 0; /* write to the sim> session */ + else { + if ((lp->conn == TMXR_LINE_DISABLED) || + ((lp->conn == 0) && lp->txbfd)){ + written = length; /* Count here output timing is correct */ + if (lp->conn == TMXR_LINE_DISABLED) + lp->txdrp += length; /* Record as having been dropped on the floor */ + } } } } @@ -987,9 +991,11 @@ tptr = (char *) calloc (1, 1); if (tptr == NULL) /* no more mem? */ return tptr; -if (lp->destination || lp->port || lp->txlogname || (lp->conn == TMXR_LINE_DISABLED)) { +if (lp->destination || lp->port || lp->txlogname || (lp->conn == TMXR_LINE_DISABLED) || lp->console) { if ((lp->mp->lines > 1) || (lp->port)) sprintf (growstring(&tptr, 32), "Line=%d", (int)(lp-lp->mp->ldsc)); + if (lp->console) + sprintf (growstring(&tptr, 32), "CONSOLE"); if (lp->conn == TMXR_LINE_DISABLED) sprintf (growstring(&tptr, 32), ",Disabled"); if (lp->modem_control != lp->mp->modem_control) @@ -2919,7 +2925,7 @@ ETH_DEV *eth; SOCKET sock; SERHANDLE serport; CONST char *tptr = cptr; -t_bool nolog, notelnet, listennotelnet, nomessage, listennomessage, modem_control, loopback, datagram, packet, disabled; +t_bool nolog, notelnet, listennotelnet, nomessage, listennomessage, modem_control, loopback, datagram, packet, disabled, console; int32 listenbacklog; TMLN *lp; t_stat r = SCPE_OK; @@ -2934,6 +2940,7 @@ for (i = 0; i < mp->lines; i++) { /* initialize lines */ if (lp->bpsfactor == 0.0) lp->bpsfactor = 1.0; } +console = FALSE; notelnet = listennotelnet = mp->notelnet; nomessage = listennomessage = mp->nomessage; listenbacklog = mp->backlog; @@ -3077,6 +3084,10 @@ while (*tptr) { strlcpy (speed, cptr, sizeof(speed)); continue; } + if (0 == MATCH_CMD (gbuf, "CONSOLE")) { + console = TRUE; + continue; + } cptr = get_glyph (gbuf, port, ';'); if (sim_parse_addr (port, NULL, 0, NULL, NULL, 0, NULL, NULL)) return sim_messagef (SCPE_ARG, "Invalid Port Specifier: %s\n", port); @@ -3177,6 +3188,10 @@ while (*tptr) { if (destination[0] || listen[0] || loopback || framer[0]) return sim_messagef (SCPE_ARG, "Can't disable line with%s%s%s%s%s%s%s\n", destination[0] ? " CONNECT=" : "", destination, listen[0] ? " " : "", listen, loopback ? " LOOPBACK" : "", framer[0] ? " SYNC=" : "", framer); } + if (console) { + if (destination[0] || listen[0] || loopback || framer[0] || disabled) + return sim_messagef (SCPE_ARG, "Can't have console line with%s%s%s%s%s%s%s%s\n", destination[0] ? " CONNECT=" : "", destination, listen[0] ? " " : "", listen, loopback ? " LOOPBACK" : "", framer[0] ? " SYNC=" : "", framer, disabled ? " DISABLED" : ""); + } if (destination[0]) { /* Validate destination */ if (framer[0]) @@ -3387,6 +3402,10 @@ while (*tptr) { tmxr_set_line_speed (lp, speed); } } + if (console) { + lp->console = TRUE; + lp->conn = 1; + } if (destination[0]) { if (mp->lines > 1) return sim_messagef (SCPE_ARG, "Ambiguous Destination specification\n"); @@ -3736,6 +3755,8 @@ t_stat tmxr_set_console_units (UNIT *rxuptr, UNIT *txuptr) { extern TMXR sim_con_tmxr; +rxuptr->tmxr = &sim_con_tmxr; +txuptr->tmxr = &sim_con_tmxr; tmxr_set_line_unit (&sim_con_tmxr, 0, rxuptr); tmxr_set_line_output_unit (&sim_con_tmxr, 0, txuptr); return SCPE_OK; @@ -4105,7 +4126,7 @@ return SCPE_OK; The listening socket associated with multiplexer descriptor "mp" is closed and deallocated. In addition, all current Telnet sessions are disconnected. - Serial and outgoing sessions are also disconnected. + Serial, outgoing sessions and console connections are also disconnected. */ t_stat tmxr_close_master (TMXR *mp) @@ -4129,6 +4150,7 @@ for (i = 0; i < mp->lines; i++) { /* loop thru conn */ sim_control_serial (lp->serport, 0, TMXR_MDM_DTR|TMXR_MDM_RTS, NULL);/* drop DTR and RTS */ tmxr_close_ln (lp); } + lp->console = FALSE; free (lp->destination); lp->destination = NULL; free (lp->acl); @@ -5215,7 +5237,7 @@ int32 i, t; if (mp == NULL) return SCPE_IERR; for (i = t = 0; i < mp->lines; i++) - if ((mp->ldsc[i].sock != 0) || (mp->ldsc[i].serport != 0)) + if ((mp->ldsc[i].sock != 0) || (mp->ldsc[i].serport != 0) || (mp->ldsc[i].console != 0)) t = t + 1; if (mp->lines > 1) fprintf (st, "%d current connection%s", t, (t != 1) ? "s" : ""); diff --git a/sim_tmxr.h b/sim_tmxr.h index 4d4a62c8..cfcf5041 100644 --- a/sim_tmxr.h +++ b/sim_tmxr.h @@ -192,6 +192,7 @@ struct tmln { t_bool ser_connect_pending; /* serial connection notice pending */ SOCKET connecting; /* Outgoing socket while connecting */ char *destination; /* Outgoing destination address:port */ + t_bool console; /* simulator I/O to console session */ t_bool loopback; /* Line in loopback mode */ t_bool halfduplex; /* Line in half-duplex mode */ t_bool datagram; /* Line is datagram packet oriented */