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 size_t copy_len = strlen (template);
655 if (copy_len >= sizeof (state -> filename))
656 copy_len = sizeof (state -> filename) - 1;
657 memcpy (state -> filename, template, copy_len);
658 state -> filename[copy_len] = '\0';
659
660 if (state -> punfile_raw < 0)
661 perror("creating punch '.pun' file\r\n");
662
663 }
664
665 static void write_punch_files (pun_state_t * state, word36* in_buffer, int word_count,
666 bool banner_card)
667 {
668 if (word_count != WORDS_PER_CARD)
669 {
670 sim_warn ("Unable to interpret punch buffer due to wrong length, not writing output!\r\n");
671 return;
672 }
673
674 uint8 byte_buffer[120];
675 (void)memset(&byte_buffer, 0, sizeof(byte_buffer));
676
677 word12 word12_buffer[80];
678 (void)memset(&word12_buffer, 0, sizeof(word12_buffer));
679
680 for (int nibble_index = 0; nibble_index < (CARD_COL_COUNT * NIBBLES_PER_COL); nibble_index++)
681 {
682 int byte_offset = nibble_index / 2;
683 int word36_offset = nibble_index / 9;
684 int nibble_offset = nibble_index % 9;
685 uint8 nibble = (in_buffer[word36_offset] >> ((8 - nibble_offset) * 4)) & 0xF;
686
687 if (nibble_index & 0x1)
688 byte_buffer [byte_offset] |= nibble;
689 else
690 byte_buffer [byte_offset] |= (nibble << 4);
691
692 int word12_offset = nibble_index / 3;
693 int word12_nibble_offset = 2 - (nibble_index % 3);
694
695 word12_buffer[word12_offset] |= nibble << (word12_nibble_offset * 4);
696 }
697
698 if (state->log_cards)
699 {
700 sim_printf("word12_buffer:\r\n");
701 for (uint i = 0; i < 80; i++)
702 {
703 sim_printf(" %04o\r\n", word12_buffer[i]);
704 }
705 sim_printf("\r\n");
706 }
707
708 if (state -> punfile_raw >= 0)
709 {
710 if (write (state -> punfile_raw, byte_buffer,
711 sizeof(byte_buffer)) != sizeof(byte_buffer)) {
712 sim_warn ("Failed to write to .raw card punch file!\r\n");
713 perror("Writing .raw punch file\r\n");
714 }
715 }
716
717 }
718
719 static void log_card(word12 tally, word36 * buffer)
720 {
721 sim_printf ("tally %d\r\n", tally);
722
723 for (uint i = 0; i < tally; i ++)
724 {
725 sim_printf (" %012llo\r\n", (unsigned long long)buffer [i]);
726 }
727 sim_printf ("\r\n");
728
729 if (tally != WORDS_PER_CARD)
730 {
731 sim_warn("Unable to log punch card, tally is not 27 (%d)\r\n", tally);
732 return;
733 }
734
735 for (uint row = 0; row < 12; row ++)
736 {
737 for (uint col = 0; col < 80; col ++)
738 {
739
740 uint wordno = col / 3;
741 uint fieldno = col % 3;
742 word1 bit = getbits36_1 (buffer [wordno], fieldno * 12 + row);
743 if (bit)
744 sim_printf ("*");
745 else
746 sim_printf (" ");
747 }
748 sim_printf ("\r\n");
749 }
750 sim_printf ("\r\n");
751
752 for (uint row = 0; row < 12; row ++)
753 {
754
755 for (int col = 79; col >= 0; col --)
756 {
757
758 uint wordno = (uint) col / 3;
759 uint fieldno = (uint) col % 3;
760 word1 bit = getbits36_1 (buffer [wordno], fieldno * 12 + row);
761 if (bit)
762 sim_printf ("*");
763 else
764 sim_printf (" ");
765 }
766 sim_printf ("\r\n");
767 }
768 sim_printf ("\r\n");
769 }
770
771 static void print_event(enum parse_event event)
772 {
773 switch (event)
774 {
775 case NoEvent:
776 sim_warn("[No Event]");
777 break;
778 case BannerCard:
779 sim_warn("[Banner Card]");
780 break;
781 case EndOfDeckCard:
782 sim_warn("[End Of Deck Card]");
783 break;
784 case Card:
785 sim_warn("[Card]");
786 break;
787 case Done:
788 sim_warn("[Done]");
789 break;
790 default:
791 sim_warn("[unknown event %d]", event);
792 break;
793 }
794 }
795
796 static void print_state(enum parse_state state)
797 {
798 switch (state)
799 {
800 case Idle:
801 sim_warn("[Idle]");
802 break;
803 case StartingJob:
804 sim_warn("[Starting Job]");
805 break;
806 case PunchGlyphLookup:
807 sim_warn("[Punch Glyph Lookup]");
808 break;
809 case EndOfHeader:
810 sim_warn("[End Of Header]");
811 break;
812 case CacheCard:
813 sim_warn("[Cache Card]");
814 break;
815 case EndOfDeck:
816 sim_warn("[End Of Deck]");
817 break;
818 case EndOfJob:
819 sim_warn("[End Of Job]");
820 break;
821 default:
822 sim_warn("[unknown state %d]", state);
823 break;
824 }
825 }
826
827 static void print_transition(enum parse_state old_state, enum parse_event event,
828 enum parse_state new_state)
829 {
830 sim_warn(">>> Punch Transition: ");
831 print_event(event);
832 sim_warn(" = ");
833 print_state(old_state);
834 sim_warn(" -> ");
835 print_state(new_state);
836 sim_warn("\r\n");
837 }
838
839 static void clear_card_cache(pun_state_t * state)
840 {
841 CARD_CACHE_ENTRY *current_entry = state -> first_cached_card;
842 while (current_entry != NULL)
843 {
844 CARD_CACHE_ENTRY *old_entry = current_entry;
845 current_entry = current_entry->next_entry;
846 FREE(old_entry);
847 }
848
849 state -> first_cached_card = NULL;
850 state -> last_cached_card = NULL;
851 }
852
853 static void save_card_in_cache(pun_state_t * state, word12 tally, word36 * card_buffer)
854 {
855 CARD_CACHE_ENTRY *new_entry = malloc(sizeof(CARD_CACHE_ENTRY));
856 if (!new_entry)
857 {
858 (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
859 __func__, __FILE__, __LINE__);
860 #if defined(USE_BACKTRACE)
861 # if defined(SIGUSR2)
862 (void)raise(SIGUSR2);
863
864 # endif
865 #endif
866 abort();
867 }
868
869 new_entry -> tally = tally;
870 memcpy(&new_entry -> card_data, card_buffer, sizeof(word36) * tally);
871 new_entry -> next_entry = NULL;
872
873 if (state -> first_cached_card == NULL)
874 {
875 state -> first_cached_card = new_entry;
876 state -> last_cached_card = new_entry;
877 }
878 else
879 {
880 state -> last_cached_card -> next_entry = new_entry;
881 state -> last_cached_card = new_entry;
882 }
883 }
884
885 static void transition_state(enum parse_event event, pun_state_t * state,
886 enum parse_state new_state)
887 {
888 if (state -> log_cards)
889 {
890 print_transition(state -> current_state, event, new_state);
891 }
892
893 state -> current_state = new_state;
894 }
895
896 static enum parse_event do_state_idle(enum parse_event event, pun_state_t * state)
897 {
898 transition_state(event, state, Idle);
899
900
901
902 return NoEvent;
903 }
904
905 static enum parse_event do_state_starting_job(enum parse_event event,
906 pun_state_t * state, word12 tally,
907 word36 * card_buffer)
908 {
909 transition_state(event, state, StartingJob);
910
911 clear_card_cache(state);
912 state -> glyph_buffer[0] = 0;
913 save_card_in_cache(state, tally, card_buffer);
914
915 return NoEvent;
916 }
917
918 static enum parse_event do_state_scan_card_for_glyphs(enum parse_event event,
919 pun_state_t * state,
920 word12 tally,
921 word36 * card_buffer)
922 {
923 transition_state(event, state, PunchGlyphLookup);
924
925 scan_card_for_glyphs(state, card_buffer);
926
927 save_card_in_cache(state, tally, card_buffer);
928
929 return NoEvent;
930 }
931
932 static enum parse_event do_state_end_of_header(enum parse_event event,
933 pun_state_t * state,
934 word12 tally,
935 word36 * card_buffer)
936 {
937 transition_state(event, state, EndOfHeader);
938
939 save_card_in_cache(state, tally, card_buffer);
940
941 if (state -> log_cards)
942 {
943 sim_printf("\r\n++++ Glyph Buffer ++++\r\n'%s'\r\n", state -> glyph_buffer);
944 }
945
946 char punch_file_name[SIR_MAXPATH+1];
947 if (strlen(state -> glyph_buffer) < 86)
948 {
949 sim_warn ("*** Punch: glyph buffer too short, unable to parse file name '%s'\r\n",
950 state -> glyph_buffer);
951 punch_file_name[0] = 0;
952 }
953 else
954 {
955 (void)sprintf (punch_file_name, "%5.5s.%22.22s",
956 &state -> glyph_buffer[14],
957 &state -> glyph_buffer[88]
958 );
959 remove_spaces(punch_file_name);
960 }
961
962 strncpy(state -> raw_file_name, punch_file_name, sizeof(state -> raw_file_name) - 1);
963 state->raw_file_name[sizeof(state->raw_file_name) - 1] = '\0';
964
965 create_punch_file(state);
966
967
968 CARD_CACHE_ENTRY *current_entry = state -> first_cached_card;
969 while (current_entry != NULL)
970 {
971 write_punch_files (state, current_entry -> card_data, WORDS_PER_CARD, true);
972 current_entry = current_entry->next_entry;
973 }
974
975 clear_card_cache(state);
976
977 return NoEvent;
978 }
979
980 static enum parse_event do_state_cache_card(enum parse_event event,
981 pun_state_t * state,
982 word12 tally,
983 word36 * card_buffer)
984 {
985 transition_state(event, state, CacheCard);
986
987 save_card_in_cache(state, tally, card_buffer);
988
989 return NoEvent;
990 }
991
992 static enum parse_event do_state_end_of_deck(enum parse_event event,
993 pun_state_t * state,
994 word12 tally,
995 word36 * card_buffer)
996 {
997 transition_state(event, state, EndOfDeck);
998
999 save_card_in_cache(state, tally, card_buffer);
1000
1001 return NoEvent;
1002 }
1003
1004 static enum parse_event do_state_end_of_job(enum parse_event event,
1005 pun_state_t * state,
1006 word12 tally,
1007 word36 * card_buffer)
1008 {
1009 transition_state(event, state, EndOfJob);
1010
1011
1012 CARD_CACHE_ENTRY *current_entry = state -> first_cached_card;
1013 while (current_entry != NULL)
1014 {
1015 write_punch_files (state, current_entry -> card_data, WORDS_PER_CARD,
1016 (current_entry -> next_entry == NULL));
1017 current_entry = current_entry->next_entry;
1018 }
1019
1020 clear_card_cache(state);
1021
1022 write_punch_files (state, card_buffer, tally, true);
1023
1024
1025 if (state -> punfile_raw >= 0)
1026 {
1027 close (state -> punfile_raw);
1028 state -> punfile_raw = -1;
1029
1030 int rename_attempts = 0;
1031 const int MAX_RENAME_ATTEMPTS = 10000;
1032 bool rename_successful = false;
1033
1034 char old_open_filename [SIR_MAXPATH];
1035 char new_target_filename [SIR_MAXPATH];
1036
1037 strcpy (old_open_filename, state -> filename);
1038
1039 char * open_ext_pos = strstr (old_open_filename, ".OPEN");
1040 if (open_ext_pos)
1041 {
1042 strncpy (new_target_filename, old_open_filename, open_ext_pos - old_open_filename);
1043 new_target_filename [open_ext_pos - old_open_filename] = '\0';
1044 }
1045 else
1046 strcpy (new_target_filename, old_open_filename);
1047
1048 while (rename_attempts < MAX_RENAME_ATTEMPTS)
1049 {
1050 if (safe_rename (old_open_filename, new_target_filename) == 0)
1051 {
1052 rename_successful = true;
1053 break;
1054 }
1055
1056 char new_random_part [7];
1057 const char charset [] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1058 const int charset_size = sizeof (charset) - 1;
1059
1060 for (int i = 0; i < 6; ++i)
1061 new_random_part [i] = charset [random () % charset_size];
1062
1063 new_random_part [6] = '\0';
1064
1065 char * prt_ext_pos = strstr (new_target_filename, ".pun");
1066 if (prt_ext_pos)
1067 {
1068 char * random_start_pos = prt_ext_pos - 6;
1069 if (random_start_pos >= new_target_filename)
1070 (void)memmove (random_start_pos, new_random_part, 6);
1071 }
1072
1073 rename_attempts++;
1074 }
1075
1076 if (! rename_successful)
1077 sim_warn ("Failed to rename '%s': %s (Error %d)\r\n", old_open_filename, xstrerror_l (errno), errno);
1078 }
1079
1080 return Done;
1081 }
1082
1083 static void unexpected_event(enum parse_event event, pun_state_t * state)
1084 {
1085 sim_warn("*** Punch: Unexpected event ");
1086 print_event(event);
1087
1088 sim_warn(" in state ");
1089 print_state(state -> current_state);
1090
1091 sim_warn("***\r\n");
1092 }
1093
1094 static void parse_card(pun_state_t * state, word12 tally, word36 * card_buffer)
1095 {
1096 enum parse_event event = Card;
1097
1098 if (tally == WORDS_PER_CARD && memcmp (card_buffer, eodCard, sizeof (eodCard)) == 0)
1099 {
1100 event = EndOfDeckCard;
1101 }
1102
1103 if (tally == WORDS_PER_CARD && memcmp (card_buffer, bannerCard, sizeof (bannerCard)) == 0)
1104 {
1105 event = BannerCard;
1106 }
1107
1108 while (event != NoEvent)
1109 {
1110 enum parse_event current_event = event;
1111 event = NoEvent;
1112
1113 switch (current_event)
1114 {
1115 case BannerCard:
1116 switch (state -> current_state)
1117 {
1118 case Idle:
1119 event = do_state_starting_job(current_event, state, tally, card_buffer);
1120 break;
1121
1122 case PunchGlyphLookup:
1123 event = do_state_end_of_header(current_event, state, tally, card_buffer);
1124 break;
1125
1126 case EndOfDeck:
1127 event = do_state_end_of_job(current_event, state, tally, card_buffer);
1128 break;
1129
1130 default:
1131 unexpected_event(current_event, state);
1132 break;
1133 }
1134 break;
1135
1136 case EndOfDeckCard:
1137 switch (state -> current_state)
1138 {
1139 case StartingJob:
1140 event = do_state_end_of_deck(current_event, state, tally, card_buffer);
1141 break;
1142
1143 case PunchGlyphLookup:
1144 event = do_state_end_of_deck(current_event, state, tally, card_buffer);
1145 break;
1146
1147 case EndOfHeader:
1148 event = do_state_end_of_deck(current_event, state, tally, card_buffer);
1149 break;
1150
1151 case CacheCard:
1152 event = do_state_end_of_deck(current_event, state, tally, card_buffer);
1153 break;
1154
1155 case EndOfDeck:
1156 event = do_state_end_of_deck(current_event, state, tally, card_buffer);
1157 break;
1158
1159 default:
1160 unexpected_event(current_event, state);
1161 break;
1162 }
1163 break;
1164
1165 case Card:
1166 switch (state -> current_state)
1167 {
1168 case StartingJob:
1169 event = do_state_scan_card_for_glyphs(current_event, state, tally, card_buffer);
1170 break;
1171
1172 case PunchGlyphLookup:
1173 event = do_state_scan_card_for_glyphs(current_event, state, tally, card_buffer);
1174 break;
1175
1176 case EndOfHeader:
1177 event = do_state_cache_card(current_event, state, tally, card_buffer);
1178 break;
1179
1180 case CacheCard:
1181 event = do_state_cache_card(current_event, state, tally, card_buffer);
1182 break;
1183
1184 case EndOfDeck:
1185 event = do_state_cache_card(current_event, state, tally, card_buffer);
1186 break;
1187
1188 default:
1189 unexpected_event(current_event, state);
1190 break;
1191 }
1192 break;
1193
1194 case Done:
1195 switch (state -> current_state)
1196 {
1197 case EndOfJob:
1198 event = do_state_idle(current_event, state);
1199 break;
1200
1201 default:
1202 unexpected_event(current_event, state);
1203 break;
1204 }
1205 break;
1206
1207 default:
1208 sim_warn("*** Error: Punch received unknown event!\r\n");
1209 break;
1210 }
1211 }
1212
1213 }
1214
1215 static int punWriteRecord (uint iomUnitIdx, uint chan)
1216 {
1217 iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
1218 uint dev_code = p->IDCW_DEV_CODE;
1219 uint ctlr_unit_idx = get_ctlr_idx (iomUnitIdx, chan);
1220 uint devUnitIdx = cables->urp_to_urd[ctlr_unit_idx][dev_code].unit_idx;
1221 UNIT * unitp = & pun_unit [devUnitIdx];
1222 long pun_unit_num = PUN_UNIT_NUM (unitp);
1223
1224 p -> isRead = false;
1225 if (p -> DDCW_TALLY != WORDS_PER_CARD)
1226 {
1227 sim_warn ("%s expected tally of 27\r\n", __func__);
1228 p -> chanStatus = chanStatIncorrectDCW;
1229 p -> stati = 05001;
1230 return -1;
1231 }
1232
1233
1234
1235
1236
1237
1238 word36 buffer [p -> DDCW_TALLY];
1239 uint wordsProcessed = 0;
1240 iom_indirect_data_service (iomUnitIdx, chan, buffer, & wordsProcessed, false);
1241 p->initiate = false;
1242
1243 if (pun_state [pun_unit_num] . log_cards)
1244 {
1245 log_card(p -> DDCW_TALLY, buffer);
1246 }
1247
1248 parse_card( &pun_state [pun_unit_num], p -> DDCW_TALLY, buffer);
1249
1250 p -> stati = 04000;
1251 return 0;
1252 }
1253
1254 iom_cmd_rc_t pun_iom_cmd (uint iomUnitIdx, uint chan) {
1255 iom_cmd_rc_t rc = IOM_CMD_PROCEED;
1256 iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
1257 uint dev_code = p->IDCW_DEV_CODE;
1258 #if defined(TESTING)
1259 cpu_state_t * cpup = _cpup;
1260
1261 sim_debug (DBG_TRACE, & pun_dev, "%s: PUN %c%02o_%02o\r\n",
1262 __func__, iomChar (iomUnitIdx), chan, dev_code);
1263 #endif
1264
1265 uint ctlr_unit_idx = get_ctlr_idx (iomUnitIdx, chan);
1266 uint devUnitIdx = cables->urp_to_urd[ctlr_unit_idx][dev_code].unit_idx;
1267 pun_state_t * statep = & pun_state[devUnitIdx];
1268
1269
1270 if (IS_IDCW (p)) {
1271
1272 statep->ioMode = punNoMode;
1273 switch (p->IDCW_DEV_CMD) {
1274 case 011:
1275 sim_debug (DBG_DEBUG, & pun_dev, "%s: Punch Binary\r\n", __func__);
1276 statep->ioMode = punWrBin;
1277 p->stati = 04000;
1278 break;
1279
1280 case 031:
1281 sim_debug (DBG_DEBUG, & pun_dev, "%s: Set Diagnostic Mode\r\n", __func__);
1282 p->stati = 04000;
1283 break;
1284
1285 case 040:
1286 sim_debug (DBG_DEBUG, & pun_dev, "%s: Reset Status\r\n", __func__);
1287 p->stati = 04000;
1288 p->isRead = false;
1289 break;
1290
1291 default:
1292 if (p->IDCW_DEV_CMD != 051)
1293 sim_warn ("%s: PUN unrecognized device command %02o\r\n", __func__, p->IDCW_DEV_CMD);
1294 p->stati = 04501;
1295 p->chanStatus = chanStatIncorrectDCW;
1296 return IOM_CMD_ERROR;
1297 }
1298 sim_debug (DBG_DEBUG, & pun_dev, "%s: stati %04o\r\n", __func__, p->stati);
1299 return IOM_CMD_PROCEED;
1300 }
1301
1302
1303 switch (statep->ioMode) {
1304 case punNoMode:
1305
1306
1307
1308 break;
1309
1310 case punWrBin: {
1311 int rc = punWriteRecord (iomUnitIdx, chan);
1312 if (rc)
1313 return IOM_CMD_ERROR;
1314 }
1315 break;
1316
1317 default:
1318 sim_warn ("%s: Unrecognized ioMode %d\r\n", __func__, statep->ioMode);
1319 return IOM_CMD_ERROR;
1320 }
1321 return rc;
1322 }
1323
1324 static t_stat pun_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr, UNUSED int val,
1325 UNUSED const void * desc)
1326 {
1327 sim_printf("Number of PUN units in system is %d\r\n", pun_dev . numunits);
1328 return SCPE_OK;
1329 }
1330
1331 static t_stat pun_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value, const char * cptr,
1332 UNUSED void * desc)
1333 {
1334 if (! cptr)
1335 return SCPE_ARG;
1336 int n = atoi (cptr);
1337 if (n < 1 || n > N_PUN_UNITS_MAX)
1338 return SCPE_ARG;
1339 pun_dev . numunits = (uint32) n;
1340 return SCPE_OK;
1341 }
1342
1343 static t_stat pun_show_device_name (UNUSED FILE * st, UNIT * uptr,
1344 UNUSED int val, UNUSED const void * desc)
1345 {
1346 long n = PUN_UNIT_NUM (uptr);
1347 if (n < 0 || n >= N_PUN_UNITS_MAX)
1348 return SCPE_ARG;
1349 sim_printf("name : %s", pun_state [n] . device_name);
1350 return SCPE_OK;
1351 }
1352
1353 static t_stat pun_set_device_name (UNUSED UNIT * uptr, UNUSED int32 value,
1354 UNUSED const char * cptr, UNUSED void * desc)
1355 {
1356 long n = PUN_UNIT_NUM (uptr);
1357 if (n < 0 || n >= N_PUN_UNITS_MAX)
1358 return SCPE_ARG;
1359 if (cptr)
1360 {
1361 strncpy (pun_state [n] . device_name, cptr, MAX_DEV_NAME_LEN - 1);
1362 pun_state [n] . device_name [MAX_DEV_NAME_LEN - 1] = 0;
1363 }
1364 else
1365 pun_state [n] . device_name [0] = 0;
1366 return SCPE_OK;
1367 }
1368
1369 static t_stat pun_set_path (UNUSED UNIT * uptr, UNUSED int32 value,
1370 const UNUSED char * cptr, UNUSED void * desc)
1371 {
1372 if (! cptr)
1373 return SCPE_ARG;
1374
1375 size_t len = strlen(cptr);
1376
1377
1378
1379 if (len >= (sizeof(pun_path_prefix) - 2))
1380 return SCPE_ARG;
1381
1382 strncpy(pun_path_prefix, cptr, sizeof(pun_path_prefix) - 1);
1383 if (len > 0)
1384 {
1385 if (pun_path_prefix[len - 1] != '/')
1386 {
1387 pun_path_prefix[len++] = '/';
1388 pun_path_prefix[len] = 0;
1389 }
1390 }
1391 return SCPE_OK;
1392 }
1393
1394 static t_stat pun_show_path (UNUSED FILE * st, UNUSED UNIT * uptr,
1395 UNUSED int val, UNUSED const void * desc)
1396 {
1397 if (pun_path_prefix [0])
1398 {
1399 sim_printf("\rPath to card punch directories is \"%s\".\r\n", pun_path_prefix);
1400 }
1401 else
1402 {
1403 sim_printf("\rPath to card punch directories is unset.\r\n");
1404 }
1405 return SCPE_OK;
1406 }
1407
1408 static t_stat pun_set_config (UNUSED UNIT * uptr, UNUSED int32 value,
1409 const char * cptr, UNUSED void * desc)
1410 {
1411 int devUnitIdx = (int) PUN_UNIT_NUM (uptr);
1412 pun_state_t * psp = pun_state + devUnitIdx;
1413 config_state_t cfg_state = { NULL, NULL };
1414
1415 for (;;)
1416 {
1417 int64_t v;
1418 int rc = cfg_parse (__func__, cptr, pun_config_list, & cfg_state, & v);
1419 if (rc == -1)
1420 break;
1421
1422 if (rc == -2)
1423 {
1424 cfg_parse_done (& cfg_state);
1425 return SCPE_ARG;
1426 }
1427 const char * p = pun_config_list[rc].name;
1428
1429 if (strcmp (p, "logcards") == 0)
1430 {
1431 psp->log_cards = v != 0;
1432 continue;
1433 }
1434
1435 sim_warn ("error: pun_set_config: Invalid cfg_parse rc <%ld>\r\n",
1436 (long) rc);
1437 cfg_parse_done (& cfg_state);
1438 return SCPE_ARG;
1439 }
1440 cfg_parse_done (& cfg_state);
1441 return SCPE_OK;
1442 }
1443
1444 static t_stat pun_show_config (UNUSED FILE * st, UNUSED UNIT * uptr,
1445 UNUSED int val, UNUSED const void * desc)
1446 {
1447 int devUnitIdx = (int) PUN_UNIT_NUM (uptr);
1448 pun_state_t * psp = pun_state + devUnitIdx;
1449 sim_msg ("logcards : %d", psp->log_cards);
1450 return SCPE_OK;
1451 }