root/src/dps8/dps8_crdpun.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pun_init
  2. pun_reset
  3. remove_spaces
  4. log_char_matrix_pattern
  5. search_glyph_patterns
  6. get_lace_char
  7. scan_card_for_glyphs
  8. create_punch_file
  9. write_punch_files
  10. log_card
  11. print_event
  12. print_state
  13. print_transition
  14. clear_card_cache
  15. save_card_in_cache
  16. transition_state
  17. do_state_idle
  18. do_state_starting_job
  19. do_state_scan_card_for_glyphs
  20. do_state_end_of_header
  21. do_state_cache_card
  22. do_state_end_of_deck
  23. do_state_end_of_job
  24. unexpected_event
  25. parse_card
  26. punWriteRecord
  27. pun_iom_cmd
  28. pun_show_nunits
  29. pun_set_nunits
  30. pun_show_device_name
  31. pun_set_device_name
  32. pun_set_path
  33. pun_show_path
  34. pun_set_config
  35. pun_show_config

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * SPDX-License-Identifier: Multics
   5  * scspell-id: 7ec3e12d-f62d-11ec-8431-80ee73e9b8e7
   6  *
   7  * ---------------------------------------------------------------------------
   8  *
   9  * Copyright (c) 2007-2013 Michael Mondy
  10  * Copyright (c) 2012-2016 Harry Reed
  11  * Copyright (c) 2013-2016 Charles Anthony
  12  * Copyright (c) 2021 Dean Anderson
  13  * Copyright (c) 2021-2025 The DPS8M Development Team
  14  *
  15  * This software is made available under the terms of the ICU License.
  16  * See the LICENSE.md file at the top-level directory of this distribution.
  17  *
  18  * ---------------------------------------------------------------------------
  19  *
  20  * This source file may contain code comments that adapt, include, and/or
  21  * incorporate Multics program code and/or documentation distributed under
  22  * the Multics License.  In the event of any discrepancy between code
  23  * comments herein and the original Multics materials, the original Multics
  24  * materials should be considered authoritative unless otherwise noted.
  25  * For more details and historical background, see the LICENSE.md file at
  26  * the top-level directory of this distribution.
  27  *
  28  * ---------------------------------------------------------------------------
  29  */
  30 
  31 //-V::1048
  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 /* if defined(__MINGW64__) || defined(__MINGW32__) */
  58 
  59 #define DBG_CTR 1
  60 
  61 #if defined(FREE)
  62 # undef FREE
  63 #endif /* if defined(FREE) */
  64 #define FREE(p) do  \
  65   {                 \
  66     free((p));      \
  67     (p) = NULL;     \
  68   } while(0)
  69 
  70 //-- // XXX We use this where we assume there is only one unit
  71 //-- #define ASSUME0 0
  72 //--
  73 
  74 #define N_PUN_UNITS 1 // default
  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 }, // Don't move as it messes up DBG message
 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 /* if !defined(SPEED) */
 133     {
 134       MTAB_XTD | MTAB_VDV | \
 135       MTAB_NMO | MTAB_VALR,                 /* Mask               */
 136       0,                                    /* Match              */
 137       "NUNITS",                             /* Print string       */
 138       "NUNITS",                             /* Match string       */
 139       pun_set_nunits,                       /* Validation routine */
 140       pun_show_nunits,                      /* Display routine    */
 141       "Number of PUN units in the system",  /* Value descriptor   */
 142       NULL                                  /* Help               */
 143     },
 144     {
 145       MTAB_XTD | MTAB_VUN | \
 146       MTAB_VALR | MTAB_NC,                  /* Mask               */
 147       0,                                    /* Match              */
 148       "NAME",                               /* Print string       */
 149       "NAME",                               /* Match string       */
 150       pun_set_device_name,                  /* Validation routine */
 151       pun_show_device_name,                 /* Display routine    */
 152       "Set the punch device name",          /* Value descriptor   */
 153       NULL                                  /* Help               */
 154     },
 155     {
 156       MTAB_XTD | MTAB_VDV | MTAB_NMO | \
 157       MTAB_VALR | MTAB_NC,                  /* Mask               */
 158       0,                                    /* Match              */
 159       "PATH",                               /* Print string       */
 160       "PATH",                               /* Match string       */
 161       pun_set_path,                         /* Validation Routine */
 162       pun_show_path,                        /* Display Routine    */
 163       "Path to card punch directories",     /* Value Descriptor   */
 164       NULL                                  /* Help               */
 165     },
 166     {
 167       MTAB_XTD | MTAB_VUN,                  /* Mask               */
 168       0,                                    /* Match              */
 169       (char *) "CONFIG",                    /* Print string       */
 170       (char *) "CONFIG",                    /* Match string       */
 171       pun_set_config,                       /* Validation routine */
 172       pun_show_config,                      /* Display routine    */
 173       NULL,                                 /* Value descriptor   */
 174       NULL,                                 /* Help               */
 175     },
 176 
 177     { 0, 0, NULL, NULL, 0, 0, NULL, NULL }
 178   };
 179 
 180 DEVICE pun_dev = {
 181     "PUN",        /* Name                */
 182     pun_unit,     /* Units               */
 183     NULL,         /* Registers           */
 184     pun_mod,      /* Modifiers           */
 185     N_PUN_UNITS,  /* #Units              */
 186     10,           /* Address radix       */
 187     24,           /* Address width       */
 188     1,            /* Address increment   */
 189     8,            /* Data radix          */
 190     36,           /* Data width          */
 191     NULL,         /* Examine             */
 192     NULL,         /* Deposit             */
 193     pun_reset,    /* Reset               */
 194     NULL,         /* Boot                */
 195     NULL,         /* Attach              */
 196     NULL,         /* Detach              */
 197     NULL,         /* Context             */
 198     DEV_DEBUG,    /* Flags               */
 199     0,            /* Debug control flags */
 200     pun_dt,       /* Debug flag names    */
 201     NULL,         /* Memory size change  */
 202     NULL,         /* Logical name        */
 203     NULL,         /* Help                */
 204     NULL,         /* Attach help         */
 205     NULL,         /* Attach context      */
 206     NULL,         /* Description         */
 207     NULL          /* End                 */
 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;                   // fd of file to get all cards in punch code (w/banner cards)
 253     bool log_cards;                     // Flag to log card images
 254     enum parse_state current_state;
 255     char raw_file_name [SIR_MAXPATH + 1];  // Name associated with punfile_raw
 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];   // The -68 is to leave room for file name
 265 
 266 /*
 267  * pun_init()
 268  */
 269 
 270 // Once-only initialization
 271 
 272 void pun_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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  *                  Glyph Pattern Lookup
 378  * This is the parsing of the "lace" cards and extracting the ASCII characters
 379  * have been punched into the cards (as glyphs) so the operator knows how to
 380  * deliver the deck.
 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       // Asterisk
 388       { 037, 037, 037, 037, 037 },
 389       // Space
 390       { 000, 000, 000, 000, 000 },
 391       // Period
 392       { 000, 003, 003, 003, 000 },
 393       // >
 394       { 021, 000, 012, 000, 004 },
 395       // A
 396       { 037, 024, 024, 024, 037 },
 397       // B
 398       { 037, 025, 025, 025, 012 },
 399       // C
 400       { 037, 021, 021, 021, 021 },
 401       // D
 402       { 037, 021, 021, 021, 016 },
 403       // E
 404       { 037, 025, 025, 025, 021 },
 405       // F
 406       { 037, 024, 024, 024, 020 },
 407       // G
 408       { 037, 021, 021, 025, 027 },
 409       // H
 410       { 037, 004, 004, 004, 037 },
 411       // I
 412       { 021, 021, 037, 021, 021 },
 413       // J
 414       { 003, 001, 001, 001, 037 },
 415       // K
 416       { 037, 004, 004, 012, 021 },
 417       // L
 418       { 037, 001, 001, 001, 001 },
 419       // M
 420       { 037, 010, 004, 010, 037 },
 421       // N
 422       { 037, 010, 004, 002, 037 },
 423       // O
 424       { 037, 021, 021, 021, 037 },
 425       // P
 426       { 037, 024, 024, 024, 034 },
 427       // Q
 428       { 037, 021, 025, 023, 037 },
 429       // R
 430       { 037, 024, 024, 026, 035 },
 431       // S
 432       { 035, 025, 025, 025, 027 },
 433       // T
 434       { 020, 020, 037, 020, 020 },
 435       // U
 436       { 037, 001, 001, 001, 037 },
 437       // V
 438       { 030, 006, 001, 006, 030 },
 439       // W
 440       { 037, 002, 004, 002, 037 },
 441       // X
 442       { 021, 012, 004, 012, 021 },
 443       // Y
 444       { 020, 010, 007, 010, 020 },
 445       // Z
 446       { 021, 027, 025, 035, 021 },
 447       // 0
 448       { 016, 021, 021, 021, 016 },
 449       // 1
 450       { 000, 010, 000, 037, 000 },
 451       // 2
 452       { 023, 025, 025, 025, 035 },
 453       // 3
 454       { 021, 025, 025, 025, 037 },
 455       // 4
 456       { 034, 004, 004, 004, 037 },
 457       // 5
 458       { 035, 025, 025, 025, 022 },
 459       // 6
 460       { 037, 005, 005, 005, 007 },
 461       // 7
 462       { 020, 021, 022, 024, 030 },
 463       // 8
 464       { 012, 025, 025, 025, 012 },
 465       // 9
 466       { 034, 024, 024, 024, 037 },
 467       // Underscore
 468       { 001, 001, 001, 001, 001 },
 469       // Hyphen
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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;                              // Top or bottom line of chars
 566     uint char_offset   = (char_pos < 11) ? char_pos : char_pos - 11; // Character num in the line
 567     uint word_offset   = glyph_char_word_offset[char_offset];        // Starting word in the buffer
 568     uint nibble_offset = glyph_nibble_offset[char_offset];           // Starting nibble in the word
 569     word12 col_buffer[5];                                            // Extracted 5 cols for char
 570 
 571     // Extract the five 12-bit words from the main buffer that make up the character
 572     // Note that in this process we reverse the character image so it reads normally
 573     // (characters are punched in reverse)
 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     // Now shift the characters into the 5x5 matrix buffer
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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) // Low nibble of byte
 688             byte_buffer [byte_offset] |= nibble;
 689           else                    // High nibble of byte
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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             // 3 cols/word
 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         //for (uint col = 0; col < 80; col ++)
 755         for (int col = 79; col >= 0; col --)
 756           {
 757             // 3 cols/word
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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         /*NOTREACHED*/ /* unreachable */
 864 # endif /* if defined(SIGUSR2) */
 865 #endif /* if defined(USE_BACKTRACE) */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 897   {
 898     transition_state(event, state, Idle);
 899 
 900     // No Action
 901 
 902     return NoEvent;
 903   }
 904 
 905 static enum parse_event do_state_starting_job(enum parse_event event,
     /* [previous][next][first][last][top][bottom][index][help] */
 906                                               pun_state_t * state, word12 tally,
 907                                               word36 * card_buffer)
 908   {
 909     transition_state(event, state, StartingJob);
 910 
 911     clear_card_cache(state);                            // Clear card cache
 912     state -> glyph_buffer[0] = 0;                       // Clear Glyph Buffer
 913     save_card_in_cache(state, tally, card_buffer);      // Save card in cache
 914 
 915     return NoEvent;
 916   }
 917 
 918 static enum parse_event do_state_scan_card_for_glyphs(enum parse_event event,
     /* [previous][next][first][last][top][bottom][index][help] */
 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);    // Save card in cache
 928 
 929     return NoEvent;
 930   }
 931 
 932 static enum parse_event do_state_end_of_header(enum parse_event event,
     /* [previous][next][first][last][top][bottom][index][help] */
 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);      // Save card in cache
 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);                           // Create spool file
 966 
 967     // Write cached cards to spool file
 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);                            // Clear card cache
 976 
 977     return NoEvent;
 978   }
 979 
 980 static enum parse_event do_state_cache_card(enum parse_event event,
     /* [previous][next][first][last][top][bottom][index][help] */
 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);      // Save card in cache
 988 
 989     return NoEvent;
 990   }
 991 
 992 static enum parse_event do_state_end_of_deck(enum parse_event event,
     /* [previous][next][first][last][top][bottom][index][help] */
 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);      // Save card in cache
1000 
1001     return NoEvent;
1002   }
1003 
1004 static enum parse_event do_state_end_of_job(enum parse_event event,
     /* [previous][next][first][last][top][bottom][index][help] */
1005                                             pun_state_t * state,
1006                                             word12 tally,
1007                                             word36 * card_buffer)
1008   {
1009     transition_state(event, state, EndOfJob);
1010 
1011     // Write cached cards to spool file
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);                                // Clear card cache
1021 
1022     write_punch_files (state, card_buffer, tally, true);    // Write card to spool file
1023 
1024     // Close punch files
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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); //-V1037
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); //-V1037
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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; //-V536  // BUG: arbitrary error code; config switch
1230         return -1;
1231       }
1232 
1233 //dcl 1 raw aligned,    /* raw column binary card image */
1234 //    2 col (1:80) bit (12) unal,                             /* 80 columns */
1235 //    2 pad bit (12) unal;
1236 
1237     // Copy from core to buffer
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; //-V536
1251     return 0;
1252   }
1253 
1254 iom_cmd_rc_t pun_iom_cmd (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
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   // IDCW?
1270   if (IS_IDCW (p)) {
1271     // IDCW
1272     statep->ioMode = punNoMode;
1273     switch (p->IDCW_DEV_CMD) {
1274       case 011: // CMD 011 Punch binary
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: // CMD 031 Set Diagnostic Mode (load_mpc.pl1)
1281         sim_debug (DBG_DEBUG, & pun_dev, "%s: Set Diagnostic Mode\r\n", __func__);
1282         p->stati = 04000;
1283         break;
1284 
1285       case 040: // CMD 40 Reset status
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) // ignore bootload console probe
1293           sim_warn ("%s: PUN unrecognized device command  %02o\r\n", __func__, p->IDCW_DEV_CMD);
1294         p->stati      = 04501; // cmd reject, invalid opcode
1295         p->chanStatus = chanStatIncorrectDCW;
1296         return IOM_CMD_ERROR;
1297     } // switch IDCW_DEV_CMD
1298     sim_debug (DBG_DEBUG, & pun_dev, "%s: stati %04o\r\n", __func__, p->stati);
1299     return IOM_CMD_PROCEED;
1300   } // IDCW
1301 
1302   // Not IDCW; TDCW are captured in IOM, so must be IOTD, IOTP or IOTNP
1303   switch (statep->ioMode) {
1304     case punNoMode:
1305       //sim_printf ("%s: Unexpected IOTx\r\n", __func__);
1306       //sim_warn ("%s: Unexpected IOTx\r\n", __func__);
1307       //return IOM_CMD_ERROR;
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,
     /* [previous][next][first][last][top][bottom][index][help] */
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,
     /* [previous][next][first][last][top][bottom][index][help] */
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,
     /* [previous][next][first][last][top][bottom][index][help] */
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,
     /* [previous][next][first][last][top][bottom][index][help] */
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,
     /* [previous][next][first][last][top][bottom][index][help] */
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     // Verify that we don't exceed the maximum prefix
1378     // size ( -2 for the null terminator and a possible '/')
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,
     /* [previous][next][first][last][top][bottom][index][help] */
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,
     /* [previous][next][first][last][top][bottom][index][help] */
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) // done
1420           break;
1421 
1422         if (rc == -2) // error
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       } // process statements
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,
     /* [previous][next][first][last][top][bottom][index][help] */
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   }

/* [previous][next][first][last][top][bottom][index][help] */