root/src/simh/sim_console.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sim_con_poll_svc
  2. sim_con_reset
  3. sim_set_console
  4. sim_show_console
  5. sim_set_remote_console
  6. sim_show_remote_console
  7. sim_rem_con_poll_svc
  8. x_continue_cmd
  9. x_step_cmd
  10. x_run_cmd
  11. x_help_cmd
  12. _sim_rem_message
  13. _sim_rem_log_out
  14. sim_remote_process_command
  15. sim_rem_con_data_svc
  16. sim_rem_con_reset
  17. sim_set_rem_telnet
  18. sim_set_rem_connections
  19. sim_set_rem_timeout
  20. sim_set_kmap
  21. sim_show_kmap
  22. sim_set_pchar
  23. sim_set_cons_speed
  24. sim_show_cons_speed
  25. sim_set_logon
  26. sim_set_logoff
  27. sim_show_log
  28. sim_set_debon
  29. sim_debug_flush
  30. sim_set_deboff
  31. sim_show_debug
  32. sim_set_telnet
  33. sim_set_notelnet
  34. sim_show_telnet
  35. sim_set_cons_buff
  36. sim_set_cons_unbuff
  37. sim_set_cons_log
  38. sim_set_cons_nolog
  39. sim_show_cons_log
  40. sim_show_cons_buff
  41. sim_show_cons_expect
  42. sim_open_logfile
  43. sim_close_logfile
  44. sim_logfile_name
  45. sim_check_console
  46. sim_cons_get_send
  47. sim_cons_get_expect
  48. sim_show_cons_send_input
  49. sim_poll_kbd
  50. sim_putchar
  51. sim_ttinit
  52. sim_ttrun
  53. sim_ttcmd
  54. sim_ttclose
  55. sim_ttisatty
  56. ControlHandler
  57. sim_os_ttinit
  58. sim_os_ttrun
  59. sim_os_ttcmd
  60. sim_os_ttclose
  61. sim_os_ttisatty
  62. sim_os_poll_kbd
  63. sim_os_putchar
  64. sim_os_ttinit
  65. sim_os_ttrun
  66. sim_os_ttcmd
  67. sim_os_ttclose
  68. sim_os_ttisatty
  69. sim_os_poll_kbd
  70. sim_os_putchar
  71. sim_os_ttinit
  72. sim_os_ttrun
  73. sim_os_ttcmd
  74. sim_os_ttclose
  75. sim_os_ttisatty
  76. sim_os_poll_kbd
  77. sim_os_putchar
  78. decode
  79. sim_set_halt
  80. sim_set_response
  81. sim_set_delay

   1 /*
   2  * sim_console.c: simulator console I/O library
   3  *
   4  * vim: filetype=c:tabstop=4:ai:expandtab
   5  * SPDX-License-Identifier: MIT
   6  * scspell-id: a2e214e2-f62a-11ec-89cf-80ee73e9b8e7
   7  *
   8  * ---------------------------------------------------------------------------
   9  *
  10  * Copyright (c) 1993-2014 Robert M. Supnik
  11  * Copyright (c) 2021-2023 The DPS8M Development Team
  12  *
  13  * Permission is hereby granted, free of charge, to any person obtaining a
  14  * copy of this software and associated documentation files (the "Software"),
  15  * to deal in the Software without restriction, including without limitation
  16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  17  * and/or sell copies of the Software, and to permit persons to whom the
  18  * Software is furnished to do so, subject to the following conditions:
  19  *
  20  * The above copyright notice and this permission notice shall be included in
  21  * all copies or substantial portions of the Software.
  22  *
  23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  24  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  26  * ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  27  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
  28  * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  29  * SOFTWARE.
  30  *
  31  * Except as contained in this notice, the name of Robert M. Supnik shall not
  32  * be used in advertising or otherwise to promote the sale, use or other
  33  * dealings in this Software without prior written authorization from
  34  * Robert M. Supnik.
  35  *
  36  * ---------------------------------------------------------------------------
  37  */
  38 
  39 //-V::701
  40 
  41 /*
  42  * This module implements the following routines to support terminal and
  43  * Remote Console I/O:
  44  *
  45  * sim_poll_kbd                 poll for keyboard input
  46  * sim_putchar                  output character to console
  47  * sim_set_console              set console parameters
  48  * sim_show_console             show console parameters
  49  * sim_set_remote_console       set remote console parameters
  50  * sim_show_remote_console      show remote console parameters
  51  * sim_set_cons_buff            set console buffered
  52  * sim_set_cons_unbuff          set console unbuffered
  53  * sim_set_cons_log             set console log
  54  * sim_set_cons_nolog           set console nolog
  55  * sim_show_cons_buff           show console buffered
  56  * sim_show_cons_log            show console log
  57  * sim_cons_get_send            get console send structure address
  58  * sim_cons_get_expect          get console expect structure address
  59  * sim_show_cons_send_input     show pending input data
  60  * sim_show_cons_expect         show expect rules and state
  61  * sim_ttinit                   called once to get initial terminal state
  62  * sim_ttrun                    called to put terminal into run state
  63  * sim_ttcmd                    called to return terminal to command state
  64  * sim_ttclose                  called once before the simulator exits
  65  * sim_ttisatty                 called to determine if running interactively
  66  * sim_os_poll_kbd              poll for keyboard input
  67  * sim_os_putchar               output character to console
  68  *
  69  * The first group is OS-independent; the second group is OS-dependent.
  70  *
  71  * The following routines are exposed but deprecated:
  72  *
  73  * sim_set_telnet               set console to Telnet port
  74  * sim_set_notelnet             close console Telnet port
  75  * sim_show_telnet              show console status
  76  */
  77 
  78 #include "sim_defs.h"
  79 #include "sim_tmxr.h"
  80 #include "sim_timer.h"
  81 #include <ctype.h>
  82 #include <signal.h>
  83 #include <math.h>
  84 
  85 #define DBG_CTR 0
  86 
  87 #include "../dps8/dps8.h"
  88 
  89 #ifdef __HAIKU__
  90 # define nice(n) ({})
  91 #endif /* ifdef __HAIKU__ */
  92 
  93 #ifdef TESTING
  94 # include "../dps8/dps8_cpu.h"
  95 # undef realloc
  96 # undef FREE
  97 # define FREE(p) free(p)
  98 # define realloc trealloc
  99 #endif /* ifdef TESTING */
 100 
 101 /* Forward Declarations of Platform specific routines */
 102 
 103 static t_stat sim_os_poll_kbd (void);
 104 static t_stat sim_os_putchar (int32 out);
 105 static t_stat sim_os_ttinit (void);
 106 static t_stat sim_os_ttrun (void);
 107 static t_stat sim_os_ttcmd (void);
 108 static t_stat sim_os_ttclose (void);
 109 static t_bool sim_os_ttisatty (void);
 110 
 111 static t_stat sim_set_rem_telnet (int32 flag, CONST char *cptr);
 112 static t_stat sim_set_rem_connections (int32 flag, CONST char *cptr);
 113 static t_stat sim_set_rem_timeout (int32 flag, CONST char *cptr);
 114 
 115 /* Deprecated CONSOLE HALT, CONSOLE RESPONSE and CONSOLE DELAY support */
 116 static t_stat sim_set_halt (int32 flag, CONST char *cptr);
 117 static t_stat sim_set_response (int32 flag, CONST char *cptr);
 118 static t_stat sim_set_delay (int32 flag, CONST char *cptr);
 119 
 120 #define KMAP_WRU        0
 121 #define KMAP_BRK        1
 122 #define KMAP_DEL        2
 123 #define KMAP_MASK       0377
 124 #define KMAP_NZ         0400
 125 
 126 int32 sim_int_char = 005;                               /* interrupt character */
 127 int32 sim_brk_char = 000;                               /* break character */
 128 int32 sim_tt_pchar = 0x00002780;
 129 #if defined (_WIN32)
 130 int32 sim_del_char = '\b';                              /* delete character */
 131 #else
 132 int32 sim_del_char = 0177;
 133 #endif /* if defined (_WIN32) */
 134 
 135 static t_stat sim_con_poll_svc (UNIT *uptr);                /* console connection poll routine */
 136 static t_stat sim_con_reset (DEVICE *dptr);                 /* console reset routine */
 137 UNIT sim_con_unit = { UDATA (&sim_con_poll_svc, 0, 0)  };   /* console connection unit */
 138 
 139 /* debugging bitmaps */
 140 #define DBG_TRC  TMXR_DBG_TRC                           /* trace routine calls */
 141 #define DBG_XMT  TMXR_DBG_XMT                           /* display Transmitted Data */
 142 #define DBG_RCV  TMXR_DBG_RCV                           /* display Received Data */
 143 #define DBG_RET  TMXR_DBG_RET                           /* display Returned Received Data */
 144 #define DBG_ASY  TMXR_DBG_ASY                           /* asynchronous thread activity */
 145 #define DBG_EXP  0x00000001                             /* Expect match activity */
 146 #define DBG_SND  0x00000002                             /* Send (Inject) data activity */
 147 
 148 static DEBTAB sim_con_debug[] = {
 149   {"TRC", DBG_TRC},
 150   {"XMT", DBG_XMT},
 151   {"RCV", DBG_RCV},
 152   {"RET", DBG_RET},
 153   {"ASY", DBG_ASY},
 154   {"EXP", DBG_EXP},
 155   {"SND", DBG_SND},
 156   {0}
 157 };
 158 
 159 static REG sim_con_reg[] = {
 160     { ORDATAD (WRU,   sim_int_char, 8,  "interrupt character") },
 161     { ORDATAD (BRK,   sim_brk_char, 8,  "break character") },
 162     { ORDATAD (DEL,   sim_del_char, 8,  "delete character ") },
 163     { ORDATAD (PCHAR, sim_tt_pchar, 32, "printable character mask") },
 164   { 0 },
 165 };
 166 
 167 static MTAB sim_con_mod[] = {
 168   { 0 },
 169 };
 170 
 171 DEVICE sim_con_telnet = {
 172     "CON-TEL", &sim_con_unit, sim_con_reg,   sim_con_mod,
 173     1,         0,             0,             0,           0,    0,
 174     NULL,      NULL,          sim_con_reset, NULL,        NULL, NULL,
 175     NULL,      DEV_DEBUG,     0,             sim_con_debug};
 176 TMLN sim_con_ldsc = { 0 };                                             /* line descr */
 177 TMXR sim_con_tmxr = { 1, 0, 0, &sim_con_ldsc, NULL, &sim_con_telnet }; /* line mux   */
 178 
 179 SEND sim_con_send = {SEND_DEFAULT_DELAY, &sim_con_telnet, DBG_SND};
 180 EXPECT sim_con_expect = {&sim_con_telnet, DBG_EXP};
 181 
 182 static t_bool sim_con_console_port = TRUE;
 183 
 184 /* Unit service for console connection polling */
 185 
 186 static t_stat sim_con_poll_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 187 {
 188 if ((sim_con_tmxr.master  == 0) &&                      /* not Telnet and not WRU polling? */
 189     (sim_con_ldsc.serport == 0) &&
 190     /* cppcheck-suppress knownConditionTrueFalse */
 191     (sim_con_console_port))
 192     return SCPE_OK;                                     /* done */
 193 if (tmxr_poll_conn (&sim_con_tmxr) >= 0)                /* poll connect */
 194     sim_con_ldsc.rcve = 1;                              /* rcv enabled */
 195 sim_activate_after(uptr, 1000000);                      /* check again in 1 second */
 196 /* cppcheck-suppress knownConditionTrueFalse */
 197 if (!sim_con_console_port)                              /* WRU poll needed */
 198     sim_poll_kbd();                                     /* sets global stop_cpu when WRU received */
 199 if (sim_con_ldsc.conn)
 200     tmxr_send_buffered_data (&sim_con_ldsc);            /* try to flush any buffered data */
 201 return SCPE_OK;
 202 }
 203 
 204 static t_stat sim_con_reset (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 205 {
 206 return sim_con_poll_svc (&dptr->units[0]);              /* establish polling as needed */
 207 }
 208 
 209 /* Set/show data structures */
 210 
 211 static CTAB set_con_tab[] = {
 212     { "WRU",         &sim_set_kmap,             KMAP_WRU | KMAP_NZ },
 213     { "BRK",         &sim_set_kmap,             KMAP_BRK },
 214     { "DEL",         &sim_set_kmap,             KMAP_DEL |KMAP_NZ },
 215     { "PCHAR",       &sim_set_pchar,            0 },
 216     { "SPEED",       &sim_set_cons_speed,       0 },
 217     { "TELNET",      &sim_set_telnet,           0 },
 218     { "NOTELNET",    &sim_set_notelnet,         0 },
 219     { "LOG",         &sim_set_logon,            0 },
 220     { "NOLOG",       &sim_set_logoff,           0 },
 221     { "DEBUG",       &sim_set_debon,            0 },
 222     { "NODEBUG",     &sim_set_deboff,           0 },
 223 #define CMD_WANTSTR     0100000
 224     { "HALT",        &sim_set_halt,             1 | CMD_WANTSTR },
 225     { "NOHALT",      &sim_set_halt,             0 },
 226     { "DELAY",       &sim_set_delay,            0 },
 227     { "RESPONSE",    &sim_set_response,         1 | CMD_WANTSTR },
 228     { "NORESPONSE",  &sim_set_response,         0 },
 229     { NULL,          NULL,                      0 }
 230     };
 231 
 232 static CTAB set_rem_con_tab[] = {
 233     { "CONNECTIONS", &sim_set_rem_connections,  0 },
 234     { "TELNET",      &sim_set_rem_telnet,       1 },
 235     { "NOTELNET",    &sim_set_rem_telnet,       0 },
 236     { "TIMEOUT",     &sim_set_rem_timeout,      0 },
 237     { NULL,          NULL,                      0 }
 238     };
 239 
 240 static SHTAB show_con_tab[] = {
 241     { "WRU",         &sim_show_kmap,            KMAP_WRU },
 242     { "BRK",         &sim_show_kmap,            KMAP_BRK },
 243     { "DEL",         &sim_show_kmap,            KMAP_DEL },
 244     { "SPEED",       &sim_show_cons_speed,      0 },
 245     { "LOG",         &sim_show_cons_log,        0 },
 246     { "TELNET",      &sim_show_telnet,          0 },
 247     { "BUFFERED",    &sim_show_cons_buff,       0 },
 248     { "EXPECT",      &sim_show_cons_expect,     0 },
 249     { "HALT",        &sim_show_cons_expect,     0 },
 250     { "INPUT",       &sim_show_cons_send_input, 0 },
 251     { "RESPONSE",    &sim_show_cons_send_input, 0 },
 252     { "DELAY",       &sim_show_cons_expect,     0 },
 253     { NULL,          NULL,                      0 }
 254     };
 255 
 256 static CTAB set_con_telnet_tab[] = {
 257     { "LOG",         &sim_set_cons_log,         0 },
 258     { "NOLOG",       &sim_set_cons_nolog,       0 },
 259     { "BUFFERED",    &sim_set_cons_buff,        0 },
 260     { "NOBUFFERED",  &sim_set_cons_unbuff,      0 },
 261     { "UNBUFFERED",  &sim_set_cons_unbuff,      0 },
 262     { NULL,          NULL,                      0 }
 263     };
 264 
 265 static int32 *cons_kmap[] = {
 266     &sim_int_char,
 267     &sim_brk_char,
 268     &sim_del_char
 269     };
 270 
 271 /* Console I/O package.
 272 
 273    The console terminal can be attached to the controlling window
 274    or to a Telnet connection.  If attached to a Telnet connection,
 275    the console is described by internal terminal multiplexor
 276    sim_con_tmxr and internal terminal line description sim_con_ldsc.
 277 */
 278 
 279 /* SET CONSOLE command */
 280 
 281 t_stat sim_set_console (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 282 {
 283 char *cvptr, gbuf[CBUFSIZE];
 284 CTAB *ctptr;
 285 t_stat r;
 286 
 287 if ((cptr == NULL) || (*cptr == 0))
 288     return SCPE_2FARG;
 289 while (*cptr != 0) {                                    /* do all mods */
 290     cptr = get_glyph_nc (cptr, gbuf, ',');              /* get modifier */
 291     if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
 292         *cvptr++ = 0;
 293     (void)get_glyph (gbuf, gbuf, 0);                    /* modifier to UC */
 294     if ((ctptr = find_ctab (set_con_tab, gbuf))) {      /* match? */
 295         r = ctptr->action (ctptr->arg, cvptr);          /* do the rest */
 296         if (r != SCPE_OK)
 297             return r;
 298         }
 299     else return SCPE_NOPARAM;
 300     }
 301 return SCPE_OK;
 302 }
 303 
 304 /* SHOW CONSOLE command */
 305 
 306 t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 307 {
 308 char gbuf[CBUFSIZE];
 309 SHTAB *shptr;
 310 int32 i;
 311 
 312 if (*cptr == 0) {                                       /* show all */
 313     for (i = 0; show_con_tab[i].name; i++)
 314         show_con_tab[i].action (st, dptr, uptr, show_con_tab[i].arg, cptr);
 315     return SCPE_OK;
 316     }
 317 while (*cptr != 0) {
 318     cptr = get_glyph (cptr, gbuf, ',');                 /* get modifier */
 319     if ((shptr = find_shtab (show_con_tab, gbuf)))
 320          shptr->action (st, dptr, uptr, shptr->arg, NULL);
 321     else return SCPE_NOPARAM;
 322     }
 323 return SCPE_OK;
 324 }
 325 
 326 t_stat sim_rem_con_poll_svc (UNIT *uptr);               /* remote console connection poll routine */
 327 t_stat sim_rem_con_data_svc (UNIT *uptr);               /* remote console connection data routine */
 328 t_stat sim_rem_con_reset (DEVICE *dptr);                /* remote console reset routine */
 329 UNIT sim_rem_con_unit[2] = {
 330     { UDATA (&sim_rem_con_poll_svc, UNIT_IDLE, 0)  },   /* remote console connection polling unit */
 331     { UDATA (&sim_rem_con_data_svc, UNIT_IDLE, 0)  }};  /* console data handling unit */
 332 
 333 DEBTAB sim_rem_con_debug[] = {
 334   {"TRC", DBG_TRC},
 335   {"XMT", DBG_XMT},
 336   {"RCV", DBG_RCV},
 337   {0}
 338 };
 339 
 340 MTAB sim_rem_con_mod[] = {
 341   { 0 },
 342 };
 343 
 344 DEVICE sim_remote_console = {
 345     "REM-CON", sim_rem_con_unit, NULL, sim_rem_con_mod,
 346     2, 0, 0, 0, 0, 0,
 347     NULL, NULL, sim_rem_con_reset, NULL, NULL, NULL,
 348     NULL, DEV_DEBUG | DEV_NOSAVE, 0, sim_rem_con_debug};
 349 #define MAX_REMOTE_SESSIONS 40  /* Arbitrary Session Limit */
 350 static int32 *sim_rem_buf_size             = NULL;
 351 static int32 *sim_rem_buf_ptr              = NULL;
 352 static char **sim_rem_buf                  = NULL;
 353 static t_bool *sim_rem_single_mode         = NULL;  /* per line command mode (single command or must continue) */
 354 static TMXR sim_rem_con_tmxr               = { 0, 0, 0, NULL, NULL, &sim_remote_console };/* remote console line mux */
 355 static uint32 sim_rem_read_timeout         = 30;    /* seconds before automatic continue */
 356 static uint32 *sim_rem_read_timeouts       = NULL;  /* per line read timeout (default from sim_rem_read_timeout) */
 357 static int32 sim_rem_active_number         = -1;    /* -1 - not active, >= 0 is index of active console */
 358 int32 sim_rem_cmd_active_line              = -1;    /* step in progress on line # */
 359 static CTAB *sim_rem_active_command        = NULL;  /* active command */
 360 static char *sim_rem_command_buf;                   /* active command buffer */
 361 static t_bool sim_log_temp                 = FALSE; /* temporary log file active */
 362 static char sim_rem_con_temp_name[PATH_MAX+1];
 363 static t_bool sim_rem_master_mode          = FALSE; /* Master Mode Enabled Flag */
 364 static t_bool sim_rem_master_was_enabled   = FALSE; /* Master was Enabled */
 365 static t_bool sim_rem_master_was_connected = FALSE; /* Master Mode has been connected */
 366 static t_offset sim_rem_cmd_log_start      = 0;     /* Log File saved position */
 367 
 368 /* SET REMOTE CONSOLE command */
 369 
 370 t_stat sim_set_remote_console (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 371 {
 372 char *cvptr, gbuf[CBUFSIZE];
 373 CTAB *ctptr;
 374 t_stat r;
 375 
 376 if ((cptr == NULL) || (*cptr == 0))
 377     return SCPE_2FARG;
 378 while (*cptr != 0) {                                    /* do all mods */
 379     cptr = get_glyph_nc (cptr, gbuf, ',');              /* get modifier */
 380     if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
 381         *cvptr++ = 0;
 382     (void)get_glyph (gbuf, gbuf, 0);                    /* modifier to UC */
 383     if ((ctptr = find_ctab (set_rem_con_tab, gbuf))) {  /* match? */
 384         r = ctptr->action (ctptr->arg, cvptr);          /* do the rest */
 385         if (r != SCPE_OK)
 386             return r;
 387         }
 388     else return SCPE_NOPARAM;
 389     }
 390 return SCPE_OK;
 391 }
 392 
 393 /* SHOW REMOTE CONSOLE command */
 394 
 395 t_stat sim_show_remote_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 396 {
 397 int32 i, connections;
 398 TMLN *lp;
 399 
 400 if (*cptr != 0)
 401     return SCPE_NOPARAM;
 402 if (sim_rem_active_number >= 0) {
 403     if (sim_rem_master_mode && (sim_rem_active_number == 0))
 404         fprintf (st, "Running from Master Mode Remote Console Connection\n");
 405     else
 406         fprintf (st, "Running from Remote Console Connection %lu\n", (unsigned long)sim_rem_active_number);
 407     }
 408 if (sim_rem_con_tmxr.lines > 1)
 409     fprintf (st, "Remote Console Input Connections from %lu sources are supported concurrently\n", (unsigned long)sim_rem_con_tmxr.lines);
 410 if (sim_rem_read_timeout)
 411     fprintf (st, "Remote Console Input automatically continues after %lu seconds\n", (unsigned long)sim_rem_read_timeout);
 412 if (!sim_rem_con_tmxr.master)
 413     fprintf (st, "Remote Console Command input is disabled\n");
 414 else
 415     fprintf (st, "Remote Console Command Input listening on TCP port: %s\n", sim_rem_con_unit[0].filename);
 416 for (i=connections=0; i<sim_rem_con_tmxr.lines; i++) {
 417     lp = &sim_rem_con_tmxr.ldsc[i];
 418     if (!lp->conn)
 419         continue;
 420     ++connections;
 421     if (connections == 1)
 422         fprintf (st, "Remote Console Connections:\n");
 423     tmxr_fconns (st, lp, i);
 424     if (sim_rem_read_timeouts[i] != sim_rem_read_timeout) {
 425         if (sim_rem_read_timeouts[i])
 426             fprintf (st, "Remote Console Input on connection %lu automatically continues after %lu seconds\n", (unsigned long)i, (unsigned long)sim_rem_read_timeouts[i]);
 427         else
 428             fprintf (st, "Remote Console Input on connection %lu does not continue automatically\n", (unsigned long)i);
 429         }
 430     }
 431 return SCPE_OK;
 432 }
 433 
 434 /* Unit service for remote console connection polling */
 435 
 436 t_stat sim_rem_con_poll_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 437 {
 438 int32 c;
 439 
 440 c = tmxr_poll_conn (&sim_rem_con_tmxr);
 441 if (c >= 0) {                                           /* poll connect */
 442     TMLN *lp = &sim_rem_con_tmxr.ldsc[c];
 443     char wru_name[8];
 444 
 445     sim_activate_after(uptr+1, 1000000);                /* start data poll after 1 second */
 446     lp->rcve                 = 1;                       /* rcv enabled */
 447     sim_rem_buf_ptr[c]       = 0;                       /* start with empty command buffer */
 448     sim_rem_single_mode[c]   = TRUE;                    /* start in single command mode */
 449     sim_rem_read_timeouts[c] = sim_rem_read_timeout;    /* Start with default timeout */
 450     if (isprint(sim_int_char&0xFF))
 451         sprintf(wru_name, "'%c'", sim_int_char&0xFF);
 452     else
 453         if (sim_int_char <= 26)
 454             sprintf(wru_name, "^%c", '@' + (sim_int_char&0xFF));
 455         else
 456             sprintf(wru_name, "'\\%03o'", sim_int_char&0xFF);
 457     tmxr_linemsgf (lp, "%s Remote Console\r\n"
 458                        "Enter single commands or to enter multiple command mode enter the %s character\r"
 459                        "%s",
 460                        sim_name, wru_name,
 461                        ((sim_rem_master_mode && (c == 0)) ? "" : "\nSimulator Running..."));
 462     if (sim_rem_master_mode && (c == 0))                /* Master Mode session? */
 463         sim_rem_single_mode[c] = FALSE;                 /*  start in multi-command mode */
 464     tmxr_send_buffered_data (lp);                       /* flush buffered data */
 465     }
 466 sim_activate_after(uptr, 1000000);                      /* check again in 1 second */
 467 if (sim_con_ldsc.conn)
 468     tmxr_send_buffered_data (&sim_con_ldsc);            /* try to flush any buffered data */
 469 return SCPE_OK;
 470 }
 471 
 472 static t_stat x_continue_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 473 {
 474 return SCPE_IERR;           /* This routine should never be called */
 475 }
 476 
 477 static t_stat x_step_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 478 {
 479 return SCPE_IERR;           /* This routine should never be called */
 480 }
 481 
 482 static t_stat x_run_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 483 {
 484 return SCPE_IERR;           /* This routine should never be called */
 485 }
 486 
 487 static t_stat x_help_cmd (int32 flag, CONST char *cptr);
 488 
 489 static CTAB allowed_remote_cmds[] = {
 490     { "EXAMINE",  &exdep_cmd,      EX_E },
 491     { "DEPOSIT",  &exdep_cmd,      EX_D },
 492     { "EVALUATE", &eval_cmd,       0 },
 493     { "ATTACH",   &attach_cmd,     0 },
 494     { "DETACH",   &detach_cmd,     0 },
 495 
 496 
 497 
 498 
 499     { "CONTINUE", &x_continue_cmd, 0 },
 500     { "STEP",     &x_step_cmd,     0 },
 501     { "ECHO",     &echo_cmd,       0 },
 502     { "SET",      &set_cmd,        0 },
 503     { "SHOW",     &show_cmd,       0 },
 504     { "HELP",     &x_help_cmd,     0 },
 505     { NULL,       NULL }
 506     };
 507 
 508 static CTAB allowed_master_remote_cmds[] = {
 509     { "EXAMINE",  &exdep_cmd,      EX_E },
 510     { "DEPOSIT",  &exdep_cmd,      EX_D },
 511     { "EVALUATE", &eval_cmd,       0 },
 512     { "ATTACH",   &attach_cmd,     0 },
 513     { "DETACH",   &detach_cmd,     0 },
 514 
 515 
 516 
 517 
 518     { "CONTINUE", &x_continue_cmd, 0 },
 519     { "STEP",     &x_step_cmd,     0 },
 520     { "ECHO",     &echo_cmd,       0 },
 521     { "SET",      &set_cmd,        0 },
 522     { "SHOW",     &show_cmd,       0 },
 523     { "HELP",     &x_help_cmd,     0 },
 524     { "EXIT",     &exit_cmd,       0 },
 525     { "QUIT",     &exit_cmd,       0 },
 526     { "RUN",      &x_run_cmd,      RU_RUN },
 527     { "GO",       &x_run_cmd,      RU_GO },
 528     { "BOOT",     &x_run_cmd,      RU_BOOT },
 529     { "BREAK",    &brk_cmd,        SSH_ST },
 530     { "NOBREAK",  &brk_cmd,        SSH_CL },
 531     { NULL,       NULL }
 532     };
 533 
 534 static CTAB allowed_single_remote_cmds[] = {
 535     { "ATTACH",   &attach_cmd,     0 },
 536     { "DETACH",   &detach_cmd,     0 },
 537     { "EXAMINE",  &exdep_cmd,      EX_E },
 538     { "EVALUATE", &eval_cmd,       0 },
 539     { "ECHO",     &echo_cmd,       0 },
 540     { "SHOW",     &show_cmd,       0 },
 541     { "HELP",     &x_help_cmd,     0 },
 542     { NULL,       NULL }
 543     };
 544 
 545 static t_stat x_help_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 546 {
 547 CTAB *cmdp, *cmdph;
 548 
 549 if (*cptr) {
 550     int32 saved_switches = sim_switches;
 551     t_stat r;
 552 
 553     sim_switches |= SWMASK ('F');
 554     r = help_cmd (flag, cptr);
 555     sim_switches = saved_switches;
 556     return r;
 557     }
 558 sim_printf ("Help is available for the following Remote Console commands:\r\n");
 559 for (cmdp=allowed_remote_cmds; cmdp->name != NULL; ++cmdp) {
 560     cmdph = find_cmd (cmdp->name);
 561     if (cmdph && cmdph->help)
 562         sim_printf ("    %s\r\n", cmdp->name);
 563     }
 564 sim_printf ("Enter \"HELP cmd\" for detailed help on a command\r\n");
 565 return SCPE_OK;
 566 }
 567 
 568 static t_stat _sim_rem_message (const char *cmd, t_stat stat)
     /* [previous][next][first][last][top][bottom][index][help] */
 569 {
 570 CTAB *cmdp = NULL;
 571 t_stat stat_nomessage = stat & SCPE_NOMESSAGE;  /* extract possible message suppression flag */
 572 
 573 cmdp = find_cmd (cmd);
 574 stat = SCPE_BARE_STATUS(stat);              /* remove possible flag */
 575 if (!stat_nomessage) {
 576     if (cmdp && (cmdp->message))                /* special message handler? */
 577         cmdp->message (NULL, stat);             /* let it deal with display */
 578     else {
 579         if (stat >= SCPE_BASE)                  /* error? */
 580             sim_printf ("%s\r\n", sim_error_text (stat));
 581         }
 582     }
 583 return stat;
 584 }
 585 
 586 static void _sim_rem_log_out (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
 587 {
 588 char cbuf[4*CBUFSIZE];
 589 
 590 if (sim_log) {
 591     int32 unwritten;
 592 
 593     fflush (sim_log);
 594     (void)sim_fseeko (sim_log, sim_rem_cmd_log_start, SEEK_SET);
 595     cbuf[sizeof(cbuf)-1] = '\0';
 596     while (fgets (cbuf, sizeof(cbuf)-1, sim_log))
 597         tmxr_linemsgf (lp, "%s", cbuf);
 598     if (!tmxr_input_pending_ln (lp)) {
 599         do {
 600             unwritten = tmxr_send_buffered_data (lp);
 601             if (unwritten == lp->txbsz)
 602                 sim_os_ms_sleep (100);
 603             } while (unwritten == lp->txbsz);
 604         }
 605     }
 606 }
 607 
 608 void sim_remote_process_command (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 609 {
 610 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE], *argv[1] = {NULL};
 611 CONST char *cptr;
 612 int32 saved_switches = sim_switches;
 613 t_stat stat;
 614 
 615 strcpy (cbuf, sim_rem_command_buf);
 616 while (isspace((unsigned char)cbuf[0]))
 617     memmove (cbuf, cbuf+1, strlen(cbuf+1)+1);   /* skip leading whitespace */
 618 sim_sub_args (cbuf, sizeof(cbuf), argv);
 619 cptr = cbuf;
 620 cptr = get_glyph (cptr, gbuf, 0);               /* get command glyph */
 621 sim_rem_active_command = find_cmd (gbuf);       /* find command */
 622 
 623 sim_ttcmd ();                                   /* restore console */
 624 stat = sim_rem_active_command->action (sim_rem_active_command->arg, cptr);/* execute command */
 625 if (stat != SCPE_OK)
 626     stat = _sim_rem_message (gbuf, stat);       /* display results */
 627 sim_last_cmd_stat = SCPE_BARE_STATUS(stat);
 628 sim_ttrun ();                                   /* set console mode */
 629 sim_cancel (&sim_rem_con_unit[1]);              /* force immediate activation of sim_rem_con_data_svc */
 630 sim_activate (&sim_rem_con_unit[1], -1);
 631 sim_switches = saved_switches;                  /* restore original switches */
 632 }
 633 
 634 /* Unit service for remote console data polling */
 635 
 636 t_stat sim_rem_con_data_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 637 {
 638 int32 i, j, c = 0;
 639 t_stat stat = SCPE_OK;
 640 int32 steps = 0;
 641 t_bool was_active_command = (sim_rem_cmd_active_line != -1);
 642 t_bool got_command;
 643 t_bool close_session = FALSE;
 644 TMLN *lp;
 645 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE], *argv[1] = {NULL};
 646 CONST char *cptr;
 647 CTAB *cmdp = NULL;
 648 CTAB *basecmdp = NULL;
 649 uint32 read_start_time = 0;
 650 
 651 tmxr_poll_rx (&sim_rem_con_tmxr);                      /* poll input */
 652 for (i=(was_active_command ? sim_rem_cmd_active_line : 0);
 653      (i < sim_rem_con_tmxr.lines);
 654      i++) {
 655     t_bool master_session = (sim_rem_master_mode && (i == 0));
 656 
 657     lp = &sim_rem_con_tmxr.ldsc[i];
 658     if (!lp->conn)
 659         continue;
 660     if (master_session && !sim_rem_master_was_connected) {
 661         tmxr_linemsgf (lp, "\nMaster Mode Session\r\n");
 662         tmxr_send_buffered_data (lp);                   /* flush any buffered data */
 663         }
 664     sim_rem_master_was_connected |= master_session;     /* Remember if master ever connected */
 665     stat = SCPE_OK;
 666     if ((was_active_command) ||
 667         (master_session && !sim_rem_single_mode[i])) {
 668         if (was_active_command) {
 669             sim_rem_cmd_active_line = -1;               /* Done with active command */
 670             if (!sim_rem_active_command) {              /* STEP command? */
 671                 stat = SCPE_STEP;
 672                 _sim_rem_message ("STEP", stat);        /* produce a STEP complete message */
 673                 }
 674             _sim_rem_log_out (lp);
 675             sim_rem_active_command = NULL;              /* Restart loop to process available input */
 676             was_active_command = FALSE;
 677             i = -1;
 678             continue;
 679             }
 680         else {
 681             sim_is_running = 0;
 682             sim_stop_timer_services ();
 683             for (j=0; j < sim_rem_con_tmxr.lines; j++) {
 684                 TMLN *lpj = &sim_rem_con_tmxr.ldsc[j];
 685                 if ((i == j) || (!lpj->conn))
 686                     continue;
 687                 tmxr_linemsgf (lpj, "\nRemote Master Console(%s) Entering Commands\n", lp->ipad);
 688                 tmxr_send_buffered_data (lpj);         /* flush any buffered data */
 689                 }
 690             lp = &sim_rem_con_tmxr.ldsc[i];
 691             }
 692         }
 693     else {
 694         c = tmxr_getc_ln (lp);
 695         if (!(TMXR_VALID & c))
 696             continue;
 697         c = c & ~TMXR_VALID;
 698         if (sim_rem_single_mode[i]) {
 699             if (c == sim_int_char) {                    /* ^E (the interrupt character) must start continue mode console interaction */
 700                 sim_rem_single_mode[i] = FALSE;         /* enter multi command mode */
 701                 sim_is_running = 0;
 702                 sim_stop_timer_services ();
 703                 stat = SCPE_STOP;
 704                 _sim_rem_message ("RUN", stat);
 705                 _sim_rem_log_out (lp);
 706                 for (j=0; j < sim_rem_con_tmxr.lines; j++) {
 707                     TMLN *lpj = &sim_rem_con_tmxr.ldsc[j];
 708                     if ((i == j) || (!lpj->conn))
 709                         continue;
 710                     tmxr_linemsgf (lpj, "\nRemote Console %lu(%s) Entering Commands\n", (unsigned long)i, lp->ipad);
 711                     tmxr_send_buffered_data (lpj);      /* flush any buffered data */
 712                     }
 713                 lp = &sim_rem_con_tmxr.ldsc[i];
 714                 if (!master_session)
 715                     tmxr_linemsg (lp, "\r\nSimulator paused.\r\n");
 716                 if (!master_session && sim_rem_read_timeouts[i]) {
 717                     tmxr_linemsgf (lp, "Simulation will resume automatically if input is not received in %lu seconds\n", (unsigned long)sim_rem_read_timeouts[i]);
 718                     tmxr_linemsgf (lp, "\r\n");
 719                     tmxr_send_buffered_data (lp);       /* flush any buffered data */
 720                     }
 721                 }
 722             else {
 723                 if ((sim_rem_buf_ptr[i] == 0) &&        /* At beginning of input line */
 724                     ((c == '\n') ||                     /* Ignore bare LF between commands (Microsoft Telnet bug) */
 725                      (c == '\r')))                      /* Ignore empty commands */
 726                     continue;
 727                 if ((c == '\004') || (c == '\032')) {   /* EOF character (^D or ^Z) ? */
 728                     tmxr_linemsgf (lp, "\r\nGoodbye\r\n");
 729                     tmxr_send_buffered_data (lp);       /* flush any buffered data */
 730                     tmxr_reset_ln (lp);
 731                     continue;
 732                     }
 733                 if (sim_rem_buf_ptr[i] == 0) {
 734                     /* we just picked up the first character on a command line */
 735                     if (!master_session)
 736                         tmxr_linemsgf (lp, "\r\n%s", sim_prompt);
 737                     else
 738                         tmxr_linemsgf (lp, "\r\n%s", sim_is_running ? "SIM> " : "sim> ");
 739                     sim_debug (DBG_XMT, &sim_remote_console, "Prompt Written: %s\n", sim_is_running ? "SIM> " : "sim> ");
 740                     if (!tmxr_input_pending_ln (lp))
 741                         tmxr_send_buffered_data (lp);   /* flush any buffered data */
 742                     }
 743                 }
 744             }
 745         }
 746     got_command = FALSE;
 747     while (1) {
 748         if (stat == SCPE_EXIT)
 749             return stat|SCPE_NOMESSAGE;
 750         if (!sim_rem_single_mode[i]) {
 751             read_start_time = sim_os_msec();
 752             if (master_session)
 753                 tmxr_linemsg (lp, "sim> ");
 754             else
 755                 tmxr_linemsg (lp, sim_prompt);
 756             tmxr_send_buffered_data (lp);               /* flush any buffered data */
 757             }
 758         do {
 759             if (!sim_rem_single_mode[i]) {
 760                 c = tmxr_getc_ln (lp);
 761                 if (!(TMXR_VALID & c)) {
 762                     tmxr_send_buffered_data (lp);       /* flush any buffered data */
 763                     if (!master_session &&
 764                         sim_rem_read_timeouts[i] &&
 765                         ((sim_os_msec() - read_start_time)/1000 >= sim_rem_read_timeouts[i])) {
 766                         while (sim_rem_buf_ptr[i] > 0) {/* Erase current input line */
 767                             tmxr_linemsg (lp, "\b \b");
 768                             --sim_rem_buf_ptr[i];
 769                             }
 770                         if (sim_rem_buf_ptr[i]+80 >= sim_rem_buf_size[i]) {
 771                             sim_rem_buf_size[i] += 1024;
 772                             sim_rem_buf[i] = (char *)realloc (sim_rem_buf[i], sim_rem_buf_size[i]);
 773                             }
 774                         strcpy (sim_rem_buf[i], "CONTINUE         ! Automatic continue due to timeout");
 775                         tmxr_linemsgf (lp, "%s\n", sim_rem_buf[i]);
 776                         got_command = TRUE;
 777                         break;
 778                         }
 779                     sim_os_ms_sleep (50);
 780                     tmxr_poll_rx (&sim_rem_con_tmxr);   /* poll input */
 781                     if (!lp->conn) {                    /* if connection lost? */
 782                         sim_rem_single_mode[i] = TRUE;  /* No longer multi-command more */
 783                         break;                          /* done waiting */
 784                         }
 785                     continue;
 786                     }
 787                 read_start_time = sim_os_msec();
 788                 c = c & ~TMXR_VALID;
 789                 }
 790             switch (c) {
 791                 case 0:     /* no data */
 792                     break;
 793                 case '\b':  /* Backspace */
 794                 case 127:   /* Rubout */
 795                     if (sim_rem_buf_ptr[i] > 0) {
 796                         tmxr_linemsg (lp, "\b \b");
 797                         --sim_rem_buf_ptr[i];
 798                         }
 799                     break;
 800                 case 27:   /* escape */
 801                 case 21:   /* ^U */
 802                     while (sim_rem_buf_ptr[i] > 0) {
 803                         tmxr_linemsg (lp, "\b \b");
 804                         --sim_rem_buf_ptr[i];
 805                         }
 806                     break;
 807                 case '\n':
 808                     if (sim_rem_buf_ptr[i] == 0)
 809                         break;
 810                 /*FALLTHRU*/ /* fallthrough */ /* fall through */
 811                 case '\r':
 812                     tmxr_linemsg (lp, "\r\n");
 813                     if (sim_rem_buf_ptr[i]+1 >= sim_rem_buf_size[i]) {
 814                         sim_rem_buf_size[i] += 1024;
 815                         sim_rem_buf[i] = (char *)realloc (sim_rem_buf[i], sim_rem_buf_size[i]);
 816                         }
 817                     sim_rem_buf[i][sim_rem_buf_ptr[i]++] = '\0';
 818                     sim_debug (DBG_RCV, &sim_remote_console, "Got Command (%lu bytes still in buffer): %s\n", (unsigned long)tmxr_input_pending_ln (lp), sim_rem_buf[i]);
 819                     got_command = TRUE;
 820                     break;
 821                 case '\004': /* EOF (^D) */
 822                 case '\032': /* EOF (^Z) */
 823                     while (sim_rem_buf_ptr[i] > 0) {    /* Erase current input line */
 824                         tmxr_linemsg (lp, "\b \b");
 825                         --sim_rem_buf_ptr[i];
 826                         }
 827                     if (!sim_rem_single_mode[i]) {
 828                         if (sim_rem_buf_ptr[i]+80 >= sim_rem_buf_size[i]) {
 829                             sim_rem_buf_size[i] += 1024;
 830                             sim_rem_buf[i] = (char *)realloc (sim_rem_buf[i], sim_rem_buf_size[i]);
 831                             }
 832                         strcpy (sim_rem_buf[i], "CONTINUE         ! Automatic continue before close");
 833                         tmxr_linemsgf (lp, "%s\n", sim_rem_buf[i]);
 834                         got_command = TRUE;
 835                         }
 836                     close_session = TRUE;
 837                     break;
 838                 default:
 839                     tmxr_putc_ln (lp, c);
 840                     if (sim_rem_buf_ptr[i]+2 >= sim_rem_buf_size[i]) {
 841                         sim_rem_buf_size[i] += 1024;
 842                         sim_rem_buf[i] = (char *)realloc (sim_rem_buf[i], sim_rem_buf_size[i]);
 843                         }
 844                     sim_rem_buf[i][sim_rem_buf_ptr[i]++] = (char)c;
 845                     sim_rem_buf[i][sim_rem_buf_ptr[i]] = '\0';
 846                     if (((size_t)sim_rem_buf_ptr[i]) >= sizeof(cbuf))
 847                         got_command = TRUE;             /* command too long */
 848                     break;
 849                 }
 850             c = 0;
 851             if ((!got_command) && (sim_rem_single_mode[i]) && (tmxr_input_pending_ln (lp))) {
 852                 c = tmxr_getc_ln (lp);
 853                 c = c & ~TMXR_VALID;
 854                 }
 855             } while ((!got_command) && ((!sim_rem_single_mode[i]) || c));
 856         if (!tmxr_input_pending_ln (lp))
 857             tmxr_send_buffered_data (lp);               /* flush any buffered data */
 858         if ((sim_rem_single_mode[i]) && !got_command) {
 859             break;
 860             }
 861         sim_printf ("Remote Console Command from %s> %s\r\n", lp->ipad, sim_rem_buf[i]);
 862         got_command = FALSE;
 863         if (strlen(sim_rem_buf[i]) >= sizeof(cbuf)) {
 864             sim_printf ("\r\nLine too long. Ignored.  Continuing Simulator execution\r\n");
 865             tmxr_linemsgf (lp, "\nLine too long. Ignored.  Continuing Simulator execution\n");
 866             tmxr_send_buffered_data (lp);               /* try to flush any buffered data */
 867             break;
 868             }
 869         strcpy (cbuf, sim_rem_buf[i]);
 870         sim_rem_buf_ptr[i] = 0;
 871         sim_rem_buf[i][sim_rem_buf_ptr[i]] = '\0';
 872         while (isspace((unsigned char)cbuf[0]))
 873             memmove (cbuf, cbuf+1, strlen(cbuf+1)+1);   /* skip leading whitespace */
 874         if (cbuf[0] == '\0') {
 875             if (sim_rem_single_mode[i]) {
 876                 sim_rem_single_mode[i] = FALSE;
 877                 break;
 878                 }
 879             else
 880                 continue;
 881             }
 882         strcpy (sim_rem_command_buf, cbuf);
 883         sim_sub_args (cbuf, sizeof(cbuf), argv);
 884         cptr = cbuf;
 885         cptr = get_glyph (cptr, gbuf, 0);               /* get command glyph */
 886         sim_switches = 0;                               /* init switches */
 887         sim_rem_active_number = i;
 888         if (!sim_log) {                                 /* Not currently logging? */
 889             int32 save_quiet = sim_quiet;
 890 
 891             sim_quiet = 1;
 892             sprintf (sim_rem_con_temp_name, "sim_remote_console_%d.temporary_log", (int)getpid());
 893             sim_set_logon (0, sim_rem_con_temp_name);
 894             sim_quiet = save_quiet;
 895             sim_log_temp = TRUE;
 896             }
 897         sim_rem_cmd_log_start = sim_ftell (sim_log);
 898         basecmdp = find_cmd (gbuf);                     /* validate basic command */
 899         if (basecmdp == NULL) {
 900             if ((gbuf[0] == ';') || (gbuf[0] == '#')) { /* ignore comment */
 901                 sim_rem_cmd_active_line = i;
 902                 was_active_command = TRUE;
 903                 sim_rem_active_command = &allowed_single_remote_cmds[0];/* Dummy */
 904                 i = i - 1;
 905                 break;
 906                 }
 907             else
 908                 stat = SCPE_UNK;
 909             }
 910         else {
 911             if ((cmdp = find_ctab (sim_rem_single_mode[i] ? allowed_single_remote_cmds : (master_session ? allowed_master_remote_cmds : allowed_remote_cmds), gbuf))) {/* lookup command */
 912                 if (cmdp->action == &x_continue_cmd)
 913                     stat = SCPE_OK;
 914                 else {
 915                     if (cmdp->action == &exit_cmd)
 916                         return SCPE_EXIT;
 917                     if (cmdp->action == &x_step_cmd) {
 918                         steps = 1;                      /* default of 1 instruction */
 919                         stat = SCPE_OK;
 920                         if (*cptr != 0) {               /* argument? */
 921                              cptr = get_glyph (cptr, gbuf, 0);/* get next glyph */
 922                              if (*cptr != 0)            /* should be end */
 923                                  stat = SCPE_2MARG;
 924                              else {
 925                                  steps = (int32) get_uint (gbuf, 10, INT_MAX, &stat);
 926                                  if ((stat != SCPE_OK) || (steps <= 0)) /* error? */
 927                                      stat = SCPE_ARG;
 928                                  }
 929                              }
 930                         if (stat != SCPE_OK)
 931                             cmdp = NULL;
 932                         }
 933                     else {
 934                         if (cmdp->action == &x_run_cmd) {
 935                             sim_switches |= SIM_SW_HIDE;/* Request Setup only */
 936                             stat = basecmdp->action (cmdp->arg, cptr);
 937                             sim_switches &= ~SIM_SW_HIDE;/* Done with Setup only mode */
 938                             if (stat == SCPE_OK) {
 939                                 /* switch to CONTINUE after x_run_cmd() did RUN setup */
 940                                 cmdp = find_ctab (allowed_master_remote_cmds, "CONTINUE");
 941                                 }
 942                             }
 943                         else
 944                             stat = SCPE_REMOTE;         /* force processing outside of sim_instr() */
 945                         }
 946                     }
 947                 }
 948             else
 949                 stat = SCPE_INVREM;
 950             }
 951         sim_rem_active_number = -1;
 952         if ((stat != SCPE_OK) && (stat != SCPE_REMOTE))
 953             stat = _sim_rem_message (gbuf, stat);
 954         _sim_rem_log_out (lp);
 955         if (master_session && !sim_rem_master_mode) {
 956             sim_rem_single_mode[i] = TRUE;
 957             return SCPE_STOP;
 958             }
 959         if (cmdp && (cmdp->action == &x_continue_cmd)) {
 960             sim_rem_cmd_active_line = -1;               /* Not active_command */
 961             if (sim_log_temp &&                         /* If we setup a temporary log, clean it now  */
 962                 (!sim_rem_master_mode)) {
 963                 int32 save_quiet = sim_quiet;
 964 
 965                 sim_quiet = 1;
 966                 sim_set_logoff (0, NULL);
 967                 sim_quiet = save_quiet;
 968                 remove (sim_rem_con_temp_name);
 969                 sim_log_temp = FALSE;
 970                 }
 971             else {
 972                 fflush (sim_log);
 973                 sim_rem_cmd_log_start = sim_ftell (sim_log);
 974                 }
 975             if (!sim_rem_single_mode[i]) {
 976                 tmxr_linemsg (lp, "Simulator Running...");
 977                 tmxr_send_buffered_data (lp);
 978                 for (j=0; j < sim_rem_con_tmxr.lines; j++) {
 979                     TMLN *lpj = &sim_rem_con_tmxr.ldsc[j];
 980                     if ((i == j) || (!lpj->conn))
 981                         continue;
 982                     tmxr_linemsg (lpj, "Simulator Running...");
 983                     tmxr_send_buffered_data (lpj);
 984                     }
 985                 sim_is_running = 1;
 986                 sim_start_timer_services ();
 987                 }
 988             /* cppcheck-suppress knownConditionTrueFalse */
 989             if (cmdp && (cmdp->action == &x_continue_cmd)) //-V560
 990                 sim_rem_single_mode[i] = TRUE;
 991             else {
 992                 if (!sim_rem_single_mode[i]) {
 993                     if (master_session)
 994                         tmxr_linemsgf (lp, "%s", "sim> ");
 995                     else
 996                         tmxr_linemsgf (lp, "%s", sim_prompt);
 997                     tmxr_send_buffered_data (lp);
 998                     }
 999                 }
1000             break;
1001             }
1002         if ((cmdp && (cmdp->action == &x_step_cmd)) ||
1003             (stat == SCPE_REMOTE)) {
1004             sim_rem_cmd_active_line = i;
1005             break;
1006             }
1007         }
1008     if (close_session) {
1009         tmxr_linemsgf (lp, "\r\nGoodbye\r\n");
1010         tmxr_send_buffered_data (lp);                   /* flush any buffered data */
1011         tmxr_reset_ln (lp);
1012         sim_rem_single_mode[i] = FALSE;
1013         }
1014     }
1015 if (sim_rem_master_was_connected &&                     /* Master mode ever connected? */
1016     !sim_rem_con_tmxr.ldsc[0].sock)                     /* Master Connection lost? */
1017     return SCPE_EXIT;                                   /* simulator has been 'unplugged' */
1018 if (sim_rem_cmd_active_line != -1) {
1019     if (steps)
1020         sim_activate(uptr, steps);                      /* check again after 'steps' instructions */
1021     else
1022         return SCPE_REMOTE;                             /* force sim_instr() to exit to process command */
1023     }
1024 else
1025     sim_activate_after(uptr, 100000);                   /* check again in 100 milliseconds */
1026 if (sim_rem_master_was_enabled && !sim_rem_master_mode) {/* Transitioning out of master mode? */
1027     lp = &sim_rem_con_tmxr.ldsc[0];
1028     tmxr_linemsgf (lp, "Non Master Mode Session...");   /* report transition */
1029     tmxr_send_buffered_data (lp);                       /* flush any buffered data */
1030     return SCPE_STOP|SCPE_NOMESSAGE;                    /* Unwind to the normal input path */
1031     }
1032 else
1033     return SCPE_OK;                                     /* keep going */
1034 }
1035 
1036 t_stat sim_rem_con_reset (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1037 {
1038 if (sim_rem_con_tmxr.lines) {
1039     int32 i;
1040 
1041     for (i=0; i<sim_rem_con_tmxr.lines; i++)
1042         if (sim_rem_con_tmxr.ldsc[i].conn)
1043             break;
1044     if (i != sim_rem_con_tmxr.lines)
1045         sim_activate_after (&dptr->units[1], 100000);   /* continue polling for open sessions */
1046     return sim_rem_con_poll_svc (&dptr->units[0]);      /* establish polling as needed */
1047     }
1048 return SCPE_OK;
1049 }
1050 
1051 static t_stat sim_set_rem_telnet (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1052 {
1053 t_stat r;
1054 
1055 if (flag) {
1056     r = sim_parse_addr (cptr, NULL, 0, NULL, NULL, 0, NULL, NULL);
1057     if (r == SCPE_OK) {
1058         if (sim_rem_con_tmxr.master)                    /* already open? */
1059             sim_set_rem_telnet (0, NULL);               /* close first */
1060         if (sim_rem_con_tmxr.lines == 0)                /* Ir no connection limit set */
1061             sim_set_rem_connections (0, "1");           /* use 1 */
1062         sim_rem_con_tmxr.buffered = 1400;               /* Use big enough buffers */
1063         sim_register_internal_device (&sim_remote_console);
1064         r = tmxr_attach (&sim_rem_con_tmxr, &sim_rem_con_unit[0], cptr);/* open master socket */
1065         if (r == SCPE_OK)
1066             sim_activate_after(&sim_rem_con_unit[0], 1000000); /* check for connection in 1 second */
1067         return r;
1068         }
1069     return SCPE_NOPARAM;
1070     }
1071 else {
1072     if (sim_rem_con_tmxr.master) {
1073         int32 i;
1074 
1075         tmxr_detach (&sim_rem_con_tmxr, &sim_rem_con_unit[0]);
1076         for (i=0; i<sim_rem_con_tmxr.lines; i++) {
1077             FREE (sim_rem_buf[i]);
1078             sim_rem_buf[i] = NULL;
1079             sim_rem_buf_size[i] = 0;
1080             sim_rem_buf_ptr[i] = 0;
1081             sim_rem_single_mode[i] = TRUE;
1082             }
1083         }
1084     }
1085 return SCPE_OK;
1086 }
1087 
1088 static t_stat sim_set_rem_connections (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1089 {
1090 int32 lines;
1091 t_stat r;
1092 int32 i;
1093 
1094 if (cptr == NULL)
1095     return SCPE_ARG;
1096 lines = (int32) get_uint (cptr, 10, MAX_REMOTE_SESSIONS, &r);
1097 if (r != SCPE_OK)
1098     return r;
1099 if (sim_rem_con_tmxr.master)
1100     return SCPE_ARG;
1101 for (i=0; i<sim_rem_con_tmxr.lines; i++)
1102     FREE (sim_rem_buf[i]);
1103 sim_rem_con_tmxr.lines = lines;
1104 sim_rem_con_tmxr.ldsc = (TMLN *)realloc (sim_rem_con_tmxr.ldsc, sizeof(*sim_rem_con_tmxr.ldsc)*lines);
1105 if (!sim_rem_con_tmxr.ldsc)
1106   {
1107     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1108              __func__, __FILE__, __LINE__);
1109 #if defined(USE_BACKTRACE)
1110 # ifdef SIGUSR2
1111     (void)raise(SIGUSR2);
1112     /*NOTREACHED*/ /* unreachable */
1113 # endif /* ifdef SIGUSR2 */
1114 #endif /* if defined(USE_BACKTRACE) */
1115     abort();
1116   }
1117 memset (sim_rem_con_tmxr.ldsc, 0, sizeof(*sim_rem_con_tmxr.ldsc)*lines);
1118 sim_rem_buf = (char **)realloc (sim_rem_buf, sizeof(*sim_rem_buf)*lines);
1119 if (!sim_rem_buf)
1120   {
1121     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1122              __func__, __FILE__, __LINE__);
1123 #if defined(USE_BACKTRACE)
1124 # ifdef SIGUSR2
1125     (void)raise(SIGUSR2);
1126     /*NOTREACHED*/ /* unreachable */
1127 # endif /* ifdef SIGUSR2 */
1128 #endif /* if defined(USE_BACKTRACE) */
1129     abort();
1130   }
1131 memset (sim_rem_buf, 0, sizeof(*sim_rem_buf)*lines);
1132 sim_rem_buf_size = (int32 *)realloc (sim_rem_buf_size, sizeof(*sim_rem_buf_size)*lines);
1133 if (!sim_rem_buf_size)
1134   {
1135     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1136              __func__, __FILE__, __LINE__);
1137 #if defined(USE_BACKTRACE)
1138 # ifdef SIGUSR2
1139     (void)raise(SIGUSR2);
1140     /*NOTREACHED*/ /* unreachable */
1141 # endif /* ifdef SIGUSR2 */
1142 #endif /* if defined(USE_BACKTRACE) */
1143     abort();
1144   }
1145 memset (sim_rem_buf_size, 0, sizeof(*sim_rem_buf_size)*lines);
1146 sim_rem_buf_ptr = (int32 *)realloc (sim_rem_buf_ptr, sizeof(*sim_rem_buf_ptr)*lines);
1147 if (!sim_rem_buf_ptr)
1148   {
1149     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1150              __func__, __FILE__, __LINE__);
1151 #if defined(USE_BACKTRACE)
1152 # ifdef SIGUSR2
1153     (void)raise(SIGUSR2);
1154     /*NOTREACHED*/ /* unreachable */
1155 # endif /* ifdef SIGUSR2 */
1156 #endif /* if defined(USE_BACKTRACE) */
1157     abort();
1158   }
1159 memset (sim_rem_buf_ptr, 0, sizeof(*sim_rem_buf_ptr)*lines);
1160 sim_rem_single_mode = (t_bool *)realloc (sim_rem_single_mode, sizeof(*sim_rem_single_mode)*lines);
1161 if (!sim_rem_single_mode)
1162   {
1163     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1164              __func__, __FILE__, __LINE__);
1165 #if defined(USE_BACKTRACE)
1166 # ifdef SIGUSR2
1167     (void)raise(SIGUSR2);
1168     /*NOTREACHED*/ /* unreachable */
1169 # endif /* ifdef SIGUSR2 */
1170 #endif /* if defined(USE_BACKTRACE) */
1171     abort();
1172   }
1173 memset (sim_rem_single_mode, 0, sizeof(*sim_rem_single_mode)*lines);
1174 sim_rem_read_timeouts = (uint32 *)realloc (sim_rem_read_timeouts, sizeof(*sim_rem_read_timeouts)*lines);
1175 if (!sim_rem_read_timeouts)
1176   {
1177     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1178              __func__, __FILE__, __LINE__);
1179 #if defined(USE_BACKTRACE)
1180 # ifdef SIGUSR2
1181     (void)raise(SIGUSR2);
1182     /*NOTREACHED*/ /* unreachable */
1183 # endif /* ifdef SIGUSR2 */
1184 #endif /* if defined(USE_BACKTRACE) */
1185     abort();
1186   }
1187 memset (sim_rem_read_timeouts, 0, sizeof(*sim_rem_read_timeouts)*lines);
1188 sim_rem_command_buf = (char *)realloc (sim_rem_command_buf, 4*CBUFSIZE+1);
1189 if (!sim_rem_command_buf)
1190   {
1191     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1192              __func__, __FILE__, __LINE__);
1193 #if defined(USE_BACKTRACE)
1194 # ifdef SIGUSR2
1195     (void)raise(SIGUSR2);
1196     /*NOTREACHED*/ /* unreachable */
1197 # endif /* ifdef SIGUSR2 */
1198 #endif /* if defined(USE_BACKTRACE) */
1199     abort();
1200   }
1201 memset (sim_rem_command_buf, 0, 4*CBUFSIZE+1);
1202 return SCPE_OK;
1203 }
1204 
1205 static t_stat sim_set_rem_timeout (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1206 {
1207 int32 timeout;
1208 t_stat r;
1209 
1210 if (cptr == NULL)
1211     return SCPE_ARG;
1212 timeout = (int32) get_uint (cptr, 10, 3600, &r);
1213 if (r != SCPE_OK)
1214     return r;
1215 if (sim_rem_active_number >= 0)
1216     sim_rem_read_timeouts[sim_rem_active_number] = timeout;
1217 else
1218     sim_rem_read_timeout = timeout;
1219 return SCPE_OK;
1220 }
1221 
1222 /* Set keyboard map */
1223 
1224 t_stat sim_set_kmap (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1225 {
1226 DEVICE *dptr = sim_devices[0];
1227 int32 val, rdx;
1228 t_stat r;
1229 
1230 if ((cptr == NULL) || (*cptr == 0))
1231     return SCPE_2FARG;
1232 if (dptr->dradix == 16) rdx = 16;
1233 else rdx = 8;
1234 val = (int32) get_uint (cptr, rdx, 0177, &r); //-V536
1235 if ((r != SCPE_OK) ||
1236     ((val == 0) && (flag & KMAP_NZ)))
1237     return SCPE_ARG;
1238 *(cons_kmap[flag & KMAP_MASK]) = val;
1239 return SCPE_OK;
1240 }
1241 
1242 /* Show keyboard map */
1243 
1244 t_stat sim_show_kmap (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1245 {
1246 if (sim_devices[0]->dradix == 16)
1247     fprintf (st, "%s = %X\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK]));
1248 else fprintf (st, "%s = %o\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK]));
1249 return SCPE_OK;
1250 }
1251 
1252 /* Set printable characters */
1253 
1254 t_stat sim_set_pchar (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1255 {
1256 DEVICE *dptr = sim_devices[0];
1257 uint32 val, rdx;
1258 t_stat r;
1259 
1260 if ((cptr == NULL) || (*cptr == 0))
1261     return SCPE_2FARG;
1262 if (dptr->dradix == 16) rdx = 16;
1263 else rdx = 8;
1264 val = (uint32) get_uint (cptr, rdx, 0xFFFFFFFF, &r);
1265 if ((r != SCPE_OK) ||
1266     ((val & 0x00002400) == 0))
1267     return SCPE_ARG;
1268 sim_tt_pchar = val;
1269 return SCPE_OK;
1270 }
1271 
1272 /* Set input speed (bps) */
1273 
1274 t_stat sim_set_cons_speed (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1275 {
1276 return tmxr_set_line_speed (&sim_con_ldsc, cptr);
1277 }
1278 
1279 t_stat sim_show_cons_speed (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1280 {
1281 if (sim_con_ldsc.rxbps) {
1282     fprintf (st, "Speed = %ld", (long)sim_con_ldsc.rxbps);
1283     if (sim_con_ldsc.rxbpsfactor != TMXR_RX_BPS_UNIT_SCALE)
1284         fprintf (st, "*%.0f", sim_con_ldsc.rxbpsfactor/TMXR_RX_BPS_UNIT_SCALE);
1285     fprintf (st, " bps\n");
1286     }
1287 return SCPE_OK;
1288 }
1289 
1290 /* Set log routine */
1291 
1292 t_stat sim_set_logon (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1293 {
1294 char gbuf[CBUFSIZE];
1295 t_stat r;
1296 time_t now;
1297 
1298 if ((cptr == NULL) || (*cptr == 0))                     /* need arg */
1299     return SCPE_2FARG;
1300 cptr = get_glyph_nc (cptr, gbuf, 0);                    /* get file name */
1301 if (*cptr != 0)                                         /* now eol? */
1302     return SCPE_2MARG;
1303 sim_set_logoff (0, NULL);                               /* close cur log */
1304 r = sim_open_logfile (gbuf, (sim_switches & SWMASK ('B')) == SWMASK ('B'),
1305                             &sim_log, &sim_log_ref);    /* open log */
1306 if (r != SCPE_OK)                                       /* error? */
1307     return r;
1308 if (!sim_quiet)
1309     printf ("Logging to file \"%s\"\n",
1310              sim_logfile_name (sim_log, sim_log_ref));
1311 fprintf (sim_log, "Logging to file \"%s\"\n",
1312              sim_logfile_name (sim_log, sim_log_ref));  /* start of log */
1313 time(&now);
1314 fprintf (sim_log, "Logging to file \"%s\" at %s", sim_logfile_name (sim_log, sim_log_ref), ctime(&now));
1315 return SCPE_OK;
1316 }
1317 
1318 /* Set nolog routine */
1319 
1320 t_stat sim_set_logoff (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1321 {
1322 if (cptr && (*cptr != 0))                               /* now eol? */
1323     return SCPE_2MARG;
1324 if (sim_log == NULL)                                    /* no log? */
1325     return SCPE_OK;
1326 if (!sim_quiet)
1327     printf ("Log file closed\n");
1328 fprintf (sim_log, "Log file closed\n");
1329 sim_close_logfile (&sim_log_ref);                       /* close log */
1330 sim_log = NULL;
1331 return SCPE_OK;
1332 }
1333 
1334 /* Show log status */
1335 
1336 t_stat sim_show_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1337 {
1338 if (cptr && (*cptr != 0))
1339     return SCPE_2MARG;
1340 if (sim_log)
1341     fprintf (st, "Logging enabled to \"%s\"\n",
1342                  sim_logfile_name (sim_log, sim_log_ref));
1343 else fprintf (st, "Logging disabled\n");
1344 return SCPE_OK;
1345 }
1346 
1347 /* Set debug routine */
1348 
1349 t_stat sim_set_debon (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1350 {
1351 char gbuf[CBUFSIZE];
1352 t_stat r;
1353 time_t now;
1354 
1355 #if defined(__MACH__) && defined(__APPLE__) && \
1356   ( defined(__PPC__) || defined(_ARCH_PPC) )
1357 # include <mach/clock.h>
1358 # include <mach/mach.h>
1359 # ifdef MACOSXPPC
1360 #  undef MACOSXPPC
1361 # endif /* ifdef MACOSXPPC */
1362 # define MACOSXPPC 1
1363 #endif /* if defined(__MACH__) && defined(__APPLE__) &&
1364            ( defined(__PPC__) || defined(_ARCH_PPC) ) */
1365 
1366 sim_deb_switches = sim_switches;                        /* save debug switches */
1367 if ((cptr == NULL) || (*cptr == 0))                     /* need arg */
1368     return SCPE_2FARG;
1369 cptr = get_glyph_nc (cptr, gbuf, 0);                    /* get file name */
1370 if (*cptr != 0)                                         /* now eol? */
1371     return SCPE_2MARG;
1372 r = sim_open_logfile (gbuf, FALSE, &sim_deb, &sim_deb_ref);
1373 
1374 if (r != SCPE_OK)
1375     return r;
1376 
1377 if (sim_deb_switches & SWMASK ('R')) {
1378 #ifdef MACOSXPPC
1379     clock_serv_t cclock;
1380     mach_timespec_t mts;
1381     host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
1382     clock_get_time(cclock, &mts);
1383     mach_port_deallocate(mach_task_self(), cclock);
1384     sim_deb_basetime.tv_sec = mts.tv_sec;
1385     sim_deb_basetime.tv_nsec = mts.tv_nsec;
1386 #else
1387     clock_gettime(CLOCK_REALTIME, &sim_deb_basetime);
1388 #endif /* ifdef MACOSXPPC */
1389     if (!(sim_deb_switches & (SWMASK ('A') | SWMASK ('T'))))
1390         sim_deb_switches |= SWMASK ('T');
1391     }
1392 if (!sim_quiet) {
1393     sim_printf ("Debug output to \"%s\"\n", sim_logfile_name (sim_deb, sim_deb_ref));
1394     if (sim_deb_switches & SWMASK ('P'))
1395         sim_printf ("   Debug messages contain current PC value\n");
1396     if (sim_deb_switches & SWMASK ('T'))
1397         sim_printf ("   Debug messages display time of day as hh:mm:ss.msec%s\n", sim_deb_switches & SWMASK ('R') ? " relative to the start of debugging" : "");
1398     if (sim_deb_switches & SWMASK ('A'))
1399         sim_printf ("   Debug messages display time of day as seconds.msec%s\n", sim_deb_switches & SWMASK ('R') ? " relative to the start of debugging" : "");
1400     time(&now);
1401     fprintf (sim_deb, "Debug output to \"%s\" at %s", sim_logfile_name (sim_deb, sim_deb_ref), ctime(&now));
1402     show_version (sim_deb, NULL, NULL, 0, NULL);
1403     }
1404 if (sim_deb_switches & SWMASK ('N'))
1405     sim_deb_switches &= ~SWMASK ('N');          /* Only process the -N flag initially */
1406 
1407 return SCPE_OK;
1408 }
1409 
1410 t_stat sim_debug_flush (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1411 {
1412 int32 saved_quiet = sim_quiet;
1413 int32 saved_sim_switches = sim_switches;
1414 int32 saved_deb_switches = sim_deb_switches;
1415 struct timespec saved_deb_basetime = sim_deb_basetime;
1416 char saved_debug_filename[CBUFSIZE];
1417 
1418 if (sim_deb == NULL)                                    /* no debug? */
1419     return SCPE_OK;
1420 
1421 if (sim_deb == sim_log) {                               /* debug is log */
1422     fflush (sim_deb);                                   /* fflush is the best we can do */
1423     return SCPE_OK;
1424     }
1425 
1426 strcpy (saved_debug_filename, sim_logfile_name (sim_deb, sim_deb_ref));
1427 
1428 sim_quiet = 1;
1429 sim_set_deboff (0, NULL);
1430 sim_switches = saved_deb_switches;
1431 sim_set_debon (0, saved_debug_filename);
1432 sim_deb_basetime = saved_deb_basetime;
1433 sim_switches = saved_sim_switches;
1434 sim_quiet = saved_quiet;
1435 return SCPE_OK;
1436 }
1437 
1438 /* Set nodebug routine */
1439 
1440 t_stat sim_set_deboff (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1441 {
1442 if (cptr && (*cptr != 0))                               /* now eol? */
1443     return SCPE_2MARG;
1444 if (sim_deb == NULL)                                    /* no debug? */
1445     return SCPE_OK;
1446 sim_close_logfile (&sim_deb_ref);
1447 sim_deb = NULL;
1448 sim_deb_switches = 0;
1449 if (!sim_quiet)
1450     sim_printf ("Debug output disabled\n");
1451 return SCPE_OK;
1452 }
1453 
1454 /* Show debug routine */
1455 
1456 t_stat sim_show_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1457 {
1458 int32 i;
1459 
1460 if (cptr && (*cptr != 0))
1461     return SCPE_2MARG;
1462 if (sim_deb) {
1463     fprintf (st, "Debug output enabled to \"%s\"\n",
1464                  sim_logfile_name (sim_deb, sim_deb_ref));
1465     if (sim_deb_switches & SWMASK ('P'))
1466         fprintf (st, "   Debug messages contain current PC value\n");
1467     if (sim_deb_switches & SWMASK ('T'))
1468         fprintf (st, "   Debug messages display time of day as hh:mm:ss.msec%s\n", sim_deb_switches & SWMASK ('R') ? " relative to the start of debugging" : "");
1469     if (sim_deb_switches & SWMASK ('A'))
1470         fprintf (st, "   Debug messages display time of day as seconds.msec%s\n", sim_deb_switches & SWMASK ('R') ? " relative to the start of debugging" : "");
1471     for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
1472         if (!(dptr->flags & DEV_DIS) &&
1473             (dptr->flags & DEV_DEBUG) &&
1474             (dptr->dctrl)) {
1475             fprintf (st, "Device: %-6s ", dptr->name);
1476             show_dev_debug (st, dptr, NULL, 0, NULL);
1477             }
1478         }
1479     for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
1480         if (!(dptr->flags & DEV_DIS) &&
1481             (dptr->flags & DEV_DEBUG) &&
1482             (dptr->dctrl)) {
1483             fprintf (st, "Device: %-6s ", dptr->name);
1484             show_dev_debug (st, dptr, NULL, 0, NULL);
1485             }
1486         }
1487     }
1488 else fprintf (st, "Debug output disabled\n");
1489 return SCPE_OK;
1490 }
1491 
1492 /* SET CONSOLE command */
1493 
1494 /* Set console to Telnet port (and parameters) */
1495 
1496 t_stat sim_set_telnet (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1497 {
1498 char *cvptr, gbuf[CBUFSIZE];
1499 CTAB *ctptr;
1500 t_stat r;
1501 
1502 if ((cptr == NULL) || (*cptr == 0))
1503     return SCPE_2FARG;
1504 while (*cptr != 0) {                                      /* do all mods */
1505     cptr = get_glyph_nc (cptr, gbuf, ',');                /* get modifier */
1506     if ((cvptr = strchr (gbuf, '=')))                     /* = value? */
1507         *cvptr++ = 0;
1508     (void)get_glyph (gbuf, gbuf, 0);                      /* modifier to UC */
1509     if ((ctptr = find_ctab (set_con_telnet_tab, gbuf))) { /* match? */
1510         r = ctptr->action (ctptr->arg, cvptr);            /* do the rest */
1511         if (r != SCPE_OK)
1512             return r;
1513         }
1514     else {
1515         if (sim_con_tmxr.master)                        /* already open? */
1516             sim_set_notelnet (0, NULL);                 /* close first */
1517         r = tmxr_attach (&sim_con_tmxr, &sim_con_unit, gbuf);/* open master socket */
1518         if (r == SCPE_OK)
1519             sim_activate_after(&sim_con_unit, 1000000); /* check for connection in 1 second */
1520         else
1521             return r;
1522         }
1523     }
1524 return SCPE_OK;
1525 }
1526 
1527 /* Close console Telnet port */
1528 
1529 t_stat sim_set_notelnet (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1530 {
1531 if (cptr && (*cptr != 0))                               /* too many arguments? */
1532     return SCPE_2MARG;
1533 if (sim_con_tmxr.master == 0)                           /* ignore if already closed */
1534     return SCPE_OK;
1535 return tmxr_close_master (&sim_con_tmxr);               /* close master socket */
1536 }
1537 
1538 /* Show console Telnet status */
1539 
1540 t_stat sim_show_telnet (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1541 {
1542 if (cptr && (*cptr != 0))
1543     return SCPE_2MARG;
1544 if ((sim_con_tmxr.master == 0) &&
1545     (sim_con_ldsc.serport == 0))
1546     fprintf (st, "Connected to console window\n");
1547 else {
1548     if (sim_con_ldsc.serport) {
1549         fprintf (st, "Connected to ");
1550         tmxr_fconns (st, &sim_con_ldsc, -1);
1551         }
1552     else
1553         if (sim_con_ldsc.sock == 0)
1554             fprintf (st, "Listening on port %s\n", sim_con_tmxr.port);
1555         else {
1556             fprintf (st, "Listening on port %s, connection from %s\n",
1557                 sim_con_tmxr.port, sim_con_ldsc.ipad);
1558             tmxr_fconns (st, &sim_con_ldsc, -1);
1559             }
1560     tmxr_fstats (st, &sim_con_ldsc, -1);
1561     }
1562 return SCPE_OK;
1563 }
1564 
1565 /* Set console to Buffering  */
1566 
1567 t_stat sim_set_cons_buff (int32 flg, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1568 {
1569 char cmdbuf[CBUFSIZE];
1570 
1571 sprintf(cmdbuf, "BUFFERED%c%s", cptr ? '=' : '\0', cptr ? cptr : "");
1572 return tmxr_open_master (&sim_con_tmxr, cmdbuf);      /* open master socket */
1573 }
1574 
1575 /* Set console to NoBuffering */
1576 
1577 t_stat sim_set_cons_unbuff (int32 flg, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1578 {
1579 char cmdbuf[CBUFSIZE];
1580 
1581 sprintf(cmdbuf, "UNBUFFERED%c%s", cptr ? '=' : '\0', cptr ? cptr : "");
1582 return tmxr_open_master (&sim_con_tmxr, cmdbuf);      /* open master socket */
1583 }
1584 
1585 /* Set console to Logging */
1586 
1587 t_stat sim_set_cons_log (int32 flg, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1588 {
1589 char cmdbuf[CBUFSIZE];
1590 
1591 sprintf(cmdbuf, "LOG%c%s", cptr ? '=' : '\0', cptr ? cptr : "");
1592 return tmxr_open_master (&sim_con_tmxr, cmdbuf);      /* open master socket */
1593 }
1594 
1595 /* Set console to NoLogging */
1596 
1597 t_stat sim_set_cons_nolog (int32 flg, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1598 {
1599 char cmdbuf[CBUFSIZE];
1600 
1601 sprintf(cmdbuf, "NOLOG%c%s", cptr ? '=' : '\0', cptr ? cptr : "");
1602 return tmxr_open_master (&sim_con_tmxr, cmdbuf);      /* open master socket */
1603 }
1604 
1605 t_stat sim_show_cons_log (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1606 {
1607 if (cptr && (*cptr != 0))
1608     return SCPE_2MARG;
1609 if (sim_con_tmxr.ldsc->txlog)
1610     fprintf (st, "Log File being written to %s\n", sim_con_tmxr.ldsc->txlogname);
1611 else
1612     fprintf (st, "No Logging\n");
1613 return SCPE_OK;
1614 }
1615 
1616 t_stat sim_show_cons_buff (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1617 {
1618 if (cptr && (*cptr != 0))
1619     return SCPE_2MARG;
1620 if (!sim_con_tmxr.ldsc->txbfd)
1621     fprintf (st, "Unbuffered\n");
1622 else
1623     fprintf (st, "Buffer Size = %d\n", sim_con_tmxr.ldsc->txbsz);
1624 return SCPE_OK;
1625 }
1626 
1627 /* Set console to Serial port (and parameters) */
1628 
1629 /* Show the console expect rules and state */
1630 
1631 t_stat sim_show_cons_expect (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1632 {
1633 return sim_exp_show (st, &sim_con_expect, cptr);
1634 }
1635 
1636 /* Log File Open/Close/Show Support */
1637 
1638 /* Open log file */
1639 
1640 t_stat sim_open_logfile (const char *filename, t_bool binary, FILE **pf, FILEREF **pref)
     /* [previous][next][first][last][top][bottom][index][help] */
1641 {
1642 char gbuf[CBUFSIZE];
1643 const char *tptr;
1644 
1645 if ((filename == NULL) || (*filename == 0))             /* too few arguments? */
1646     return SCPE_2FARG;
1647 tptr = get_glyph (filename, gbuf, 0);
1648 if (*tptr != 0)                                         /* now eol? */
1649     return SCPE_2MARG;
1650 sim_close_logfile (pref);
1651 *pf = NULL;
1652 if (strcmp (gbuf, "LOG") == 0) {                        /* output to log? */
1653     if (sim_log == NULL)                                /* any log? */
1654         return SCPE_ARG;
1655     *pf = sim_log;
1656     *pref = sim_log_ref;
1657     if (*pref)
1658         ++(*pref)->refcount;
1659     }
1660 else if (strcmp (gbuf, "DEBUG") == 0) {                 /* output to debug? */
1661     if (sim_deb == NULL)                                /* any debug? */
1662         return SCPE_ARG;
1663     *pf = sim_deb;
1664     *pref = sim_deb_ref;
1665     if (*pref)
1666         ++(*pref)->refcount;
1667     }
1668 else if (strcmp (gbuf, "STDOUT") == 0) {                /* output to stdout? */
1669     *pf = stdout;
1670     *pref = NULL;
1671     }
1672 else if (strcmp (gbuf, "STDERR") == 0) {                /* output to stderr? */
1673     *pf = stderr;
1674     *pref = NULL;
1675     }
1676 else {
1677     *pref = (FILEREF *)calloc (1, sizeof(**pref));
1678     if (!*pref)
1679         return SCPE_MEM;
1680     (void)get_glyph_nc (filename, gbuf, 0);             /* reparse */
1681     strncpy ((*pref)->name, gbuf, sizeof((*pref)->name));
1682     if (sim_switches & SWMASK ('N'))                    /* if a new log file is requested */
1683         *pf = sim_fopen (gbuf, (binary ? "w+b" : "w+"));/*   then open an empty file */
1684     else                                                /* otherwise */
1685         *pf = sim_fopen (gbuf, (binary ? "a+b" : "a+"));/*   append to an existing file */
1686     if (*pf == NULL) {                                  /* error? */
1687         FREE (*pref);
1688         *pref = NULL;
1689         return SCPE_OPENERR;
1690         }
1691     (*pref)->file = *pf;
1692     (*pref)->refcount = 1;                               /* need close */
1693     }
1694 return SCPE_OK;
1695 }
1696 
1697 /* Close log file */
1698 
1699 t_stat sim_close_logfile (FILEREF **pref)
     /* [previous][next][first][last][top][bottom][index][help] */
1700 {
1701 if (NULL == *pref)
1702     return SCPE_OK;
1703 (*pref)->refcount = (*pref)->refcount  - 1;
1704 if ((*pref)->refcount > 0) {
1705     *pref = NULL;
1706     return SCPE_OK;
1707     }
1708 fclose ((*pref)->file);
1709 FREE (*pref);
1710 *pref = NULL;
1711 return SCPE_OK;
1712 }
1713 
1714 /* Show logfile support routine */
1715 
1716 const char *sim_logfile_name (const FILE *st, FILEREF *ref)
     /* [previous][next][first][last][top][bottom][index][help] */
1717 {
1718 if (!st)
1719     return "";
1720 if (st == stdout)
1721     return "STDOUT";
1722 if (st == stderr)
1723     return "STDERR";
1724 if (!ref)
1725     return "";
1726 return ref->name;
1727 }
1728 
1729 /* Check connection before executing
1730    (including a remote console which may be required in master mode) */
1731 
1732 t_stat sim_check_console (int32 sec)
     /* [previous][next][first][last][top][bottom][index][help] */
1733 {
1734 int32 c, trys = 0;
1735 
1736 if (sim_rem_master_mode) {
1737     for (;trys < sec; ++trys) {
1738         sim_rem_con_poll_svc (&sim_rem_con_unit[0]);
1739         if (sim_rem_con_tmxr.ldsc[0].conn)
1740             break;
1741         if ((trys % 10) == 0) {                         /* Status every 10 sec */
1742             sim_printf ("Waiting for Remote Console connection\r\n");
1743             fflush (stdout);
1744             if (sim_log)                                /* log file? */
1745                 fflush (sim_log);
1746             }
1747         sim_os_sleep (1);                               /* wait 1 second */
1748         }
1749     if ((sim_rem_con_tmxr.ldsc[0].conn) &&
1750         (!sim_con_ldsc.serport) &&
1751         (sim_con_tmxr.master == 0) &&
1752         (sim_con_console_port)) {
1753         tmxr_linemsgf (&sim_rem_con_tmxr.ldsc[0], "\r\nConsole port must be Telnet or Serial with Master Remote Console\r\n");
1754         tmxr_linemsgf (&sim_rem_con_tmxr.ldsc[0], "Goodbye\r\n");
1755         while (tmxr_send_buffered_data (&sim_rem_con_tmxr.ldsc[0]))
1756             sim_os_ms_sleep (100);
1757         sim_os_ms_sleep (100);
1758         tmxr_reset_ln (&sim_rem_con_tmxr.ldsc[0]);
1759         sim_printf ("Console port must be Telnet or Serial with Master Remote Console\r\n");
1760         return SCPE_EXIT;
1761         }
1762     }
1763 if (trys == sec) {
1764     return SCPE_TTMO;                                   /* timed out */
1765     }
1766 if (sim_con_ldsc.serport)
1767     if (tmxr_poll_conn (&sim_con_tmxr) >= 0)
1768         sim_con_ldsc.rcve = 1;                          /* rcv enabled */
1769 if ((sim_con_tmxr.master == 0) ||                       /* console or not Telnet? done */
1770     (sim_con_ldsc.serport))
1771     return SCPE_OK;
1772 if (sim_con_ldsc.conn || sim_con_ldsc.txbfd) {          /* connected or buffered ? */
1773     tmxr_poll_rx (&sim_con_tmxr);                       /* poll (check disconn) */
1774     if (sim_con_ldsc.conn || sim_con_ldsc.txbfd) {      /* still connected? */
1775         if (!sim_con_ldsc.conn) {
1776             sim_printf ("Running with Buffered Console\r\n"); /* print transition */
1777             fflush (stdout);
1778             if (sim_log)                                /* log file? */
1779                 fflush (sim_log);
1780             }
1781         return SCPE_OK;
1782         }
1783     }
1784 for (; trys < sec; trys++) {                            /* loop */
1785     if (tmxr_poll_conn (&sim_con_tmxr) >= 0) {          /* poll connect */
1786         sim_con_ldsc.rcve = 1;                          /* rcv enabled */
1787         if (trys) {                                     /* if delayed */
1788             sim_printf ("Running\r\n");                 /* print transition */
1789             fflush (stdout);
1790             if (sim_log)                                /* log file? */
1791                 fflush (sim_log);
1792             }
1793         return SCPE_OK;                                 /* ready to proceed */
1794         }
1795     c = sim_os_poll_kbd ();                             /* check for stop char */
1796     if ((c == SCPE_STOP) || stop_cpu)
1797         return SCPE_STOP;
1798     if ((trys % 10) == 0) {                             /* Status every 10 sec */
1799         sim_printf ("Waiting for console Telnet connection\r\n");
1800         fflush (stdout);
1801         if (sim_log)                                    /* log file? */
1802             fflush (sim_log);
1803         }
1804     sim_os_sleep (1);                                   /* wait 1 second */
1805     }
1806 return SCPE_TTMO;                                       /* timed out */
1807 }
1808 
1809 /* Get Send object address for console */
1810 
1811 SEND *sim_cons_get_send (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1812 {
1813 return &sim_con_send;
1814 }
1815 
1816 /* Get Expect object address for console */
1817 
1818 EXPECT *sim_cons_get_expect (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1819 {
1820 return &sim_con_expect;
1821 }
1822 
1823 /* Display console Queued input data status */
1824 
1825 t_stat sim_show_cons_send_input (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1826 {
1827 return sim_show_send_input (st, &sim_con_send);
1828 }
1829 
1830 /* Poll for character */
1831 
1832 t_stat sim_poll_kbd (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1833 {
1834 t_stat c;
1835 
1836 if (!sim_localopc)
1837   return SCPE_OK;
1838 
1839 if (sim_send_poll_data (&sim_con_send, &c))                 /* injected input characters available? */
1840     return c;
1841 if (!sim_rem_master_mode) {
1842     if ((sim_con_ldsc.rxbps) &&                             /* rate limiting && */
1843         (sim_gtime () < sim_con_ldsc.rxnexttime))           /* too soon? */
1844         return SCPE_OK;                                     /* not yet */
1845     c = sim_os_poll_kbd ();                                 /* get character */
1846     if (c == SCPE_STOP) {                                   /* ^E */
1847         stop_cpu = 1;                                       /* Force a stop (which is picked up by sim_process_event */
1848         return SCPE_OK;
1849         }
1850     if ((sim_con_tmxr.master == 0) &&                       /* not Telnet? */
1851         (sim_con_ldsc.serport == 0)) {                      /* and not serial? */
1852         if (c && sim_con_ldsc.rxbps)                        /* got something && rate limiting? */
1853             sim_con_ldsc.rxnexttime =                       /* compute next input time */
1854                 floor (sim_gtime () + ((sim_con_ldsc.rxdelta * sim_timer_inst_per_sec ())/sim_con_ldsc.rxbpsfactor));
1855         return c;                                           /* in-window */
1856         }
1857     if (!sim_con_ldsc.conn) {                               /* no telnet or serial connection? */
1858         if (!sim_con_ldsc.txbfd)                            /* unbuffered? */
1859             return SCPE_LOST;                               /* connection lost */
1860         if (tmxr_poll_conn (&sim_con_tmxr) >= 0)            /* poll connect */
1861             sim_con_ldsc.rcve = 1;                          /* rcv enabled */
1862         else                                                /* fall through to poll reception */
1863             return SCPE_OK;                                 /* unconnected and buffered - nothing to receive */
1864         }
1865     }
1866 tmxr_poll_rx (&sim_con_tmxr);                               /* poll for input */
1867 if ((c = (t_stat)tmxr_getc_ln (&sim_con_ldsc)))             /* any char? */
1868     return (c & (SCPE_BREAK | 0377)) | SCPE_KFLAG;
1869 return SCPE_OK;
1870 }
1871 
1872 /* Output character */
1873 
1874 t_stat sim_putchar (int32 c)
     /* [previous][next][first][last][top][bottom][index][help] */
1875 {
1876 sim_exp_check (&sim_con_expect, c);
1877 if ((sim_con_tmxr.master == 0) &&                       /* not Telnet? */
1878     (sim_con_ldsc.serport == 0)) {                      /* and not serial port */
1879     if (sim_log)                                        /* log file? */
1880         fputc (c, sim_log);
1881     return sim_os_putchar (c);                          /* in-window version */
1882     }
1883 if (!sim_con_ldsc.conn) {                               /* no Telnet or serial connection? */
1884     if (!sim_con_ldsc.txbfd)                            /* unbuffered? */
1885         return SCPE_LOST;                               /* connection lost */
1886     if (tmxr_poll_conn (&sim_con_tmxr) >= 0)            /* poll connect */
1887         sim_con_ldsc.rcve = 1;                          /* rcv enabled */
1888     }
1889 tmxr_putc_ln (&sim_con_ldsc, c);                        /* output char */
1890 tmxr_poll_tx (&sim_con_tmxr);                           /* poll xmt */
1891 return SCPE_OK;
1892 }
1893 
1894 /* Tab stop array handling
1895 
1896    *desc points to a uint8 array of length val
1897 
1898    Columns with tabs set are non-zero; columns without tabs are 0 */
1899 
1900 t_stat sim_ttinit (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1901 {
1902 sim_con_tmxr.ldsc->mp = &sim_con_tmxr;
1903 sim_register_internal_device (&sim_con_telnet);
1904 tmxr_startup ();
1905 return sim_os_ttinit ();
1906 }
1907 
1908 t_stat sim_ttrun (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1909 {
1910 if (!sim_con_tmxr.ldsc->uptr) {                         /* If simulator didn't declare its input polling unit */
1911     sim_con_unit.dynflags &= ~UNIT_TM_POLL;             /* we can't poll asynchronously */
1912     sim_con_unit.dynflags |= TMUF_NOASYNCH;             /* disable asynchronous behavior */
1913     }
1914 
1915 
1916 
1917 
1918 tmxr_start_poll ();
1919 return sim_os_ttrun ();
1920 }
1921 
1922 t_stat sim_ttcmd (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1923 {
1924 tmxr_stop_poll ();
1925 return sim_os_ttcmd ();
1926 }
1927 
1928 t_stat sim_ttclose (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1929 {
1930 t_stat r1 = tmxr_shutdown ();
1931 t_stat r2 = sim_os_ttclose ();
1932 
1933 if (r1 != SCPE_OK)
1934     return r1;
1935 
1936 return r2;
1937 }
1938 
1939 t_bool sim_ttisatty (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1940 {
1941 return sim_os_ttisatty ();
1942 }
1943 
1944 /* Platform specific routine definitions */
1945 
1946 #if defined (_WIN32)
1947 
1948 # include <fcntl.h>
1949 # include <io.h>
1950 # include <windows.h>
1951 # define RAW_MODE 0
1952 static HANDLE std_input;
1953 static HANDLE std_output;
1954 static DWORD saved_mode;
1955 
1956 /* Note: This routine catches all the potential events which some aspect
1957          of the windows system can generate.  The CTRL_C_EVENT won't be
1958          generated by a  user typing in a console session since that
1959          session is in RAW mode.  In general, Ctrl-C on a simulator's
1960          console terminal is a useful character to be passed to the
1961          simulator.  This code does nothing to disable or affect that. */
1962 
1963 static BOOL WINAPI
1964 ControlHandler(DWORD dwCtrlType)
     /* [previous][next][first][last][top][bottom][index][help] */
1965     {
1966     DWORD Mode;
1967     extern void int_handler (int sig);
1968 
1969     switch (dwCtrlType)
1970         {
1971         case CTRL_BREAK_EVENT:      // Use CTRL-Break or CTRL-C to simulate
1972         case CTRL_C_EVENT:          // SERVICE_CONTROL_STOP in debug mode
1973             int_handler(0);
1974             return TRUE;
1975         case CTRL_CLOSE_EVENT:      // Window is Closing
1976         case CTRL_LOGOFF_EVENT:     // User is logging off
1977             if (!GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &Mode))
1978                 return TRUE;        // Not our User, so ignore
1979             /* fall through */ /* fallthrough */
1980         case CTRL_SHUTDOWN_EVENT:   // System is shutting down
1981             int_handler(0);
1982             return TRUE;
1983         }
1984     return FALSE;
1985     }
1986 
1987 static t_stat sim_os_ttinit (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1988 {
1989 SetConsoleCtrlHandler( ControlHandler, TRUE );
1990 std_input = GetStdHandle (STD_INPUT_HANDLE);
1991 std_output = GetStdHandle (STD_OUTPUT_HANDLE);
1992 if ((std_input) &&                                      /* Not Background process? */
1993     (std_input != INVALID_HANDLE_VALUE))
1994     GetConsoleMode (std_input, &saved_mode);            /* Save Mode */
1995 return SCPE_OK;
1996 }
1997 
1998 static t_stat sim_os_ttrun (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1999 {
2000 if ((std_input) &&                                      /* If Not Background process? */
2001     (std_input != INVALID_HANDLE_VALUE) &&
2002     (!GetConsoleMode(std_input, &saved_mode) ||         /* Set mode to RAW */
2003      !SetConsoleMode(std_input, RAW_MODE)))
2004     return SCPE_TTYERR;
2005 if (sim_log) {
2006     fflush (sim_log);
2007     _setmode (_fileno (sim_log), _O_BINARY);
2008     }
2009 sim_os_set_thread_priority (PRIORITY_BELOW_NORMAL);
2010 return SCPE_OK;
2011 }
2012 
2013 static t_stat sim_os_ttcmd (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2014 {
2015 if (sim_log) {
2016     fflush (sim_log);
2017     _setmode (_fileno (sim_log), _O_TEXT);
2018     }
2019 sim_os_set_thread_priority (PRIORITY_NORMAL);
2020 if ((std_input) &&                                      /* If Not Background process? */
2021     (std_input != INVALID_HANDLE_VALUE) &&
2022     (!SetConsoleMode(std_input, saved_mode)))           /* Restore Normal mode */
2023     return SCPE_TTYERR;
2024 return SCPE_OK;
2025 }
2026 
2027 static t_stat sim_os_ttclose (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2028 {
2029 return SCPE_OK;
2030 }
2031 
2032 static t_bool sim_os_ttisatty (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2033 {
2034 DWORD Mode;
2035 
2036 return (std_input) && (std_input != INVALID_HANDLE_VALUE) && GetConsoleMode (std_input, &Mode);
2037 }
2038 
2039 static t_stat sim_os_poll_kbd (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2040 {
2041 int c = -1;
2042 DWORD nkbevents, nkbevent;
2043 INPUT_RECORD rec;
2044 
2045 sim_debug (DBG_TRC, &sim_con_telnet, "sim_os_poll_kbd()\n");
2046 
2047 if ((std_input == NULL) ||                              /* No keyboard for */
2048     (std_input == INVALID_HANDLE_VALUE))                /* background processes */
2049     return SCPE_OK;
2050 if (!GetNumberOfConsoleInputEvents(std_input, &nkbevents))
2051     return SCPE_TTYERR;
2052 while (c == -1) {
2053     if (0 == nkbevents)
2054         return SCPE_OK;
2055     if (!ReadConsoleInput(std_input, &rec, 1, &nkbevent))
2056         return SCPE_TTYERR;
2057     if (0 == nkbevent)
2058         return SCPE_OK;
2059     --nkbevents;
2060     if (rec.EventType == KEY_EVENT) {
2061         if (rec.Event.KeyEvent.bKeyDown) {
2062             if (0 == rec.Event.KeyEvent.uChar.UnicodeChar) {     /* Special Character/Keys? */
2063                 if (rec.Event.KeyEvent.wVirtualKeyCode == VK_PAUSE) /* Pause/Break Key */
2064                     c = sim_brk_char | SCPE_BREAK;
2065                 else
2066                     if (rec.Event.KeyEvent.wVirtualKeyCode == '2')  /* ^@ */
2067                         c = 0;                                      /* return NUL */
2068             } else
2069                 c = rec.Event.KeyEvent.uChar.AsciiChar;
2070             }
2071       }
2072     }
2073 if ((c & 0177) == sim_del_char)
2074     c = 0177;
2075 if ((c & 0177) == sim_int_char)
2076     return SCPE_STOP;
2077 if ((sim_brk_char && ((c & 0177) == sim_brk_char)) || (c & SCPE_BREAK))
2078     return SCPE_BREAK;
2079 return c | SCPE_KFLAG;
2080 }
2081 
2082 # define BELL_CHAR         7         /* Bell Character */
2083 # define BELL_INTERVAL_MS  500       /* No more than 2 Bell Characters Per Second */
2084 static t_stat sim_os_putchar (int32 c)
     /* [previous][next][first][last][top][bottom][index][help] */
2085 {
2086 DWORD unused;
2087 static uint32 last_bell_time;
2088 
2089 if (!sim_localopc)
2090   return SCPE_OK;
2091 
2092 if (c != 0177) {
2093     if (c == BELL_CHAR) {
2094         uint32 now = sim_os_msec ();
2095 
2096         if ((now - last_bell_time) > BELL_INTERVAL_MS) {
2097             WriteConsoleA(std_output, &c, 1, &unused, NULL);
2098             last_bell_time = now;
2099             }
2100         }
2101     else
2102         WriteConsoleA(std_output, &c, 1, &unused, NULL);
2103     }
2104 return SCPE_OK;
2105 }
2106 
2107 #elif defined (BSDTTY)
2108 
2109 /* BSD Routines */
2110 
2111 # include <sgtty.h>
2112 # include <fcntl.h>
2113 # include <unistd.h>
2114 
2115 struct sgttyb  cmdtty,     runtty;                       /* V6/V7 stty data */
2116 struct tchars  cmdtchars,  runtchars;                    /* V7 editing */
2117 struct ltchars cmdltchars, runltchars;                   /* 4.2 BSD editing */
2118 int cmdfl,runfl;                                         /* TTY flags */
2119 
2120 static t_stat sim_os_ttinit (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2121 {
2122 cmdfl = fcntl (0, F_GETFL, 0);                           /* get old flags  and status */
2123 runfl = cmdfl | FNDELAY;
2124 if (ioctl (0, TIOCGETP, &cmdtty) < 0)
2125     return SCPE_TTIERR;
2126 if (ioctl (0, TIOCGETC, &cmdtchars) < 0)
2127     return SCPE_TTIERR;
2128 if (ioctl (0, TIOCGLTC, &cmdltchars) < 0)
2129     return SCPE_TTIERR;
2130 runtty              = cmdtty;                            /* initial run state */
2131 runtty.sg_flags     = cmdtty.sg_flags & ~(ECHO|CRMOD) | CBREAK;
2132 runtchars.t_intrc   = sim_int_char;                      /* interrupt */
2133 runtchars.t_quitc   = 0xFF;                              /* no quit */
2134 runtchars.t_startc  = 0xFF;                              /* no host sync */
2135 runtchars.t_stopc   = 0xFF;
2136 runtchars.t_eofc    = 0xFF;
2137 runtchars.t_brkc    = 0xFF;
2138 runltchars.t_suspc  = 0xFF;                              /* no specials of any kind */
2139 runltchars.t_dsuspc = 0xFF;
2140 runltchars.t_rprntc = 0xFF;
2141 runltchars.t_flushc = 0xFF;
2142 runltchars.t_werasc = 0xFF;
2143 runltchars.t_lnextc = 0xFF;
2144 return SCPE_OK;                                          /* return success */
2145 }
2146 
2147 static t_stat sim_os_ttrun (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2148 {
2149 runtchars.t_intrc = sim_int_char;                        /* in case changed */
2150 (void)fcntl (0, F_SETFL, runfl);                         /* non-block mode */
2151 if (ioctl (0, TIOCSETP, &runtty) < 0)
2152     return SCPE_TTIERR;
2153 if (ioctl (0, TIOCSETC, &runtchars) < 0)
2154     return SCPE_TTIERR;
2155 if (ioctl (0, TIOCSLTC, &runltchars) < 0)
2156     return SCPE_TTIERR;
2157 sim_os_set_thread_priority (PRIORITY_BELOW_NORMAL);      /* lower priority */
2158 return SCPE_OK;
2159 }
2160 
2161 static t_stat sim_os_ttcmd (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2162 {
2163 sim_os_set_thread_priority (PRIORITY_NORMAL);            /* restore priority */
2164 (void)fcntl (0, F_SETFL, cmdfl);                         /* block mode */
2165 if (ioctl (0, TIOCSETP, &cmdtty) < 0)
2166     return SCPE_TTIERR;
2167 if (ioctl (0, TIOCSETC, &cmdtchars) < 0)
2168     return SCPE_TTIERR;
2169 if (ioctl (0, TIOCSLTC, &cmdltchars) < 0)
2170     return SCPE_TTIERR;
2171 return SCPE_OK;
2172 }
2173 
2174 static t_stat sim_os_ttclose (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2175 {
2176 return sim_ttcmd ();
2177 }
2178 
2179 static t_bool sim_os_ttisatty (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2180 {
2181 return isatty (fileno (stdin));
2182 }
2183 
2184 static t_stat sim_os_poll_kbd (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2185 {
2186 int status;
2187 unsigned char buf[1];
2188 
2189 status = read (0, buf, 1);
2190 if (status != 1) return SCPE_OK;
2191 if (sim_brk_char && (buf[0] == sim_brk_char))
2192     return SCPE_BREAK;
2193 if (sim_int_char && (buf[0] == sim_int_char))
2194     return SCPE_STOP;
2195 return (buf[0] | SCPE_KFLAG);
2196 }
2197 
2198 static t_stat sim_os_putchar (int32 out)
     /* [previous][next][first][last][top][bottom][index][help] */
2199 {
2200 char c;
2201 
2202 if (!sim_localopc)
2203   return SCPE_OK;
2204 
2205 c = out;
2206 if (write (1, &c, 1)) {};
2207 return SCPE_OK;
2208 }
2209 
2210 /* POSIX UNIX routines, from Leendert Van Doorn */
2211 
2212 #else
2213 
2214 # include <termios.h>
2215 # include <unistd.h>
2216 
2217 struct termios cmdtty, runtty;
2218 
2219 static t_stat sim_os_ttinit (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2220 {
2221 if (!isatty (fileno (stdin)))                                  /* skip if !tty */
2222     return SCPE_OK;
2223 if (tcgetattr (0, &cmdtty) < 0)                                /* get old flags */
2224     return SCPE_TTIERR;
2225 runtty                = cmdtty;
2226 runtty.c_lflag        = runtty.c_lflag & ~(ECHO | ICANON);     /* no echo or edit */
2227 runtty.c_oflag        = runtty.c_oflag & ~OPOST;               /* no output edit */
2228 runtty.c_iflag        = runtty.c_iflag & ~ICRNL;               /* no cr conversion */
2229 runtty.c_cc[VINTR]    = sim_int_char;                          /* interrupt */
2230 runtty.c_cc[VQUIT]    = 0;                                     /* no quit */
2231 runtty.c_cc[VERASE]   = 0;
2232 runtty.c_cc[VKILL]    = 0;
2233 runtty.c_cc[VEOF]     = 0;
2234 runtty.c_cc[VEOL]     = 0;
2235 runtty.c_cc[VSTART]   = 0;                                     /* no host sync */
2236 runtty.c_cc[VSUSP]    = 0;
2237 runtty.c_cc[VSTOP]    = 0;
2238 # if defined (VREPRINT)
2239 runtty.c_cc[VREPRINT] = 0;                                     /* no specials */
2240 # endif
2241 # if defined (VDISCARD)
2242 runtty.c_cc[VDISCARD] = 0;
2243 # endif
2244 # if defined (VWERASE)
2245 runtty.c_cc[VWERASE]  = 0;
2246 # endif
2247 # if defined (VLNEXT)
2248 runtty.c_cc[VLNEXT]   = 0;
2249 # endif
2250 runtty.c_cc[VMIN]     = 0;                                     /* no waiting */
2251 runtty.c_cc[VTIME]    = 0;
2252 # if defined (VDSUSP)
2253 runtty.c_cc[VDSUSP]   = 0;
2254 # endif
2255 # if defined (VSTATUS)
2256 runtty.c_cc[VSTATUS]  = 0;
2257 # endif
2258 return SCPE_OK;
2259 }
2260 
2261 static t_stat sim_os_ttrun (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2262 {
2263 if (!isatty (fileno (stdin)))                           /* skip if !tty */
2264     return SCPE_OK;
2265 runtty.c_cc[VINTR] = sim_int_char;                      /* in case changed */
2266 if (tcsetattr (0, TCSAFLUSH, &runtty) < 0)
2267     return SCPE_TTIERR;
2268 sim_os_set_thread_priority (PRIORITY_BELOW_NORMAL);     /* try to lower pri */
2269 return SCPE_OK;
2270 }
2271 
2272 static t_stat sim_os_ttcmd (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2273 {
2274 if (!isatty (fileno (stdin)))                           /* skip if !tty */
2275     return SCPE_OK;
2276 sim_os_set_thread_priority (PRIORITY_NORMAL);           /* try to raise pri */
2277 if (tcsetattr (0, TCSAFLUSH, &cmdtty) < 0)
2278     return SCPE_TTIERR;
2279 return SCPE_OK;
2280 }
2281 
2282 static t_stat sim_os_ttclose (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2283 {
2284 return sim_ttcmd ();
2285 }
2286 
2287 static t_bool sim_os_ttisatty (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2288 {
2289 return isatty (fileno (stdin));
2290 }
2291 
2292 static t_stat sim_os_poll_kbd (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2293 {
2294 int status;
2295 unsigned char buf[1];
2296 
2297 status = read (0, buf, 1);
2298 if (status != 1) return SCPE_OK;
2299 if (sim_brk_char && (buf[0] == sim_brk_char))
2300     return SCPE_BREAK;
2301 if (sim_int_char && (buf[0] == sim_int_char))
2302     return SCPE_STOP;
2303 return (buf[0] | SCPE_KFLAG);
2304 }
2305 
2306 static t_stat sim_os_putchar (int32 out)
     /* [previous][next][first][last][top][bottom][index][help] */
2307 {
2308 char c;
2309 
2310 if (!sim_localopc)
2311   return SCPE_OK;
2312 
2313 c = out;
2314 (void)!write (1, &c, 1);
2315 return SCPE_OK;
2316 }
2317 
2318 #endif
2319 
2320 /* Decode a string.
2321 
2322    A string containing encoded control characters is decoded into the equivalent
2323    character string.  Escape targets @, A-Z, and [\]^_ form control characters
2324    000-037.
2325 */
2326 #define ESC_CHAR '~'
2327 
2328 static void decode (char *decoded, const char *encoded)
     /* [previous][next][first][last][top][bottom][index][help] */
2329 {
2330 char c;
2331 
2332 /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
2333 while ((c = *decoded++ = *encoded++))                   /* copy the character */
2334     if (c == ESC_CHAR) {                                /* does it start an escape? */
2335         if ((isalpha ((unsigned char)*encoded)) ||      /* is next character "A-Z" or "a-z"? */
2336             (*encoded == '@') ||                        /*   or "@"? */
2337             ((*encoded >= '[') && (*encoded <= '_')))   /*   or "[\]^_"? */
2338 
2339             *(decoded - 1) = *encoded++ & 037;          /* convert back to control character */
2340         else {
2341             if ((*encoded == '\0') ||                   /* single escape character at EOL? */
2342                  (*encoded++ != ESC_CHAR))              /*   or not followed by another escape? */
2343                 decoded--;                              /* drop the encoding */
2344             }
2345         }
2346 return;
2347 }
2348 
2349 /* Set console halt */
2350 
2351 static t_stat sim_set_halt (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2352 {
2353 if (flag == 0)                                              /* no halt? */
2354     sim_exp_clrall (&sim_con_expect);                       /* disable halt checks */
2355 else {
2356     char *mbuf;
2357     char *mbuf2;
2358 
2359     if (cptr == NULL || *cptr == 0)                         /* no match string? */
2360         return SCPE_2FARG;                                  /* need an argument */
2361 
2362     sim_exp_clrall (&sim_con_expect);                       /* make sure that none currently exist */
2363 
2364     mbuf = (char *)malloc (1 + strlen (cptr));
2365     if (!mbuf)
2366       {
2367         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2368                  __func__, __FILE__, __LINE__);
2369 #if defined(USE_BACKTRACE)
2370 # ifdef SIGUSR2
2371         (void)raise(SIGUSR2);
2372         /*NOTREACHED*/ /* unreachable */
2373 # endif /* ifdef SIGUSR2 */
2374 #endif /* if defined(USE_BACKTRACE) */
2375         abort();
2376       }
2377     decode (mbuf, cptr);                                    /* save decoded match string */
2378 
2379     mbuf2 = (char *)malloc (3 + strlen(cptr));
2380     if (!mbuf2)
2381       {
2382         fprintf (stderr, "\rFATAL: Out out memory! Aborting at %s[%s:%d]\r\n",
2383                  __func__, __FILE__, __LINE__);
2384 #if defined(USE_BACKTRACE)
2385 # ifdef SIGUSR2
2386         (void)raise(SIGUSR2);
2387         /*NOTREACHED*/ /* unreachable */
2388 # endif /* ifdef SIGUSR2 */
2389 #endif /* if defined(USE_BACKTRACE) */
2390         abort();
2391       }
2392     sprintf (mbuf2, "%s%s%s", (sim_switches & SWMASK ('A')) ? "\n" : "",
2393                               mbuf,
2394                               (sim_switches & SWMASK ('I')) ? "" : "\n");
2395     FREE (mbuf);
2396     mbuf = sim_encode_quoted_string ((uint8 *)mbuf2, strlen (mbuf2));
2397     sim_exp_set (&sim_con_expect, mbuf, 0, sim_con_expect.after, EXP_TYP_PERSIST, NULL);
2398     FREE (mbuf);
2399     FREE (mbuf2);
2400     }
2401 
2402 return SCPE_OK;
2403 }
2404 
2405 /* Set console response */
2406 
2407 static t_stat sim_set_response (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2408 {
2409 if (flag == 0)                                          /* no response? */
2410     sim_send_clear (&sim_con_send);
2411 else {
2412     uint8 *rbuf;
2413 
2414     if (cptr == NULL || *cptr == 0)
2415         return SCPE_2FARG;                              /* need arg */
2416 
2417     rbuf = (uint8 *)malloc (1 + strlen(cptr));
2418     if (!rbuf)
2419       {
2420         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2421                  __func__, __FILE__, __LINE__);
2422 #if defined(USE_BACKTRACE)
2423 # ifdef SIGUSR2
2424         (void)raise(SIGUSR2);
2425         /*NOTREACHED*/ /* unreachable */
2426 # endif /* ifdef SIGUSR2 */
2427 #endif /* if defined(USE_BACKTRACE) */
2428         abort();
2429       }
2430 
2431     decode ((char *)rbuf, cptr);                        /* decod string */
2432     sim_send_input (&sim_con_send, rbuf, strlen((char *)rbuf), 0, 0); /* queue it for output */
2433     FREE (rbuf);
2434     }
2435 
2436 return SCPE_OK;
2437 }
2438 
2439 /* Set console delay */
2440 
2441 static t_stat sim_set_delay (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2442 {
2443 int32 val;
2444 t_stat r;
2445 
2446 if (cptr == NULL || *cptr == 0)                         /* no argument string? */
2447     return SCPE_2FARG;                                  /* need an argument */
2448 
2449 val = (int32) get_uint (cptr, 10, INT_MAX, &r);         /* parse the argument */
2450 
2451 if (r == SCPE_OK)                                       /* parse OK? */
2452     sim_con_expect.after = val;                         /* save the delay value */
2453 
2454 return r;
2455 }

/* [previous][next][first][last][top][bottom][index][help] */