1 /* sim_console.c: simulator console I/O library
2
3 Copyright (c) 1993-2012, Robert M Supnik
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
25
26 18-Mar-12 RMS Removed unused reference to sim_switches (Dave Bryan)
27 20-Jan-11 MP Added support for BREAK key on Windows
28 30-Sep-06 RMS Fixed non-printable characters in KSR mode
29 22-Jun-06 RMS Implemented SET/SHOW PCHAR
30 31-May-06 JDB Fixed bug if SET CONSOLE DEBUG with no argument
31 22-Nov-05 RMS Added central input/output conversion support
32 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy
33 28-Oct-04 JDB Fixed SET CONSOLE to allow comma-separated parameters
34 20-Aug-04 RMS Added OS/2 EMX fixes (Holger Veit)
35 14-Jul-04 RMS Revised Windows console code (Dave Bryan)
36 28-May-04 RMS Added SET/SHOW CONSOLE
37 RMS Added break, delete character maps
38 02-Jan-04 RMS Removed timer routines, added Telnet console routines
39 RMS Moved console logging to OS-independent code
40 25-Apr-03 RMS Added long seek support (Mark Pizzolato)
41 Added Unix priority control (Mark Pizzolato)
42 24-Sep-02 RMS Removed VT support, added Telnet console support
43 Added CGI support (Brian Knittel)
44 Added MacOS sleep (Peter Schorn)
45 14-Jul-02 RMS Added Windows priority control (Mark Pizzolato)
46 20-May-02 RMS Added Windows VT support (Fischer Franz)
47 01-Feb-02 RMS Added VAX fix from Robert Alan Byer
48 19-Sep-01 RMS More MacOS changes
49 31-Aug-01 RMS Changed int64 to t_int64 for Windoze
50 20-Jul-01 RMS Added MacOS support (Louis Chretien, Peter Schorn, Ben Supnik)
51 15-May-01 RMS Added logging support
52 05-Mar-01 RMS Added clock calibration support
53 08-Dec-00 BKR Added OS/2 support (Bruce Ray)
54 18-Aug-98 RMS Added BeOS support
55 13-Oct-97 RMS Added NetBSD terminal support
56 25-Jan-97 RMS Added POSIX terminal I/O support
57 02-Jan-97 RMS Fixed bug in sim_poll_kbd
58
59 This module implements the following routines to support terminal I/O:
60
61 sim_poll_kbd - poll for keyboard input
62 sim_putchar - output character to console
63 sim_putchar_s - output character to console, stall if congested
64 sim_set_console - set console parameters
65 sim_show_console - show console parameters
66 sim_tt_inpcvt - convert input character per mode
67 sim_tt_outcvt - convert output character per mode
68
69 sim_ttinit - called once to get initial terminal state
70 sim_ttrun - called to put terminal into run state
71 sim_ttcmd - called to return terminal to command state
72 sim_ttclose - called once before the simulator exits
73 sim_os_poll_kbd - poll for keyboard input
74 sim_os_putchar - output character to console
75
76 The first group is OS-independent; the second group is OS-dependent.
77
78 The following routines are exposed but deprecated:
79
80 sim_set_telnet - set console to Telnet port
81 sim_set_notelnet - close console Telnet port
82 sim_show_telnet - show console status
83 */
84
85 #include "sim_defs.h"
86 #include "sim_sock.h"
87 #include "sim_tmxr.h"
88 #include <ctype.h>
89
90 #define KMAP_WRU 0
91 #define KMAP_BRK 1
92 #define KMAP_DEL 2
93 #define KMAP_MASK 0377
94 #define KMAP_NZ 0400
95
96 int32 sim_int_char = 005; /* interrupt character */
97 int32 sim_brk_char = 000; /* break character */
98 int32 sim_tt_pchar = 0x00002780;
99 #if defined (_WIN32) || defined (__OS2__) || (defined (__MWERKS__) && defined (macintosh))
100 int32 sim_del_char = '\b'; /* delete character */
101 #else
102 int32 sim_del_char = 0177;
103 #endif
104 TMLN sim_con_ldsc = { 0 }; /* console line descr */
105 TMXR sim_con_tmxr = { 1, 0, 0, &sim_con_ldsc }; /* console line mux */
106
107 extern volatile int32 stop_cpu;
108 extern int32 sim_quiet, sim_deb_close;
109 extern FILE *sim_log, *sim_deb;
110 extern DEVICE *sim_devices[];
111
112 /* Set/show data structures */
113
114 static CTAB set_con_tab[] = {
115 { "WRU", &sim_set_kmap, KMAP_WRU | KMAP_NZ },
116 { "BRK", &sim_set_kmap, KMAP_BRK },
117 { "DEL", &sim_set_kmap, KMAP_DEL |KMAP_NZ },
118 { "PCHAR", &sim_set_pchar, 0 },
119 { "TELNET", &sim_set_telnet, 0 },
120 { "NOTELNET", &sim_set_notelnet, 0 },
121 { "LOG", &sim_set_logon, 0 },
122 { "NOLOG", &sim_set_logoff, 0 },
123 { "DEBUG", &sim_set_debon, 0 },
124 { "NODEBUG", &sim_set_deboff, 0 },
125 { NULL, NULL, 0 }
126 };
127
128 static SHTAB show_con_tab[] = {
129 { "WRU", &sim_show_kmap, KMAP_WRU },
130 { "BRK", &sim_show_kmap, KMAP_BRK },
131 { "DEL", &sim_show_kmap, KMAP_DEL },
132 { "PCHAR", &sim_show_pchar, 0 },
133 { "LOG", &sim_show_log, 0 },
134 { "TELNET", &sim_show_telnet, 0 },
135 { "DEBUG", &sim_show_debug, 0 },
136 { NULL, NULL, 0 }
137 };
138
139 static int32 *cons_kmap[] = {
140 &sim_int_char,
141 &sim_brk_char,
142 &sim_del_char
143 };
144
145 /* Console I/O package.
146
147 The console terminal can be attached to the controlling window
148 or to a Telnet connection. If attached to a Telnet connection,
149 the console is described by internal terminal multiplexor
150 sim_con_tmxr and internal terminal line description sim_con_ldsc.
151 */
152
153 /* SET CONSOLE command */
154
sim_set_console(int32 flag,char * cptr)155 t_stat sim_set_console (int32 flag, char *cptr)
156 {
157 char *cvptr, gbuf[CBUFSIZE];
158 CTAB *ctptr;
159 t_stat r;
160
161 if ((cptr == NULL) || (*cptr == 0))
162 return SCPE_2FARG;
163 while (*cptr != 0) { /* do all mods */
164 cptr = get_glyph_nc (cptr, gbuf, ','); /* get modifier */
165 if ((cvptr = strchr (gbuf, '='))) /* = value? */
166 *cvptr++ = 0;
167 get_glyph (gbuf, gbuf, 0); /* modifier to UC */
168 if ((ctptr = find_ctab (set_con_tab, gbuf))) { /* match? */
169 r = ctptr->action (ctptr->arg, cvptr); /* do the rest */
170 if (r != SCPE_OK)
171 return r;
172 }
173 else return SCPE_NOPARAM;
174 }
175 return SCPE_OK;
176 }
177
178 /* SHOW CONSOLE command */
179
sim_show_console(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,char * cptr)180 t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
181 {
182 char gbuf[CBUFSIZE];
183 SHTAB *shptr;
184 int32 i;
185
186 if (*cptr == 0) { /* show all */
187 for (i = 0; show_con_tab[i].name; i++)
188 show_con_tab[i].action (st, dptr, uptr, show_con_tab[i].arg, cptr);
189 return SCPE_OK;
190 }
191 while (*cptr != 0) {
192 cptr = get_glyph (cptr, gbuf, ','); /* get modifier */
193 if ((shptr = find_shtab (show_con_tab, gbuf)))
194 shptr->action (st, dptr, uptr, shptr->arg, cptr);
195 else return SCPE_NOPARAM;
196 }
197 return SCPE_OK;
198 }
199
200 /* Set keyboard map */
201
sim_set_kmap(int32 flag,char * cptr)202 t_stat sim_set_kmap (int32 flag, char *cptr)
203 {
204 DEVICE *dptr = sim_devices[0];
205 int32 val, rdx;
206 t_stat r;
207
208 if ((cptr == NULL) || (*cptr == 0))
209 return SCPE_2FARG;
210 if (dptr->dradix == 16) rdx = 16;
211 else rdx = 8;
212 val = (int32) get_uint (cptr, rdx, 0177, &r);
213 if ((r != SCPE_OK) ||
214 ((val == 0) && (flag & KMAP_NZ)))
215 return SCPE_ARG;
216 *(cons_kmap[flag & KMAP_MASK]) = val;
217 return SCPE_OK;
218 }
219
220 /* Show keyboard map */
221
sim_show_kmap(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,char * cptr)222 t_stat sim_show_kmap (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
223 {
224 if (sim_devices[0]->dradix == 16)
225 fprintf (st, "%s = %X\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK]));
226 else fprintf (st, "%s = %o\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK]));
227 return SCPE_OK;
228 }
229
230 /* Set printable characters */
231
sim_set_pchar(int32 flag,char * cptr)232 t_stat sim_set_pchar (int32 flag, char *cptr)
233 {
234 DEVICE *dptr = sim_devices[0];
235 uint32 val, rdx;
236 t_stat r;
237
238 if ((cptr == NULL) || (*cptr == 0))
239 return SCPE_2FARG;
240 if (dptr->dradix == 16) rdx = 16;
241 else rdx = 8;
242 val = (uint32) get_uint (cptr, rdx, 0xFFFFFFFF, &r);
243 if ((r != SCPE_OK) ||
244 ((val & 0x00002400) == 0))
245 return SCPE_ARG;
246 sim_tt_pchar = val;
247 return SCPE_OK;
248 }
249
250 /* Show printable characters */
251
sim_show_pchar(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,char * cptr)252 t_stat sim_show_pchar (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
253 {
254 if (sim_devices[0]->dradix == 16)
255 fprintf (st, "pchar mask = %X\n", sim_tt_pchar);
256 else fprintf (st, "pchar mask = %o\n", sim_tt_pchar);
257 return SCPE_OK;
258 }
259
260 /* Set log routine */
261
sim_set_logon(int32 flag,char * cptr)262 t_stat sim_set_logon (int32 flag, char *cptr)
263 {
264 char gbuf[CBUFSIZE];
265
266 if ((cptr == NULL) || (*cptr == 0)) /* need arg */
267 return SCPE_2FARG;
268 cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */
269 if (*cptr != 0) /* now eol? */
270 return SCPE_2MARG;
271 sim_set_logoff (0, NULL); /* close cur log */
272 sim_log = sim_fopen (gbuf, "a"); /* open log */
273 if (sim_log == NULL) /* error? */
274 return SCPE_OPENERR;
275 if (!sim_quiet)
276 printf ("Logging to file \"%s\"\n", gbuf);
277 fprintf (sim_log, "Logging to file \"%s\"\n", gbuf); /* start of log */
278 return SCPE_OK;
279 }
280
281 /* Set nolog routine */
282
sim_set_logoff(int32 flag,char * cptr)283 t_stat sim_set_logoff (int32 flag, char *cptr)
284 {
285 if (cptr && (*cptr != 0)) /* now eol? */
286 return SCPE_2MARG;
287 if (sim_log == NULL) /* no log? */
288 return SCPE_OK;
289 if (!sim_quiet)
290 printf ("Log file closed\n");
291 fprintf (sim_log, "Log file closed\n"); /* close log */
292 fclose (sim_log);
293 sim_log = NULL;
294 return SCPE_OK;
295 }
296
297 /* Show log status */
298
sim_show_log(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,char * cptr)299 t_stat sim_show_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
300 {
301 if (cptr && (*cptr != 0))
302 return SCPE_2MARG;
303 if (sim_log)
304 fputs ("Logging enabled\n", st);
305 else fputs ("Logging disabled\n", st);
306 return SCPE_OK;
307 }
308
309 /* Set debug routine */
310
sim_set_debon(int32 flag,char * cptr)311 t_stat sim_set_debon (int32 flag, char *cptr)
312 {
313 char *tptr, gbuf[CBUFSIZE];
314
315 if ((cptr == NULL) || (*cptr == 0)) /* too few arguments? */
316 return SCPE_2FARG;
317 tptr = get_glyph (cptr, gbuf, 0); /* get file name */
318 if (*tptr != 0) /* now eol? */
319 return SCPE_2MARG;
320 sim_set_deboff (0, NULL); /* close cur debug */
321 if (strcmp (gbuf, "LOG") == 0) { /* debug to log? */
322 if (sim_log == NULL) /* any log? */
323 return SCPE_ARG;
324 sim_deb = sim_log;
325 }
326 else if (strcmp (gbuf, "STDOUT") == 0) /* debug to stdout? */
327 sim_deb = stdout;
328 else if (strcmp (gbuf, "STDERR") == 0) /* debug to stderr? */
329 sim_deb = stderr;
330 else {
331 cptr = get_glyph_nc (cptr, gbuf, 0); /* reparse */
332 sim_deb = sim_fopen (gbuf, "a"); /* open debug */
333 if (sim_deb == NULL) /* error? */
334 return SCPE_OPENERR;
335 sim_deb_close = 1; /* need close */
336 }
337 if (!sim_quiet)
338 printf ("Debug output to \"%s\"\n", gbuf);
339 if (sim_log)
340 fprintf (sim_log, "Debug output to \"%s\"\n", gbuf);
341 return SCPE_OK;
342 }
343
344 /* Set nodebug routine */
345
sim_set_deboff(int32 flag,char * cptr)346 t_stat sim_set_deboff (int32 flag, char *cptr)
347 {
348 if (cptr && (*cptr != 0)) /* now eol? */
349 return SCPE_2MARG;
350 if (sim_deb == NULL) /* no log? */
351 return SCPE_OK;
352 if (!sim_quiet)
353 printf ("Debug output disabled\n");
354 if (sim_log)
355 fprintf (sim_log, "Debug output disabled\n");
356 if (sim_deb_close) /* close if needed */
357 fclose (sim_deb);
358 sim_deb_close = 0;
359 sim_deb = NULL;
360 return SCPE_OK;
361 }
362
363 /* Show debug routine */
364
sim_show_debug(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,char * cptr)365 t_stat sim_show_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
366 {
367 if (cptr && (*cptr != 0))
368 return SCPE_2MARG;
369 if (sim_deb)
370 fputs ("Debug output enabled\n", st);
371 else fputs ("Debug output disabled\n", st);
372 return SCPE_OK;
373 }
374
375 /* Set console to Telnet port */
376
sim_set_telnet(int32 flg,char * cptr)377 t_stat sim_set_telnet (int32 flg, char *cptr)
378 {
379 if ((cptr == NULL) || (*cptr == 0)) /* too few arguments? */
380 return SCPE_2FARG;
381 if (sim_con_tmxr.master) /* already open? */
382 return SCPE_ALATT;
383 return tmxr_open_master (&sim_con_tmxr, cptr); /* open master socket */
384 }
385
386 /* Close console Telnet port */
387
sim_set_notelnet(int32 flag,char * cptr)388 t_stat sim_set_notelnet (int32 flag, char *cptr)
389 {
390 if (cptr && (*cptr != 0)) /* too many arguments? */
391 return SCPE_2MARG;
392 if (sim_con_tmxr.master == 0) /* ignore if already closed */
393 return SCPE_OK;
394 return tmxr_close_master (&sim_con_tmxr); /* close master socket */
395 }
396
397 /* Show console Telnet status */
398
sim_show_telnet(FILE * st,DEVICE * dunused,UNIT * uunused,int32 flag,char * cptr)399 t_stat sim_show_telnet (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, char *cptr)
400 {
401 if (cptr && (*cptr != 0))
402 return SCPE_2MARG;
403 if (sim_con_tmxr.master == 0)
404 fprintf (st, "Connected to console window\n");
405 else if (sim_con_ldsc.conn == 0)
406 fprintf (st, "Listening on port %d\n", sim_con_tmxr.port);
407 else {
408 fprintf (st, "Listening on port %d, connected to socket %d\n",
409 sim_con_tmxr.port, sim_con_ldsc.conn);
410 tmxr_fconns (st, &sim_con_ldsc, -1);
411 tmxr_fstats (st, &sim_con_ldsc, -1);
412 }
413 return SCPE_OK;
414 }
415
416 /* Check connection before executing */
417
sim_check_console(int32 sec)418 t_stat sim_check_console (int32 sec)
419 {
420 int32 c, i;
421
422 if (sim_con_tmxr.master == 0) /* not Telnet? done */
423 return SCPE_OK;
424 if (sim_con_ldsc.conn) { /* connected? */
425 tmxr_poll_rx (&sim_con_tmxr); /* poll (check disconn) */
426 if (sim_con_ldsc.conn) /* still connected? */
427 return SCPE_OK;
428 }
429 for (i = 0; i < sec; i++) { /* loop */
430 if (tmxr_poll_conn (&sim_con_tmxr) >= 0) { /* poll connect */
431 sim_con_ldsc.rcve = 1; /* rcv enabled */
432 if (i) { /* if delayed */
433 printf ("Running\n"); /* print transition */
434 fflush (stdout);
435 }
436 return SCPE_OK; /* ready to proceed */
437 }
438 c = sim_os_poll_kbd (); /* check for stop char */
439 if ((c == SCPE_STOP) || stop_cpu)
440 return SCPE_STOP;
441 if ((i % 10) == 0) { /* Status every 10 sec */
442 printf ("Waiting for console Telnet connection\n");
443 fflush (stdout);
444 }
445 sim_os_sleep (1); /* wait 1 second */
446 }
447 return SCPE_TTMO; /* timed out */
448 }
449
450 /* Poll for character */
451
sim_poll_kbd(void)452 t_stat sim_poll_kbd (void)
453 {
454 int32 c;
455
456 c = sim_os_poll_kbd (); /* get character */
457 if ((c == SCPE_STOP) || (sim_con_tmxr.master == 0)) /* ^E or not Telnet? */
458 return c; /* in-window */
459 if (sim_con_ldsc.conn == 0) /* no Telnet conn? */
460 return SCPE_LOST;
461 tmxr_poll_rx (&sim_con_tmxr); /* poll for input */
462 if ((c = tmxr_getc_ln (&sim_con_ldsc))) /* any char? */
463 return (c & (SCPE_BREAK | 0377)) | SCPE_KFLAG;
464 return SCPE_OK;
465 }
466
467 /* Output character */
468
sim_putchar(int32 c)469 t_stat sim_putchar (int32 c)
470 {
471 if (sim_log) /* log file? */
472 fputc (c, sim_log);
473 if (sim_con_tmxr.master == 0) /* not Telnet? */
474 return sim_os_putchar (c); /* in-window version */
475 if (sim_con_ldsc.conn == 0) /* no Telnet conn? */
476 return SCPE_LOST;
477 tmxr_putc_ln (&sim_con_ldsc, c); /* output char */
478 tmxr_poll_tx (&sim_con_tmxr); /* poll xmt */
479 return SCPE_OK;
480 }
481
sim_putchar_s(int32 c)482 t_stat sim_putchar_s (int32 c)
483 {
484 t_stat r;
485
486 if (sim_log) fputc (c, sim_log); /* log file? */
487 if (sim_con_tmxr.master == 0) /* not Telnet? */
488 return sim_os_putchar (c); /* in-window version */
489 if (sim_con_ldsc.conn == 0) /* no Telnet conn? */
490 return SCPE_LOST;
491 if (sim_con_ldsc.xmte == 0) /* xmt disabled? */
492 r = SCPE_STALL;
493 else r = tmxr_putc_ln (&sim_con_ldsc, c); /* no, Telnet output */
494 tmxr_poll_tx (&sim_con_tmxr); /* poll xmt */
495 return r; /* return status */
496 }
497
498 /* Input character processing */
499
sim_tt_inpcvt(int32 c,uint32 mode)500 int32 sim_tt_inpcvt (int32 c, uint32 mode)
501 {
502 uint32 md = mode & TTUF_M_MODE;
503
504 if (md != TTUF_MODE_8B) {
505 c = c & 0177;
506 if (md == TTUF_MODE_UC) {
507 if (islower (c))
508 c = toupper (c);
509 if (mode & TTUF_KSR)
510 c = c | 0200;
511 }
512 }
513 else c = c & 0377;
514 return c;
515 }
516
517 /* Output character processing */
518
sim_tt_outcvt(int32 c,uint32 mode)519 int32 sim_tt_outcvt (int32 c, uint32 mode)
520 {
521 uint32 md = mode & TTUF_M_MODE;
522
523 if (md != TTUF_MODE_8B) {
524 c = c & 0177;
525 if (md == TTUF_MODE_UC) {
526 if (islower (c))
527 c = toupper (c);
528 if ((mode & TTUF_KSR) && (c >= 0140))
529 return -1;
530 }
531 if (((md == TTUF_MODE_UC) || (md == TTUF_MODE_7P)) &&
532 ((c == 0177) ||
533 ((c < 040) && !((sim_tt_pchar >> c) & 1))))
534 return -1;
535 }
536 else c = c & 0377;
537 return c;
538 }
539
540 /* VMS routines, from Ben Thomas, with fixes from Robert Alan Byer */
541
542 #if defined (VMS)
543
544 #if defined(__VAX)
545 #define sys$assign SYS$ASSIGN
546 #define sys$qiow SYS$QIOW
547 #endif
548
549 #include <descrip.h>
550 #include <ttdef.h>
551 #include <tt2def.h>
552 #include <iodef.h>
553 #include <ssdef.h>
554 #include <starlet.h>
555 #include <unistd.h>
556
557 #define EFN 0
558 uint32 tty_chan = 0;
559
560 typedef struct {
561 unsigned short sense_count;
562 unsigned char sense_first_char;
563 unsigned char sense_reserved;
564 unsigned int stat;
565 unsigned int stat2; } SENSE_BUF;
566
567 typedef struct {
568 unsigned short status;
569 unsigned short count;
570 unsigned int dev_status; } IOSB;
571
572 SENSE_BUF cmd_mode = { 0 };
573 SENSE_BUF run_mode = { 0 };
574
sim_ttinit(void)575 t_stat sim_ttinit (void)
576 {
577 unsigned int status;
578 IOSB iosb;
579 $DESCRIPTOR (terminal_device, "tt");
580
581 status = sys$assign (&terminal_device, &tty_chan, 0, 0);
582 if (status != SS$_NORMAL)
583 return SCPE_TTIERR;
584 status = sys$qiow (EFN, tty_chan, IO$_SENSEMODE, &iosb, 0, 0,
585 &cmd_mode, sizeof (cmd_mode), 0, 0, 0, 0);
586 if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
587 return SCPE_TTIERR;
588 run_mode = cmd_mode;
589 run_mode.stat = cmd_mode.stat | TT$M_NOECHO & ~(TT$M_HOSTSYNC | TT$M_TTSYNC);
590 run_mode.stat2 = cmd_mode.stat2 | TT2$M_PASTHRU;
591 return SCPE_OK;
592 }
593
sim_ttrun(void)594 t_stat sim_ttrun (void)
595 {
596 unsigned int status;
597 IOSB iosb;
598
599 status = sys$qiow (EFN, tty_chan, IO$_SETMODE, &iosb, 0, 0,
600 &run_mode, sizeof (run_mode), 0, 0, 0, 0);
601 if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
602 return SCPE_TTIERR;
603 return SCPE_OK;
604 }
605
sim_ttcmd(void)606 t_stat sim_ttcmd (void)
607 {
608 unsigned int status;
609 IOSB iosb;
610
611 status = sys$qiow (EFN, tty_chan, IO$_SETMODE, &iosb, 0, 0,
612 &cmd_mode, sizeof (cmd_mode), 0, 0, 0, 0);
613 if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
614 return SCPE_TTIERR;
615 return SCPE_OK;
616 }
617
sim_ttclose(void)618 t_stat sim_ttclose (void)
619 {
620 return sim_ttcmd ();
621 }
622
sim_os_poll_kbd(void)623 t_stat sim_os_poll_kbd (void)
624 {
625 unsigned int status, term[2];
626 unsigned char buf[4];
627 IOSB iosb;
628 SENSE_BUF sense;
629
630 term[0] = 0; term[1] = 0;
631 status = sys$qiow (EFN, tty_chan, IO$_SENSEMODE | IO$M_TYPEAHDCNT, &iosb,
632 0, 0, &sense, 8, 0, term, 0, 0);
633 if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
634 return SCPE_TTIERR;
635 if (sense.sense_count == 0) return SCPE_OK;
636 term[0] = 0; term[1] = 0;
637 status = sys$qiow (EFN, tty_chan,
638 IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO,
639 &iosb, 0, 0, buf, 1, 0, term, 0, 0);
640 if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
641 return SCPE_OK;
642 if (buf[0] == sim_int_char) return SCPE_STOP;
643 if (sim_brk_char && (buf[0] == sim_brk_char))
644 return SCPE_BREAK;
645 return (buf[0] | SCPE_KFLAG);
646 }
647
sim_os_putchar(int32 out)648 t_stat sim_os_putchar (int32 out)
649 {
650 unsigned int status;
651 char c;
652 IOSB iosb;
653
654 c = out;
655 status = sys$qiow (EFN, tty_chan, IO$_WRITELBLK | IO$M_NOFORMAT,
656 &iosb, 0, 0, &c, 1, 0, 0, 0, 0);
657 if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
658 return SCPE_TTOERR;
659 return SCPE_OK;
660 }
661
662 /* Win32 routines */
663
664 #elif defined (_WIN32)
665
666 #include <fcntl.h>
667 #include <io.h>
668 #include <windows.h>
669 #define RAW_MODE 0
670 static HANDLE std_input;
671 static HANDLE std_output;
672 static DWORD saved_mode;
673
674 static BOOL WINAPI
ControlHandler(DWORD dwCtrlType)675 ControlHandler(DWORD dwCtrlType)
676 {
677 DWORD Mode;
678 extern void int_handler (int sig);
679
680 switch (dwCtrlType)
681 {
682 case CTRL_BREAK_EVENT: // Use CTRL-Break or CTRL-C to simulate
683 case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
684 int_handler(0);
685 return TRUE;
686 case CTRL_CLOSE_EVENT: // Window is Closing
687 case CTRL_LOGOFF_EVENT: // User is logging off
688 if (!GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &Mode))
689 return TRUE; // Not our User, so ignore
690 case CTRL_SHUTDOWN_EVENT: // System is shutting down
691 int_handler(0);
692 return TRUE;
693 }
694 return FALSE;
695 }
696
sim_ttinit(void)697 t_stat sim_ttinit (void)
698 {
699 SetConsoleCtrlHandler( ControlHandler, TRUE );
700 std_input = GetStdHandle (STD_INPUT_HANDLE);
701 std_output = GetStdHandle (STD_OUTPUT_HANDLE);
702 if ((std_input == INVALID_HANDLE_VALUE) ||
703 !GetConsoleMode (std_input, &saved_mode))
704 return SCPE_TTYERR;
705 return SCPE_OK;
706 }
707
sim_ttrun(void)708 t_stat sim_ttrun (void)
709 {
710 if (!GetConsoleMode(std_input, &saved_mode) ||
711 !SetConsoleMode(std_input, RAW_MODE))
712 return SCPE_TTYERR;
713 if (sim_log) {
714 fflush (sim_log);
715 _setmode (_fileno (sim_log), _O_BINARY);
716 }
717 SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
718 return SCPE_OK;
719 }
720
sim_ttcmd(void)721 t_stat sim_ttcmd (void)
722 {
723 if (sim_log) {
724 fflush (sim_log);
725 _setmode (_fileno (sim_log), _O_TEXT);
726 }
727 SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_NORMAL);
728 if (!SetConsoleMode(std_input, saved_mode)) return SCPE_TTYERR;
729 return SCPE_OK;
730 }
731
sim_ttclose(void)732 t_stat sim_ttclose (void)
733 {
734 return SCPE_OK;
735 }
736
sim_os_poll_kbd(void)737 t_stat sim_os_poll_kbd (void)
738 {
739 int c = -1;
740 DWORD nkbevents, nkbevent;
741 INPUT_RECORD rec;
742
743 if (!GetNumberOfConsoleInputEvents(std_input, &nkbevents))
744 return SCPE_TTYERR;
745 while (c == -1) {
746 if (0 == nkbevents)
747 return SCPE_OK;
748 if (!ReadConsoleInput(std_input, &rec, 1, &nkbevent))
749 return SCPE_TTYERR;
750 if (0 == nkbevent)
751 return SCPE_OK;
752 --nkbevents;
753 if (rec.EventType == KEY_EVENT) {
754 if (rec.Event.KeyEvent.bKeyDown) {
755 if (0 == rec.Event.KeyEvent.uChar.UnicodeChar) { /* Special Character/Keys? */
756 if (rec.Event.KeyEvent.wVirtualKeyCode == VK_PAUSE) /* Pause/Break Key */
757 c = sim_brk_char | SCPE_BREAK;
758 else
759 if (rec.Event.KeyEvent.wVirtualKeyCode == '2') /* ^@ */
760 c = 0; /* return NUL */
761 } else
762 c = rec.Event.KeyEvent.uChar.AsciiChar;
763 }
764 }
765 }
766 if ((c & 0177) == sim_del_char)
767 c = 0177;
768 if ((c & 0177) == sim_int_char)
769 return SCPE_STOP;
770 if ((sim_brk_char && ((c & 0177) == sim_brk_char)) || (c & SCPE_BREAK))
771 return SCPE_BREAK;
772 return c | SCPE_KFLAG;
773 }
774
sim_os_putchar(int32 c)775 t_stat sim_os_putchar (int32 c)
776 {
777 DWORD unused;
778
779 if (c != 0177)
780 WriteConsoleA(std_output, &c, 1, &unused, NULL);
781 return SCPE_OK;
782 }
783
784 /* OS/2 routines, from Bruce Ray and Holger Veit */
785
786 #elif defined (__OS2__)
787
788 #include <conio.h>
789
sim_ttinit(void)790 t_stat sim_ttinit (void)
791 {
792 return SCPE_OK;
793 }
794
sim_ttrun(void)795 t_stat sim_ttrun (void)
796 {
797 return SCPE_OK;
798 }
799
sim_ttcmd(void)800 t_stat sim_ttcmd (void)
801 {
802 return SCPE_OK;
803 }
804
sim_ttclose(void)805 t_stat sim_ttclose (void)
806 {
807 return SCPE_OK;
808 }
809
sim_os_poll_kbd(void)810 t_stat sim_os_poll_kbd (void)
811 {
812 int c;
813
814 #if defined (__EMX__)
815 switch (c = _read_kbd(0,0,0)) { /* EMX has _read_kbd */
816
817 case -1: /* no char*/
818 return SCPE_OK;
819
820 case 0: /* char pending */
821 c = _read_kbd(0,1,0);
822 break;
823
824 default: /* got char */
825 break;
826 }
827 #else
828 if (!kbhit ())
829 return SCPE_OK;
830 c = getch();
831 #endif
832 if ((c & 0177) == sim_del_char)
833 c = 0177;
834 if ((c & 0177) == sim_int_char)
835 return SCPE_STOP;
836 if (sim_brk_char && ((c & 0177) == sim_brk_char))
837 return SCPE_BREAK;
838 return c | SCPE_KFLAG;
839 }
840
sim_os_putchar(int32 c)841 t_stat sim_os_putchar (int32 c)
842 {
843 if (c != 0177) {
844 #if defined (__EMX__)
845 putchar (c);
846 #else
847 putch (c);
848 #endif
849 fflush (stdout);
850 }
851 return SCPE_OK;
852 }
853
854 /* Metrowerks CodeWarrior Macintosh routines, from Louis Chretien and
855 Peter Schorn */
856
857 #elif defined (__MWERKS__) && defined (macintosh)
858
859 #include <console.h>
860 #include <Mactypes.h>
861 #include <string.h>
862 #include <sioux.h>
863 #include <unistd.h>
864 #include <siouxglobals.h>
865 #include <Traps.h>
866 #include <LowMem.h>
867
868 /* function prototypes */
869
870 Boolean SIOUXIsAppWindow(WindowPtr window);
871 void SIOUXDoMenuChoice(long menuValue);
872 void SIOUXUpdateMenuItems(void);
873 void SIOUXUpdateScrollbar(void);
874 int ps_kbhit(void);
875 int ps_getch(void);
876
877 extern char sim_name[];
878 extern pSIOUXWin SIOUXTextWindow;
879 static CursHandle iBeamCursorH = NULL; /* contains the iBeamCursor */
880
updateCursor(void)881 static void updateCursor(void) {
882 WindowPtr window;
883 window = FrontWindow();
884 if (SIOUXIsAppWindow(window)) {
885 GrafPtr savePort;
886 Point localMouse;
887 GetPort(&savePort);
888 SetPort(window);
889 #if TARGET_API_MAC_CARBON
890 GetGlobalMouse(&localMouse);
891 #else
892 localMouse = LMGetMouseLocation();
893 #endif
894 GlobalToLocal(&localMouse);
895 if (PtInRect(localMouse, &(*SIOUXTextWindow->edit)->viewRect) && iBeamCursorH) {
896 SetCursor(*iBeamCursorH);
897 }
898 else {
899 SetCursor(&qd.arrow);
900 }
901 TEIdle(SIOUXTextWindow->edit);
902 SetPort(savePort);
903 }
904 else {
905 SetCursor(&qd.arrow);
906 TEIdle(SIOUXTextWindow->edit);
907 }
908 return;
909 }
910
ps_kbhit(void)911 int ps_kbhit(void) {
912 EventRecord event;
913 int c;
914 updateCursor();
915 SIOUXUpdateScrollbar();
916 while (GetNextEvent(updateMask | osMask | mDownMask | mUpMask | activMask |
917 highLevelEventMask | diskEvt, &event)) {
918 SIOUXHandleOneEvent(&event);
919 }
920 if (SIOUXQuitting) {
921 exit(1);
922 }
923 if (EventAvail(keyDownMask,&event)) {
924 c = event.message&charCodeMask;
925 if ((event.modifiers & cmdKey) && (c > 0x20)) {
926 GetNextEvent(keyDownMask, &event);
927 SIOUXHandleOneEvent(&event);
928 if (SIOUXQuitting) {
929 exit(1);
930 }
931 return false;
932 }
933 return true;
934 }
935 else {
936 return false;
937 }
938 }
939
ps_getch(void)940 int ps_getch(void) {
941 int c;
942 EventRecord event;
943 fflush(stdout);
944 updateCursor();
945 while(!GetNextEvent(keyDownMask,&event)) {
946 if (GetNextEvent(updateMask | osMask | mDownMask | mUpMask | activMask |
947 highLevelEventMask | diskEvt, &event)) {
948 SIOUXUpdateScrollbar();
949 SIOUXHandleOneEvent(&event);
950 }
951 }
952 if (SIOUXQuitting) {
953 exit(1);
954 }
955 c = event.message&charCodeMask;
956 if ((event.modifiers & cmdKey) && (c > 0x20)) {
957 SIOUXUpdateMenuItems();
958 SIOUXDoMenuChoice(MenuKey(c));
959 }
960 if (SIOUXQuitting) {
961 exit(1);
962 }
963 return c;
964 }
965
966 /* Note that this only works if the call to sim_ttinit comes before any output to the console */
967
sim_ttinit(void)968 t_stat sim_ttinit (void) {
969 int i;
970 /* this blank will later be replaced by the number of characters */
971 char title[50] = " ";
972 unsigned char ptitle[50];
973 SIOUXSettings.autocloseonquit = TRUE;
974 SIOUXSettings.asktosaveonclose = FALSE;
975 SIOUXSettings.showstatusline = FALSE;
976 SIOUXSettings.columns = 80;
977 SIOUXSettings.rows = 40;
978 SIOUXSettings.toppixel = 42;
979 SIOUXSettings.leftpixel = 6;
980 iBeamCursorH = GetCursor(iBeamCursor);
981 strcat(title, sim_name);
982 strcat(title, " Simulator");
983 title[0] = strlen(title) - 1; /* Pascal string done */
984 for (i = 0; i <= title[0]; i++) { /* copy to unsigned char */
985 ptitle[i] = title[i];
986 }
987 SIOUXSetTitle(ptitle);
988 return SCPE_OK;
989 }
990
sim_ttrun(void)991 t_stat sim_ttrun (void)
992 {
993 return SCPE_OK;
994 }
995
sim_ttcmd(void)996 t_stat sim_ttcmd (void)
997 {
998 return SCPE_OK;
999 }
1000
sim_ttclose(void)1001 t_stat sim_ttclose (void)
1002 {
1003 return SCPE_OK;
1004 }
1005
sim_os_poll_kbd(void)1006 t_stat sim_os_poll_kbd (void)
1007 {
1008 int c;
1009
1010 if (!ps_kbhit ())
1011 return SCPE_OK;
1012 c = ps_getch();
1013 if ((c & 0177) == sim_del_char)
1014 c = 0177;
1015 if ((c & 0177) == sim_int_char) return SCPE_STOP;
1016 if (sim_brk_char && ((c & 0177) == sim_brk_char))
1017 return SCPE_BREAK;
1018 return c | SCPE_KFLAG;
1019 }
1020
sim_os_putchar(int32 c)1021 t_stat sim_os_putchar (int32 c)
1022 {
1023 if (c != 0177) {
1024 putchar (c);
1025 fflush (stdout);
1026 }
1027 return SCPE_OK;
1028 }
1029
1030 /* BSD UNIX routines */
1031
1032 #elif defined (BSDTTY)
1033
1034 #include <sgtty.h>
1035 #include <fcntl.h>
1036 #include <unistd.h>
1037
1038 struct sgttyb cmdtty,runtty; /* V6/V7 stty data */
1039 struct tchars cmdtchars,runtchars; /* V7 editing */
1040 struct ltchars cmdltchars,runltchars; /* 4.2 BSD editing */
1041 int cmdfl,runfl; /* TTY flags */
1042
sim_ttinit(void)1043 t_stat sim_ttinit (void)
1044 {
1045 cmdfl = fcntl (0, F_GETFL, 0); /* get old flags and status */
1046 runfl = cmdfl | FNDELAY;
1047 if (ioctl (0, TIOCGETP, &cmdtty) < 0)
1048 return SCPE_TTIERR;
1049 if (ioctl (0, TIOCGETC, &cmdtchars) < 0)
1050 return SCPE_TTIERR;
1051 if (ioctl (0, TIOCGLTC, &cmdltchars) < 0)
1052 return SCPE_TTIERR;
1053 runtty = cmdtty; /* initial run state */
1054 runtty.sg_flags = cmdtty.sg_flags & ~(ECHO|CRMOD) | CBREAK;
1055 runtchars.t_intrc = sim_int_char; /* interrupt */
1056 runtchars.t_quitc = 0xFF; /* no quit */
1057 runtchars.t_startc = 0xFF; /* no host sync */
1058 runtchars.t_stopc = 0xFF;
1059 runtchars.t_eofc = 0xFF;
1060 runtchars.t_brkc = 0xFF;
1061 runltchars.t_suspc = 0xFF; /* no specials of any kind */
1062 runltchars.t_dsuspc = 0xFF;
1063 runltchars.t_rprntc = 0xFF;
1064 runltchars.t_flushc = 0xFF;
1065 runltchars.t_werasc = 0xFF;
1066 runltchars.t_lnextc = 0xFF;
1067 return SCPE_OK; /* return success */
1068 }
1069
sim_ttrun(void)1070 t_stat sim_ttrun (void)
1071 {
1072 runtchars.t_intrc = sim_int_char; /* in case changed */
1073 fcntl (0, F_SETFL, runfl); /* non-block mode */
1074 if (ioctl (0, TIOCSETP, &runtty) < 0)
1075 return SCPE_TTIERR;
1076 if (ioctl (0, TIOCSETC, &runtchars) < 0)
1077 return SCPE_TTIERR;
1078 if (ioctl (0, TIOCSLTC, &runltchars) < 0)
1079 return SCPE_TTIERR;
1080 nice (10); /* lower priority */
1081 return SCPE_OK;
1082 }
1083
sim_ttcmd(void)1084 t_stat sim_ttcmd (void)
1085 {
1086 nice (-10); /* restore priority */
1087 fcntl (0, F_SETFL, cmdfl); /* block mode */
1088 if (ioctl (0, TIOCSETP, &cmdtty) < 0)
1089 return SCPE_TTIERR;
1090 if (ioctl (0, TIOCSETC, &cmdtchars) < 0)
1091 return SCPE_TTIERR;
1092 if (ioctl (0, TIOCSLTC, &cmdltchars) < 0)
1093 return SCPE_TTIERR;
1094 return SCPE_OK;
1095 }
1096
sim_ttclose(void)1097 t_stat sim_ttclose (void)
1098 {
1099 return sim_ttcmd ();
1100 }
1101
sim_os_poll_kbd(void)1102 t_stat sim_os_poll_kbd (void)
1103 {
1104 int status;
1105 unsigned char buf[1];
1106
1107 status = read (0, buf, 1);
1108 if (status != 1) return SCPE_OK;
1109 if (sim_brk_char && (buf[0] == sim_brk_char))
1110 return SCPE_BREAK;
1111 else return (buf[0] | SCPE_KFLAG);
1112 }
1113
sim_os_putchar(int32 out)1114 t_stat sim_os_putchar (int32 out)
1115 {
1116 char c;
1117
1118 c = out;
1119 write (1, &c, 1);
1120 return SCPE_OK;
1121 }
1122
1123 /* POSIX UNIX routines, from Leendert Van Doorn */
1124
1125 #else
1126
1127 #include <termios.h>
1128 #include <unistd.h>
1129
1130 struct termios cmdtty, runtty;
1131 static int prior_norm = 1;
1132
sim_ttinit(void)1133 t_stat sim_ttinit (void)
1134 {
1135 if (!isatty (fileno (stdin))) /* skip if !tty */
1136 return SCPE_OK;
1137 if (tcgetattr (0, &cmdtty) < 0) /* get old flags */
1138 return SCPE_TTIERR;
1139 runtty = cmdtty;
1140 runtty.c_lflag = runtty.c_lflag & ~(ECHO | ICANON); /* no echo or edit */
1141 runtty.c_oflag = runtty.c_oflag & ~OPOST; /* no output edit */
1142 runtty.c_iflag = runtty.c_iflag & ~ICRNL; /* no cr conversion */
1143 runtty.c_cc[VINTR] = sim_int_char; /* interrupt */
1144 runtty.c_cc[VQUIT] = 0; /* no quit */
1145 runtty.c_cc[VERASE] = 0;
1146 runtty.c_cc[VKILL] = 0;
1147 runtty.c_cc[VEOF] = 0;
1148 runtty.c_cc[VEOL] = 0;
1149 runtty.c_cc[VSTART] = 0; /* no host sync */
1150 runtty.c_cc[VSUSP] = 0;
1151 runtty.c_cc[VSTOP] = 0;
1152 #if defined (VREPRINT)
1153 runtty.c_cc[VREPRINT] = 0; /* no specials */
1154 #endif
1155 #if defined (VDISCARD)
1156 runtty.c_cc[VDISCARD] = 0;
1157 #endif
1158 #if defined (VWERASE)
1159 runtty.c_cc[VWERASE] = 0;
1160 #endif
1161 #if defined (VLNEXT)
1162 runtty.c_cc[VLNEXT] = 0;
1163 #endif
1164 runtty.c_cc[VMIN] = 0; /* no waiting */
1165 runtty.c_cc[VTIME] = 0;
1166 #if defined (VDSUSP)
1167 runtty.c_cc[VDSUSP] = 0;
1168 #endif
1169 #if defined (VSTATUS)
1170 runtty.c_cc[VSTATUS] = 0;
1171 #endif
1172 return SCPE_OK;
1173 }
1174
sim_ttrun(void)1175 t_stat sim_ttrun (void)
1176 {
1177 if (!isatty (fileno (stdin))) /* skip if !tty */
1178 return SCPE_OK;
1179 runtty.c_cc[VINTR] = sim_int_char; /* in case changed */
1180 if (tcsetattr (0, TCSAFLUSH, &runtty) < 0)
1181 return SCPE_TTIERR;
1182 if (prior_norm) { /* at normal pri? */
1183 errno = 0;
1184 nice (10); /* try to lower pri */
1185 prior_norm = errno; /* if no error, done */
1186 }
1187 return SCPE_OK;
1188 }
1189
sim_ttcmd(void)1190 t_stat sim_ttcmd (void)
1191 {
1192 if (!isatty (fileno (stdin))) /* skip if !tty */
1193 return SCPE_OK;
1194 if (!prior_norm) { /* priority down? */
1195 errno = 0;
1196 nice (-10); /* try to raise pri */
1197 prior_norm = (errno == 0); /* if no error, done */
1198 }
1199 if (tcsetattr (0, TCSAFLUSH, &cmdtty) < 0)
1200 return SCPE_TTIERR;
1201 return SCPE_OK;
1202 }
1203
sim_ttclose(void)1204 t_stat sim_ttclose (void)
1205 {
1206 return sim_ttcmd ();
1207 }
1208
sim_os_poll_kbd(void)1209 t_stat sim_os_poll_kbd (void)
1210 {
1211 int status;
1212 unsigned char buf[1];
1213
1214 status = read (0, buf, 1);
1215 if (status != 1) return SCPE_OK;
1216 if (sim_brk_char && (buf[0] == sim_brk_char))
1217 return SCPE_BREAK;
1218 else return (buf[0] | SCPE_KFLAG);
1219 }
1220
sim_os_putchar(int32 out)1221 t_stat sim_os_putchar (int32 out)
1222 {
1223 char c;
1224
1225 c = out;
1226 write (1, &c, 1);
1227 return SCPE_OK;
1228 }
1229
1230 #endif
1231