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