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

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