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