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