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

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