This source file includes following definitions.
- ta_flush
- ta_push
- ta_peek
- ta_get
- opc_reset
- check_attn_key
- console_init
- console_exit
- opc_autoinput_set
- clear_opc_autoinput
- add_opc_autoinput
- opc_autoinput_show
- console_attn
- console_attn_idx
- newlineOff
- newlineOn
- handleRCP
- sendConsole
- consoleProcessIdx
- consoleProcess
- opc_iom_cmd
- opc_svc
- opc_show_nunits
- opc_set_nunits
- opc_set_config
- opc_show_config
- opc_show_device_name
- opc_set_device_name
- opc_set_console_port
- opc_show_console_port
- opc_set_console_address
- opc_show_console_address
- opc_set_console_pw
- opc_show_console_pw
- console_putstr
- consolePutchar0
- console_putchar
- consoleConnectPrompt
- startRemoteConsole
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <signal.h>
33 #if !defined(__MINGW64__) && !defined(__MINGW32__)
34 # include <termios.h>
35 #endif
36 #include <ctype.h>
37
38 #include "dps8.h"
39 #include "dps8_iom.h"
40 #include "dps8_sys.h"
41 #include "dps8_console.h"
42 #include "dps8_cable.h"
43 #include "dps8_cpu.h"
44 #include "dps8_faults.h"
45 #include "dps8_scu.h"
46 #include "dps8_mt.h"
47 #include "dps8_disk.h"
48 #include "dps8_utils.h"
49 #if defined(LOCKLESS)
50 # include "threadz.h"
51 #endif
52
53 #include "libtelnet.h"
54 #if defined(CONSOLE_FIX)
55 # include "threadz.h"
56 #endif
57
58 #if defined(SIM_NAME)
59 # undef SIM_NAME
60 #endif
61 #define SIM_NAME "DPS8M"
62
63 #define DBG_CTR 1
64 #define ASSUME0 0
65
66 #if defined(FREE)
67 # undef FREE
68 #endif
69 #define FREE(p) do \
70 { \
71 free((p)); \
72 (p) = NULL; \
73 } while(0)
74
75
76
77
78
79
80 static t_stat opc_reset (DEVICE * dptr);
81 static t_stat opc_show_nunits (FILE *st, UNIT *uptr, int val,
82 const void *desc);
83 static t_stat opc_set_nunits (UNIT * uptr, int32 value, const char * cptr,
84 void * desc);
85 static t_stat opc_autoinput_set (UNIT *uptr, int32 val, const char *cptr,
86 void *desc);
87 static t_stat opc_autoinput_show (FILE *st, UNIT *uptr, int val,
88 const void *desc);
89 static t_stat opc_set_config (UNUSED UNIT * uptr, UNUSED int32 value,
90 const char * cptr, UNUSED void * desc);
91 static t_stat opc_show_config (UNUSED FILE * st, UNUSED UNIT * uptr,
92 UNUSED int val, UNUSED const void * desc);
93 static t_stat opc_set_console_port (UNIT * uptr, UNUSED int32 value,
94 const char * cptr, UNUSED void * desc);
95 static t_stat opc_show_console_port (UNUSED FILE * st, UNIT * uptr,
96 UNUSED int val, UNUSED const void * desc);
97 static t_stat opc_set_console_address (UNIT * uptr, UNUSED int32 value,
98 const char * cptr, UNUSED void * desc);
99 static t_stat opc_show_console_address (UNUSED FILE * st, UNIT * uptr,
100 UNUSED int val, UNUSED const void * desc);
101 static t_stat opc_set_console_pw (UNIT * uptr, UNUSED int32 value,
102 const char * cptr, UNUSED void * desc);
103 static t_stat opc_show_console_pw (UNUSED FILE * st, UNIT * uptr,
104 UNUSED int val, UNUSED const void * desc);
105 static t_stat opc_set_device_name (UNIT * uptr, UNUSED int32 value,
106 const char * cptr, UNUSED void * desc);
107 static t_stat opc_show_device_name (UNUSED FILE * st, UNIT * uptr,
108 UNUSED int val, UNUSED const void * desc);
109
110 static MTAB opc_mtab[] =
111 {
112 {
113 MTAB_unit_nouc,
114 0,
115 "AUTOINPUT",
116 "AUTOINPUT",
117 opc_autoinput_set,
118 opc_autoinput_show,
119 NULL,
120 NULL
121 },
122
123 {
124 MTAB_dev_valr,
125 0,
126 "NUNITS",
127 "NUNITS",
128 opc_set_nunits,
129 opc_show_nunits,
130 "Number of OPC units in the system",
131 NULL
132 },
133
134 {
135 MTAB_unit_uc,
136 0,
137 (char *) "CONFIG",
138 (char *) "CONFIG",
139 opc_set_config,
140 opc_show_config,
141 NULL,
142 NULL,
143 },
144 {
145 MTAB_XTD | MTAB_VUN | \
146 MTAB_VALR | MTAB_NC,
147 0,
148 "NAME",
149 "NAME",
150 opc_set_device_name,
151 opc_show_device_name,
152 "Set the device name",
153 NULL
154 },
155
156 {
157 MTAB_unit_valr_nouc,
158 0,
159 "PORT",
160 "PORT",
161 opc_set_console_port,
162 opc_show_console_port,
163 "Set the console port number",
164 NULL
165 },
166
167 {
168 MTAB_unit_valr_nouc,
169 0,
170 "ADDRESS",
171 "ADDRESS",
172 opc_set_console_address,
173 opc_show_console_address,
174 "Set the console IP Address",
175 NULL
176 },
177
178 {
179 MTAB_unit_valr_nouc,
180 0,
181 "PW",
182 "PW",
183 opc_set_console_pw,
184 opc_show_console_pw,
185 "Set the console password",
186 NULL
187 },
188
189 MTAB_eol
190 };
191
192 static DEBTAB opc_dt[] =
193 {
194 { "NOTIFY", DBG_NOTIFY, NULL },
195 { "INFO", DBG_INFO, NULL },
196 { "ERR", DBG_ERR, NULL },
197 { "WARN", DBG_WARN, NULL },
198 { "DEBUG", DBG_DEBUG, NULL },
199 { "ALL", DBG_ALL, NULL },
200 { NULL, 0, NULL }
201 };
202
203
204
205
206
207
208 #define N_OPC_UNITS 1
209 #define OPC_UNIT_IDX(uptr) ((uptr) - opc_unit)
210
211
212
213 #if defined(LOCKLESS)
214
215
216
217 # define ACTIVATE_1SEC 1000
218 #else
219
220
221
222 # define ACTIVATE_1SEC 4000000
223 #endif
224
225 static t_stat opc_svc (UNIT * unitp);
226
227 UNIT opc_unit[N_OPC_UNITS_MAX] = {
228 #if defined(NO_C_ELLIPSIS)
229 { UDATA (& opc_svc, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
230 { UDATA (& opc_svc, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
231 { UDATA (& opc_svc, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
232 { UDATA (& opc_svc, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
233 { UDATA (& opc_svc, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
234 { UDATA (& opc_svc, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
235 { UDATA (& opc_svc, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
236 { UDATA (& opc_svc, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
237 #else
238 [0 ... N_OPC_UNITS_MAX - 1] = {
239 UDATA (& opc_svc, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
240 }
241 #endif
242 };
243
244 DEVICE opc_dev = {
245 "OPC",
246 opc_unit,
247 NULL,
248 opc_mtab,
249 N_OPC_UNITS,
250 10,
251 8,
252 1,
253 8,
254 8,
255 NULL,
256 NULL,
257 opc_reset,
258 NULL,
259 NULL,
260 NULL,
261 NULL,
262 DEV_DEBUG,
263 0,
264 opc_dt,
265 NULL,
266 NULL,
267 NULL,
268 NULL,
269 NULL,
270 NULL,
271 NULL
272 };
273
274 enum console_model { m6001 = 0, m6004 = 1, m6601 = 2 };
275
276
277 typedef struct opc_state_t
278 {
279
280 unsigned char *tailp;
281 unsigned char *readp;
282
283
284 unsigned char *auto_input;
285 unsigned char *autop;
286
287
288 time_t startTime;
289 UNIT * unitp;
290
291
292 uv_access console_access;
293
294 enum console_model model;
295 enum console_mode { opc_no_mode, opc_read_mode, opc_write_mode } io_mode;
296
297
298 uint tally;
299 uint daddr;
300 int chan;
301
302
303
304 int autoaccept;
305
306 int noempty;
307
308 int attn_flush;
309
310 int simh_buffer_cnt;
311
312
313
314 int carrierPosition;
315
316 bool echo;
317
318 bool attn_pressed;
319 bool simh_attn_pressed;
320
321 bool bcd;
322
323
324 bool escapeSequence;
325
326 char device_name [MAX_DEV_NAME_LEN];
327
328
329
330
331 #define bufsize 257
332 unsigned char keyboardLineBuffer[bufsize];
333 bool tabStops [bufsize];
334
335 #define simh_buffer_sz 4096
336 char simh_buffer[simh_buffer_sz];
337 } opc_state_t;
338
339 static opc_state_t console_state[N_OPC_UNITS_MAX];
340
341 static char * bcd_code_page =
342 "01234567"
343 "89[#@;>?"
344 " ABCDEFG"
345 "HI&.](<\\"
346 "^JKLMNOP"
347 "QR-$*);'"
348 "+/STUVWX"
349 "YZ_,%=\"!";
350
351
352
353
354
355 #if !defined(TA_BUFFER_SIZE)
356 # define TA_BUFFER_SIZE 65536
357 #endif
358
359 static int ta_buffer[TA_BUFFER_SIZE];
360 static uint ta_cnt = 0;
361 static uint ta_next = 0;
362 static bool ta_ovf = false;
363
364 static void ta_flush (void)
365 {
366 ta_cnt = ta_next = 0;
367 ta_ovf = false;
368 }
369
370 static void ta_push (int c)
371 {
372
373 if (ta_cnt >= TA_BUFFER_SIZE)
374 {
375 if (! ta_ovf)
376 sim_print ("typeahead buffer overflow");
377 ta_ovf = true;
378 return;
379 }
380 ta_buffer [ta_cnt ++] = c;
381 }
382
383 static int ta_peek (void)
384 {
385 if (ta_next >= ta_cnt)
386 return SCPE_OK;
387 int c = ta_buffer[ta_next];
388 return c;
389 }
390
391 static int ta_get (void)
392 {
393 if (ta_next >= ta_cnt)
394 return SCPE_OK;
395 int c = ta_buffer[ta_next ++];
396 if (ta_next >= ta_cnt)
397 ta_flush ();
398 return c;
399 }
400
401 static t_stat opc_reset (UNUSED DEVICE * dptr)
402 {
403 for (uint i = 0; i < N_OPC_UNITS_MAX; i ++)
404 {
405 console_state[i].io_mode = opc_no_mode;
406 console_state[i].tailp = console_state[i].keyboardLineBuffer;
407 console_state[i].readp = console_state[i].keyboardLineBuffer;
408 console_state[i].carrierPosition = 1;
409 (void)memset (console_state[i].tabStops, 0, sizeof (console_state[i].tabStops));
410 console_state[i].escapeSequence = false;
411 }
412 return SCPE_OK;
413 }
414
415 int check_attn_key (void)
416 {
417 for (uint i = 0; i < opc_dev.numunits; i ++)
418 {
419 opc_state_t * csp = console_state + i;
420 if (csp->attn_pressed)
421 {
422 csp->attn_pressed = false;
423 sim_usleep (1000);
424 return (int) i;
425 }
426 }
427 return -1;
428 }
429
430
431
432 void console_init (void)
433 {
434 opc_reset (& opc_dev);
435 for (uint i = 0; i < N_OPC_UNITS_MAX; i ++)
436 {
437 opc_state_t * csp = console_state + i;
438 csp->model = m6001;
439 csp->auto_input = NULL;
440 csp->autop = NULL;
441 csp->attn_pressed = false;
442 csp->simh_attn_pressed = false;
443 csp->simh_buffer_cnt = 0;
444 strcpy (csp->console_access.pw, "MulticsRulez");
445
446 csp->autoaccept = 0;
447 csp->noempty = 0;
448 csp->attn_flush = 1;
449 csp->carrierPosition = 1;
450 csp->escapeSequence = 1;
451 (void)memset (csp->tabStops, 0, sizeof (csp->tabStops));
452 }
453 }
454
455
456
457 void console_exit (void) {
458 for (uint i = 0; i < N_OPC_UNITS_MAX; i ++) {
459 opc_state_t * csp = console_state + i;
460 if (csp->auto_input) {
461 FREE (csp->auto_input);
462 csp->auto_input = NULL;
463 }
464 if (csp->console_access.telnetp) {
465 sim_warn ("console_exit freeing console %u telnetp %p\r\n", i, csp->console_access.telnetp);
466 telnet_free (csp->console_access.telnetp);
467 csp->console_access.telnetp = NULL;
468 }
469 }
470 }
471
472 static int opc_autoinput_set (UNIT * uptr, UNUSED int32 val,
473 const char * cptr, UNUSED void * desc)
474 {
475 int devUnitIdx = (int) OPC_UNIT_IDX (uptr);
476 opc_state_t * csp = console_state + devUnitIdx;
477
478 if (cptr)
479 {
480 unsigned char * new = (unsigned char *) strdupesc (cptr);
481 if (csp-> auto_input)
482 {
483 size_t nl = strlen ((char *) new);
484 size_t ol = strlen ((char *) csp->auto_input);
485
486 unsigned char * old = realloc (csp->auto_input, nl + ol + 1);
487 if (!old)
488 {
489 (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
490 __func__, __FILE__, __LINE__);
491 #if defined(USE_BACKTRACE)
492 # if defined(SIGUSR2)
493 (void)raise(SIGUSR2);
494
495 # endif
496 #endif
497 abort();
498 }
499 strcpy ((char *) old + ol, (char *) new);
500 csp->auto_input = old;
501 FREE (new);
502 }
503 else
504 csp->auto_input = new;
505 }
506 else
507 {
508 if (csp->auto_input)
509 FREE (csp->auto_input);
510 csp->auto_input = NULL;
511 }
512 csp->autop = csp->auto_input;
513 return SCPE_OK;
514 }
515
516 int clear_opc_autoinput (int32 flag, UNUSED const char * cptr)
517 {
518 opc_state_t * csp = console_state + flag;
519 if (csp->auto_input)
520 FREE (csp->auto_input);
521 csp->auto_input = NULL;
522 csp->autop = csp->auto_input;
523 return SCPE_OK;
524 }
525
526 int add_opc_autoinput (int32 flag, const char * cptr)
527 {
528 opc_state_t * csp = console_state + flag;
529 unsigned char * new = (unsigned char *) strdupesc (cptr);
530 if (csp->auto_input)
531 {
532 size_t nl = strlen ((char *) new);
533 size_t ol = strlen ((char *) csp->auto_input);
534
535 unsigned char * old = realloc (csp->auto_input, nl + ol + 1);
536 if (!old)
537 {
538 (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
539 __func__, __FILE__, __LINE__);
540 #if defined(USE_BACKTRACE)
541 # if defined(SIGUSR2)
542 (void)raise(SIGUSR2);
543
544 # endif
545 #endif
546 abort();
547 }
548 strcpy ((char *) old + ol, (char *) new);
549 csp->auto_input = old;
550 FREE (new);
551 }
552 else
553 csp->auto_input = new;
554 csp->autop = csp->auto_input;
555 return SCPE_OK;
556 }
557
558 static int opc_autoinput_show (UNUSED FILE * st, UNIT * uptr,
559 UNUSED int val, UNUSED const void * desc)
560 {
561 int conUnitIdx = (int) OPC_UNIT_IDX (uptr);
562 opc_state_t * csp = console_state + conUnitIdx;
563 if (csp->auto_input)
564 sim_print ("autoinput: '%s'", csp->auto_input);
565 else
566 sim_print ("autoinput: empty");
567 return SCPE_OK;
568 }
569
570 static t_stat console_attn (UNUSED UNIT * uptr);
571
572 static UNIT attn_unit[N_OPC_UNITS_MAX] = {
573 #if defined(NO_C_ELLIPSIS)
574 { UDATA (& console_attn, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
575 { UDATA (& console_attn, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
576 { UDATA (& console_attn, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
577 { UDATA (& console_attn, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
578 { UDATA (& console_attn, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
579 { UDATA (& console_attn, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
580 { UDATA (& console_attn, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
581 { UDATA (& console_attn, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
582 #else
583 [0 ... N_OPC_UNITS_MAX - 1] = {
584 UDATA (& console_attn, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
585 }
586 #endif
587 };
588
589 static t_stat console_attn (UNUSED UNIT * uptr)
590 {
591 uint con_unit_idx = (uint) (uptr - attn_unit);
592 uint ctlr_port_num = 0;
593 uint iom_unit_idx = cables->opc_to_iom[con_unit_idx][ctlr_port_num].iom_unit_idx;
594 uint chan_num = cables->opc_to_iom[con_unit_idx][ctlr_port_num].chan_num;
595 uint dev_code = 0;
596
597 send_special_interrupt (iom_unit_idx, chan_num, dev_code, 0, 0);
598 return SCPE_OK;
599 }
600
601 void console_attn_idx (int conUnitIdx)
602 {
603 console_attn (attn_unit + conUnitIdx);
604 }
605
606 #if !defined(__MINGW64__) && !defined(__MINGW32__)
607 static struct termios ttyTermios;
608 static bool ttyTermiosOk = false;
609
610 static void newlineOff (void)
611 {
612 if (! isatty (0))
613 return;
614 if (! ttyTermiosOk)
615 {
616 int rc = tcgetattr (0, & ttyTermios);
617 if (rc)
618 return;
619 ttyTermiosOk = true;
620 }
621 struct termios runtty;
622 runtty = ttyTermios;
623 runtty.c_oflag &= (unsigned int) ~OPOST;
624 # if defined(__ANDROID__)
625 # define TCSA_TYPE TCSANOW
626 (void)fflush(stdout);
627 (void)fflush(stderr);
628 # else
629 # define TCSA_TYPE TCSAFLUSH
630 # endif
631 tcsetattr (0, TCSA_TYPE, & runtty);
632 }
633
634 static void newlineOn (void)
635 {
636 if (! isatty (0))
637 return;
638 if (! ttyTermiosOk)
639 return;
640 # if defined(__ANDROID__)
641 (void)fflush(stdout);
642 (void)fflush(stderr);
643 # endif
644 tcsetattr (0, TCSA_TYPE, & ttyTermios);
645 }
646 #endif
647
648 static void handleRCP (uint con_unit_idx, char * text)
649 {
650
651
652
653
654
655
656
657
658
659 size_t len = strlen (text);
660 char label [len + 1];
661 char with [len + 1];
662 char drive [len + 1];
663 int rc = sscanf (text, "%*d.%*d RCP: Mount Reel %s %s ring on %s",
664 label, with, drive);
665 if (rc == 3)
666 {
667 bool withring = (strcmp (with, "with") == 0);
668 char labelDotTap[strlen (label) + strlen (".tap") + 1];
669 strcpy (labelDotTap, label);
670 strcat (labelDotTap, ".tap");
671 attachTape (labelDotTap, withring, drive);
672 return;
673 }
674
675 rc = sscanf (text, "%*d.%*d RCP: Remount Reel %s %s ring on %s",
676 label, with, drive);
677 if (rc == 3)
678 {
679 bool withring = (strcmp (with, "with") == 0);
680 char labelDotTap [strlen (label) + strlen (".tap") + 1];
681 strcpy (labelDotTap, label);
682 strcat (labelDotTap, ".tap");
683 attachTape (labelDotTap, withring, drive);
684 return;
685 }
686
687
688
689 if (console_state[con_unit_idx].autoaccept)
690 {
691 rc = sscanf (text, "%*d as dial_ctl_: Channel %s dialed to Initializer",
692 label);
693 if (rc == 1)
694 {
695
696 opc_autoinput_set (opc_unit + con_unit_idx, 0, "accept ", NULL);
697 opc_autoinput_set (opc_unit + con_unit_idx, 0, label, NULL);
698 opc_autoinput_set (opc_unit + con_unit_idx, 0, "\r", NULL);
699
700 if (console_state[con_unit_idx].io_mode != opc_read_mode)
701 console_state[con_unit_idx].attn_pressed = true;
702 return;
703 }
704 }
705 }
706
707
708 static void sendConsole (int conUnitIdx, word12 stati)
709 {
710 opc_state_t * csp = console_state + conUnitIdx;
711 uint tally = csp->tally;
712 uint ctlr_port_num = 0;
713 uint iomUnitIdx = cables->opc_to_iom[conUnitIdx][ctlr_port_num].iom_unit_idx;
714 uint chan_num = cables->opc_to_iom[conUnitIdx][ctlr_port_num].chan_num;
715 iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan_num];
716
717
718 if (csp->io_mode != opc_read_mode)
719 {
720 sim_warn ("%s called with io_mode != opc_read_mode (%d)\n",
721 __func__, csp->io_mode);
722 return;
723 }
724
725 uint n_chars = (uint) (csp->tailp - csp->readp);
726 uint n_words;
727 if (csp->bcd)
728
729 n_words = ((n_chars+2) + 5) / 6;
730 else
731 n_words = (n_chars + 3) / 4;
732
733 word36 buf[n_words + 1];
734
735 (void)memset (buf, 0, sizeof (word36) * (n_words + 1));
736 word36 * bufp = buf;
737
738
739
740 if ((!csp->bcd) && csp->noempty && n_chars == 0 && tally)
741 {
742 n_chars = 1;
743 n_words = 1;
744 putbits36_9 (bufp, 0, '@');
745 tally --;
746 }
747 else
748 {
749 int bcd_nl_state = 0;
750 while (tally && csp->readp < csp->tailp)
751 {
752 if (csp->bcd)
753 {
754
755
756 * bufp = 0;
757 for (uint charno = 0; charno < 4; ++ charno)
758 {
759 unsigned char c;
760 if (csp->readp >= csp->tailp)
761 {
762 if (bcd_nl_state == 0)
763 {
764 c = '!';
765 bcd_nl_state = 1;
766 }
767 else if (bcd_nl_state == 1)
768 {
769 c = '1';
770 bcd_nl_state = 2;
771 }
772 else
773 break;
774 }
775 else
776 c = (unsigned char) (* csp->readp ++);
777 c = (unsigned char) toupper (c);
778 int i;
779 for (i = 0; i < 64; i ++)
780 if (bcd_code_page[i] == c)
781 break;
782 if (i >= 64)
783 {
784 sim_warn ("Character %o does not map to BCD; replacing with '?'\n", c);
785 i = 017;
786 }
787 putbits36_6 (bufp, charno * 6, (word6) i);
788 }
789 }
790 else
791 {
792 * bufp = 0ul;
793 for (uint charno = 0; charno < 4; ++ charno)
794 {
795 if (csp->readp >= csp->tailp)
796 break;
797 unsigned char c = (unsigned char) (* csp->readp ++);
798 c &= 0177;
799 putbits36_9 (bufp, charno * 9, c);
800 }
801 }
802 bufp ++;
803 tally --;
804 }
805 if (csp->readp < csp->tailp)
806 {
807 sim_warn ("opc_iom_io: discarding %d characters from end of line\n",
808 (int) (csp->tailp - csp->readp));
809 }
810 }
811
812 iom_indirect_data_service (iomUnitIdx, chan_num, buf, & n_words, true);
813
814 p->charPos = n_chars % 4;
815 p->stati = (word12) stati;
816
817 csp->readp = csp->keyboardLineBuffer;
818 csp->tailp = csp->keyboardLineBuffer;
819 csp->io_mode = opc_no_mode;
820
821 send_terminate_interrupt (iomUnitIdx, chan_num);
822 }
823
824 static void console_putchar (int conUnitIdx, char ch);
825 static void console_putstr (int conUnitIdx, char * str);
826
827
828 static void consoleProcessIdx (int conUnitIdx)
829 {
830 opc_state_t * csp = console_state + conUnitIdx;
831 int c;
832
833
834
835 for (;;)
836 {
837 c = sim_poll_kbd ();
838 if (c == SCPE_OK)
839 c = accessGetChar (& csp->console_access);
840
841
842
843 if (breakEnable && stop_cpu)
844 {
845 console_putstr (conUnitIdx, " Got <sim stop> \r\n");
846 return;
847 }
848
849
850
851
852
853 if (breakEnable && c == SCPE_STOP)
854 {
855 console_putstr (conUnitIdx, " Got <sim stop> \r\n");
856 stop_cpu = 1;
857 return;
858 }
859
860
861
862 if (breakEnable && c == SCPE_BREAK)
863 {
864 console_putstr (conUnitIdx, " Got <sim stop> \r\n");
865 stop_cpu = 1;
866 return;
867 }
868
869
870
871 if (c == SCPE_OK)
872 break;
873
874
875
876 if (c < SCPE_KFLAG)
877 {
878 sim_printf ("impossible %d %o\n", c, c);
879 continue;
880 }
881
882
883
884 int ch = c - SCPE_KFLAG;
885
886
887
888
889 if (csp->io_mode != opc_read_mode)
890 {
891 if (ch == '\033' || ch == '\001')
892 {
893 if (csp->attn_flush)
894 ta_flush ();
895 csp->attn_pressed = true;
896 continue;
897 }
898 }
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923 if (ch == 023)
924 {
925 if (! csp->simh_attn_pressed)
926 {
927 ta_flush ();
928 csp->simh_attn_pressed = true;
929 csp->simh_buffer_cnt = 0;
930 console_putstr (conUnitIdx, "^S\r\n" SIM_NAME "> ");
931 }
932 continue;
933 }
934
935
936
937
938
939
940
941
942
943
944
945 if (ch == 020) {
946 sim_usleep (1000);
947 if (csp->io_mode != opc_read_mode) {
948 if (csp->attn_flush)
949 ta_flush ();
950 csp->attn_pressed = true;
951 }
952 continue;
953 }
954
955
956
957 if (ch == 024)
958 {
959 char buf[256];
960 char cms[3] = "?RW";
961
962 (void)sprintf (buf, "^T attn %c %c cnt %d next %d\r\n",
963 console_state[0].attn_pressed+'0',
964 cms[console_state[0].io_mode],
965 ta_cnt, ta_next);
966 console_putstr (conUnitIdx, buf);
967 continue;
968 }
969
970
971
972 if (csp->simh_attn_pressed)
973 {
974 ta_get ();
975 if (ch == '\177' || ch == '\010')
976 {
977 if (csp->simh_buffer_cnt > 0)
978 {
979 -- csp->simh_buffer_cnt;
980 csp->simh_buffer[csp->simh_buffer_cnt] = 0;
981 console_putstr (conUnitIdx, "\b \b");
982 }
983 return;
984 }
985
986
987
988 if (ch == '\022')
989 {
990 console_putstr (conUnitIdx, "^R\r\n" SIM_NAME "> ");
991 for (int i = 0; i < csp->simh_buffer_cnt; i ++)
992 console_putchar (conUnitIdx, (char) (csp->simh_buffer[i]));
993 return;
994 }
995
996
997
998 if (ch == '\025')
999 {
1000 console_putstr (conUnitIdx, "^U\r\n" SIM_NAME "> ");
1001 csp->simh_buffer_cnt = 0;
1002 return;
1003 }
1004
1005
1006
1007 if (ch == '\012' || ch == '\015')
1008 {
1009 console_putstr (conUnitIdx, "\r\n");
1010 csp->simh_buffer[csp->simh_buffer_cnt] = 0;
1011
1012 char * cptr = csp->simh_buffer;
1013 char gbuf[simh_buffer_sz];
1014 cptr = (char *) get_glyph (cptr, gbuf, 0);
1015 if (strlen (gbuf))
1016 {
1017 CTAB *cmdp;
1018 if ((cmdp = find_cmd (gbuf)))
1019 {
1020 t_stat stat = cmdp->action (cmdp->arg, cptr);
1021
1022 if (stat != SCPE_OK)
1023 {
1024 char buf[4096];
1025 (void)sprintf (buf, "\r%s returned %d '%s'\r\n",
1026 SIM_NAME, stat, sim_error_text (stat));
1027 console_putstr (conUnitIdx, buf);
1028 }
1029 }
1030 else
1031 console_putstr (conUnitIdx,
1032 "\rUnrecognized " SIM_NAME " command.\r\n");
1033 }
1034 csp->simh_buffer_cnt = 0;
1035 csp->simh_buffer[0] = 0;
1036 csp->simh_attn_pressed = false;
1037 return;
1038 }
1039
1040
1041
1042 if (ch == '\033' || ch == '\004' || ch == '\032')
1043 {
1044 console_putstr (conUnitIdx, "\r\n" SIM_NAME " cancel\r\n");
1045
1046 csp->simh_buffer_cnt = 0;
1047 csp->simh_buffer[0] = 0;
1048 csp->simh_attn_pressed = false;
1049 return;
1050 }
1051
1052
1053
1054 if (isprint (ch))
1055 {
1056
1057 if (csp->simh_buffer_cnt + 1 >= simh_buffer_sz)
1058 return;
1059 csp->simh_buffer[csp->simh_buffer_cnt ++] = (char) ch;
1060 console_putchar (conUnitIdx, (char) ch);
1061 return;
1062 }
1063 return;
1064 }
1065
1066
1067
1068 ta_push (c);
1069 }
1070
1071
1072
1073 if (breakEnable && stop_cpu)
1074 {
1075 console_putstr (conUnitIdx, " Got <sim stop> \r\n");
1076 return;
1077 }
1078
1079
1080
1081
1082 if (csp->io_mode == opc_read_mode &&
1083 csp->autop != NULL)
1084 {
1085 int announce = 1;
1086 for (;;)
1087 {
1088 if (csp->tailp >= csp->keyboardLineBuffer + sizeof (csp->keyboardLineBuffer))
1089 {
1090 sim_warn ("getConsoleInput: Buffer full; flushing autoinput.\n");
1091 sendConsole (conUnitIdx, 04000);
1092 return;
1093 }
1094 unsigned char c = * (csp->autop);
1095 if (c == 4)
1096 {
1097 FREE (csp->auto_input);
1098 csp->auto_input = NULL;
1099 csp->autop = NULL;
1100
1101 csp->readp = csp->keyboardLineBuffer;
1102 csp->tailp = csp->keyboardLineBuffer;
1103 sendConsole (conUnitIdx, 04310);
1104
1105 console_putstr (conUnitIdx, "CONSOLE: RELEASED\r\n");
1106 sim_usleep (1000);
1107 return;
1108 }
1109 if (c == 030 || c == 031)
1110 {
1111
1112
1113 return;
1114 }
1115 if (c == 0)
1116 {
1117 FREE (csp->auto_input);
1118 csp->auto_input = NULL;
1119 csp->autop = NULL;
1120 goto eol;
1121 }
1122 if (announce)
1123 {
1124 console_putstr (conUnitIdx, "[auto-input] ");
1125 announce = 0;
1126 }
1127 csp->autop ++;
1128
1129 if (c == '\012' || c == '\015')
1130 {
1131 eol:
1132 if (csp->echo)
1133 console_putstr (conUnitIdx, "\r\n");
1134 sendConsole (conUnitIdx, 04000);
1135 return;
1136 }
1137 else
1138 {
1139 * csp->tailp ++ = c;
1140 if (csp->echo)
1141 console_putchar (conUnitIdx, (char) c);
1142 }
1143 }
1144 }
1145
1146
1147
1148
1149 if (csp->io_mode == opc_read_mode &&
1150 csp->tailp == csp->keyboardLineBuffer)
1151 {
1152 if (csp->startTime + 30 <= time (NULL))
1153 {
1154 console_putstr (conUnitIdx, "CONSOLE: TIMEOUT\r\n");
1155 sim_usleep (1000);
1156 csp->readp = csp->keyboardLineBuffer;
1157 csp->tailp = csp->keyboardLineBuffer;
1158 sendConsole (conUnitIdx, 04310);
1159
1160 }
1161 }
1162
1163
1164
1165 c = ta_peek ();
1166
1167
1168 if (c == SCPE_OK)
1169 return;
1170
1171
1172 if (c < SCPE_KFLAG)
1173 {
1174 sim_printf ("impossible %d %o\n", c, c);
1175 return;
1176 }
1177
1178
1179
1180 int ch = c - SCPE_KFLAG;
1181
1182
1183 if (csp->io_mode != opc_read_mode)
1184 {
1185 if (ch == '\033' || ch == '\001')
1186 {
1187 ta_get ();
1188 csp->attn_pressed = true;
1189 }
1190 return;
1191 }
1192
1193 if (ch == '\177' || ch == '\010')
1194 {
1195 ta_get ();
1196 if (csp->tailp > csp->keyboardLineBuffer)
1197 {
1198 * csp->tailp = 0;
1199 -- csp->tailp;
1200 if (csp->echo)
1201 console_putstr (conUnitIdx, "\b \b");
1202 }
1203 return;
1204 }
1205
1206 if (ch == '\022')
1207 {
1208 ta_get ();
1209 if (csp->echo)
1210 {
1211 console_putstr (conUnitIdx, "^R\r\n");
1212 for (unsigned char * p = csp->keyboardLineBuffer; p < csp->tailp; p ++)
1213 console_putchar (conUnitIdx, (char) (*p));
1214 return;
1215 }
1216 }
1217
1218 if (ch == '\025')
1219 {
1220 ta_get ();
1221 console_putstr (conUnitIdx, "^U\r\n");
1222 csp->tailp = csp->keyboardLineBuffer;
1223 return;
1224 }
1225
1226 if (ch == '\030')
1227 {
1228 ta_get ();
1229 console_putstr (conUnitIdx, "^X\r\n");
1230 csp->tailp = csp->keyboardLineBuffer;
1231 return;
1232 }
1233
1234 if (ch == '\012' || ch == '\015')
1235 {
1236 ta_get ();
1237 if (csp->echo)
1238 console_putstr (conUnitIdx, "\r\n");
1239 sendConsole (conUnitIdx, 04000);
1240 return;
1241 }
1242
1243 if (ch == '\033' || ch == '\004' || ch == '\032')
1244 {
1245 ta_get ();
1246 console_putstr (conUnitIdx, "\r\n");
1247
1248 csp->readp = csp->keyboardLineBuffer;
1249 csp->tailp = csp->keyboardLineBuffer;
1250 sendConsole (conUnitIdx, 04310);
1251
1252 console_putstr (conUnitIdx, "CONSOLE: RELEASED\n");
1253 sim_usleep (1000);
1254 return;
1255 }
1256
1257 if (isprint (ch))
1258 {
1259
1260 ta_get ();
1261 if (csp->tailp >= csp->keyboardLineBuffer + sizeof (csp->keyboardLineBuffer))
1262 return;
1263
1264 * csp->tailp ++ = (unsigned char) ch;
1265 if (csp->echo)
1266 console_putchar (conUnitIdx, (char) ch);
1267 return;
1268 }
1269
1270 ta_get ();
1271 return;
1272 }
1273
1274 void consoleProcess (void)
1275 {
1276 for (int conUnitIdx = 0; conUnitIdx < (int) opc_dev.numunits; conUnitIdx ++)
1277 consoleProcessIdx (conUnitIdx);
1278 }
1279
1280
1281
1282
1283
1284
1285
1286
1287 iom_cmd_rc_t opc_iom_cmd (uint iomUnitIdx, uint chan) {
1288 iom_cmd_rc_t rc = IOM_CMD_PROCEED;
1289 #if defined(TESTING)
1290 cpu_state_t * cpup = _cpup;
1291 #endif
1292
1293 #if defined(LOCKLESS)
1294 lock_libuv ();
1295 #endif
1296
1297 iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
1298 uint con_unit_idx = get_ctlr_idx (iomUnitIdx, chan);
1299 UNIT * unitp = & opc_unit[con_unit_idx];
1300 opc_state_t * csp = console_state + con_unit_idx;
1301
1302 p->dev_code = p->IDCW_DEV_CODE;
1303 p->stati = 0;
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313 if (IS_IDCW (p)) {
1314
1315
1316 switch (p->IDCW_DEV_CMD) {
1317 case 000:
1318 sim_debug (DBG_DEBUG, & opc_dev, "%s: Status request\n", __func__);
1319 csp->io_mode = opc_no_mode;
1320 p->stati = 04000;
1321 break;
1322
1323 case 003:
1324 sim_debug (DBG_DEBUG, & tape_dev, "%s: Read BCD echoed\n", __func__);
1325 csp->io_mode = opc_read_mode;
1326 p->recordResidue --;
1327 csp->echo = true;
1328 csp->bcd = true;
1329 p->stati = 04000;
1330 break;
1331
1332 case 013:
1333 sim_debug (DBG_DEBUG, & opc_dev, "%s: Write BCD\n", __func__);
1334 p->isRead = false;
1335 csp->bcd = true;
1336 csp->io_mode = opc_write_mode;
1337 p->recordResidue --;
1338 p->stati = 04000;
1339 break;
1340
1341 case 023:
1342 sim_debug (DBG_DEBUG, & tape_dev, "%s: Read ASCII echoed\n", __func__);
1343 csp->io_mode = opc_read_mode;
1344 p->recordResidue --;
1345 csp->echo = true;
1346 csp->bcd = false;
1347 p->stati = 04000;
1348 break;
1349
1350 case 033:
1351 sim_debug (DBG_DEBUG, & opc_dev, "%s: Write ASCII\n", __func__);
1352 p->isRead = false;
1353 csp->bcd = false;
1354 csp->io_mode = opc_write_mode;
1355 p->recordResidue --;
1356 p->stati = 04000;
1357 break;
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368 case 040:
1369 sim_debug (DBG_DEBUG, & opc_dev, "%s: Reset\n", __func__);
1370 p->stati = 04000;
1371
1372
1373
1374
1375
1376
1377
1378 break;
1379
1380 case 043:
1381 sim_debug (DBG_DEBUG, & tape_dev, "%s: Read ASCII unechoed\n", __func__);
1382 csp->io_mode = opc_read_mode;
1383 p->recordResidue --;
1384 csp->echo = false;
1385 csp->bcd = false;
1386 p->stati = 04000;
1387 break;
1388
1389 case 051:
1390 sim_debug (DBG_DEBUG, & opc_dev, "%s: Alert\n", __func__);
1391 p->isRead = false;
1392 console_putstr ((int) con_unit_idx, "CONSOLE: ALERT\r\n");
1393 console_putchar ((int) con_unit_idx, '\a');
1394 p->stati = 04000;
1395 sim_usleep (1000);
1396 if (csp->model == m6001 && p->isPCW) {
1397 rc = IOM_CMD_DISCONNECT;
1398 goto done;
1399 }
1400 break;
1401
1402 case 057:
1403
1404
1405
1406
1407 sim_debug (DBG_DEBUG, & opc_dev, "%s: Read ID\n", __func__);
1408 p->stati = 04500;
1409 if (csp->model == m6001 && p->isPCW) {
1410 rc = IOM_CMD_DISCONNECT;
1411 goto done;
1412 }
1413 break;
1414
1415 case 060:
1416 sim_debug (DBG_DEBUG, & opc_dev, "%s: Lock\n", __func__);
1417 console_putstr ((int) con_unit_idx, "CONSOLE: LOCK\r\n");
1418 p->stati = 04000;
1419 sim_usleep (1000);
1420 break;
1421
1422 case 063:
1423 sim_debug (DBG_DEBUG, & opc_dev, "%s: Unlock\n", __func__);
1424 console_putstr ((int) con_unit_idx, "CONSOLE: UNLOCK\r\n");
1425 p->stati = 04000;
1426 sim_usleep (1000);
1427 break;
1428
1429 default:
1430 sim_debug (DBG_DEBUG, & opc_dev, "%s: Unknown command 0%o\n", __func__, p->IDCW_DEV_CMD);
1431 p->stati = 04501;
1432 rc = IOM_CMD_ERROR;
1433 goto done;
1434 }
1435 goto done;
1436 }
1437
1438
1439 switch (csp->io_mode) {
1440 case opc_no_mode:
1441 sim_warn ("%s: Unexpected IOTx\n", __func__);
1442 rc = IOM_CMD_ERROR;
1443 goto done;
1444
1445 case opc_read_mode: {
1446 if (csp->tailp != csp->keyboardLineBuffer) {
1447 sim_warn ("%s: Discarding previously buffered input.\n", __func__);
1448 }
1449 uint tally = p->DDCW_TALLY;
1450 uint daddr = p->DDCW_ADDR;
1451
1452 if (tally == 0) {
1453 tally = 4096;
1454 }
1455
1456 csp->tailp = csp->keyboardLineBuffer;
1457 csp->readp = csp->keyboardLineBuffer;
1458 csp->startTime = time (NULL);
1459 csp->tally = tally;
1460 csp->daddr = daddr;
1461 csp->unitp = unitp;
1462 csp->chan = (int) chan;
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473 if (csp->autop && (*csp->autop == 030 || *csp->autop == 031)) {
1474
1475
1476
1477
1478
1479 clear_opc_autoinput (ASSUME0, NULL);
1480 ta_flush ();
1481 sim_printf \
1482 ("\r\nScript wedged and abandoned; autoinput and typeahead buffers flushed\r\n");
1483 }
1484 rc = IOM_CMD_PENDING;
1485 goto done;
1486 }
1487
1488 case opc_write_mode: {
1489 uint tally = p->DDCW_TALLY;
1490
1491
1492
1493
1494 if (tally == 0) {
1495 tally = 4096;
1496 }
1497
1498 word36 buf[tally];
1499 iom_indirect_data_service (iomUnitIdx, chan, buf, & tally, false);
1500 p->initiate = false;
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533 char text[tally * 4 + 1];
1534 char * textp = text;
1535 word36 * bufp = buf;
1536 * textp = 0;
1537 #if !defined(__MINGW64__)
1538 newlineOff ();
1539 #endif
1540
1541
1542
1543 int escape_cnt = 0;
1544
1545 while (tally) {
1546 word36 datum = * bufp ++;
1547 tally --;
1548 if (csp->bcd) {
1549
1550 for (int i = 0; i < 6; i ++) {
1551 word36 narrow_char = datum >> 30;
1552
1553 datum = datum << 6;
1554 narrow_char &= 077;
1555 char ch = bcd_code_page [narrow_char];
1556 if (escape_cnt == 2) {
1557 console_putchar ((int) con_unit_idx, ch);
1558 * textp ++ = ch;
1559 escape_cnt = 0;
1560 } else if (ch == '!') {
1561 escape_cnt ++;
1562 } else if (escape_cnt == 1) {
1563 uint lp = (uint)narrow_char;
1564
1565
1566 if (lp == 060 || lp == 075 ) {
1567 p->stati = 04320;
1568 goto done;
1569 }
1570 if (lp == 0)
1571 lp = 1;
1572 if (lp >= 16) {
1573 console_putstr ((int) con_unit_idx, "\f");
1574
1575 } else {
1576 for (uint i = 0; i < lp; i ++) {
1577 console_putstr ((int) con_unit_idx, "\r\n");
1578
1579
1580 }
1581 }
1582 escape_cnt = 0;
1583 } else if (ch == '?') {
1584 escape_cnt = 0;
1585 } else {
1586 console_putchar ((int) con_unit_idx, ch);
1587 * textp ++ = ch;
1588 escape_cnt = 0;
1589 }
1590 }
1591 } else {
1592 for (int i = 0; i < 4; i ++) {
1593 word36 wide_char = datum >> 27;
1594
1595 datum = datum << 9;
1596 char ch = wide_char & 0x7f;
1597 if (ch != 0177 && ch != 0) {
1598 console_putchar ((int) con_unit_idx, ch);
1599 * textp ++ = ch;
1600 }
1601 }
1602 }
1603 }
1604 * textp ++ = 0;
1605
1606
1607 if (csp->autop && * csp->autop == 030) {
1608
1609
1610
1611 size_t expl = strcspn ((char *) (csp->autop + 1), "\030");
1612
1613 if (strncmp (text, (char *) (csp->autop + 1), expl) == 0) {
1614 csp->autop += expl + 2;
1615 sim_usleep (1000);
1616 sim_activate (& attn_unit[con_unit_idx], ACTIVATE_1SEC);
1617 }
1618 }
1619
1620 if (csp->autop && * csp->autop == 031) {
1621
1622
1623
1624 size_t expl = strcspn ((char *) (csp->autop + 1), "\031");
1625
1626 char needle [expl + 1];
1627 strncpy (needle, (char *) csp->autop + 1, expl);
1628 needle [expl] = 0;
1629 if (strstr (text, needle)) {
1630 csp->autop += expl + 2;
1631 sim_usleep (1000);
1632 sim_activate (& attn_unit[con_unit_idx], ACTIVATE_1SEC);
1633 }
1634 }
1635 handleRCP (con_unit_idx, text);
1636 #if !defined(__MINGW64__)
1637 newlineOn ();
1638 #endif
1639 p->stati = 04000;
1640 goto done;
1641 }
1642 }
1643
1644 done:
1645 #if defined(LOCKLESS)
1646 unlock_libuv ();
1647 #endif
1648 return rc;
1649 }
1650
1651 static t_stat opc_svc (UNIT * unitp)
1652 {
1653 int con_unit_idx = (int) OPC_UNIT_IDX (unitp);
1654 uint ctlr_port_num = 0;
1655 uint iom_unit_idx = cables->opc_to_iom[con_unit_idx][ctlr_port_num].iom_unit_idx;
1656 uint chan_num = cables->opc_to_iom[con_unit_idx][ctlr_port_num].chan_num;
1657
1658 opc_iom_cmd (iom_unit_idx, chan_num);
1659 return SCPE_OK;
1660 }
1661
1662 static t_stat opc_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr,
1663 UNUSED int val, UNUSED const void * desc)
1664 {
1665 sim_print ("%d units\n", opc_dev.numunits);
1666 return SCPE_OK;
1667 }
1668
1669 static t_stat opc_set_nunits (UNUSED UNIT * uptr, int32 UNUSED value,
1670 const char * cptr, UNUSED void * desc)
1671 {
1672 if (! cptr)
1673 return SCPE_ARG;
1674 int n = atoi (cptr);
1675 if (n < 1 || n > N_OPC_UNITS_MAX)
1676 return SCPE_ARG;
1677 opc_dev.numunits = (uint32) n;
1678 return SCPE_OK;
1679 }
1680
1681 static config_value_list_t cfg_on_off[] =
1682 {
1683 { "off", 0 },
1684 { "on", 1 },
1685 { "disable", 0 },
1686 { "enable", 1 },
1687 { NULL, 0 }
1688 };
1689
1690 static config_value_list_t cfg_model[] =
1691 {
1692 { "m6001", m6001 },
1693 { "m6004", m6004 },
1694 { "m6601", m6601 },
1695 { NULL, 0 }
1696 };
1697
1698 static config_list_t opc_config_list[] =
1699 {
1700 { "autoaccept", 0, 1, cfg_on_off },
1701 { "noempty", 0, 1, cfg_on_off },
1702 { "attn_flush", 0, 1, cfg_on_off },
1703 { "model", 1, 0, cfg_model },
1704 { NULL, 0, 0, NULL }
1705 };
1706
1707 static t_stat opc_set_config (UNUSED UNIT * uptr, UNUSED int32 value,
1708 const char * cptr, UNUSED void * desc)
1709 {
1710 int devUnitIdx = (int) OPC_UNIT_IDX (uptr);
1711 opc_state_t * csp = console_state + devUnitIdx;
1712
1713 config_state_t cfg_state = { NULL, NULL };
1714
1715 for (;;)
1716 {
1717 int64_t v;
1718 int rc = cfg_parse (__func__, cptr, opc_config_list,
1719 & cfg_state, & v);
1720 if (rc == -1)
1721 break;
1722
1723 if (rc == -2)
1724 {
1725 cfg_parse_done (& cfg_state);
1726 return SCPE_ARG;
1727 }
1728 const char * p = opc_config_list[rc].name;
1729
1730 if (strcmp (p, "autoaccept") == 0)
1731 {
1732 csp->autoaccept = (int) v;
1733 continue;
1734 }
1735
1736 if (strcmp (p, "noempty") == 0)
1737 {
1738 csp->noempty = (int) v;
1739 continue;
1740 }
1741
1742 if (strcmp (p, "attn_flush") == 0)
1743 {
1744 csp->attn_flush = (int) v;
1745 continue;
1746 }
1747
1748 if (strcmp (p, "model") == 0)
1749 {
1750 csp->model = (enum console_model) v;
1751 continue;
1752 }
1753
1754 sim_warn ("error: opc_set_config: Invalid cfg_parse rc <%d>\n",
1755 rc);
1756 cfg_parse_done (& cfg_state);
1757 return SCPE_ARG;
1758 }
1759 cfg_parse_done (& cfg_state);
1760 return SCPE_OK;
1761 }
1762
1763 static t_stat opc_show_config (UNUSED FILE * st, UNUSED UNIT * uptr,
1764 UNUSED int val, UNUSED const void * desc)
1765 {
1766 int devUnitIdx = (int) OPC_UNIT_IDX (uptr);
1767 opc_state_t * csp = console_state + devUnitIdx;
1768 sim_msg ("flags : ");
1769 sim_msg ("autoaccept=%d, ", csp->autoaccept);
1770 sim_msg ("noempty=%d, ", csp->noempty);
1771 sim_msg ("attn_flush=%d", csp->attn_flush);
1772 return SCPE_OK;
1773 }
1774
1775 static t_stat opc_show_device_name (UNUSED FILE * st, UNIT * uptr,
1776 UNUSED int val, UNUSED const void * desc)
1777 {
1778 int n = (int) OPC_UNIT_IDX (uptr);
1779 if (n < 0 || n >= N_OPC_UNITS_MAX)
1780 return SCPE_ARG;
1781 sim_printf("name : OPC%d", n);
1782 return SCPE_OK;
1783 }
1784
1785 static t_stat opc_set_device_name (UNIT * uptr, UNUSED int32 value,
1786 const char * cptr, UNUSED void * desc)
1787 {
1788 int n = (int) OPC_UNIT_IDX (uptr);
1789 if (n < 0 || n >= N_OPC_UNITS_MAX)
1790 return SCPE_ARG;
1791 if (cptr)
1792 {
1793 strncpy (console_state[n].device_name, cptr, MAX_DEV_NAME_LEN-1);
1794 console_state[n].device_name[MAX_DEV_NAME_LEN-1] = 0;
1795 }
1796 else
1797 console_state[n].device_name[0] = 0;
1798 return SCPE_OK;
1799 }
1800
1801 static t_stat opc_set_console_port (UNIT * uptr, UNUSED int32 value,
1802 const char * cptr, UNUSED void * desc)
1803 {
1804 int dev_idx = (int) OPC_UNIT_IDX (uptr);
1805 if (dev_idx < 0 || dev_idx >= N_OPC_UNITS_MAX)
1806 return SCPE_ARG;
1807
1808 if (cptr)
1809 {
1810 int port = atoi (cptr);
1811 if (port < 0 || port > 65535)
1812 return SCPE_ARG;
1813 console_state[dev_idx].console_access.port = port;
1814 if (port != 0)
1815 sim_msg ("[OPC emulation: TELNET server port set to %d]\r\n", (int)port);
1816 }
1817 else
1818 console_state[dev_idx].console_access.port = 0;
1819 return SCPE_OK;
1820 }
1821
1822 static t_stat opc_show_console_port (UNUSED FILE * st, UNIT * uptr,
1823 UNUSED int val, UNUSED const void * desc)
1824 {
1825 int dev_idx = (int) OPC_UNIT_IDX (uptr);
1826 if (dev_idx < 0 || dev_idx >= N_OPC_UNITS_MAX)
1827 return SCPE_ARG;
1828 if (console_state[dev_idx].console_access.port)
1829 sim_printf("port : %d", console_state[dev_idx].console_access.port);
1830 else
1831 sim_printf("port : disabled");
1832 return SCPE_OK;
1833 }
1834
1835 static t_stat opc_set_console_address (UNIT * uptr, UNUSED int32 value,
1836 const char * cptr, UNUSED void * desc)
1837 {
1838 int dev_idx = (int) OPC_UNIT_IDX (uptr);
1839 if (dev_idx < 0 || dev_idx >= N_OPC_UNITS_MAX)
1840 return SCPE_ARG;
1841
1842 if (console_state[dev_idx].console_access.address)
1843 {
1844 FREE (console_state[dev_idx].console_access.address);
1845 console_state[dev_idx].console_access.address = NULL;
1846 }
1847
1848 if (cptr)
1849 {
1850 console_state[dev_idx].console_access.address = strdup (cptr);
1851 if (!console_state[dev_idx].console_access.address)
1852 {
1853 (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1854 __func__, __FILE__, __LINE__);
1855 #if defined(USE_BACKTRACE)
1856 # if defined(SIGUSR2)
1857 (void)raise(SIGUSR2);
1858
1859 # endif
1860 #endif
1861 abort();
1862 }
1863 sim_msg ("\r[OPC emulation: OPC%d server address set to %s]\r\n",
1864 dev_idx, console_state[dev_idx].console_access.address);
1865 }
1866
1867 return SCPE_OK;
1868 }
1869
1870 static t_stat opc_show_console_address (UNUSED FILE * st, UNIT * uptr,
1871 UNUSED int val, UNUSED const void * desc)
1872 {
1873 int dev_idx = (int) OPC_UNIT_IDX (uptr);
1874 if (dev_idx < 0 || dev_idx >= N_OPC_UNITS_MAX)
1875 return SCPE_ARG;
1876 if (console_state[dev_idx].console_access.address)
1877 sim_printf("address : %s", console_state[dev_idx].console_access.address);
1878 else
1879 sim_printf("address : any");
1880 return SCPE_OK;
1881 }
1882
1883 static t_stat opc_set_console_pw (UNIT * uptr, UNUSED int32 value,
1884 const char * cptr, UNUSED void * desc)
1885 {
1886 long dev_idx = (int) OPC_UNIT_IDX (uptr);
1887 if (dev_idx < 0 || dev_idx >= N_OPC_UNITS_MAX)
1888 return SCPE_ARG;
1889
1890 if (cptr && (strlen(cptr) > 0))
1891 {
1892 char token[strlen (cptr)+1];
1893 int rc = sscanf (cptr, "%s", token);
1894 if (rc != 1)
1895 return SCPE_ARG;
1896 if (strlen (token) > PW_SIZE)
1897 return SCPE_ARG;
1898 strcpy (console_state[dev_idx].console_access.pw, token);
1899 }
1900 else
1901 {
1902 sim_msg ("no password\n");
1903 console_state[dev_idx].console_access.pw[0] = 0;
1904 }
1905
1906 return SCPE_OK;
1907 }
1908
1909 static t_stat opc_show_console_pw (UNUSED FILE * st, UNIT * uptr,
1910 UNUSED int val, UNUSED const void * desc)
1911 {
1912 int dev_idx = (int) OPC_UNIT_IDX (uptr);
1913 if (dev_idx < 0 || dev_idx >= N_OPC_UNITS_MAX)
1914 return SCPE_ARG;
1915 sim_printf("password : %s", console_state[dev_idx].console_access.pw);
1916 return SCPE_OK;
1917 }
1918
1919 static void console_putstr (int conUnitIdx, char * str)
1920 {
1921 size_t l = strlen (str);
1922 for (size_t i = 0; i < l; i ++)
1923 sim_putchar (str[i]);
1924 opc_state_t * csp = console_state + conUnitIdx;
1925 if (csp->console_access.loggedOn)
1926 accessStartWrite (csp->console_access.client, str,
1927 (ssize_t) l);
1928 }
1929
1930 static void consolePutchar0 (int conUnitIdx, char ch) {
1931 opc_state_t * csp = console_state + conUnitIdx;
1932 sim_putchar (ch);
1933 if (csp->console_access.loggedOn)
1934 accessStartWrite (csp->console_access.client, & ch, 1);
1935 }
1936
1937 static void console_putchar (int conUnitIdx, char ch) {
1938 opc_state_t * csp = console_state + conUnitIdx;
1939 if (csp->escapeSequence) {
1940 csp->escapeSequence = false;
1941 if (ch == '1') {
1942 if (csp->carrierPosition >= 1 && csp->carrierPosition <= 256) {
1943 csp->tabStops[csp->carrierPosition] = true;
1944 }
1945 } else if (ch == '2') {
1946 (void)memset (csp->tabStops, 0, sizeof (csp->tabStops));
1947 } else {
1948 sim_warn ("Unrecognized escape sequence \\033\\%03o\r\n", ch);
1949 }
1950 } else if (isprint (ch)) {
1951 consolePutchar0 (conUnitIdx, ch);
1952 csp->carrierPosition ++;
1953 } else if (ch == '\t') {
1954 while (csp->carrierPosition < bufsize - 1) {
1955 consolePutchar0 (conUnitIdx, ' ');
1956 csp->carrierPosition ++;
1957 if (csp->tabStops[csp->carrierPosition])
1958 break;
1959 }
1960 } else if (ch == '\b') {
1961 consolePutchar0 (conUnitIdx, ch);
1962 csp->carrierPosition --;
1963 } else if (ch == '\f' || ch == '\r') {
1964 consolePutchar0 (conUnitIdx, ch);
1965 csp->carrierPosition = 1;
1966 } else if (ch == '\033') {
1967 csp->escapeSequence = true;
1968 } else {
1969 consolePutchar0 (conUnitIdx, ch);
1970 }
1971 }
1972
1973 static void consoleConnectPrompt (uv_tcp_t * client)
1974 {
1975 accessStartWriteStr (client, "password: \r\n");
1976 uv_access * console_access = (uv_access *) client->data;
1977 console_access->pwPos = 0;
1978 }
1979
1980 void startRemoteConsole (void)
1981 {
1982 for (int conUnitIdx = 0; conUnitIdx < N_OPC_UNITS_MAX; conUnitIdx ++)
1983 {
1984 console_state[conUnitIdx].console_access.connectPrompt = consoleConnectPrompt;
1985 console_state[conUnitIdx].console_access.connected = NULL;
1986 console_state[conUnitIdx].console_access.useTelnet = true;
1987 #if defined(CONSOLE_FIX)
1988 # if defined(LOCKLESS)
1989 lock_libuv ();
1990 # endif
1991 #endif
1992 uv_open_access (& console_state[conUnitIdx].console_access);
1993 #if defined(CONSOLE_FIX)
1994 # if defined(LOCKLESS)
1995 unlock_libuv ();
1996 # endif
1997 #endif
1998 }
1999 }