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