This source file includes following definitions.
- pun_init
- pun_reset
- remove_spaces
- log_char_matrix_pattern
- search_glyph_patterns
- get_lace_char
- scan_card_for_glyphs
- create_punch_file
- write_punch_files
- log_card
- print_event
- print_state
- print_transition
- clear_card_cache
- save_card_in_cache
- transition_state
- do_state_idle
- do_state_starting_job
- do_state_scan_card_for_glyphs
- do_state_end_of_header
- do_state_cache_card
- do_state_end_of_deck
- do_state_end_of_job
- unexpected_event
- parse_card
- punWriteRecord
- pun_iom_cmd
- pun_show_nunits
- pun_set_nunits
- pun_show_device_name
- pun_set_device_name
- pun_set_path
- pun_show_path
- pun_set_config
- pun_show_config
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 <ctype.h>
35 #include <signal.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <sys/stat.h>
40
41 #include "dps8.h"
42 #include "dps8_iom.h"
43 #include "dps8_crdpun.h"
44 #include "dps8_sys.h"
45 #include "dps8_cable.h"
46 #include "dps8_cpu.h"
47 #include "dps8_scu.h"
48 #include "dps8_faults.h"
49 #include "dps8_utils.h"
50 #include "utfile.h"
51 #include "safemove.h"
52
53 #if defined(__MINGW64__) || defined(__MINGW32__)
54 # include "bsd_random.h"
55 # define random bsd_random
56 # define srandom bsd_srandom
57 #endif
58
59 #define DBG_CTR 1
60
61 #if defined(FREE)
62 # undef FREE
63 #endif
64 #define FREE(p) do \
65 { \
66 free((p)); \
67 (p) = NULL; \
68 } while(0)
69
70
71
72
73
74 #define N_PUN_UNITS 1
75
76 static t_stat pun_reset (DEVICE * dptr);
77 static t_stat pun_show_nunits (FILE *st, UNIT *uptr, int val, const void *desc);
78 static t_stat pun_set_nunits (UNIT * uptr, int32 value, const char * cptr, void * desc);
79 static t_stat pun_show_device_name (FILE *st, UNIT *uptr, int val, const void *desc);
80 static t_stat pun_set_device_name (UNIT * uptr, int32 value, const char * cptr, void * desc);
81 static t_stat pun_show_path (UNUSED FILE * st, UNIT * uptr, UNUSED int val,
82 UNUSED const void * desc);
83 static t_stat pun_set_path (UNUSED UNIT * uptr, UNUSED int32 value, const UNUSED char * cptr,
84 UNUSED void * desc);
85 static t_stat pun_set_config (UNUSED UNIT * uptr, UNUSED int32 value, const char * cptr,
86 UNUSED void * desc);
87 static t_stat pun_show_config (UNUSED FILE * st, UNUSED UNIT * uptr, UNUSED int val,
88 UNUSED const void * desc);
89
90 #define UNIT_FLAGS ( UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE )
91
92 UNIT pun_unit [N_PUN_UNITS_MAX] =
93 {
94 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
95 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
96 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
97 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
98 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
99 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
100 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
101 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
102 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
103 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
104 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
105 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
106 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
107 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
108 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
109 {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL}
110 };
111
112 #define PUN_UNIT_NUM(uptr) ((uptr) - pun_unit)
113
114 static DEBTAB pun_dt [] =
115 {
116 { "NOTIFY", DBG_NOTIFY, NULL },
117 { "INFO", DBG_INFO, NULL },
118 { "ERR", DBG_ERR, NULL },
119 { "WARN", DBG_WARN, NULL },
120 { "DEBUG", DBG_DEBUG, NULL },
121 { "ALL", DBG_ALL, NULL },
122 { NULL, 0, NULL }
123 };
124
125 #define UNIT_WATCH UNIT_V_UF
126
127 static MTAB pun_mod [] =
128 {
129 #if !defined(SPEED)
130 { UNIT_WATCH, 1, "WATCH", "WATCH", 0, 0, NULL, NULL },
131 { UNIT_WATCH, 0, "NOWATCH", "NOWATCH", 0, 0, NULL, NULL },
132 #endif
133 {
134 MTAB_XTD | MTAB_VDV | \
135 MTAB_NMO | MTAB_VALR,
136 0,
137 "NUNITS",
138 "NUNITS",
139 pun_set_nunits,
140 pun_show_nunits,
141 "Number of PUN units in the system",
142 NULL
143 },
144 {
145 MTAB_XTD | MTAB_VUN | \
146 MTAB_VALR | MTAB_NC,
147 0,
148 "NAME",
149 "NAME",
150 pun_set_device_name,
151 pun_show_device_name,
152 "Set the punch device name",
153 NULL
154 },
155 {
156 MTAB_XTD | MTAB_VDV | MTAB_NMO | \
157 MTAB_VALR | MTAB_NC,
158 0,
159 "PATH",
160 "PATH",
161 pun_set_path,
162 pun_show_path,
163 "Path to card punch directories",
164 NULL
165 },
166 {
167 MTAB_XTD | MTAB_VUN,
168 0,
169 (char *) "CONFIG",
170 (char *) "CONFIG",
171 pun_set_config,
172 pun_show_config,
173 NULL,
174 NULL,
175 },
176
177 { 0, 0, NULL, NULL, 0, 0, NULL, NULL }
178 };
179
180 DEVICE pun_dev = {
181 "PUN",
182 pun_unit,
183 NULL,
184 pun_mod,
185 N_PUN_UNITS,
186 10,
187 24,
188 1,
189 8,
190 36,
191 NULL,
192 NULL,
193 pun_reset,
194 NULL,
195 NULL,
196 NULL,
197 NULL,
198 DEV_DEBUG,
199 0,
200 pun_dt,
201 NULL,
202 NULL,
203 NULL,
204 NULL,
205 NULL,
206 NULL,
207 NULL
208 };
209
210 static config_value_list_t cfg_on_off[] =
211 {
212 { "off", 0 },
213 { "on", 1 },
214 { "disable", 0 },
215 { "enable", 1 },
216 { NULL, 0 }
217 };
218
219 static config_list_t pun_config_list[] =
220 {
221 { "logcards", 0, 1, cfg_on_off },
222 { NULL, 0, 0, NULL }
223 };
224
225 #define WORDS_PER_CARD 27
226 #define MAX_GLYPH_BUFFER_LEN 1024
227 #define CARD_COL_COUNT 80
228 #define NIBBLES_PER_COL 3
229 #define GLYPHS_PER_CARD 22
230 #define CHAR_MATRIX_BYTES 5
231
232 enum parse_state {
233 Idle, StartingJob, PunchGlyphLookup, EndOfHeader, CacheCard, EndOfDeck, EndOfJob
234 };
235
236 enum parse_event {
237 NoEvent, BannerCard, EndOfDeckCard, Card, Done
238 };
239
240 typedef struct card_cache_node CARD_CACHE_ENTRY;
241
242 struct card_cache_node
243 {
244 word12 tally;
245 word36 card_data[WORDS_PER_CARD];
246 CARD_CACHE_ENTRY *next_entry;
247 };
248
249 typedef struct
250 {
251 char device_name[MAX_DEV_NAME_LEN];
252 int punfile_raw;
253 bool log_cards;
254 enum parse_state current_state;
255 char raw_file_name [SIR_MAXPATH + 1];
256 char filename [SIR_MAXPATH];
257 char glyph_buffer[MAX_GLYPH_BUFFER_LEN];
258 CARD_CACHE_ENTRY *first_cached_card;
259 CARD_CACHE_ENTRY *last_cached_card;
260 enum pun_mode { punNoMode, punWrBin } ioMode;
261 } pun_state_t ;
262
263 static pun_state_t pun_state[N_PUN_UNITS_MAX];
264 static char pun_path_prefix[SIR_MAXPATH - 68];
265
266
267
268
269
270
271
272 void pun_init (void)
273 {
274 (void)memset (pun_path_prefix, 0, sizeof (pun_path_prefix));
275 (void)memset (pun_state, 0, sizeof (pun_state));
276 for (int i = 0; i < N_PUN_UNITS_MAX; i ++)
277 {
278 pun_state [i] . punfile_raw = -1;
279 pun_state [i] . current_state = Idle;
280 pun_state [i] . filename [0] = 0;
281 }
282 }
283
284 static t_stat pun_reset (UNUSED DEVICE * dptr)
285 {
286 return SCPE_OK;
287 }
288
289
290
291
292
293
294
295
296
297
298
299
300
301 static word36 eodCard [WORDS_PER_CARD] =
302 {
303 0000500000000llu,
304 0000000000000llu,
305 0000000000000llu,
306 0000000000000llu,
307 0000000000000llu,
308 0000000002000llu,
309 0240024002400llu,
310 0370000000000llu,
311 0372121122104llu,
312 0210437370000llu,
313 0000000210021llu,
314 0002100210037llu,
315 0000000001621llu,
316 0212521252125llu,
317 0373700000000llu,
318 0371602210421llu,
319 0102137370000llu,
320 0000021002500llu,
321 0250025003700llu,
322 0000000000000llu,
323 0000000000000llu,
324 0000000000000llu,
325 0000000000000llu,
326 0000000000000llu,
327 0000000000000llu,
328 0000000000000llu,
329 0000000050000llu
330 };
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345 static word36 bannerCard [WORDS_PER_CARD] =
346 {
347 0000500000000llu,
348 0770077047704llu,
349 0770477000000llu,
350 0000000770477llu,
351 0047704770077llu,
352 0000000007700llu,
353 0770477047704llu,
354 0770000000000llu,
355 0007704770477llu,
356 0047700770000llu,
357 0000077007704llu,
358 0770477047700llu,
359 0000000000077llu,
360 0047704770477llu,
361 0007700000000llu,
362 0770077047704llu,
363 0770477000000llu,
364 0000000770477llu,
365 0047704770077llu,
366 0000000007700llu,
367 0770477047704llu,
368 0770000000000llu,
369 0007704770477llu,
370 0047700770000llu,
371 0000077007704llu,
372 0770477047700llu,
373 0000000050000llu
374 };
375
376
377
378
379
380
381
382
383 #define NUM_GLYPH_CHAR_PATTERNS 45
384
385 static uint8 glyph_char_patterns [NUM_GLYPH_CHAR_PATTERNS][CHAR_MATRIX_BYTES] =
386 {
387
388 { 037, 037, 037, 037, 037 },
389
390 { 000, 000, 000, 000, 000 },
391
392 { 000, 003, 003, 003, 000 },
393
394 { 021, 000, 012, 000, 004 },
395
396 { 037, 024, 024, 024, 037 },
397
398 { 037, 025, 025, 025, 012 },
399
400 { 037, 021, 021, 021, 021 },
401
402 { 037, 021, 021, 021, 016 },
403
404 { 037, 025, 025, 025, 021 },
405
406 { 037, 024, 024, 024, 020 },
407
408 { 037, 021, 021, 025, 027 },
409
410 { 037, 004, 004, 004, 037 },
411
412 { 021, 021, 037, 021, 021 },
413
414 { 003, 001, 001, 001, 037 },
415
416 { 037, 004, 004, 012, 021 },
417
418 { 037, 001, 001, 001, 001 },
419
420 { 037, 010, 004, 010, 037 },
421
422 { 037, 010, 004, 002, 037 },
423
424 { 037, 021, 021, 021, 037 },
425
426 { 037, 024, 024, 024, 034 },
427
428 { 037, 021, 025, 023, 037 },
429
430 { 037, 024, 024, 026, 035 },
431
432 { 035, 025, 025, 025, 027 },
433
434 { 020, 020, 037, 020, 020 },
435
436 { 037, 001, 001, 001, 037 },
437
438 { 030, 006, 001, 006, 030 },
439
440 { 037, 002, 004, 002, 037 },
441
442 { 021, 012, 004, 012, 021 },
443
444 { 020, 010, 007, 010, 020 },
445
446 { 021, 027, 025, 035, 021 },
447
448 { 016, 021, 021, 021, 016 },
449
450 { 000, 010, 000, 037, 000 },
451
452 { 023, 025, 025, 025, 035 },
453
454 { 021, 025, 025, 025, 037 },
455
456 { 034, 004, 004, 004, 037 },
457
458 { 035, 025, 025, 025, 022 },
459
460 { 037, 005, 005, 005, 007 },
461
462 { 020, 021, 022, 024, 030 },
463
464 { 012, 025, 025, 025, 012 },
465
466 { 034, 024, 024, 024, 037 },
467
468 { 001, 001, 001, 001, 001 },
469
470 { 000, 004, 004, 004, 000 },
471
472 { 000, 004, 012, 021, 000 },
473
474 { 000, 021, 012, 004, 000 },
475
476 { 001, 002, 004, 010, 020 }
477 };
478
479 static char glyph_chars [NUM_GLYPH_CHAR_PATTERNS] =
480 {
481 '*', ' ', '.', '>', 'A', 'B', 'C', 'D', 'E', 'F',
482 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
483 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
484 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
485 '_', '-', '(', ')', '/'
486 };
487
488 static uint8 glyph_char_word_offset [11] =
489 {
490 24, 22, 19, 17, 15, 12, 10, 8, 5, 3, 1
491 };
492
493 static uint8 glyph_nibble_offset [11] =
494 {
495 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2
496 };
497
498 static void remove_spaces(char *str)
499 {
500 int src = 0;
501 int dest = 0;
502 while (str[src])
503 {
504 if (str[src] != ' ')
505 {
506 str[dest++] = str[src];
507 }
508 src++;
509 }
510 str[dest] = 0;
511 }
512
513 static void log_char_matrix_pattern(uint8* char_matrix)
514 {
515 sim_print("\r\nChar Matrix\r\n");
516 for (uint col_offset = 0; col_offset < CHAR_MATRIX_BYTES; col_offset++)
517 {
518 sim_printf(" %03o\r\n", char_matrix[col_offset]);
519 }
520
521 sim_print("\r\n");
522 for (uint row = 0; row < 5; row++)
523 {
524 for (uint col = 0; col < CHAR_MATRIX_BYTES; col++)
525 {
526 if ((char_matrix[col] >> (4 - row)) & 0x1)
527 {
528 sim_print("*");
529 }
530 else
531 {
532 sim_print(" ");
533 }
534 }
535 sim_print("\r\n");
536 }
537 sim_print("\r\n");
538
539 }
540
541 static char search_glyph_patterns(uint8* matrix)
542 {
543 for (int pattern = 0; pattern < NUM_GLYPH_CHAR_PATTERNS; pattern++)
544 {
545 if (memcmp(matrix, &glyph_char_patterns[pattern], CHAR_MATRIX_BYTES) == 0)
546 {
547 return glyph_chars[pattern];
548 }
549 }
550
551 sim_warn("*** Warning: Punch found unknown block character pattern\r\n");
552 log_char_matrix_pattern(matrix);
553
554 return ' ';
555 }
556
557 static char get_lace_char(word36* buffer, uint char_pos)
558 {
559 if (char_pos >= GLYPHS_PER_CARD)
560 {
561 sim_warn("*** Error: Attempt to read punch block character out of range (%u)\r\n", char_pos);
562 return 0;
563 }
564
565 bool top = char_pos < 11;
566 uint char_offset = (char_pos < 11) ? char_pos : char_pos - 11;
567 uint word_offset = glyph_char_word_offset[char_offset];
568 uint nibble_offset = glyph_nibble_offset[char_offset];
569 word12 col_buffer[5];
570
571
572
573
574 for (uint col_offset = 0; col_offset < 5; col_offset++)
575 {
576 col_buffer[4 - col_offset] = (buffer[word_offset] >> (nibble_offset * 12)) & 0xFFF;
577 if (nibble_offset == 0)
578 {
579 nibble_offset = 2;
580 word_offset++;
581 }
582 else
583 {
584 nibble_offset--;
585 }
586 }
587
588
589 uint8 char_matrix[CHAR_MATRIX_BYTES];
590
591 for (uint col_offset = 0; col_offset < CHAR_MATRIX_BYTES; col_offset++)
592 {
593 char_matrix[col_offset] = (col_buffer[col_offset] >> (top ? 6 : 0)) & 0x1F;
594 }
595
596 char c = search_glyph_patterns(char_matrix);
597
598 return c;
599 }
600
601 static void scan_card_for_glyphs(pun_state_t * state, word36* buffer)
602 {
603 for (uint c_pos = 0; c_pos < 22; c_pos++)
604 {
605 char c = get_lace_char(buffer, c_pos);
606 uint current_length = (uint)strlen(state -> glyph_buffer);
607 if (current_length < (sizeof(state -> glyph_buffer) - 1))
608 {
609 state -> glyph_buffer[current_length++] = c;
610 state -> glyph_buffer[current_length] = 0;
611 }
612 }
613 }
614
615 static void create_punch_file(pun_state_t * state)
616 {
617 char template [4 * SIR_MAXPATH+1];
618
619 if (state -> punfile_raw != -1)
620 {
621 sim_warn ("*** Error: Punch file already open when attempting to create new file, closing old file!\r\n");
622 close (state -> punfile_raw);
623 state -> punfile_raw = -1;
624 }
625
626 if (pun_path_prefix [0])
627 {
628 char dir_path [SIR_MAXPATH];
629 (void)sprintf (dir_path, "%s%s", pun_path_prefix, state -> device_name);
630 struct stat st = {0};
631 if (stat (dir_path, & st) == -1)
632 {
633 if (x_mkdir_p (dir_path) == -1)
634 {
635 sim_warn ("Failed to create punch output directory '%s': %s (Error %d)\r\n",
636 dir_path, xstrerror_l (errno), errno);
637 return;
638 }
639 }
640
641 (void)sprintf (template, "%s/%s.spool.%s.XXXXXX.pun.OPEN",
642 dir_path,
643 state -> device_name,
644 state -> raw_file_name);
645 }
646 else
647 {
648 (void)sprintf (template, "%s.spool.%s.XXXXXX.pun.OPEN",
649 state -> device_name,
650 state -> raw_file_name);
651 }
652
653 state -> punfile_raw = utfile_mkstemps(template, 9);
654 (void)snprintf (state -> filename, sizeof (state -> filename), "%s", template);
655
656 if (state -> punfile_raw < 0)
657 perror("creating punch '.pun' file\r\n");
658
659 }
660
661 static void write_punch_files (pun_state_t * state, word36* in_buffer, int word_count,
662 bool banner_card)
663 {
664 if (word_count != WORDS_PER_CARD)
665 {
666 sim_warn ("Unable to interpret punch buffer due to wrong length, not writing output!\r\n");
667 return;
668 }
669
670 uint8 byte_buffer[120];
671 (void)memset(&byte_buffer, 0, sizeof(byte_buffer));
672
673 word12 word12_buffer[80];
674 (void)memset(&word12_buffer, 0, sizeof(word12_buffer));
675
676 for (int nibble_index = 0; nibble_index < (CARD_COL_COUNT * NIBBLES_PER_COL); nibble_index++)
677 {
678 int byte_offset = nibble_index / 2;
679 int word36_offset = nibble_index / 9;
680 int nibble_offset = nibble_index % 9;
681 uint8 nibble = (in_buffer[word36_offset] >> ((8 - nibble_offset) * 4)) & 0xF;
682
683 if (nibble_index & 0x1)
684 byte_buffer [byte_offset] |= nibble;
685 else
686 byte_buffer [byte_offset] |= (nibble << 4);
687
688 int word12_offset = nibble_index / 3;
689 int word12_nibble_offset = 2 - (nibble_index % 3);
690
691 word12_buffer[word12_offset] |= nibble << (word12_nibble_offset * 4);
692 }
693
694 if (state->log_cards)
695 {
696 sim_printf("word12_buffer:\r\n");
697 for (uint i = 0; i < 80; i++)
698 {
699 sim_printf(" %04o\r\n", word12_buffer[i]);
700 }
701 sim_printf("\r\n");
702 }
703
704 if (state -> punfile_raw >= 0)
705 {
706 if (write (state -> punfile_raw, byte_buffer,
707 sizeof(byte_buffer)) != sizeof(byte_buffer)) {
708 sim_warn ("Failed to write to .raw card punch file!\r\n");
709 perror("Writing .raw punch file\r\n");
710 }
711 }
712
713 }
714
715 static void log_card(word12 tally, word36 * buffer)
716 {
717 sim_printf ("tally %d\r\n", tally);
718
719 for (uint i = 0; i < tally; i ++)
720 {
721 sim_printf (" %012llo\r\n", (unsigned long long)buffer [i]);
722 }
723 sim_printf ("\r\n");
724
725 if (tally != WORDS_PER_CARD)
726 {
727 sim_warn("Unable to log punch card, tally is not 27 (%d)\r\n", tally);
728 return;
729 }
730
731 for (uint row = 0; row < 12; row ++)
732 {
733 for (uint col = 0; col < 80; col ++)
734 {
735
736 uint wordno = col / 3;
737 uint fieldno = col % 3;
738 word1 bit = getbits36_1 (buffer [wordno], fieldno * 12 + row);
739 if (bit)
740 sim_printf ("*");
741 else
742 sim_printf (" ");
743 }
744 sim_printf ("\r\n");
745 }
746 sim_printf ("\r\n");
747
748 for (uint row = 0; row < 12; row ++)
749 {
750
751 for (int col = 79; col >= 0; col --)
752 {
753
754 uint wordno = (uint) col / 3;
755 uint fieldno = (uint) col % 3;
756 word1 bit = getbits36_1 (buffer [wordno], fieldno * 12 + row);
757 if (bit)
758 sim_printf ("*");
759 else
760 sim_printf (" ");
761 }
762 sim_printf ("\r\n");
763 }
764 sim_printf ("\r\n");
765 }
766
767 static void print_event(enum parse_event event)
768 {
769 switch (event)
770 {
771 case NoEvent:
772 sim_warn("[No Event]");
773 break;
774 case BannerCard:
775 sim_warn("[Banner Card]");
776 break;
777 case EndOfDeckCard:
778 sim_warn("[End Of Deck Card]");
779 break;
780 case Card:
781 sim_warn("[Card]");
782 break;
783 case Done:
784 sim_warn("[Done]");
785 break;
786 default:
787 sim_warn("[unknown event %d]", event);
788 break;
789 }
790 }
791
792 static void print_state(enum parse_state state)
793 {
794 switch (state)
795 {
796 case Idle:
797 sim_warn("[Idle]");
798 break;
799 case StartingJob:
800 sim_warn("[Starting Job]");
801 break;
802 case PunchGlyphLookup:
803 sim_warn("[Punch Glyph Lookup]");
804 break;
805 case EndOfHeader:
806 sim_warn("[End Of Header]");
807 break;
808 case CacheCard:
809 sim_warn("[Cache Card]");
810 break;
811 case EndOfDeck:
812 sim_warn("[End Of Deck]");
813 break;
814 case EndOfJob:
815 sim_warn("[End Of Job]");
816 break;
817 default:
818 sim_warn("[unknown state %d]", state);
819 break;
820 }
821 }
822
823 static void print_transition(enum parse_state old_state, enum parse_event event,
824 enum parse_state new_state)
825 {
826 sim_warn(">>> Punch Transition: ");
827 print_event(event);
828 sim_warn(" = ");
829 print_state(old_state);
830 sim_warn(" -> ");
831 print_state(new_state);
832 sim_warn("\r\n");
833 }
834
835 static void clear_card_cache(pun_state_t * state)
836 {
837 CARD_CACHE_ENTRY *current_entry = state -> first_cached_card;
838 while (current_entry != NULL)
839 {
840 CARD_CACHE_ENTRY *old_entry = current_entry;
841 current_entry = current_entry->next_entry;
842 FREE(old_entry);
843 }
844
845 state -> first_cached_card = NULL;
846 state -> last_cached_card = NULL;
847 }
848
849 static void save_card_in_cache(pun_state_t * state, word12 tally, word36 * card_buffer)
850 {
851 CARD_CACHE_ENTRY *new_entry = malloc(sizeof(CARD_CACHE_ENTRY));
852 if (!new_entry)
853 {
854 (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
855 __func__, __FILE__, __LINE__);
856 #if defined(USE_BACKTRACE)
857 # if defined(SIGUSR2)
858 (void)raise(SIGUSR2);
859
860 # endif
861 #endif
862 abort();
863 }
864
865 new_entry -> tally = tally;
866 memcpy(&new_entry -> card_data, card_buffer, sizeof(word36) * tally);
867 new_entry -> next_entry = NULL;
868
869 if (state -> first_cached_card == NULL)
870 {
871 state -> first_cached_card = new_entry;
872 state -> last_cached_card = new_entry;
873 }
874 else
875 {
876 state -> last_cached_card -> next_entry = new_entry;
877 state -> last_cached_card = new_entry;
878 }
879 }
880
881 static void transition_state(enum parse_event event, pun_state_t * state,
882 enum parse_state new_state)
883 {
884 if (state -> log_cards)
885 {
886 print_transition(state -> current_state, event, new_state);
887 }
888
889 state -> current_state = new_state;
890 }
891
892 static enum parse_event do_state_idle(enum parse_event event, pun_state_t * state)
893 {
894 transition_state(event, state, Idle);
895
896
897
898 return NoEvent;
899 }
900
901 static enum parse_event do_state_starting_job(enum parse_event event,
902 pun_state_t * state, word12 tally,
903 word36 * card_buffer)
904 {
905 transition_state(event, state, StartingJob);
906
907 clear_card_cache(state);
908 state -> glyph_buffer[0] = 0;
909 save_card_in_cache(state, tally, card_buffer);
910
911 return NoEvent;
912 }
913
914 static enum parse_event do_state_scan_card_for_glyphs(enum parse_event event,
915 pun_state_t * state,
916 word12 tally,
917 word36 * card_buffer)
918 {
919 transition_state(event, state, PunchGlyphLookup);
920
921 scan_card_for_glyphs(state, card_buffer);
922
923 save_card_in_cache(state, tally, card_buffer);
924
925 return NoEvent;
926 }
927
928 static enum parse_event do_state_end_of_header(enum parse_event event,
929 pun_state_t * state,
930 word12 tally,
931 word36 * card_buffer)
932 {
933 transition_state(event, state, EndOfHeader);
934
935 save_card_in_cache(state, tally, card_buffer);
936
937 if (state -> log_cards)
938 {
939 sim_printf("\r\n++++ Glyph Buffer ++++\r\n'%s'\r\n", state -> glyph_buffer);
940 }
941
942 char punch_file_name[SIR_MAXPATH+1];
943 if (strlen(state -> glyph_buffer) < 86)
944 {
945 sim_warn ("*** Punch: glyph buffer too short, unable to parse file name '%s'\r\n",
946 state -> glyph_buffer);
947 punch_file_name[0] = 0;
948 }
949 else
950 {
951 (void)sprintf (punch_file_name, "%5.5s.%22.22s",
952 &state -> glyph_buffer[14],
953 &state -> glyph_buffer[88]
954 );
955 remove_spaces(punch_file_name);
956 }
957
958 strncpy(state -> raw_file_name, punch_file_name, sizeof(state -> raw_file_name) - 1);
959 state->raw_file_name[sizeof(state->raw_file_name) - 1] = '\0';
960
961 create_punch_file(state);
962
963
964 CARD_CACHE_ENTRY *current_entry = state -> first_cached_card;
965 while (current_entry != NULL)
966 {
967 write_punch_files (state, current_entry -> card_data, WORDS_PER_CARD, true);
968 current_entry = current_entry->next_entry;
969 }
970
971 clear_card_cache(state);
972
973 return NoEvent;
974 }
975
976 static enum parse_event do_state_cache_card(enum parse_event event,
977 pun_state_t * state,
978 word12 tally,
979 word36 * card_buffer)
980 {
981 transition_state(event, state, CacheCard);
982
983 save_card_in_cache(state, tally, card_buffer);
984
985 return NoEvent;
986 }
987
988 static enum parse_event do_state_end_of_deck(enum parse_event event,
989 pun_state_t * state,
990 word12 tally,
991 word36 * card_buffer)
992 {
993 transition_state(event, state, EndOfDeck);
994
995 save_card_in_cache(state, tally, card_buffer);
996
997 return NoEvent;
998 }
999
1000 static enum parse_event do_state_end_of_job(enum parse_event event,
1001 pun_state_t * state,
1002 word12 tally,
1003 word36 * card_buffer)
1004 {
1005 transition_state(event, state, EndOfJob);
1006
1007
1008 CARD_CACHE_ENTRY *current_entry = state -> first_cached_card;
1009 while (current_entry != NULL)
1010 {
1011 write_punch_files (state, current_entry -> card_data, WORDS_PER_CARD,
1012 (current_entry -> next_entry == NULL));
1013 current_entry = current_entry->next_entry;
1014 }
1015
1016 clear_card_cache(state);
1017
1018 write_punch_files (state, card_buffer, tally, true);
1019
1020
1021 if (state -> punfile_raw >= 0)
1022 {
1023 close (state -> punfile_raw);
1024 state -> punfile_raw = -1;
1025
1026 int rename_attempts = 0;
1027 const int MAX_RENAME_ATTEMPTS = 10000;
1028 bool rename_successful = false;
1029
1030 char old_open_filename [SIR_MAXPATH];
1031 char new_target_filename [SIR_MAXPATH];
1032
1033 strcpy (old_open_filename, state -> filename);
1034
1035 char * open_ext_pos = strstr (old_open_filename, ".OPEN");
1036 if (open_ext_pos)
1037 {
1038 strncpy (new_target_filename, old_open_filename, open_ext_pos - old_open_filename);
1039 new_target_filename [open_ext_pos - old_open_filename] = '\0';
1040 }
1041 else
1042 strcpy (new_target_filename, old_open_filename);
1043
1044 while (rename_attempts < MAX_RENAME_ATTEMPTS)
1045 {
1046 if (safe_rename (old_open_filename, new_target_filename) == 0)
1047 {
1048 rename_successful = true;
1049 break;
1050 }
1051
1052 char new_random_part [7];
1053 const char charset [] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1054 const int charset_size = sizeof (charset) - 1;
1055
1056 for (int i = 0; i < 6; ++i)
1057 new_random_part [i] = charset [random () % charset_size];
1058
1059 new_random_part [6] = '\0';
1060
1061 char * prt_ext_pos = strstr (new_target_filename, ".pun");
1062 if (prt_ext_pos)
1063 {
1064 char * random_start_pos = prt_ext_pos - 6;
1065 if (random_start_pos >= new_target_filename)
1066 (void)memmove (random_start_pos, new_random_part, 6);
1067 }
1068
1069 rename_attempts++;
1070 }
1071
1072 if (! rename_successful)
1073 sim_warn ("Failed to rename '%s': %s (Error %d)\r\n", old_open_filename, xstrerror_l (errno), errno);
1074 }
1075
1076 return Done;
1077 }
1078
1079 static void unexpected_event(enum parse_event event, pun_state_t * state)
1080 {
1081 sim_warn("*** Punch: Unexpected event ");
1082 print_event(event);
1083
1084 sim_warn(" in state ");
1085 print_state(state -> current_state);
1086
1087 sim_warn("***\r\n");
1088 }
1089
1090 static void parse_card(pun_state_t * state, word12 tally, word36 * card_buffer)
1091 {
1092 enum parse_event event = Card;
1093
1094 if (tally == WORDS_PER_CARD && memcmp (card_buffer, eodCard, sizeof (eodCard)) == 0)
1095 {
1096 event = EndOfDeckCard;
1097 }
1098
1099 if (tally == WORDS_PER_CARD && memcmp (card_buffer, bannerCard, sizeof (bannerCard)) == 0)
1100 {
1101 event = BannerCard;
1102 }
1103
1104 while (event != NoEvent)
1105 {
1106 enum parse_event current_event = event;
1107 event = NoEvent;
1108
1109 switch (current_event)
1110 {
1111 case BannerCard:
1112 switch (state -> current_state)
1113 {
1114 case Idle:
1115 event = do_state_starting_job(current_event, state, tally, card_buffer);
1116 break;
1117
1118 case PunchGlyphLookup:
1119 event = do_state_end_of_header(current_event, state, tally, card_buffer);
1120 break;
1121
1122 case EndOfDeck:
1123 event = do_state_end_of_job(current_event, state, tally, card_buffer);
1124 break;
1125
1126 default:
1127 unexpected_event(current_event, state);
1128 break;
1129 }
1130 break;
1131
1132 case EndOfDeckCard:
1133 switch (state -> current_state)
1134 {
1135 case StartingJob:
1136 event = do_state_end_of_deck(current_event, state, tally, card_buffer);
1137 break;
1138
1139 case PunchGlyphLookup:
1140 event = do_state_end_of_deck(current_event, state, tally, card_buffer);
1141 break;
1142
1143 case EndOfHeader:
1144 event = do_state_end_of_deck(current_event, state, tally, card_buffer);
1145 break;
1146
1147 case CacheCard:
1148 event = do_state_end_of_deck(current_event, state, tally, card_buffer);
1149 break;
1150
1151 case EndOfDeck:
1152 event = do_state_end_of_deck(current_event, state, tally, card_buffer);
1153 break;
1154
1155 default:
1156 unexpected_event(current_event, state);
1157 break;
1158 }
1159 break;
1160
1161 case Card:
1162 switch (state -> current_state)
1163 {
1164 case StartingJob:
1165 event = do_state_scan_card_for_glyphs(current_event, state, tally, card_buffer);
1166 break;
1167
1168 case PunchGlyphLookup:
1169 event = do_state_scan_card_for_glyphs(current_event, state, tally, card_buffer);
1170 break;
1171
1172 case EndOfHeader:
1173 event = do_state_cache_card(current_event, state, tally, card_buffer);
1174 break;
1175
1176 case CacheCard:
1177 event = do_state_cache_card(current_event, state, tally, card_buffer);
1178 break;
1179
1180 case EndOfDeck:
1181 event = do_state_cache_card(current_event, state, tally, card_buffer);
1182 break;
1183
1184 default:
1185 unexpected_event(current_event, state);
1186 break;
1187 }
1188 break;
1189
1190 case Done:
1191 switch (state -> current_state)
1192 {
1193 case EndOfJob:
1194 event = do_state_idle(current_event, state);
1195 break;
1196
1197 default:
1198 unexpected_event(current_event, state);
1199 break;
1200 }
1201 break;
1202
1203 default:
1204 sim_warn("*** Error: Punch received unknown event!\r\n");
1205 break;
1206 }
1207 }
1208
1209 }
1210
1211 static int punWriteRecord (uint iomUnitIdx, uint chan)
1212 {
1213 iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
1214 uint dev_code = p->IDCW_DEV_CODE;
1215 uint ctlr_unit_idx = get_ctlr_idx (iomUnitIdx, chan);
1216 uint devUnitIdx = cables->urp_to_urd[ctlr_unit_idx][dev_code].unit_idx;
1217 UNIT * unitp = & pun_unit [devUnitIdx];
1218 long pun_unit_num = PUN_UNIT_NUM (unitp);
1219
1220 p -> isRead = false;
1221 if (p -> DDCW_TALLY != WORDS_PER_CARD)
1222 {
1223 sim_warn ("%s expected tally of 27\r\n", __func__);
1224 p -> chanStatus = chanStatIncorrectDCW;
1225 p -> stati = 05001;
1226 return -1;
1227 }
1228
1229
1230
1231
1232
1233
1234 word36 buffer [p -> DDCW_TALLY];
1235 uint wordsProcessed = 0;
1236 iom_indirect_data_service (iomUnitIdx, chan, buffer, & wordsProcessed, false);
1237 p->initiate = false;
1238
1239 if (pun_state [pun_unit_num] . log_cards)
1240 {
1241 log_card(p -> DDCW_TALLY, buffer);
1242 }
1243
1244 parse_card( &pun_state [pun_unit_num], p -> DDCW_TALLY, buffer);
1245
1246 p -> stati = 04000;
1247 return 0;
1248 }
1249
1250 iom_cmd_rc_t pun_iom_cmd (uint iomUnitIdx, uint chan) {
1251 iom_cmd_rc_t rc = IOM_CMD_PROCEED;
1252 iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
1253 uint dev_code = p->IDCW_DEV_CODE;
1254 #if defined(TESTING)
1255 cpu_state_t * cpup = _cpup;
1256
1257 sim_debug (DBG_TRACE, & pun_dev, "%s: PUN %c%02o_%02o\r\n",
1258 __func__, iomChar (iomUnitIdx), chan, dev_code);
1259 #endif
1260
1261 uint ctlr_unit_idx = get_ctlr_idx (iomUnitIdx, chan);
1262 uint devUnitIdx = cables->urp_to_urd[ctlr_unit_idx][dev_code].unit_idx;
1263 pun_state_t * statep = & pun_state[devUnitIdx];
1264
1265
1266 if (IS_IDCW (p)) {
1267
1268 statep->ioMode = punNoMode;
1269 switch (p->IDCW_DEV_CMD) {
1270 case 011:
1271 sim_debug (DBG_DEBUG, & pun_dev, "%s: Punch Binary\r\n", __func__);
1272 statep->ioMode = punWrBin;
1273 p->stati = 04000;
1274 break;
1275
1276 case 031:
1277 sim_debug (DBG_DEBUG, & pun_dev, "%s: Set Diagnostic Mode\r\n", __func__);
1278 p->stati = 04000;
1279 break;
1280
1281 case 040:
1282 sim_debug (DBG_DEBUG, & pun_dev, "%s: Reset Status\r\n", __func__);
1283 p->stati = 04000;
1284 p->isRead = false;
1285 break;
1286
1287 default:
1288 if (p->IDCW_DEV_CMD != 051)
1289 sim_warn ("%s: PUN unrecognized device command %02o\r\n", __func__, p->IDCW_DEV_CMD);
1290 p->stati = 04501;
1291 p->chanStatus = chanStatIncorrectDCW;
1292 return IOM_CMD_ERROR;
1293 }
1294 sim_debug (DBG_DEBUG, & pun_dev, "%s: stati %04o\r\n", __func__, p->stati);
1295 return IOM_CMD_PROCEED;
1296 }
1297
1298
1299 switch (statep->ioMode) {
1300 case punNoMode:
1301
1302
1303
1304 break;
1305
1306 case punWrBin: {
1307 int rc = punWriteRecord (iomUnitIdx, chan);
1308 if (rc)
1309 return IOM_CMD_ERROR;
1310 }
1311 break;
1312
1313 default:
1314 sim_warn ("%s: Unrecognized ioMode %d\r\n", __func__, statep->ioMode);
1315 return IOM_CMD_ERROR;
1316 }
1317 return rc;
1318 }
1319
1320 static t_stat pun_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr, UNUSED int val,
1321 UNUSED const void * desc)
1322 {
1323 sim_printf("Number of PUN units in system is %d\r\n", pun_dev . numunits);
1324 return SCPE_OK;
1325 }
1326
1327 static t_stat pun_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value, const char * cptr,
1328 UNUSED void * desc)
1329 {
1330 if (! cptr)
1331 return SCPE_ARG;
1332 int n = atoi (cptr);
1333 if (n < 1 || n > N_PUN_UNITS_MAX)
1334 return SCPE_ARG;
1335 pun_dev . numunits = (uint32) n;
1336 return SCPE_OK;
1337 }
1338
1339 static t_stat pun_show_device_name (UNUSED FILE * st, UNIT * uptr,
1340 UNUSED int val, UNUSED const void * desc)
1341 {
1342 long n = PUN_UNIT_NUM (uptr);
1343 if (n < 0 || n >= N_PUN_UNITS_MAX)
1344 return SCPE_ARG;
1345 sim_printf("name : %s", pun_state [n] . device_name);
1346 return SCPE_OK;
1347 }
1348
1349 static t_stat pun_set_device_name (UNUSED UNIT * uptr, UNUSED int32 value,
1350 UNUSED const char * cptr, UNUSED void * desc)
1351 {
1352 long n = PUN_UNIT_NUM (uptr);
1353 if (n < 0 || n >= N_PUN_UNITS_MAX)
1354 return SCPE_ARG;
1355 if (cptr)
1356 {
1357 strncpy (pun_state [n] . device_name, cptr, MAX_DEV_NAME_LEN - 1);
1358 pun_state [n] . device_name [MAX_DEV_NAME_LEN - 1] = 0;
1359 }
1360 else
1361 pun_state [n] . device_name [0] = 0;
1362 return SCPE_OK;
1363 }
1364
1365 static t_stat pun_set_path (UNUSED UNIT * uptr, UNUSED int32 value,
1366 const UNUSED char * cptr, UNUSED void * desc)
1367 {
1368 if (! cptr)
1369 return SCPE_ARG;
1370
1371 size_t len = strlen(cptr);
1372
1373
1374
1375 if (len >= (sizeof(pun_path_prefix) - 2))
1376 return SCPE_ARG;
1377
1378 strncpy(pun_path_prefix, cptr, sizeof(pun_path_prefix) - 1);
1379 if (len > 0)
1380 {
1381 if (pun_path_prefix[len - 1] != '/')
1382 {
1383 pun_path_prefix[len++] = '/';
1384 pun_path_prefix[len] = 0;
1385 }
1386 }
1387 return SCPE_OK;
1388 }
1389
1390 static t_stat pun_show_path (UNUSED FILE * st, UNUSED UNIT * uptr,
1391 UNUSED int val, UNUSED const void * desc)
1392 {
1393 if (pun_path_prefix [0])
1394 {
1395 sim_printf("\rPath to card punch directories is \"%s\".\r\n", pun_path_prefix);
1396 }
1397 else
1398 {
1399 sim_printf("\rPath to card punch directories is unset.\r\n");
1400 }
1401 return SCPE_OK;
1402 }
1403
1404 static t_stat pun_set_config (UNUSED UNIT * uptr, UNUSED int32 value,
1405 const char * cptr, UNUSED void * desc)
1406 {
1407 int devUnitIdx = (int) PUN_UNIT_NUM (uptr);
1408 pun_state_t * psp = pun_state + devUnitIdx;
1409 config_state_t cfg_state = { NULL, NULL };
1410
1411 for (;;)
1412 {
1413 int64_t v;
1414 int rc = cfg_parse (__func__, cptr, pun_config_list, & cfg_state, & v);
1415 if (rc == -1)
1416 break;
1417
1418 if (rc == -2)
1419 {
1420 cfg_parse_done (& cfg_state);
1421 return SCPE_ARG;
1422 }
1423 const char * p = pun_config_list[rc].name;
1424
1425 if (strcmp (p, "logcards") == 0)
1426 {
1427 psp->log_cards = v != 0;
1428 continue;
1429 }
1430
1431 sim_warn ("error: pun_set_config: Invalid cfg_parse rc <%ld>\r\n",
1432 (long) rc);
1433 cfg_parse_done (& cfg_state);
1434 return SCPE_ARG;
1435 }
1436 cfg_parse_done (& cfg_state);
1437 return SCPE_OK;
1438 }
1439
1440 static t_stat pun_show_config (UNUSED FILE * st, UNUSED UNIT * uptr,
1441 UNUSED int val, UNUSED const void * desc)
1442 {
1443 int devUnitIdx = (int) PUN_UNIT_NUM (uptr);
1444 pun_state_t * psp = pun_state + devUnitIdx;
1445 sim_msg ("logcards : %d", psp->log_cards);
1446 return SCPE_OK;
1447 }