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

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