mirror of
https://github.com/simh/simh.git
synced 2026-02-27 09:09:46 +00:00
CONSOLE: Add general support for SET CONSOLE SPEED=nnn
The original SET CONSOLE SPEED=nnn was added to a allow direct wired
terminal connected to a host system serial port to be a simulator's console.
The current change generalizes all console I/O such that speed is a reliable
option for direct console connections as well as serial and telnet connections.
The simple recipe to get well behaved console output speed is:
1) call tmxr_set_console_units() in the reset routine of the console DEVICE.
2) In the code path that engages console output do something similar
to this as appropriate for the system being simulated:
void txdb_wr (int32 data)
{
tto_unit.buf = data & 0377;
tto_csr = tto_csr & ~CSR_DONE;
CLR_INT (TTO);
sim_activate (&tto_unit, tto_unit.wait);
}
3) In the output unit's service routine the console output unit's service
routine do something similar to this as appropriate for the system
being simulated:
t_stat tto_svc (UNIT *uptr)
{
int32 c;
c = sim_tt_outcvt (tto_unit.buf, TT_GET_MODE (uptr->flags));
if (c >= 0) {
t_stat r;
if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */
sim_activate (uptr, uptr->wait); /* retry */
return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */
}
}
tto_csr = tto_csr | CSR_DONE;
if (tto_csr & CSR_IE)
SET_INT (TTO);
}
The almost all of the current simh simulators already are implemented
with logic like the above example. These will work just fine with the
newly regulated console speed.
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
BIN
doc/simh.doc
BIN
doc/simh.doc
Binary file not shown.
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
40
sim_tmxr.c
40
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" : "");
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user