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