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