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 return (int) i;
424 }
425 }
426 return -1;
427 }
428
429
430
431 void console_init (void)
432 {
433 opc_reset (& opc_dev);
434 for (uint i = 0; i < N_OPC_UNITS_MAX; i ++)
435 {
436 opc_state_t * csp = console_state + i;
437 csp->model = m6001;
438 csp->auto_input = NULL;
439 csp->autop = NULL;
440 csp->attn_pressed = false;
441 csp->simh_attn_pressed = false;
442 csp->simh_buffer_cnt = 0;
443 strcpy (csp->console_access.pw, "MulticsRulez");
444
445 csp->autoaccept = 0;
446 csp->noempty = 0;
447 csp->attn_flush = 1;
448 csp->carrierPosition = 1;
449 csp->escapeSequence = 1;
450 (void)memset (csp->tabStops, 0, sizeof (csp->tabStops));
451 }
452 }
453
454
455
456 void console_exit (void) {
457 for (uint i = 0; i < N_OPC_UNITS_MAX; i ++) {
458 opc_state_t * csp = console_state + i;
459 if (csp->auto_input) {
460 FREE (csp->auto_input);
461 csp->auto_input = NULL;
462 }
463 if (csp->console_access.telnetp) {
464 sim_warn ("console_exit freeing console %u telnetp %p\r\n", i, csp->console_access.telnetp);
465 telnet_free (csp->console_access.telnetp);
466 csp->console_access.telnetp = NULL;
467 }
468 }
469 }
470
471 static int opc_autoinput_set (UNIT * uptr, UNUSED int32 val,
472 const char * cptr, UNUSED void * desc)
473 {
474 int devUnitIdx = (int) OPC_UNIT_IDX (uptr);
475 opc_state_t * csp = console_state + devUnitIdx;
476
477 if (cptr)
478 {
479 unsigned char * new = (unsigned char *) strdupesc (cptr);
480 if (csp-> auto_input)
481 {
482 size_t nl = strlen ((char *) new);
483 size_t ol = strlen ((char *) csp->auto_input);
484
485 unsigned char * old = realloc (csp->auto_input, nl + ol + 1);
486 if (!old)
487 {
488 (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
489 __func__, __FILE__, __LINE__);
490 #if defined(USE_BACKTRACE)
491 # if defined(SIGUSR2)
492 (void)raise(SIGUSR2);
493
494 # endif
495 #endif
496 abort();
497 }
498 strcpy ((char *) old + ol, (char *) new);
499 csp->auto_input = old;
500 FREE (new);
501 }
502 else
503 csp->auto_input = new;
504 }
505 else
506 {
507 if (csp->auto_input)
508 FREE (csp->auto_input);
509 csp->auto_input = NULL;
510 }
511 csp->autop = csp->auto_input;
512 return SCPE_OK;
513 }
514
515 int clear_opc_autoinput (int32 flag, UNUSED const char * cptr)
516 {
517 opc_state_t * csp = console_state + flag;
518 if (csp->auto_input)
519 FREE (csp->auto_input);
520 csp->auto_input = NULL;
521 csp->autop = csp->auto_input;
522 return SCPE_OK;
523 }
524
525 int add_opc_autoinput (int32 flag, const char * cptr)
526 {
527 opc_state_t * csp = console_state + flag;
528 unsigned char * new = (unsigned char *) strdupesc (cptr);
529 if (csp->auto_input)
530 {
531 size_t nl = strlen ((char *) new);
532 size_t ol = strlen ((char *) csp->auto_input);
533
534 unsigned char * old = realloc (csp->auto_input, nl + ol + 1);
535 if (!old)
536 {
537 (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
538 __func__, __FILE__, __LINE__);
539 #if defined(USE_BACKTRACE)
540 # if defined(SIGUSR2)
541 (void)raise(SIGUSR2);
542
543 # endif
544 #endif
545 abort();
546 }
547 strcpy ((char *) old + ol, (char *) new);
548 csp->auto_input = old;
549 FREE (new);
550 }
551 else
552 csp->auto_input = new;
553 csp->autop = csp->auto_input;
554 return SCPE_OK;
555 }
556
557 static int opc_autoinput_show (UNUSED FILE * st, UNIT * uptr,
558 UNUSED int val, UNUSED const void * desc)
559 {
560 int conUnitIdx = (int) OPC_UNIT_IDX (uptr);
561 opc_state_t * csp = console_state + conUnitIdx;
562 if (csp->auto_input)
563 sim_print ("autoinput: '%s'", csp->auto_input);
564 else
565 sim_print ("autoinput: empty");
566 return SCPE_OK;
567 }
568
569 static t_stat console_attn (UNUSED UNIT * uptr);
570
571 static UNIT attn_unit[N_OPC_UNITS_MAX] = {
572 #if defined(NO_C_ELLIPSIS)
573 { UDATA (& console_attn, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
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 #else
582 [0 ... N_OPC_UNITS_MAX - 1] = {
583 UDATA (& console_attn, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
584 }
585 #endif
586 };
587
588 static t_stat console_attn (UNUSED UNIT * uptr)
589 {
590 uint con_unit_idx = (uint) (uptr - attn_unit);
591 uint ctlr_port_num = 0;
592 uint iom_unit_idx = cables->opc_to_iom[con_unit_idx][ctlr_port_num].iom_unit_idx;
593 uint chan_num = cables->opc_to_iom[con_unit_idx][ctlr_port_num].chan_num;
594 uint dev_code = 0;
595
596 send_special_interrupt (iom_unit_idx, chan_num, dev_code, 0, 0);
597 return SCPE_OK;
598 }
599
600 void console_attn_idx (int conUnitIdx)
601 {
602 console_attn (attn_unit + conUnitIdx);
603 }
604
605 #if !defined(__MINGW64__) && !defined(__MINGW32__)
606 static struct termios ttyTermios;
607 static bool ttyTermiosOk = false;
608
609 static void newlineOff (void)
610 {
611 if (! isatty (0))
612 return;
613 if (! ttyTermiosOk)
614 {
615 int rc = tcgetattr (0, & ttyTermios);
616 if (rc)
617 return;
618 ttyTermiosOk = true;
619 }
620 struct termios runtty;
621 runtty = ttyTermios;
622 runtty.c_oflag &= (unsigned int) ~OPOST;
623 # if defined(__ANDROID__)
624 # define TCSA_TYPE TCSANOW
625 (void)fflush(stdout);
626 (void)fflush(stderr);
627 # else
628 # define TCSA_TYPE TCSAFLUSH
629 # endif
630 tcsetattr (0, TCSA_TYPE, & runtty);
631 }
632
633 static void newlineOn (void)
634 {
635 if (! isatty (0))
636 return;
637 if (! ttyTermiosOk)
638 return;
639 # if defined(__ANDROID__)
640 (void)fflush(stdout);
641 (void)fflush(stderr);
642 # endif
643 tcsetattr (0, TCSA_TYPE, & ttyTermios);
644 }
645 #endif
646
647 static void handleRCP (uint con_unit_idx, char * text)
648 {
649
650
651
652
653
654
655
656
657
658 size_t len = strlen (text);
659 char label [len + 1];
660 char with [len + 1];
661 char drive [len + 1];
662 int rc = sscanf (text, "%*d.%*d RCP: Mount Reel %s %s ring on %s",
663 label, with, drive);
664 if (rc == 3)
665 {
666 bool withring = (strcmp (with, "with") == 0);
667 char labelDotTap[strlen (label) + strlen (".tap") + 1];
668 strcpy (labelDotTap, label);
669 strcat (labelDotTap, ".tap");
670 attachTape (labelDotTap, withring, drive);
671 return;
672 }
673
674 rc = sscanf (text, "%*d.%*d RCP: Remount Reel %s %s ring on %s",
675 label, with, drive);
676 if (rc == 3)
677 {
678 bool withring = (strcmp (with, "with") == 0);
679 char labelDotTap [strlen (label) + strlen (".tap") + 1];
680 strcpy (labelDotTap, label);
681 strcat (labelDotTap, ".tap");
682 attachTape (labelDotTap, withring, drive);
683 return;
684 }
685
686
687
688 if (console_state[con_unit_idx].autoaccept)
689 {
690 rc = sscanf (text, "%*d as dial_ctl_: Channel %s dialed to Initializer",
691 label);
692 if (rc == 1)
693 {
694
695 opc_autoinput_set (opc_unit + con_unit_idx, 0, "accept ", NULL);
696 opc_autoinput_set (opc_unit + con_unit_idx, 0, label, NULL);
697 opc_autoinput_set (opc_unit + con_unit_idx, 0, "\r", NULL);
698
699 if (console_state[con_unit_idx].io_mode != opc_read_mode)
700 console_state[con_unit_idx].attn_pressed = true;
701 return;
702 }
703 }
704 }
705
706
707 static void sendConsole (int conUnitIdx, word12 stati)
708 {
709 opc_state_t * csp = console_state + conUnitIdx;
710 uint tally = csp->tally;
711 uint ctlr_port_num = 0;
712 uint iomUnitIdx = cables->opc_to_iom[conUnitIdx][ctlr_port_num].iom_unit_idx;
713 uint chan_num = cables->opc_to_iom[conUnitIdx][ctlr_port_num].chan_num;
714 iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan_num];
715
716
717 if (csp->io_mode != opc_read_mode)
718 {
719 sim_warn ("%s called with io_mode != opc_read_mode (%d)\n",
720 __func__, csp->io_mode);
721 return;
722 }
723
724 uint n_chars = (uint) (csp->tailp - csp->readp);
725 uint n_words;
726 if (csp->bcd)
727
728 n_words = ((n_chars+2) + 5) / 6;
729 else
730 n_words = (n_chars + 3) / 4;
731
732 word36 buf[n_words + 1];
733
734 (void)memset (buf, 0, sizeof (word36) * (n_words + 1));
735 word36 * bufp = buf;
736
737
738
739 if ((!csp->bcd) && csp->noempty && n_chars == 0 && tally)
740 {
741 n_chars = 1;
742 n_words = 1;
743 putbits36_9 (bufp, 0, '@');
744 tally --;
745 }
746 else
747 {
748 int bcd_nl_state = 0;
749 while (tally && csp->readp < csp->tailp)
750 {
751 if (csp->bcd)
752 {
753
754
755 * bufp = 0;
756 for (uint charno = 0; charno < 4; ++ charno)
757 {
758 unsigned char c;
759 if (csp->readp >= csp->tailp)
760 {
761 if (bcd_nl_state == 0)
762 {
763 c = '!';
764 bcd_nl_state = 1;
765 }
766 else if (bcd_nl_state == 1)
767 {
768 c = '1';
769 bcd_nl_state = 2;
770 }
771 else
772 break;
773 }
774 else
775 c = (unsigned char) (* csp->readp ++);
776 c = (unsigned char) toupper (c);
777 int i;
778 for (i = 0; i < 64; i ++)
779 if (bcd_code_page[i] == c)
780 break;
781 if (i >= 64)
782 {
783 sim_warn ("Character %o does not map to BCD; replacing with '?'\n", c);
784 i = 017;
785 }
786 putbits36_6 (bufp, charno * 6, (word6) i);
787 }
788 }
789 else
790 {
791 * bufp = 0ul;
792 for (uint charno = 0; charno < 4; ++ charno)
793 {
794 if (csp->readp >= csp->tailp)
795 break;
796 unsigned char c = (unsigned char) (* csp->readp ++);
797 c &= 0177;
798 putbits36_9 (bufp, charno * 9, c);
799 }
800 }
801 bufp ++;
802 tally --;
803 }
804 if (csp->readp < csp->tailp)
805 {
806 sim_warn ("opc_iom_io: discarding %d characters from end of line\n",
807 (int) (csp->tailp - csp->readp));
808 }
809 }
810
811 iom_indirect_data_service (iomUnitIdx, chan_num, buf, & n_words, true);
812
813 p->charPos = n_chars % 4;
814 p->stati = (word12) stati;
815
816 csp->readp = csp->keyboardLineBuffer;
817 csp->tailp = csp->keyboardLineBuffer;
818 csp->io_mode = opc_no_mode;
819
820 send_terminate_interrupt (iomUnitIdx, chan_num);
821 }
822
823 static void console_putchar (int conUnitIdx, char ch);
824 static void console_putstr (int conUnitIdx, char * str);
825
826
827 static void consoleProcessIdx (int conUnitIdx)
828 {
829 opc_state_t * csp = console_state + conUnitIdx;
830 int c;
831
832
833
834 for (;;)
835 {
836 c = sim_poll_kbd ();
837 if (c == SCPE_OK)
838 c = accessGetChar (& csp->console_access);
839
840
841
842 if (breakEnable && stop_cpu)
843 {
844 console_putstr (conUnitIdx, " Got <sim stop> \r\n");
845 return;
846 }
847
848
849
850
851
852 if (breakEnable && c == SCPE_STOP)
853 {
854 console_putstr (conUnitIdx, " Got <sim stop> \r\n");
855 stop_cpu = 1;
856 return;
857 }
858
859
860
861 if (breakEnable && c == SCPE_BREAK)
862 {
863 console_putstr (conUnitIdx, " Got <sim stop> \r\n");
864 stop_cpu = 1;
865 return;
866 }
867
868
869
870 if (c == SCPE_OK)
871 break;
872
873
874
875 if (c < SCPE_KFLAG)
876 {
877 sim_printf ("impossible %d %o\n", c, c);
878 continue;
879 }
880
881
882
883 int ch = c - SCPE_KFLAG;
884
885
886
887
888 if (csp->io_mode != opc_read_mode)
889 {
890 if (ch == '\033' || ch == '\001')
891 {
892 if (csp->attn_flush)
893 ta_flush ();
894 csp->attn_pressed = true;
895 continue;
896 }
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 if (ch == 023)
923 {
924 if (! csp->simh_attn_pressed)
925 {
926 ta_flush ();
927 csp->simh_attn_pressed = true;
928 csp->simh_buffer_cnt = 0;
929 console_putstr (conUnitIdx, "^S\r\n" SIM_NAME "> ");
930 }
931 continue;
932 }
933
934
935
936
937
938
939
940
941
942
943
944 if (ch == 020) {
945 if (csp->io_mode != opc_read_mode) {
946 if (csp->attn_flush)
947 ta_flush ();
948 csp->attn_pressed = true;
949 }
950 continue;
951 }
952
953
954
955 if (ch == 024)
956 {
957 char buf[256];
958 char cms[3] = "?RW";
959
960 (void)sprintf (buf, "^T attn %c %c cnt %d next %d\r\n",
961 console_state[0].attn_pressed+'0',
962 cms[console_state[0].io_mode],
963 ta_cnt, ta_next);
964 console_putstr (conUnitIdx, buf);
965 #if defined(THREADZ) || defined(LOCKLESS)
966 (void) sprintf (buf, "syncClockMode %c syncClockModeMasterIdx %u\r\n",
967 syncClockMode ? 'T' : 'F',
968 syncClockModeMasterIdx);
969 console_putstr (conUnitIdx, buf);
970 for (int i = 0; i < N_CPU_UNITS_MAX; i ++) {
971 (void) sprintf (buf, "CPU %c %s %05o:%06o\r\n"
972 " executing %c syncClockModeMaster %c workAllocation %4ld\r\n"
973 " inMultics %c syncClockModeCache %c isSlave %c\r\n",
974 "ABCDEFGH"[i],
975 cycle_str (cpus[i].cycle),
976 cpus[i].PPR.PSR,
977 cpus[i].PPR.IC,
978 cpus[i].executing ? 'T' : 'F',
979 cpus[i].syncClockModeMaster ? 'T' : 'F',
980 cpus[i].workAllocation,
981 cpus[i].inMultics ? 'T' : 'F',
982 cpus[i].syncClockModeCache ? 'T' : 'F',
983 cpus[i].isSlave ? 'T' : 'F');
984 console_putstr (conUnitIdx, buf);
985 }
986 #endif
987
988 continue;
989 }
990
991
992
993 if (csp->simh_attn_pressed)
994 {
995 ta_get ();
996 if (ch == '\177' || ch == '\010')
997 {
998 if (csp->simh_buffer_cnt > 0)
999 {
1000 -- csp->simh_buffer_cnt;
1001 csp->simh_buffer[csp->simh_buffer_cnt] = 0;
1002 console_putstr (conUnitIdx, "\b \b");
1003 }
1004 return;
1005 }
1006
1007
1008
1009 if (ch == '\022')
1010 {
1011 console_putstr (conUnitIdx, "^R\r\n" SIM_NAME "> ");
1012 for (int i = 0; i < csp->simh_buffer_cnt; i ++)
1013 console_putchar (conUnitIdx, (char) (csp->simh_buffer[i]));
1014 return;
1015 }
1016
1017
1018
1019 if (ch == '\025')
1020 {
1021 console_putstr (conUnitIdx, "^U\r\n" SIM_NAME "> ");
1022 csp->simh_buffer_cnt = 0;
1023 return;
1024 }
1025
1026
1027
1028 if (ch == '\012' || ch == '\015')
1029 {
1030 console_putstr (conUnitIdx, "\r\n");
1031 csp->simh_buffer[csp->simh_buffer_cnt] = 0;
1032
1033 char * cptr = csp->simh_buffer;
1034 char gbuf[simh_buffer_sz];
1035 cptr = (char *) get_glyph (cptr, gbuf, 0);
1036 if (strlen (gbuf))
1037 {
1038 CTAB *cmdp;
1039 if ((cmdp = find_cmd (gbuf)))
1040 {
1041 t_stat stat = cmdp->action (cmdp->arg, cptr);
1042
1043 if (stat != SCPE_OK)
1044 {
1045 char buf[4096];
1046 (void)sprintf (buf, "\r%s returned %d '%s'\r\n",
1047 SIM_NAME, stat, sim_error_text (stat));
1048 console_putstr (conUnitIdx, buf);
1049 }
1050 }
1051 else
1052 console_putstr (conUnitIdx,
1053 "\rUnrecognized " SIM_NAME " command.\r\n");
1054 }
1055 csp->simh_buffer_cnt = 0;
1056 csp->simh_buffer[0] = 0;
1057 csp->simh_attn_pressed = false;
1058 return;
1059 }
1060
1061
1062
1063 if (ch == '\033' || ch == '\004' || ch == '\032')
1064 {
1065 console_putstr (conUnitIdx, "\r\n" SIM_NAME " cancel\r\n");
1066
1067 csp->simh_buffer_cnt = 0;
1068 csp->simh_buffer[0] = 0;
1069 csp->simh_attn_pressed = false;
1070 return;
1071 }
1072
1073
1074
1075 if (isprint (ch))
1076 {
1077
1078 if (csp->simh_buffer_cnt + 1 >= simh_buffer_sz)
1079 return;
1080 csp->simh_buffer[csp->simh_buffer_cnt ++] = (char) ch;
1081 console_putchar (conUnitIdx, (char) ch);
1082 return;
1083 }
1084 return;
1085 }
1086
1087
1088
1089 ta_push (c);
1090 }
1091
1092
1093
1094 if (breakEnable && stop_cpu)
1095 {
1096 console_putstr (conUnitIdx, " Got <sim stop> \r\n");
1097 return;
1098 }
1099
1100
1101
1102
1103 if (csp->io_mode == opc_read_mode &&
1104 csp->autop != NULL)
1105 {
1106 int announce = 1;
1107 for (;;)
1108 {
1109 if (csp->tailp >= csp->keyboardLineBuffer + sizeof (csp->keyboardLineBuffer))
1110 {
1111 sim_warn ("getConsoleInput: Buffer full; flushing autoinput.\n");
1112 sendConsole (conUnitIdx, 04000);
1113 return;
1114 }
1115 unsigned char c = * (csp->autop);
1116 if (c == 4)
1117 {
1118 FREE (csp->auto_input);
1119 csp->auto_input = NULL;
1120 csp->autop = NULL;
1121
1122 csp->readp = csp->keyboardLineBuffer;
1123 csp->tailp = csp->keyboardLineBuffer;
1124 sendConsole (conUnitIdx, 04310);
1125
1126 console_putstr (conUnitIdx, "CONSOLE: RELEASED\r\n");
1127 return;
1128 }
1129 if (c == 030 || c == 031)
1130 {
1131
1132
1133 return;
1134 }
1135 if (c == 0)
1136 {
1137 FREE (csp->auto_input);
1138 csp->auto_input = NULL;
1139 csp->autop = NULL;
1140 goto eol;
1141 }
1142 if (announce)
1143 {
1144 console_putstr (conUnitIdx, "[auto-input] ");
1145 announce = 0;
1146 }
1147 csp->autop ++;
1148
1149 if (c == '\012' || c == '\015')
1150 {
1151 eol:
1152 if (csp->echo)
1153 console_putstr (conUnitIdx, "\r\n");
1154 sendConsole (conUnitIdx, 04000);
1155 return;
1156 }
1157 else
1158 {
1159 * csp->tailp ++ = c;
1160 if (csp->echo)
1161 console_putchar (conUnitIdx, (char) c);
1162 }
1163 }
1164 }
1165
1166
1167
1168
1169 if (csp->io_mode == opc_read_mode &&
1170 csp->tailp == csp->keyboardLineBuffer)
1171 {
1172 if (csp->startTime + 30 <= time (NULL))
1173 {
1174 console_putstr (conUnitIdx, "CONSOLE: TIMEOUT\r\n");
1175 csp->readp = csp->keyboardLineBuffer;
1176 csp->tailp = csp->keyboardLineBuffer;
1177 sendConsole (conUnitIdx, 04310);
1178
1179 }
1180 }
1181
1182
1183
1184 c = ta_peek ();
1185
1186
1187 if (c == SCPE_OK)
1188 return;
1189
1190
1191 if (c < SCPE_KFLAG)
1192 {
1193 sim_printf ("impossible %d %o\n", c, c);
1194 return;
1195 }
1196
1197
1198
1199 int ch = c - SCPE_KFLAG;
1200
1201
1202 if (csp->io_mode != opc_read_mode)
1203 {
1204 if (ch == '\033' || ch == '\001')
1205 {
1206 ta_get ();
1207 csp->attn_pressed = true;
1208 }
1209 return;
1210 }
1211
1212 if (ch == '\177' || ch == '\010')
1213 {
1214 ta_get ();
1215 if (csp->tailp > csp->keyboardLineBuffer)
1216 {
1217 * csp->tailp = 0;
1218 -- csp->tailp;
1219 if (csp->echo)
1220 console_putstr (conUnitIdx, "\b \b");
1221 }
1222 return;
1223 }
1224
1225 if (ch == '\022')
1226 {
1227 ta_get ();
1228 if (csp->echo)
1229 {
1230 console_putstr (conUnitIdx, "^R\r\n");
1231 for (unsigned char * p = csp->keyboardLineBuffer; p < csp->tailp; p ++)
1232 console_putchar (conUnitIdx, (char) (*p));
1233 return;
1234 }
1235 }
1236
1237 if (ch == '\025')
1238 {
1239 ta_get ();
1240 console_putstr (conUnitIdx, "^U\r\n");
1241 csp->tailp = csp->keyboardLineBuffer;
1242 return;
1243 }
1244
1245 if (ch == '\030')
1246 {
1247 ta_get ();
1248 console_putstr (conUnitIdx, "^X\r\n");
1249 csp->tailp = csp->keyboardLineBuffer;
1250 return;
1251 }
1252
1253 if (ch == '\012' || ch == '\015')
1254 {
1255 ta_get ();
1256 if (csp->echo)
1257 console_putstr (conUnitIdx, "\r\n");
1258 sendConsole (conUnitIdx, 04000);
1259 return;
1260 }
1261
1262 if (ch == '\033' || ch == '\004' || ch == '\032')
1263 {
1264 ta_get ();
1265 console_putstr (conUnitIdx, "\r\n");
1266
1267 csp->readp = csp->keyboardLineBuffer;
1268 csp->tailp = csp->keyboardLineBuffer;
1269 sendConsole (conUnitIdx, 04310);
1270
1271 console_putstr (conUnitIdx, "CONSOLE: RELEASED\n");
1272 return;
1273 }
1274
1275 if (isprint (ch))
1276 {
1277
1278 ta_get ();
1279 if (csp->tailp >= csp->keyboardLineBuffer + sizeof (csp->keyboardLineBuffer))
1280 return;
1281
1282 * csp->tailp ++ = (unsigned char) ch;
1283 if (csp->echo)
1284 console_putchar (conUnitIdx, (char) ch);
1285 return;
1286 }
1287
1288 ta_get ();
1289 return;
1290 }
1291
1292 void consoleProcess (void)
1293 {
1294 for (int conUnitIdx = 0; conUnitIdx < (int) opc_dev.numunits; conUnitIdx ++)
1295 consoleProcessIdx (conUnitIdx);
1296 }
1297
1298
1299
1300
1301
1302
1303
1304
1305 iom_cmd_rc_t opc_iom_cmd (uint iomUnitIdx, uint chan) {
1306 iom_cmd_rc_t rc = IOM_CMD_PROCEED;
1307 #if defined(TESTING)
1308 cpu_state_t * cpup = _cpup;
1309 #endif
1310
1311 #if defined(LOCKLESS)
1312 lock_libuv ();
1313 #endif
1314
1315 iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
1316 uint con_unit_idx = get_ctlr_idx (iomUnitIdx, chan);
1317 UNIT * unitp = & opc_unit[con_unit_idx];
1318 opc_state_t * csp = console_state + con_unit_idx;
1319
1320 p->dev_code = p->IDCW_DEV_CODE;
1321 p->stati = 0;
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331 if (IS_IDCW (p)) {
1332
1333
1334 switch (p->IDCW_DEV_CMD) {
1335 case 000:
1336 sim_debug (DBG_DEBUG, & opc_dev, "%s: Status request\n", __func__);
1337 csp->io_mode = opc_no_mode;
1338 p->stati = 04000;
1339 break;
1340
1341 case 003:
1342 sim_debug (DBG_DEBUG, & tape_dev, "%s: Read BCD echoed\n", __func__);
1343 csp->io_mode = opc_read_mode;
1344 p->recordResidue --;
1345 csp->echo = true;
1346 csp->bcd = true;
1347 p->stati = 04000;
1348 break;
1349
1350 case 013:
1351 sim_debug (DBG_DEBUG, & opc_dev, "%s: Write BCD\n", __func__);
1352 p->isRead = false;
1353 csp->bcd = true;
1354 csp->io_mode = opc_write_mode;
1355 p->recordResidue --;
1356 p->stati = 04000;
1357 break;
1358
1359 case 023:
1360 sim_debug (DBG_DEBUG, & tape_dev, "%s: Read ASCII echoed\n", __func__);
1361 csp->io_mode = opc_read_mode;
1362 p->recordResidue --;
1363 csp->echo = true;
1364 csp->bcd = false;
1365 p->stati = 04000;
1366 break;
1367
1368 case 033:
1369 sim_debug (DBG_DEBUG, & opc_dev, "%s: Write ASCII\n", __func__);
1370 p->isRead = false;
1371 csp->bcd = false;
1372 csp->io_mode = opc_write_mode;
1373 p->recordResidue --;
1374 p->stati = 04000;
1375 break;
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386 case 040:
1387 sim_debug (DBG_DEBUG, & opc_dev, "%s: Reset\n", __func__);
1388 p->stati = 04000;
1389
1390
1391
1392
1393
1394
1395
1396 break;
1397
1398 case 043:
1399 sim_debug (DBG_DEBUG, & tape_dev, "%s: Read ASCII unechoed\n", __func__);
1400 csp->io_mode = opc_read_mode;
1401 p->recordResidue --;
1402 csp->echo = false;
1403 csp->bcd = false;
1404 p->stati = 04000;
1405 break;
1406
1407 case 051:
1408 sim_debug (DBG_DEBUG, & opc_dev, "%s: Alert\n", __func__);
1409 p->isRead = false;
1410 console_putstr ((int) con_unit_idx, "CONSOLE: ALERT\r\n");
1411 console_putchar ((int) con_unit_idx, '\a');
1412 p->stati = 04000;
1413 if (csp->model == m6001 && p->isPCW) {
1414 rc = IOM_CMD_DISCONNECT;
1415 goto done;
1416 }
1417 break;
1418
1419 case 057:
1420
1421
1422
1423
1424 sim_debug (DBG_DEBUG, & opc_dev, "%s: Read ID\n", __func__);
1425 p->stati = 04500;
1426 if (csp->model == m6001 && p->isPCW) {
1427 rc = IOM_CMD_DISCONNECT;
1428 goto done;
1429 }
1430 break;
1431
1432 case 060:
1433 sim_debug (DBG_DEBUG, & opc_dev, "%s: Lock\n", __func__);
1434 console_putstr ((int) con_unit_idx, "CONSOLE: LOCK\r\n");
1435 p->stati = 04000;
1436 break;
1437
1438 case 063:
1439 sim_debug (DBG_DEBUG, & opc_dev, "%s: Unlock\n", __func__);
1440 console_putstr ((int) con_unit_idx, "CONSOLE: UNLOCK\r\n");
1441 p->stati = 04000;
1442 break;
1443
1444 default:
1445 sim_debug (DBG_DEBUG, & opc_dev, "%s: Unknown command 0%o\n", __func__, p->IDCW_DEV_CMD);
1446 p->stati = 04501;
1447 rc = IOM_CMD_ERROR;
1448 goto done;
1449 }
1450 goto done;
1451 }
1452
1453
1454 switch (csp->io_mode) {
1455 case opc_no_mode:
1456 sim_warn ("%s: Unexpected IOTx\n", __func__);
1457 rc = IOM_CMD_ERROR;
1458 goto done;
1459
1460 case opc_read_mode: {
1461 if (csp->tailp != csp->keyboardLineBuffer) {
1462 sim_warn ("%s: Discarding previously buffered input.\n", __func__);
1463 }
1464 uint tally = p->DDCW_TALLY;
1465 uint daddr = p->DDCW_ADDR;
1466
1467 if (tally == 0) {
1468 tally = 4096;
1469 }
1470
1471 csp->tailp = csp->keyboardLineBuffer;
1472 csp->readp = csp->keyboardLineBuffer;
1473 csp->startTime = time (NULL);
1474 csp->tally = tally;
1475 csp->daddr = daddr;
1476 csp->unitp = unitp;
1477 csp->chan = (int) chan;
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488 if (csp->autop && (*csp->autop == 030 || *csp->autop == 031)) {
1489
1490
1491
1492
1493
1494 clear_opc_autoinput (ASSUME0, NULL);
1495 ta_flush ();
1496 sim_printf \
1497 ("\r\nScript wedged and abandoned; autoinput and typeahead buffers flushed\r\n");
1498 }
1499 rc = IOM_CMD_PENDING;
1500 goto done;
1501 }
1502
1503 case opc_write_mode: {
1504 uint tally = p->DDCW_TALLY;
1505
1506
1507
1508
1509 if (tally == 0) {
1510 tally = 4096;
1511 }
1512
1513 word36 buf[tally];
1514 iom_indirect_data_service (iomUnitIdx, chan, buf, & tally, false);
1515 p->initiate = false;
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548 char text[tally * 4 + 1];
1549 char * textp = text;
1550 word36 * bufp = buf;
1551 * textp = 0;
1552 #if !defined(__MINGW64__)
1553 newlineOff ();
1554 #endif
1555
1556
1557
1558 int escape_cnt = 0;
1559
1560 while (tally) {
1561 word36 datum = * bufp ++;
1562 tally --;
1563 if (csp->bcd) {
1564
1565 for (int i = 0; i < 6; i ++) {
1566 word36 narrow_char = datum >> 30;
1567
1568 datum = datum << 6;
1569 narrow_char &= 077;
1570 char ch = bcd_code_page [narrow_char];
1571 if (escape_cnt == 2) {
1572 console_putchar ((int) con_unit_idx, ch);
1573 * textp ++ = ch;
1574 escape_cnt = 0;
1575 } else if (ch == '!') {
1576 escape_cnt ++;
1577 } else if (escape_cnt == 1) {
1578 uint lp = (uint)narrow_char;
1579
1580
1581 if (lp == 060 || lp == 075 ) {
1582 p->stati = 04320;
1583 goto done;
1584 }
1585 if (lp == 0)
1586 lp = 1;
1587 if (lp >= 16) {
1588 console_putstr ((int) con_unit_idx, "\f");
1589
1590 } else {
1591 for (uint i = 0; i < lp; i ++) {
1592 console_putstr ((int) con_unit_idx, "\r\n");
1593
1594
1595 }
1596 }
1597 escape_cnt = 0;
1598 } else if (ch == '?') {
1599 escape_cnt = 0;
1600 } else {
1601 console_putchar ((int) con_unit_idx, ch);
1602 * textp ++ = ch;
1603 escape_cnt = 0;
1604 }
1605 }
1606 } else {
1607 for (int i = 0; i < 4; i ++) {
1608 word36 wide_char = datum >> 27;
1609
1610 datum = datum << 9;
1611 char ch = wide_char & 0x7f;
1612 if (ch != 0177 && ch != 0) {
1613 console_putchar ((int) con_unit_idx, ch);
1614 * textp ++ = ch;
1615 }
1616 }
1617 }
1618 }
1619 * textp ++ = 0;
1620
1621
1622 if (csp->autop && * csp->autop == 030) {
1623
1624
1625
1626 size_t expl = strcspn ((char *) (csp->autop + 1), "\030");
1627
1628 if (strncmp (text, (char *) (csp->autop + 1), expl) == 0) {
1629 csp->autop += expl + 2;
1630 sim_activate (& attn_unit[con_unit_idx], ACTIVATE_1SEC);
1631 }
1632 }
1633
1634 if (csp->autop && * csp->autop == 031) {
1635
1636
1637
1638 size_t expl = strcspn ((char *) (csp->autop + 1), "\031");
1639
1640 char needle [expl + 1];
1641 strncpy (needle, (char *) csp->autop + 1, expl);
1642 needle [expl] = 0;
1643 if (strstr (text, needle)) {
1644 csp->autop += expl + 2;
1645 sim_activate (& attn_unit[con_unit_idx], ACTIVATE_1SEC);
1646 }
1647 }
1648 handleRCP (con_unit_idx, text);
1649 #if !defined(__MINGW64__)
1650 newlineOn ();
1651 #endif
1652 p->stati = 04000;
1653 goto done;
1654 }
1655 }
1656
1657 done:
1658 #if defined(LOCKLESS)
1659 unlock_libuv ();
1660 #endif
1661 return rc;
1662 }
1663
1664 static t_stat opc_svc (UNIT * unitp)
1665 {
1666 int con_unit_idx = (int) OPC_UNIT_IDX (unitp);
1667 uint ctlr_port_num = 0;
1668 uint iom_unit_idx = cables->opc_to_iom[con_unit_idx][ctlr_port_num].iom_unit_idx;
1669 uint chan_num = cables->opc_to_iom[con_unit_idx][ctlr_port_num].chan_num;
1670
1671 opc_iom_cmd (iom_unit_idx, chan_num);
1672 return SCPE_OK;
1673 }
1674
1675 static t_stat opc_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr,
1676 UNUSED int val, UNUSED const void * desc)
1677 {
1678 sim_print ("%d units\n", opc_dev.numunits);
1679 return SCPE_OK;
1680 }
1681
1682 static t_stat opc_set_nunits (UNUSED UNIT * uptr, int32 UNUSED value,
1683 const char * cptr, UNUSED void * desc)
1684 {
1685 if (! cptr)
1686 return SCPE_ARG;
1687 int n = atoi (cptr);
1688 if (n < 1 || n > N_OPC_UNITS_MAX)
1689 return SCPE_ARG;
1690 opc_dev.numunits = (uint32) n;
1691 return SCPE_OK;
1692 }
1693
1694 static config_value_list_t cfg_on_off[] =
1695 {
1696 { "off", 0 },
1697 { "on", 1 },
1698 { "disable", 0 },
1699 { "enable", 1 },
1700 { NULL, 0 }
1701 };
1702
1703 static config_value_list_t cfg_model[] =
1704 {
1705 { "m6001", m6001 },
1706 { "m6004", m6004 },
1707 { "m6601", m6601 },
1708 { NULL, 0 }
1709 };
1710
1711 static config_list_t opc_config_list[] =
1712 {
1713 { "autoaccept", 0, 1, cfg_on_off },
1714 { "noempty", 0, 1, cfg_on_off },
1715 { "attn_flush", 0, 1, cfg_on_off },
1716 { "model", 1, 0, cfg_model },
1717 { NULL, 0, 0, NULL }
1718 };
1719
1720 static t_stat opc_set_config (UNUSED UNIT * uptr, UNUSED int32 value,
1721 const char * cptr, UNUSED void * desc)
1722 {
1723 int devUnitIdx = (int) OPC_UNIT_IDX (uptr);
1724 opc_state_t * csp = console_state + devUnitIdx;
1725
1726 config_state_t cfg_state = { NULL, NULL };
1727
1728 for (;;)
1729 {
1730 int64_t v;
1731 int rc = cfg_parse (__func__, cptr, opc_config_list,
1732 & cfg_state, & v);
1733 if (rc == -1)
1734 break;
1735
1736 if (rc == -2)
1737 {
1738 cfg_parse_done (& cfg_state);
1739 return SCPE_ARG;
1740 }
1741 const char * p = opc_config_list[rc].name;
1742
1743 if (strcmp (p, "autoaccept") == 0)
1744 {
1745 csp->autoaccept = (int) v;
1746 continue;
1747 }
1748
1749 if (strcmp (p, "noempty") == 0)
1750 {
1751 csp->noempty = (int) v;
1752 continue;
1753 }
1754
1755 if (strcmp (p, "attn_flush") == 0)
1756 {
1757 csp->attn_flush = (int) v;
1758 continue;
1759 }
1760
1761 if (strcmp (p, "model") == 0)
1762 {
1763 csp->model = (enum console_model) v;
1764 continue;
1765 }
1766
1767 sim_warn ("error: opc_set_config: Invalid cfg_parse rc <%d>\n",
1768 rc);
1769 cfg_parse_done (& cfg_state);
1770 return SCPE_ARG;
1771 }
1772 cfg_parse_done (& cfg_state);
1773 return SCPE_OK;
1774 }
1775
1776 static t_stat opc_show_config (UNUSED FILE * st, UNUSED UNIT * uptr,
1777 UNUSED int val, UNUSED const void * desc)
1778 {
1779 int devUnitIdx = (int) OPC_UNIT_IDX (uptr);
1780 opc_state_t * csp = console_state + devUnitIdx;
1781 sim_msg ("flags : ");
1782 sim_msg ("autoaccept=%d, ", csp->autoaccept);
1783 sim_msg ("noempty=%d, ", csp->noempty);
1784 sim_msg ("attn_flush=%d", csp->attn_flush);
1785 return SCPE_OK;
1786 }
1787
1788 static t_stat opc_show_device_name (UNUSED FILE * st, UNIT * uptr,
1789 UNUSED int val, UNUSED const void * desc)
1790 {
1791 int n = (int) OPC_UNIT_IDX (uptr);
1792 if (n < 0 || n >= N_OPC_UNITS_MAX)
1793 return SCPE_ARG;
1794 sim_printf("name : OPC%d", n);
1795 return SCPE_OK;
1796 }
1797
1798 static t_stat opc_set_device_name (UNIT * uptr, UNUSED int32 value,
1799 const char * cptr, UNUSED void * desc)
1800 {
1801 int n = (int) OPC_UNIT_IDX (uptr);
1802 if (n < 0 || n >= N_OPC_UNITS_MAX)
1803 return SCPE_ARG;
1804 if (cptr)
1805 {
1806 strncpy (console_state[n].device_name, cptr, MAX_DEV_NAME_LEN-1);
1807 console_state[n].device_name[MAX_DEV_NAME_LEN-1] = 0;
1808 }
1809 else
1810 console_state[n].device_name[0] = 0;
1811 return SCPE_OK;
1812 }
1813
1814 static t_stat opc_set_console_port (UNIT * uptr, UNUSED int32 value,
1815 const char * cptr, UNUSED void * desc)
1816 {
1817 int dev_idx = (int) OPC_UNIT_IDX (uptr);
1818 if (dev_idx < 0 || dev_idx >= N_OPC_UNITS_MAX)
1819 return SCPE_ARG;
1820
1821 if (cptr)
1822 {
1823 int port = atoi (cptr);
1824 if (port < 0 || port > 65535)
1825 return SCPE_ARG;
1826 console_state[dev_idx].console_access.port = port;
1827 if (port != 0)
1828 sim_msg ("[OPC emulation: TELNET server port set to %d]\r\n", (int)port);
1829 }
1830 else
1831 console_state[dev_idx].console_access.port = 0;
1832 return SCPE_OK;
1833 }
1834
1835 static t_stat opc_show_console_port (UNUSED FILE * st, UNIT * uptr,
1836 UNUSED int val, UNUSED const 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 if (console_state[dev_idx].console_access.port)
1842 sim_printf("port : %d", console_state[dev_idx].console_access.port);
1843 else
1844 sim_printf("port : disabled");
1845 return SCPE_OK;
1846 }
1847
1848 static t_stat opc_set_console_address (UNIT * uptr, UNUSED int32 value,
1849 const char * cptr, UNUSED void * desc)
1850 {
1851 int dev_idx = (int) OPC_UNIT_IDX (uptr);
1852 if (dev_idx < 0 || dev_idx >= N_OPC_UNITS_MAX)
1853 return SCPE_ARG;
1854
1855 if (console_state[dev_idx].console_access.address)
1856 {
1857 FREE (console_state[dev_idx].console_access.address);
1858 console_state[dev_idx].console_access.address = NULL;
1859 }
1860
1861 if (cptr)
1862 {
1863 console_state[dev_idx].console_access.address = strdup (cptr);
1864 if (!console_state[dev_idx].console_access.address)
1865 {
1866 (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1867 __func__, __FILE__, __LINE__);
1868 #if defined(USE_BACKTRACE)
1869 # if defined(SIGUSR2)
1870 (void)raise(SIGUSR2);
1871
1872 # endif
1873 #endif
1874 abort();
1875 }
1876 sim_msg ("\r[OPC emulation: OPC%d server address set to %s]\r\n",
1877 dev_idx, console_state[dev_idx].console_access.address);
1878 }
1879
1880 return SCPE_OK;
1881 }
1882
1883 static t_stat opc_show_console_address (UNUSED FILE * st, UNIT * uptr,
1884 UNUSED int val, UNUSED const void * desc)
1885 {
1886 int dev_idx = (int) OPC_UNIT_IDX (uptr);
1887 if (dev_idx < 0 || dev_idx >= N_OPC_UNITS_MAX)
1888 return SCPE_ARG;
1889 if (console_state[dev_idx].console_access.address)
1890 sim_printf("address : %s", console_state[dev_idx].console_access.address);
1891 else
1892 sim_printf("address : any");
1893 return SCPE_OK;
1894 }
1895
1896 static t_stat opc_set_console_pw (UNIT * uptr, UNUSED int32 value,
1897 const char * cptr, UNUSED void * desc)
1898 {
1899 long dev_idx = (int) OPC_UNIT_IDX (uptr);
1900 if (dev_idx < 0 || dev_idx >= N_OPC_UNITS_MAX)
1901 return SCPE_ARG;
1902
1903 if (cptr && (strlen(cptr) > 0))
1904 {
1905 char token[strlen (cptr)+1];
1906 int rc = sscanf (cptr, "%s", token);
1907 if (rc != 1)
1908 return SCPE_ARG;
1909 if (strlen (token) > PW_SIZE)
1910 return SCPE_ARG;
1911 strcpy (console_state[dev_idx].console_access.pw, token);
1912 }
1913 else
1914 {
1915 sim_msg ("no password\n");
1916 console_state[dev_idx].console_access.pw[0] = 0;
1917 }
1918
1919 return SCPE_OK;
1920 }
1921
1922 static t_stat opc_show_console_pw (UNUSED FILE * st, UNIT * uptr,
1923 UNUSED int val, UNUSED const void * desc)
1924 {
1925 int dev_idx = (int) OPC_UNIT_IDX (uptr);
1926 if (dev_idx < 0 || dev_idx >= N_OPC_UNITS_MAX)
1927 return SCPE_ARG;
1928 sim_printf("password : %s", console_state[dev_idx].console_access.pw);
1929 return SCPE_OK;
1930 }
1931
1932 static void console_putstr (int conUnitIdx, char * str)
1933 {
1934 size_t l = strlen (str);
1935 for (size_t i = 0; i < l; i ++)
1936 sim_putchar (str[i]);
1937 opc_state_t * csp = console_state + conUnitIdx;
1938 if (csp->console_access.loggedOn)
1939 accessStartWrite (csp->console_access.client, str,
1940 (ssize_t) l);
1941 }
1942
1943 static void consolePutchar0 (int conUnitIdx, char ch) {
1944 opc_state_t * csp = console_state + conUnitIdx;
1945 sim_putchar (ch);
1946 if (csp->console_access.loggedOn)
1947 accessStartWrite (csp->console_access.client, & ch, 1);
1948 }
1949
1950 static void console_putchar (int conUnitIdx, char ch) {
1951 opc_state_t * csp = console_state + conUnitIdx;
1952 if (csp->escapeSequence) {
1953 csp->escapeSequence = false;
1954 if (ch == '1') {
1955 if (csp->carrierPosition >= 1 && csp->carrierPosition <= 256) {
1956 csp->tabStops[csp->carrierPosition] = true;
1957 }
1958 } else if (ch == '2') {
1959 (void)memset (csp->tabStops, 0, sizeof (csp->tabStops));
1960 } else {
1961 sim_warn ("Unrecognized escape sequence \\033\\%03o\r\n", ch);
1962 }
1963 } else if (isprint (ch)) {
1964 consolePutchar0 (conUnitIdx, ch);
1965 csp->carrierPosition ++;
1966 } else if (ch == '\t') {
1967 while (csp->carrierPosition < bufsize - 1) {
1968 consolePutchar0 (conUnitIdx, ' ');
1969 csp->carrierPosition ++;
1970 if (csp->tabStops[csp->carrierPosition])
1971 break;
1972 }
1973 } else if (ch == '\b') {
1974 consolePutchar0 (conUnitIdx, ch);
1975 csp->carrierPosition --;
1976 } else if (ch == '\f' || ch == '\r') {
1977 consolePutchar0 (conUnitIdx, ch);
1978 csp->carrierPosition = 1;
1979 } else if (ch == '\033') {
1980 csp->escapeSequence = true;
1981 } else {
1982 consolePutchar0 (conUnitIdx, ch);
1983 }
1984 }
1985
1986 static void consoleConnectPrompt (uv_tcp_t * client)
1987 {
1988 accessStartWriteStr (client, "password: \r\n");
1989 uv_access * console_access = (uv_access *) client->data;
1990 console_access->pwPos = 0;
1991 }
1992
1993 void startRemoteConsole (void)
1994 {
1995 for (int conUnitIdx = 0; conUnitIdx < N_OPC_UNITS_MAX; conUnitIdx ++)
1996 {
1997 console_state[conUnitIdx].console_access.connectPrompt = consoleConnectPrompt;
1998 console_state[conUnitIdx].console_access.connected = NULL;
1999 console_state[conUnitIdx].console_access.useTelnet = true;
2000 #if defined(CONSOLE_FIX)
2001 # if defined(LOCKLESS)
2002 lock_libuv ();
2003 # endif
2004 #endif
2005 uv_open_access (& console_state[conUnitIdx].console_access);
2006 #if defined(CONSOLE_FIX)
2007 # if defined(LOCKLESS)
2008 unlock_libuv ();
2009 # endif
2010 #endif
2011 }
2012 }