1 /*
2 * Copyright (c) 2021 The DPS8M Development Team
3 *
4 * All rights reserved.
5 *
6 * This software is made available under the terms of the ICU
7 * License, version 1.8.1 or later. For more details, see the
8 * LICENSE.md file at the top-level directory of this distribution.
9 */
10
11 #include <stdio.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <ctype.h>
15 #include <unistd.h>
16 #include <stdbool.h>
17 #ifdef _AIX
18 # ifndef USE_POPT
19 # define USE_POPT
20 # endif /* ifndef USE_POPT */
21 #endif /* ifdef _AIX */
22 #ifdef USE_POPT
23 # include <popt.h>
24 #else
25 # include <getopt.h>
26 #endif /* ifdef USE_POPT */
27
28 #define CARD_COL_COUNT 80
29 #define NIBBLES_PER_COL 3
30 #define NIBBLES_PER_CARD (CARD_COL_COUNT * NIBBLES_PER_COL)
31 #define BYTES_PER_CARD (NIBBLES_PER_CARD / 2)
32 #define CHAR_MATRIX_BYTES 5
33 #define GLYPHS_PER_CARD 22
34 #define MAX_GLYPH_BUFFER_LEN 1024
35
36 typedef unsigned char uint8;
37 typedef unsigned short uint16;
38 typedef uint16 word12;
39 typedef unsigned int uint;
40
41 typedef struct
42 {
43 word12 column[CARD_COL_COUNT];
44 } card_image_t;
45
46 enum parse_state
47 {
48 Idle,
49 StartingJob,
50 PunchGlyphLookup,
51 EndOfHeader,
52 CacheCard,
53 EndOfDeck,
54 EndOfJob
55 };
56
57 enum parse_event
58 {
59 NoEvent,
60 BannerCard,
61 EndOfDeckCard,
62 Card,
63 Done
64 };
65
66 typedef struct card_cache_node CARD_CACHE_ENTRY;
67
68 struct card_cache_node
69 {
70 card_image_t *card;
71 CARD_CACHE_ENTRY *next_entry;
72 };
73
74 typedef struct
75 {
76 CARD_CACHE_ENTRY *first_cache_card;
77 CARD_CACHE_ENTRY *last_cache_card;
78 } CARD_CACHE;
79
80 static bool output_auto = true;
81 static bool output_glyphs = false;
82 static bool output_mcc = false;
83 static bool output_cards = false;
84 static bool output_flip = false;
85 static bool output_raw = false;
86 static bool output_debug = false;
87
88 enum parse_state current_state = Idle;
89
90 char glyph_buffer[MAX_GLYPH_BUFFER_LEN];
91
92 CARD_CACHE banner_card_cache;
93 CARD_CACHE data_card_cache;
94 CARD_CACHE trailer_card_cache;
95
96 // Card image of the banner card
97 static word12 banner_card[] =
98 {
99 00005,
100 00000,
101 00000,
102 07700,
103 07704,
104 07704,
105 07704,
106 07700,
107 00000,
108 00000,
109 00077,
110 00477,
111 00477,
112 00477,
113 00077,
114 00000,
115 00000,
116 07700,
117 07704,
118 07704,
119 07704,
120 07700,
121 00000,
122 00000,
123 00077,
124 00477,
125 00477,
126 00477,
127 00077,
128 00000,
129 00000,
130 07700,
131 07704,
132 07704,
133 07704,
134 07700,
135 00000,
136 00000,
137 00077,
138 00477,
139 00477,
140 00477,
141 00077,
142 00000,
143 00000,
144 07700,
145 07704,
146 07704,
147 07704,
148 07700,
149 00000,
150 00000,
151 00077,
152 00477,
153 00477,
154 00477,
155 00077,
156 00000,
157 00000,
158 07700,
159 07704,
160 07704,
161 07704,
162 07700,
163 00000,
164 00000,
165 00077,
166 00477,
167 00477,
168 00477,
169 00077,
170 00000,
171 00000,
172 07700,
173 07704,
174 07704,
175 07704,
176 07700,
177 00000,
178 00005,
179 };
180
181 // Card image of the End Of Deck card
182 static word12 end_of_deck_card[] =
183 {
184 00005,
185 00000,
186 00000,
187 00000,
188 00000,
189 00000,
190 00000,
191 00000,
192 00000,
193 00000,
194 00000,
195 00000,
196 00000,
197 00000,
198 00000,
199 00000,
200 00000,
201 02000,
202 02400,
203 02400,
204 02400,
205 03700,
206 00000,
207 00000,
208 03721,
209 02112,
210 02104,
211 02104,
212 03737,
213 00000,
214 00000,
215 00021,
216 00021,
217 00021,
218 00021,
219 00037,
220 00000,
221 00000,
222 01621,
223 02125,
224 02125,
225 02125,
226 03737,
227 00000,
228 00000,
229 03716,
230 00221,
231 00421,
232 01021,
233 03737,
234 00000,
235 00000,
236 02100,
237 02500,
238 02500,
239 02500,
240 03700,
241 00000,
242 00000,
243 00000,
244 00000,
245 00000,
246 00000,
247 00000,
248 00000,
249 00000,
250 00000,
251 00000,
252 00000,
253 00000,
254 00000,
255 00000,
256 00000,
257 00000,
258 00000,
259 00000,
260 00000,
261 00000,
262 00000,
263 00005,
264 };
265
266 /*
267 * MCC Punch Codes -> ASCI Conversion Table
268 * The table entry is the MCC Punch Code and the
269 * index of the entry is the ASCII character code.
270 */
271
272 static word12 mcc_punch_codes[128] =
273 {
274 05403, // ? (9-12-0-8-1) 0xb03 2819
275 04401, // ? (9-12-1) 0x901 2305
276 04201, // ? (9-12-2) 0x881 2177
277 04101, // ? (9-12-3) 0x841 2113
278 00005, // ? (9-7) 0x5 5
279 01023, // ? (9-0-8-5) 0x213 531
280 01013, // ? (9-0-8-6) 0x20b 523
281 01007, // ? (9-0-8-7) 0x207 519
282 02011, // ? (9-11-6) 0x409 1033
283 04021, // ? (9-12-5) 0x811 2065
284 02021, // ? (9-11-5) 0x411 1041
285 04103, // ? (9-12-8-3) 0x843 2115
286 04043, // ? (9-12-8-4) 0x823 2083
287 04023, // ? (9-12-8-5) 0x813 2067
288 04013, // ? (9-12-8-6) 0x80b 2059
289 04007, // ? (9-12-8-7) 0x807 2055
290 06403, // ? (12-11-9-8-1) 0xd03 3331
291 02401, // ? (9-11-1) 0x501 1281
292 02201, // ? (9-11-2) 0x481 1153
293 02101, // ? (9-11-3) 0x441 1089
294 00043, // ? (9-8-4) 0x23 35
295 00023, // ? (9-8-5) 0x13 19
296 00201, // ? (9-2) 0x81 129
297 01011, // ? (9-0-6) 0x209 521
298 02003, // ? (9-11-8) 0x403 1027
299 02403, // ? (9-11-8-1) 0x503 1283
300 00007, // ? (9-8-7) 0x7 7
301 01005, // ? (9-0-7) 0x205 517
302 02043, // ? (9-11-8-4) 0x423 1059
303 02023, // ? (9-11-8-5) 0x413 1043
304 02013, // ? (9-11-8-6) 0x40b 1035
305 02007, // ? (9-11-8-7) 0x407 1031
306 00000, // () 0x0 0
307 02202, // ! (11-8-2) 0x482 1154
308 00006, // " (8-7) 0x6 6
309 00102, // # (8-3) 0x42 66
310 02102, // $ (11-8-3) 0x442 1090
311 01042, // % (0-8-4) 0x222 546
312 04000, // & (12) 0x800 2048
313 00022, // ' (8-5) 0x12 18
314 04022, // ( (12-8-5) 0x812 2066
315 02022, // ) (11-8-5) 0x412 1042
316 02042, // * (11-8-4) 0x422 1058
317 04012, // + (12-8-6) 0x80a 2058
318 01102, // , (0-8-3) 0x242 578
319 02000, // - (11) 0x400 1024
320 04102, // . (12-8-3) 0x842 2114
321 01400, // / (0-1) 0x300 768
322 01000, // 0 (0) 0x200 512
323 00400, // 1 (1) 0x100 256
324 00200, // 2 (2) 0x80 128
325 00100, // 3 (3) 0x40 64
326 00040, // 4 (4) 0x20 32
327 00020, // 5 (5) 0x10 16
328 00010, // 6 (6) 0x8 8
329 00004, // 7 (7) 0x4 4
330 00002, // 8 (8) 0x2 2
331 00001, // 9 (9) 0x1 1
332 00202, // : (8-2) 0x82 130
333 02012, // ; (11-8-6) 0x40a 1034
334 04042, // < (12-8-4) 0x822 2082
335 00012, // = (8-6) 0xa 10
336 01012, // > (0-8-6) 0x20a 522
337 01006, // ? (0-8-7) 0x206 518
338 00042, // @ (8-4) 0x22 34
339 04400, // A (12-1) 0x900 2304
340 04200, // B (12-2) 0x880 2176
341 04100, // C (12-3) 0x840 2112
342 04040, // D (12-4) 0x820 2080
343 04020, // E (12-5) 0x810 2064
344 04010, // F (12-6) 0x808 2056
345 04004, // G (12-7) 0x804 2052
346 04002, // H (12-8) 0x802 2050
347 04001, // I (12-9) 0x801 2049
348 02400, // J (11-1) 0x500 1280
349 02200, // K (11-2) 0x480 1152
350 02100, // L (11-3) 0x440 1088
351 02040, // M (11-4) 0x420 1056
352 02020, // N (11-5) 0x410 1040
353 02010, // O (11-6) 0x408 1032
354 02004, // P (11-7) 0x404 1028
355 02002, // Q (11-8) 0x402 1026
356 02001, // R (11-9) 0x401 1025
357 01200, // S (0-2) 0x280 640
358 01100, // T (0-3) 0x240 576
359 01040, // U (0-4) 0x220 544
360 01020, // V (0-5) 0x210 528
361 01010, // W (0-6) 0x208 520
362 01004, // X (0-7) 0x204 516
363 01002, // Y (0-8) 0x202 514
364 01001, // Z (0-9) 0x201 513
365 05022, // [ (12-0-8-5) 0xa12 2578
366 04202, // \ (12-8-2) 0x882 2178
367 06022, // ] (12-11-8-5) 0xc12 3090
368 02006, // ^ (11-8-7) 0x406 1030
369 01022, // _ (0-8-5) 0x212 530
370 00402, // ` (8-1) 0x102 258
371 05400, // a (12-0-1) 0xb00 2816
372 05200, // b (12-0-2) 0xa80 2688
373 05100, // c (12-0-3) 0xa40 2624
374 05040, // d (12-0-4) 0xa20 2592
375 05020, // e (12-0-5) 0xa10 2576
376 05010, // f (12-0-6) 0xa08 2568
377 05004, // g (12-0-7) 0xa04 2564
378 05002, // h (12-0-8) 0xa02 2562
379 05001, // i (12-0-9) 0xa01 2561
380 06400, // j (12-11-1) 0xd00 3328
381 06200, // k (12-11-2) 0xc80 3200
382 06100, // l (12-11-3) 0xc40 3136
383 06040, // m (12-11-4) 0xc20 3104
384 06020, // n (12-11-5) 0xc10 3088
385 06010, // o (12-11-6) 0xc08 3080
386 06004, // p (12-11-7) 0xc04 3076
387 06002, // q (12-11-8) 0xc02 3074
388 06001, // r (12-11-9) 0xc01 3073
389 03200, // s (11-0-2) 0x680 1664
390 03100, // t (11-0-3) 0x640 1600
391 03040, // u (11-0-4) 0x620 1568
392 03020, // v (11-0-5) 0x610 1552
393 03010, // w (11-0-6) 0x608 1544
394 03004, // x (11-0-7) 0x604 1540
395 03002, // y (11-0-8) 0x602 1538
396 03001, // z (11-0-9) 0x601 1537
397 05000, // { (12-0) 0xa00 2560
398 04006, // | (12-8-7) 0x806 2054
399 03000, // } (11-0) 0x600 1536
400 03400, // ~ (11-0-1) 0x700 1792
401 04005, // (12-7-9) 0x805 2053
402 };
403
404 static word12 row_bit_masks[] =
405 {
406 0x800,
407 0x400,
408 0x200,
409 0x100,
410 0x080,
411 0x040,
412 0x020,
413 0x010,
414 0x008,
415 0x004,
416 0x002,
417 0x001,
418 };
419
mcc_to_ascii(word12 punch_code)420 static int mcc_to_ascii(word12 punch_code)
421 {
422 for (uint i = 0; i < 128; i++)
423 {
424 if (mcc_punch_codes[i] == punch_code)
425 {
426 return i;
427 }
428 }
429
430 return -1;
431 }
432
433 // Scans data cards to determine if they contain all valid MCC punch codes
check_for_valid_mcc_cards()434 static bool check_for_valid_mcc_cards()
435 {
436 CARD_CACHE_ENTRY *current_card = data_card_cache.first_cache_card;
437 while (current_card != NULL)
438 {
439 for (uint col = 0; col < CARD_COL_COUNT; col++)
440 {
441 if (mcc_to_ascii(current_card->card->column[col]) == -1)
442 {
443 return false;
444 }
445 }
446
447 current_card = current_card->next_entry;
448 }
449 return true;
450 }
451
convert_mcc_to_ascii(word12 * buffer,char * ascii_string)452 static void convert_mcc_to_ascii(word12 *buffer, char *ascii_string)
453 {
454 for (uint i = 0; i < CARD_COL_COUNT; i++)
455 {
456 int c = mcc_to_ascii(buffer[i]);
457 if (output_debug)
458 {
459 fprintf(stderr, "+++ Punch Code %04o = '%c'\n", buffer[i], c);
460 }
461 if (c == -1)
462 {
463 c = ' ';
464 }
465 ascii_string[i] = c;
466 }
467 ascii_string[CARD_COL_COUNT] = 0;
468 }
469
470 /*
471 * Glyph Pattern Lookup
472 * This is the parsing of the "lace" cards and extracting the ASCII characters
473 * have been punched into the cards (as glphys) so the operator knows how to
474 * deliver the deck.
475 */
476
477 #define NUM_GLYPH_CHAR_PATTERNS 45
478
479 static uint8 glyph_char_patterns[NUM_GLYPH_CHAR_PATTERNS][CHAR_MATRIX_BYTES] =
480 {
481 // Asterisk
482 {037, 037, 037, 037, 037},
483 // Space
484 {000, 000, 000, 000, 000},
485 // Period
486 {000, 003, 003, 003, 000},
487 // >
488 {021, 000, 012, 000, 004},
489 // A
490 {037, 024, 024, 024, 037},
491 // B
492 {037, 025, 025, 025, 012},
493 // C
494 {037, 021, 021, 021, 021},
495 // D
496 {037, 021, 021, 021, 016},
497 // E
498 {037, 025, 025, 025, 021},
499 // F
500 {037, 024, 024, 024, 020},
501 // G
502 {037, 021, 021, 025, 027},
503 // H
504 {037, 004, 004, 004, 037},
505 // I
506 {021, 021, 037, 021, 021},
507 // J
508 {003, 001, 001, 001, 037},
509 // K
510 {037, 004, 004, 012, 021},
511 // L
512 {037, 001, 001, 001, 001},
513 // M
514 {037, 010, 004, 010, 037},
515 // N
516 {037, 010, 004, 002, 037},
517 // O
518 {037, 021, 021, 021, 037},
519 // P
520 {037, 024, 024, 024, 034},
521 // Q
522 {037, 021, 025, 023, 037},
523 // R
524 {037, 024, 024, 026, 035},
525 // S
526 {035, 025, 025, 025, 027},
527 // T
528 {020, 020, 037, 020, 020},
529 // U
530 {037, 001, 001, 001, 037},
531 // V
532 {030, 006, 001, 006, 030},
533 // W
534 {037, 002, 004, 002, 037},
535 // X
536 {021, 012, 004, 012, 021},
537 // Y
538 {020, 010, 007, 010, 020},
539 // Z
540 {021, 027, 025, 035, 021},
541 // 0
542 {016, 021, 021, 021, 016},
543 // 1
544 {000, 010, 000, 037, 000},
545 // 2
546 {023, 025, 025, 025, 035},
547 // 3
548 {021, 025, 025, 025, 037},
549 // 4
550 {034, 004, 004, 004, 037},
551 // 5
552 {035, 025, 025, 025, 022},
553 // 6
554 {037, 005, 005, 005, 007},
555 // 7
556 {020, 021, 022, 024, 030},
557 // 8
558 {012, 025, 025, 025, 012},
559 // 9
560 {034, 024, 024, 024, 037},
561 // Underscore
562 {001, 001, 001, 001, 001},
563 // Hyphen
564 {000, 004, 004, 004, 000},
565 // (
566 {000, 004, 012, 021, 000},
567 // )
568 {000, 021, 012, 004, 000},
569 // /
570 {001, 002, 004, 010, 020}};
571
572 static char glyph_chars[NUM_GLYPH_CHAR_PATTERNS] =
573 {
574 '*', ' ', '.', '>', 'A', 'B', 'C', 'D', 'E', 'F',
575 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
576 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
577 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
578 '_', '-', '(', ')', '/'};
579
580 static uint8 glyph_starting_column[11] =
581 {
582 73, 66, 59, 52, 45, 38, 31, 24, 17, 10, 3};
583
584 static char row_prefix_chars[] = "&-0123456789";
585
print_card(FILE * out_file,card_image_t * card,bool flip_card)586 static void print_card(FILE *out_file, card_image_t *card, bool flip_card)
587 {
588 if (output_cards)
589 {
590 fprintf(out_file, " +--------------------------------------------------------------------------------+\n");
591 for (int row = 0; row < 12; row++)
592 {
593 fprintf(out_file, "%c|", row_prefix_chars[row]);
594 if (flip_card)
595 {
596 for (int col = CARD_COL_COUNT - 1; col >= 0; col--)
597 {
598 fprintf(out_file, card->column[col] & row_bit_masks[row] ? "*" : " ");
599 }
600 }
601 else
602 {
603 for (int col = 0; col < CARD_COL_COUNT; col++)
604 {
605 fprintf(out_file, card->column[col] & row_bit_masks[row] ? "*" : " ");
606 }
607 }
608 fprintf(out_file, "|\n");
609 }
610 fprintf(out_file, " +--------------------------------------------------------------------------------+\n\n");
611 }
612 }
613
log_char_matrix_pattern(uint8 * char_matrix)614 static void log_char_matrix_pattern(uint8 *char_matrix)
615 {
616 fprintf(stderr, "\nChar Matrix\n");
617 for (uint col_offset = 0; col_offset < CHAR_MATRIX_BYTES; col_offset++)
618 {
619 fprintf(stderr, " %03o\n", char_matrix[col_offset]);
620 }
621
622 fprintf(stderr, "\r\n");
623 for (uint row = 0; row < 5; row++)
624 {
625 for (uint col = 0; col < CHAR_MATRIX_BYTES; col++)
626 {
627 if ((char_matrix[col] >> (4 - row)) & 0x1)
628 {
629 fprintf(stderr, "*");
630 }
631 else
632 {
633 fprintf(stderr, " ");
634 }
635 }
636 fprintf(stderr, "\r\n");
637 }
638 fprintf(stderr, "\r\n");
639 }
640
search_glyph_patterns(uint8 * matrix)641 static char search_glyph_patterns(uint8 *matrix)
642 {
643 for (int pattern = 0; pattern < NUM_GLYPH_CHAR_PATTERNS; pattern++)
644 {
645 if (memcmp(matrix, &glyph_char_patterns[pattern], CHAR_MATRIX_BYTES) == 0)
646 {
647 return glyph_chars[pattern];
648 }
649 }
650
651 fprintf(stderr, "*** Warning: Punch found unknown block character pattern\n");
652 log_char_matrix_pattern(matrix);
653
654 return ' ';
655 }
656
get_lace_char(const word12 * buffer,uint char_pos)657 static char get_lace_char(const word12 *buffer, uint char_pos)
658 {
659 if (char_pos >= GLYPHS_PER_CARD)
660 {
661 fprintf(stderr, "*** Error: Attempt to read punch block character out of range (%u)\n", char_pos);
662 exit(4);
663 }
664
665 bool top = char_pos < 11; // Top or bottom line of characters
666 uint char_offset = (char_pos < 11) ? char_pos : char_pos - 11; // Character number in the line
667 word12 col_buffer[5]; // The extracted 5 columns for the character
668
669 // Extract the five 12-bit words from the main buffer that make up the character
670 // Note that in this process we reverse the character image so it reads normally
671 // (characters are punched in reverse)
672 for (uint col_offset = 0; col_offset < 5; col_offset++)
673 {
674 col_buffer[4 - col_offset] = buffer[glyph_starting_column[char_offset] + col_offset];
675 }
676
677 // Now shift the characters into the 5x5 matrix buffer
678 uint8 char_matrix[CHAR_MATRIX_BYTES];
679
680 for (uint col_offset = 0; col_offset < CHAR_MATRIX_BYTES; col_offset++)
681 {
682 char_matrix[col_offset] = (col_buffer[col_offset] >> (top ? 6 : 0)) & 0x1F;
683 }
684
685 char c = search_glyph_patterns(char_matrix);
686
687 return c;
688 }
689
scan_card_for_glyphs(card_image_t * card)690 static void scan_card_for_glyphs(card_image_t *card)
691 {
692 for (uint c_pos = 0; c_pos < 22; c_pos++)
693 {
694 char c = get_lace_char(card->column, c_pos);
695 uint current_length = strlen(glyph_buffer);
696 if (current_length < (sizeof(glyph_buffer) - 1))
697 {
698 glyph_buffer[current_length++] = c;
699 glyph_buffer[current_length] = 0;
700 }
701 }
702 }
703
allocate_card()704 static card_image_t *allocate_card()
705 {
706 card_image_t *card = malloc(sizeof(card_image_t));
707 if (card == NULL)
708 {
709 fprintf(stderr, "*** Error: Failed to allocate card image!\n");
710 exit(1);
711 }
712
713 memset(card, 0, sizeof(card_image_t));
714
715 return card;
716 }
717
read_card(FILE * in_file)718 static card_image_t *read_card(FILE *in_file)
719 {
720 uint8 byte_buffer[BYTES_PER_CARD];
721
722 int bytes_read = fread(&byte_buffer, 1, BYTES_PER_CARD, in_file);
723
724 if (bytes_read == 0)
725 {
726 if (feof(in_file))
727 {
728 // We've hit the end of file so just bail out
729 return NULL;
730 }
731 else if (ferror(in_file))
732 {
733 // Something bad happened, report the error and exit
734 perror("Reading card file\n");
735 exit(2);
736 }
737 else
738 {
739 fprintf(stderr, "*** Error: fread returned zero but failed to set the error or eof flags!\n");
740 exit(2);
741 }
742 }
743
744 // Make sure we read a full card
745 if (bytes_read != BYTES_PER_CARD)
746 {
747 fprintf(stderr, "*** Error: failed to read a full card (only read %d of %d bytes)\n", bytes_read, BYTES_PER_CARD);
748 exit(3);
749 }
750
751 // We have a full card so allocate a card and convert the byte buffer into the card image
752 card_image_t *card = allocate_card();
753
754 for (int current_nibble = 0; current_nibble < NIBBLES_PER_CARD; current_nibble++)
755 {
756 int byte_offset = current_nibble / 2;
757 int nibble_offset = (2 - (current_nibble % 3)) * 4;
758 int col = current_nibble / 3;
759
760 word12 nibble = (current_nibble & 0x1) ? byte_buffer[byte_offset] : (byte_buffer[byte_offset] >> 4);
761 nibble &= 0x00000F;
762 card->column[col] |= (nibble << nibble_offset);
763 }
764
765 if (output_debug)
766 {
767 print_card(stderr, card, false);
768 }
769
770 return card;
771 }
772
save_card_in_cache(CARD_CACHE * card_cache,card_image_t * card)773 static void save_card_in_cache(CARD_CACHE *card_cache, card_image_t *card)
774 {
775 CARD_CACHE_ENTRY *new_entry = malloc(sizeof(CARD_CACHE_ENTRY));
776
777 new_entry->card = card;
778 new_entry->next_entry = NULL;
779
780 if (card_cache->first_cache_card == NULL)
781 {
782 card_cache->first_cache_card = new_entry;
783 card_cache->last_cache_card = new_entry;
784 }
785 else
786 {
787 card_cache->last_cache_card->next_entry = new_entry;
788 card_cache->last_cache_card = new_entry;
789 }
790 }
791
print_event(enum parse_event event)792 static void print_event(enum parse_event event)
793 {
794 switch (event)
795 {
796 case NoEvent:
797 fprintf(stderr, "[No Event]");
798 break;
799 case BannerCard:
800 fprintf(stderr, "[Banner Card]");
801 break;
802 case EndOfDeckCard:
803 fprintf(stderr, "[End Of Deck Card]");
804 break;
805 case Card:
806 fprintf(stderr, "[Card]");
807 break;
808 case Done:
809 fprintf(stderr, "[Done]");
810 break;
811 default:
812 fprintf(stderr, "[unknown event %d]", event);
813 break;
814 }
815 }
816
print_state(enum parse_state state)817 static void print_state(enum parse_state state)
818 {
819 switch (state)
820 {
821 case Idle:
822 fprintf(stderr, "[Idle]");
823 break;
824 case StartingJob:
825 fprintf(stderr, "[Starting Job]");
826 break;
827 case PunchGlyphLookup:
828 fprintf(stderr, "[Punch Glyph Lookup]");
829 break;
830 case EndOfHeader:
831 fprintf(stderr, "[End Of Header]");
832 break;
833 case CacheCard:
834 fprintf(stderr, "[Cache Card]");
835 break;
836 case EndOfDeck:
837 fprintf(stderr, "[End Of Deck]");
838 break;
839 case EndOfJob:
840 fprintf(stderr, "[End Of Job]");
841 break;
842 default:
843 fprintf(stderr, "[unknown state %d]", state);
844 break;
845 }
846 }
847
transition_state(enum parse_event event,enum parse_state new_state)848 static void transition_state(enum parse_event event, enum parse_state new_state)
849 {
850 if (output_debug)
851 {
852 fprintf(stderr, ">>> State Transition: ");
853 print_event(event);
854 fprintf(stderr, " = ");
855 print_state(current_state);
856 fprintf(stderr, " -> ");
857 print_state(new_state);
858 fprintf(stderr, "\r\n");
859 }
860
861 current_state = new_state;
862 }
863
do_state_idle(enum parse_event event)864 static enum parse_event do_state_idle(enum parse_event event)
865 {
866 transition_state(event, Idle);
867
868 // no action
869
870 return NoEvent;
871 }
872
do_state_starting_job(enum parse_event event,card_image_t * card)873 static enum parse_event do_state_starting_job(enum parse_event event, card_image_t *card)
874 {
875 transition_state(event, StartingJob);
876
877 glyph_buffer[0] = 0; // Clear Glyph Buffer
878 save_card_in_cache(&banner_card_cache, card); // Save card in cache
879
880 return NoEvent;
881 }
882
do_state_scan_card_for_glyphs(enum parse_event event,card_image_t * card)883 static enum parse_event do_state_scan_card_for_glyphs(enum parse_event event, card_image_t *card)
884 {
885 transition_state(event, PunchGlyphLookup);
886
887 scan_card_for_glyphs(card);
888
889 save_card_in_cache(&banner_card_cache, card); // Save card in cache
890
891 return NoEvent;
892 }
893
do_state_end_of_header(enum parse_event event,card_image_t * card)894 static enum parse_event do_state_end_of_header(enum parse_event event, card_image_t *card)
895 {
896 transition_state(event, EndOfHeader);
897
898 save_card_in_cache(&banner_card_cache, card); // Save card in cache
899
900 if (output_debug)
901 {
902 fprintf(stderr, "\n++++ Glyph Buffer ++++\n'%s'\n", glyph_buffer);
903 }
904
905 return NoEvent;
906 }
907
do_state_cache_card(enum parse_event event,card_image_t * card)908 static enum parse_event do_state_cache_card(enum parse_event event, card_image_t *card)
909 {
910 transition_state(event, CacheCard);
911
912 save_card_in_cache(&data_card_cache, card); // Save card in cache
913
914 return NoEvent;
915 }
916
do_state_end_of_deck(enum parse_event event,card_image_t * card)917 static enum parse_event do_state_end_of_deck(enum parse_event event, card_image_t *card)
918 {
919 transition_state(event, EndOfDeck);
920
921 save_card_in_cache(&trailer_card_cache, card); // Save card in cache
922
923 return NoEvent;
924 }
925
do_state_end_of_job(enum parse_event event,card_image_t * card)926 static enum parse_event do_state_end_of_job(enum parse_event event, card_image_t *card)
927 {
928 transition_state(event, EndOfJob);
929
930 save_card_in_cache(&trailer_card_cache, card); // Save card in cache
931
932 return Done;
933 }
934
unexpected_event(enum parse_event event)935 static void unexpected_event(enum parse_event event)
936 {
937 if (output_debug)
938 {
939 fprintf(stderr, "*** Unexpected event ");
940 print_event(event);
941
942 fprintf(stderr, " in state ");
943 print_state(current_state);
944
945 fprintf(stderr, "***\n");
946 }
947 }
948
parse_card(card_image_t * card)949 static void parse_card(card_image_t *card)
950 {
951 enum parse_event event = Card;
952
953 if (memcmp(card, end_of_deck_card, sizeof(end_of_deck_card)) == 0)
954 {
955 if (output_debug)
956 {
957 fprintf(stderr, "*** Found End Of Deck Card ***\n");
958 }
959 event = EndOfDeckCard;
960 }
961
962 if (memcmp(card, banner_card, sizeof(banner_card)) == 0)
963 {
964 if (output_debug)
965 {
966 fprintf(stderr, "*** Found Banner Card ***\n");
967 }
968 event = BannerCard;
969 }
970
971 while (event != NoEvent)
972 {
973 enum parse_event current_event = event;
974 event = NoEvent;
975
976 switch (current_event)
977 {
978 case BannerCard:
979 switch (current_state)
980 {
981 case Idle:
982 event = do_state_starting_job(current_event, card);
983 break;
984
985 case PunchGlyphLookup:
986 event = do_state_end_of_header(current_event, card);
987 break;
988
989 case EndOfDeck:
990 event = do_state_end_of_job(current_event, card);
991 break;
992
993 default:
994 unexpected_event(current_event);
995 break;
996 }
997 break;
998
999 case EndOfDeckCard:
1000 switch (current_state)
1001 {
1002 case StartingJob:
1003 event = do_state_end_of_deck(current_event, card);
1004 break;
1005
1006 case PunchGlyphLookup:
1007 event = do_state_end_of_deck(current_event, card);
1008 break;
1009
1010 case EndOfHeader:
1011 event = do_state_end_of_deck(current_event, card);
1012 break;
1013
1014 case CacheCard:
1015 event = do_state_end_of_deck(current_event, card);
1016 break;
1017
1018 case EndOfDeck:
1019 event = do_state_end_of_deck(current_event, card);
1020 break;
1021
1022 default:
1023 unexpected_event(current_event);
1024 break;
1025 }
1026 break;
1027
1028 case Card:
1029 switch (current_state)
1030 {
1031 case StartingJob:
1032 event = do_state_scan_card_for_glyphs(current_event, card);
1033 break;
1034
1035 case PunchGlyphLookup:
1036 event = do_state_scan_card_for_glyphs(current_event, card);
1037 break;
1038
1039 case EndOfHeader:
1040 event = do_state_cache_card(current_event, card);
1041 break;
1042
1043 case CacheCard:
1044 event = do_state_cache_card(current_event, card);
1045 break;
1046
1047 case EndOfDeck:
1048 event = do_state_cache_card(current_event, card);
1049 break;
1050
1051 default:
1052 unexpected_event(current_event);
1053 break;
1054 }
1055 break;
1056
1057 case Done:
1058 switch (current_state)
1059 {
1060 case EndOfJob:
1061 event = do_state_idle(current_event);
1062 break;
1063
1064 default:
1065 unexpected_event(current_event);
1066 break;
1067 }
1068 break;
1069
1070 default:
1071 fprintf(stderr, "*** Error: Punch received unknown event!\n");
1072 break;
1073 }
1074 }
1075 }
1076
parse_cards(FILE * in_file)1077 static void parse_cards(FILE *in_file)
1078 {
1079 card_image_t *card;
1080 while ( (card = ( read_card(in_file) ) ) )
1081 {
1082 parse_card(card);
1083 if (output_debug)
1084 {
1085 fprintf(stderr, "\n");
1086 }
1087 }
1088 }
1089
init()1090 static void init()
1091 {
1092 memset(&banner_card_cache, 0, sizeof(banner_card_cache));
1093 memset(&data_card_cache, 0, sizeof(data_card_cache));
1094 memset(&trailer_card_cache, 0, sizeof(trailer_card_cache));
1095 }
1096
print_help(char * program)1097 static void print_help(char *program)
1098 {
1099 printf("\nMultics Punch Utility Program\n");
1100 printf("\nInvoking:\n");
1101 printf(" %s [options]\n", program);
1102 printf("Where options are:\n");
1103 printf(" -h, --help = Output this message\n");
1104 printf(" -a, --auto = Attempt to automatically determine the type of card deck\n");
1105 printf(" (Default; disables any previously enabled output options)\n");
1106 printf(" -n, --no-auto = Disable auto selection of the card format\n");
1107 printf(" (You must specify output control options)\n");
1108 printf(" -v, --version = Add version information to stderr output\n");
1109 printf("\nOutput control options:\n");
1110 printf(" By default 'auto' mode is active, where a scan attempts to determine the\n");
1111 printf(" type of deck. The scan order is: 'MCC', '7punch', and 'raw' (if neither\n");
1112 printf(" of the first two seem to apply). Note that the options below are\n");
1113 printf(" mutually exclusive.\n");
1114 printf(" -7, --7punch = Interpret the cards as 7punch data and output the data\n");
1115 printf(" (currently not supported!)\n");
1116 printf(" -c, --cards = Output an ASCII art form of the punched cards with an '*'\n");
1117 printf(" representing the punches\n");
1118 printf(" -f, --flip = If --cards is specified, the banner cards will be 'flipped'\n");
1119 printf(" so they can be read normally\n");
1120 printf(" -g, --glyphs = Output the glyphs parsed from the banner cards as ASCII\n");
1121 printf(" -m, --mcc = Interpret the cards as MCC Punch Codes\n");
1122 printf(" (Invalid punch codes will be converted to spaces)\n");
1123 printf(" -r, --raw = Dump the raw card data as 12-bit words in column order\n");
1124 printf("\nThis program will read a card spool file produced by the DPS8M Simulator,\n");
1125 printf("parse it, and produce the requested output on standard output. Note that only\n");
1126 printf("one output mode may be selected.\n");
1127
1128 printf("\n");
1129 printf("\n This software is made available under the terms of the ICU License,");
1130 printf("\n version 1.8.1 or later. For complete details, see the \"LICENSE.md\"");
1131 printf("\n included or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md");
1132 printf("\n");
1133 }
1134
1135 #ifdef USE_POPT
1136 static struct poptOption long_options[] = {
1137 #else
1138 static struct option long_options[] = {
1139 #endif /* ifdef USE_POPT */
1140 #ifdef USE_POPT
1141 {"7punch", '7', POPT_ARG_NONE, NULL, '7', "7punch", "7PUNCH"},
1142 {"auto", 'a', POPT_ARG_NONE, NULL, 'a', "auto", "AUTO"},
1143 {"cards", 'c', POPT_ARG_NONE, NULL, 'c', "cards", "CARDS"},
1144 {"debug", 'd', POPT_ARG_NONE, NULL, 'd', "debug", "DEBUG"},
1145 {"flip", 'f', POPT_ARG_NONE, NULL, 'f', "flip", "FLIP"},
1146 {"glyphs", 'g', POPT_ARG_NONE, NULL, 'g', "glyphs", "GLYPHS"},
1147 {"help", 'h', POPT_ARG_NONE, NULL, 'h', "help", "HELP"},
1148 {"mcc", 'm', POPT_ARG_NONE, NULL, 'm', "mcc", "MCC"},
1149 {"no-auto", 'n', POPT_ARG_NONE, NULL, 'n', "no-auto", "NOAUTO"},
1150 {"raw", 'r', POPT_ARG_NONE, NULL, 'r', "raw", "RAW"},
1151 {"version", 'v', POPT_ARG_NONE, NULL, 'v', "version", "VERSION"}
1152 #else
1153 {"7punch", no_argument, 0, '7'},
1154 {"auto", no_argument, 0, 'a'},
1155 {"cards", no_argument, 0, 'c'},
1156 {"debug", no_argument, 0, 'd'},
1157 {"flip", no_argument, 0, 'f'},
1158 {"glyphs", no_argument, 0, 'g'},
1159 {"help", no_argument, 0, 'h'},
1160 {"mcc", no_argument, 0, 'm'},
1161 {"no-auto", no_argument, 0, 'n'},
1162 {"raw", no_argument, 0, 'r'},
1163 {"version", no_argument, 0, 'v'},
1164 {0, 0, 0, 0 }
1165 #endif /* ifdef USE_POPT */
1166 };
1167
parse_options(int argc,char * argv[])1168 static void parse_options(int argc, char *argv[])
1169 {
1170 int c;
1171 bool done = false;
1172
1173 while (!done)
1174 {
1175 #ifdef USE_POPT
1176 poptContext opt_con;
1177 opt_con = poptGetContext(NULL, argc, (const char **)argv, long_options, 0);
1178 while ((c = poptGetNextOpt(opt_con)) >= 0) {
1179 #else
1180 int option_index = 0;
1181 c = getopt_long(argc, argv, "7acdfghmnrvV", long_options, &option_index);
1182 #endif /* ifdef USE_POPT */
1183
1184 switch (c)
1185 {
1186 case -1:
1187 done = true;
1188 break;
1189
1190 case '7':
1191 fprintf(stderr, "*** Sorry: 7punch format is not yet supported!\n");
1192 exit(1);
1193
1194 case 'a':
1195 output_auto = true;
1196 output_cards = false;
1197 output_glyphs = false;
1198 output_mcc = false;
1199 output_raw = false;
1200 break;
1201
1202 case 'c':
1203 output_auto = false;
1204 output_cards = true;
1205 output_glyphs = false;
1206 output_mcc = false;
1207 output_raw = false;
1208 break;
1209
1210 case 'd':
1211 output_debug = true;
1212 break;
1213
1214 case 'f':
1215 output_flip = true;
1216 break;
1217
1218 case 'g':
1219 output_auto = false;
1220 output_cards = false;
1221 output_glyphs = true;
1222 output_mcc = false;
1223 output_raw = false;
1224 break;
1225
1226 case 'm':
1227 output_auto = false;
1228 output_cards = false;
1229 output_glyphs = false;
1230 output_mcc = true;
1231 output_raw = false;
1232 break;
1233
1234 case 'n':
1235 output_auto = false;
1236 break;
1237
1238 case 'r':
1239 output_auto = false;
1240 output_cards = false;
1241 output_glyphs = false;
1242 output_mcc = false;
1243 output_raw = true;
1244 break;
1245
1246 case 'v':
1247 fprintf(stderr, "\nVersion 0.1\n");
1248 break;
1249
1250 case 'h':
1251 case '?':
1252 print_help(argv[0]);
1253 exit(1);
1254
1255 default:
1256 fprintf(stderr, "*** Internal Error: did not recognize option when parsing options, got %d\n", c);
1257 exit(1);
1258 }
1259 #ifdef USE_POPT
1260 }
1261 #endif /* ifdef USE_POPT */
1262 }
1263
1264 // Verify selected options
1265 bool override_in_effect = output_cards || output_glyphs || output_mcc || output_raw;
1266
1267 if (!output_auto && !override_in_effect)
1268 {
1269 fprintf(stderr, "*** Error: auto mode was disabled with no override mode selected!\n");
1270 exit(1);
1271 }
1272 }
1273
dump_cards(FILE * out_file)1274 static void dump_cards(FILE *out_file)
1275 {
1276 CARD_CACHE_ENTRY *current_entry = banner_card_cache.first_cache_card;
1277 while (current_entry != NULL)
1278 {
1279 print_card(out_file, current_entry->card, output_flip);
1280 current_entry = current_entry->next_entry;
1281 }
1282
1283 current_entry = data_card_cache.first_cache_card;
1284 while (current_entry != NULL)
1285 {
1286 print_card(out_file, current_entry->card, false);
1287 current_entry = current_entry->next_entry;
1288 }
1289
1290 current_entry = trailer_card_cache.first_cache_card;
1291 while (current_entry != NULL)
1292 {
1293 print_card(out_file, current_entry->card, output_flip);
1294 current_entry = current_entry->next_entry;
1295 }
1296 }
1297
dump_mcc(FILE * out_file)1298 static void dump_mcc(FILE *out_file)
1299 {
1300 char ascii_string[CARD_COL_COUNT + 1];
1301 CARD_CACHE_ENTRY *current_entry = data_card_cache.first_cache_card;
1302 while (current_entry != NULL)
1303 {
1304 convert_mcc_to_ascii(current_entry->card->column, ascii_string);
1305 fprintf(out_file, "%s\n", ascii_string);
1306 current_entry = current_entry->next_entry;
1307 }
1308 }
1309
dump_raw(FILE * out_file)1310 static void dump_raw(FILE *out_file)
1311 {
1312 CARD_CACHE_ENTRY *current_entry = data_card_cache.first_cache_card;
1313 while (current_entry != NULL)
1314 {
1315 if (output_debug)
1316 {
1317 fprintf(stderr, "\nCard:\n");
1318 for (uint col = 0; col < CARD_COL_COUNT; col++)
1319 {
1320 fprintf(stderr, " 0x%03X\n", current_entry->card->column[col]);
1321 }
1322 }
1323 for (int current_nibble = 0; current_nibble < NIBBLES_PER_CARD; current_nibble += 2)
1324 {
1325 uint8 byte = 0;
1326
1327 uint col = current_nibble / 3;
1328 uint nibble_offset = 2 - (current_nibble % 3);
1329
1330 uint nibble = (current_entry->card->column[col] >> (nibble_offset * 4)) & 0x00F;
1331 byte |= nibble << 4;
1332
1333 col = (current_nibble + 1) / 3;
1334 nibble_offset = 2 - ((current_nibble + 1) % 3);
1335
1336 nibble = (current_entry->card->column[col] >> (nibble_offset * 4)) & 0x00F;
1337 byte |= nibble;
1338
1339 if (fwrite(&byte, 1, 1, out_file) != 1)
1340 {
1341 perror("Failed to write output file\n");
1342 exit(5);
1343 }
1344 }
1345 current_entry = current_entry->next_entry;
1346 }
1347 }
1348
main(int argc,char * argv[])1349 int main(int argc, char *argv[])
1350 {
1351 fprintf(stderr, "****\nPunch File Utility\n****\n");
1352
1353 parse_options(argc, argv);
1354
1355 init();
1356
1357 parse_cards(stdin);
1358
1359 if (output_auto)
1360 {
1361 if (check_for_valid_mcc_cards())
1362 {
1363 output_mcc = true;
1364 fprintf(stderr, ">> Auto-detected MCC Punch Codes <<\n");
1365 }
1366 else
1367 {
1368 output_raw = true;
1369 fprintf(stderr, ">> Auto-detection did not recognize format so output mode set to raw <<\n");
1370 }
1371 }
1372
1373 if (output_cards)
1374 {
1375 fprintf(stderr, "\n*****\nWriting ASCII Art Card Images (banner cards%s flipped)\n*****\n", output_flip ? "" : " not");
1376 dump_cards(stdout);
1377 }
1378
1379 if (output_glyphs)
1380 {
1381 fprintf(stderr, "\n*****\nWriting Banner Glyphs as ASCII\n*****\n");
1382 fprintf(stdout, "%s\n", glyph_buffer);
1383 }
1384
1385 if (output_raw)
1386 {
1387 fprintf(stderr, "\n*****\nWriting raw output\n*****\n");
1388 dump_raw(stdout);
1389 }
1390
1391 if (output_mcc)
1392 {
1393 fprintf(stderr, "\n*****\nWriting MCC output\n*****\n");
1394 dump_mcc(stdout);
1395 }
1396
1397 }
1398