root/src/dps8/dps8_crdrdr.c

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

DEFINITIONS

This source file includes following definitions.
  1. rdr_init
  2. rdr_reset
  3. asciiToH
  4. asciiToHGCOS
  5. getCardLine
  6. getCardData
  7. getRawCardData
  8. getBinCardData
  9. rdrReadRecord
  10. submit
  11. scanForCards
  12. rdrProcessEvent
  13. rdrCardReady
  14. rdr_iom_cmd
  15. rdr_show_nunits
  16. rdr_set_nunits
  17. rdr_show_device_name
  18. rdr_set_device_name
  19. rdr_set_path
  20. rdr_show_path

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * SPDX-License-Identifier: Multics
   5  * scspell-id: 89935fa0-f62d-11ec-beae-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) 2016 Michal Tomek
  13  * Copyright (c) 2021 Dean S. Anderson
  14  * Copyright (c) 2021-2025 The DPS8M Development Team
  15  *
  16  * This software is made available under the terms of the ICU License.
  17  * See the LICENSE.md file at the top-level directory of this distribution.
  18  *
  19  * ---------------------------------------------------------------------------
  20  *
  21  * This source file may contain code comments that adapt, include, and/or
  22  * incorporate Multics program code and/or documentation distributed under
  23  * the Multics License.  In the event of any discrepancy between code
  24  * comments herein and the original Multics materials, the original Multics
  25  * materials should be considered authoritative unless otherwise noted.
  26  * For more details and historical background, see the LICENSE.md file at
  27  * the top-level directory of this distribution.
  28  *
  29  * ---------------------------------------------------------------------------
  30  */
  31 
  32 #include <stdio.h>
  33 #include <ctype.h>
  34 #include <signal.h>
  35 #include <stdlib.h>
  36 #include <sys/types.h>
  37 #include <dirent.h>
  38 #include <unistd.h>
  39 #include <errno.h>
  40 #include <sys/stat.h>
  41 #include <fcntl.h>
  42 #include <stdint.h>
  43 
  44 #include "dps8.h"
  45 #include "dps8_iom.h"
  46 #include "dps8_crdrdr.h"
  47 #include "dps8_sys.h"
  48 #include "dps8_cable.h"
  49 #include "dps8_cpu.h"
  50 #include "dps8_faults.h"
  51 #include "dps8_scu.h"
  52 #include "dps8_utils.h"
  53 
  54 #if defined(NO_LOCALE)
  55 # define xstrerror_l strerror
  56 #endif
  57 
  58 #define DBG_CTR 1
  59 #define N_RDR_UNITS 1 // default
  60 
  61 #define snprintf_truncat(dst, size, ...)    \
  62   do                                        \
  63     {                                       \
  64       volatile size_t n = size;             \
  65       (void)snprintf (dst, n, __VA_ARGS__); \
  66     }                                       \
  67   while (0)
  68 
  69 static t_stat rdr_reset (DEVICE * dptr);
  70 static t_stat rdr_show_nunits (FILE *st, UNIT *uptr, int val, const void *desc);
  71 static t_stat rdr_set_nunits (UNIT * uptr, int32 value, const char * cptr, void * desc);
  72 static t_stat rdr_show_device_name (FILE *st, UNIT *uptr, int val, const void *desc);
  73 static t_stat rdr_set_device_name (UNIT * uptr, int32 value, const char * cptr, void * desc);
  74 static t_stat rdr_show_path (UNUSED FILE * st, UNIT * uptr, UNUSED int val,
  75                              UNUSED const void * desc);
  76 static t_stat rdr_set_path (UNUSED UNIT * uptr, UNUSED int32 value, const UNUSED char * cptr,
  77                             UNUSED void * desc);
  78 
  79 #define UNIT_FLAGS ( UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | \
  80                      UNIT_IDLE )
  81 UNIT rdr_unit [N_RDR_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 RDR_UNIT_NUM(uptr) ((uptr) - rdr_unit)
 102 
 103 static DEBTAB rdr_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 rdr_mod [] =
 117   {
 118 #if !defined(SPEED)
 119     { UNIT_WATCH, 1, "WATCH",   "WATCH",   0, 0, NULL, NULL },
 120     { UNIT_WATCH, 0, "NOWATCH", "NOWATCH", 0, 0, NULL, NULL },
 121 #endif /* if !defined(SPEED) */
 122     {
 123       MTAB_XTD | MTAB_VDV | \
 124       MTAB_NMO | MTAB_VALR,                 /* Mask               */
 125       0,                                    /* Match              */
 126       "NUNITS",                             /* Print string       */
 127       "NUNITS",                             /* Match string       */
 128       rdr_set_nunits,                       /* Validation routine */
 129       rdr_show_nunits,                      /* Display routine    */
 130       "Number of RDR 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       rdr_set_device_name,                  /* Validation routine */
 140       rdr_show_device_name,                 /* Display routine    */
 141       "Select the boot drive",              /* 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       rdr_set_path,                         /* Validation routine */
 151       rdr_show_path,                        /* Display routine    */
 152       "Path to card reader directories",    /* Value descriptor   */
 153       NULL                                  /* Help               */
 154     },
 155 
 156     { 0, 0, NULL, NULL, 0, 0, NULL, NULL }
 157   };
 158 
 159 DEVICE rdr_dev = {
 160     "RDR",        /* Name                */
 161     rdr_unit,     /* Units               */
 162     NULL,         /* Registers           */
 163     rdr_mod,      /* Modifiers           */
 164     N_RDR_UNITS,  /* #units              */
 165     10,           /* Address radix       */
 166     24,           /* Address width       */
 167     1,            /* Address increment   */
 168     8,            /* Data radix          */
 169     36,           /* Data width          */
 170     NULL,         /* Examine             */
 171     NULL,         /* Deposit             */
 172     rdr_reset,    /* Reset               */
 173     NULL,         /* Boot                */
 174     NULL,         /* Attach              */
 175     NULL,         /* Detach              */
 176     NULL,         /* Context             */
 177     DEV_DEBUG,    /* Flags               */
 178     0,            /* Debug control flags */
 179     rdr_dt,       /* Debug flag names    */
 180     NULL,         /* Memory size change  */
 181     NULL,         /* Logical name        */
 182     NULL,         /* Help                */
 183     NULL,         /* Attach help         */
 184     NULL,         /* Attach context      */
 185     NULL,         /* Description         */
 186     NULL          /* End                 */
 187 };
 188 
 189 enum deckFormat { sevenDeck, cardDeck, streamDeck, xDeck, gDeck, bDeck };
 190 
 191 // Windows cannot unlink an open file; rework the code to unlink the
 192 // submitted card deck after closing it.
 193 //  -- Add fname tp rdr_state
 194 //  -- Add unlink calls at eof close
 195 static struct rdr_state
 196   {
 197     enum rdr_mode
 198       {
 199         rdr_no_mode, rdr_rd_bin
 200       } io_mode;
 201     int deckfd;
 202     // Sending a ++END means that the read_cards command has to be reissued
 203     //enum { deckStart = 0, eof1Sent, uid1Sent, inputSent, eof2Sent, uid2Sent } deckState;
 204     enum { deckStart = 0, eof1Sent, uid1Sent, inputSent, eof2Sent} deckState;
 205     enum deckFormat deckFormat;
 206     bool running;
 207     char device_name [MAX_DEV_NAME_LEN];
 208     char fname [PATH_MAX+1];
 209   } rdr_state [N_RDR_UNITS_MAX];
 210 
 211 static char* rdr_name = "rdr";
 212 static char rdr_path_prefix[PATH_MAX+1];
 213 
 214 /*
 215  * rdr_init()
 216  */
 217 
 218 // Once-only initialization
 219 
 220 void rdr_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 221   {
 222     (void)memset (rdr_path_prefix, 0, sizeof (rdr_path_prefix));
 223     (void)memset (rdr_state, 0, sizeof (rdr_state));
 224     for (uint i = 0; i < N_RDR_UNITS_MAX; i ++)
 225       rdr_state [i] . deckfd = -1;
 226   }
 227 
 228 static t_stat rdr_reset (UNUSED DEVICE * dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 229   {
 230 
 231 
 232 
 233 
 234 
 235 
 236 
 237     return SCPE_OK;
 238   }
 239 
 240 // General Electric Cards
 241 //
 242 // General Electric used the following collating sequence on their machines,
 243 // including the GE 600 (the machine on which Multics was developed); this is
 244 // largely upward compatible from the IBM 026 commercial character set, and it
 245 // shows strong influence from the IBM 1401 character set while supporting the
 246 // full ASCII character set, with 64 printable characters, as it was understood
 247 // in the 1960's.
 248 //
 249 // GE   &-0123456789ABCDEFGHIJKLMNOPQR/STUVWXYZ[#@:>?+.](<\^$*);'_,%="!
 250 //      ________________________________________________________________
 251 //     /&-0123456789ABCDEFGHIJKLMNOPQR/STUVWXYZ #@:>V .¤(<§ $*);^±,%='"
 252 // 12 / O           OOOOOOOOO                        OOOOOO
 253 // 11|   O                   OOOOOOOOO                     OOOOOO
 254 //  0|    O                           OOOOOOOOO                  OOOOOO
 255 //  1|     O        O        O        O
 256 //  2|      O        O        O        O       O     O     O     O
 257 //  3|       O        O        O        O       O     O     O     O
 258 //  4|        O        O        O        O       O     O     O     O
 259 //  5|         O        O        O        O       O     O     O     O
 260 //  6|          O        O        O        O       O     O     O     O
 261 //  7|           O        O        O        O       O     O     O     O
 262 //  8|            O        O        O        O OOOOOOOOOOOOOOOOOOOOOOOO
 263 //  9|             O        O        O        O
 264 //   |__________________________________________________________________
 265 // In the above, the 0-8-2 punch shown as _ should be printed as an assignment
 266 // arrow, and the 11-8-2 punch shown as ^ should be printed as an up-arrow.
 267 // This conforms to the evolution of of these ASCII symbols from the time GE
 268 // adopted this character set and the present.
 269 
 270 // Sadly, AG91, App. C disagrees
 271 
 272 
 273 
 274 
 275 
 276 
 277 
 278 
 279 
 280 
 281 
 282 
 283 
 284 
 285 
 286 
 287 
 288 
 289 
 290 
 291 
 292 
 293 
 294 
 295 
 296 
 297 
 298 
 299 
 300 
 301 
 302 
 303 
 304 
 305 
 306 
 307 
 308 
 309 
 310 
 311 
 312 
 313 
 314 
 315 
 316 
 317 
 318 
 319 
 320 
 321 
 322 
 323 
 324 
 325 
 326 
 327 
 328 
 329 
 330 
 331 
 332 
 333 
 334 
 335 
 336 
 337 
 338 
 339 
 340 
 341 
 342 
 343 
 344 
 345 
 346 
 347 
 348 
 349 
 350 
 351 
 352 
 353 
 354 
 355 
 356 
 357 
 358 
 359 
 360 // From card_codes_.alm
 361 
 362 static uint16 table [128] =
 363   {
 364     05403, 04401, 04201, 04101, 00005, 01023, 01013, 01007,
 365     02011, 04021, 02021, 04103, 04043, 04023, 04013, 04007,
 366     06403, 02401, 02201, 02101, 00043, 00023, 00201, 01011,
 367     02003, 02403, 00007, 01005, 02043, 02023, 02013, 02007,
 368     00000, 02202, 00006, 00102, 02102, 01042, 04000, 00022,
 369     04022, 02022, 02042, 04012, 01102, 02000, 04102, 01400,
 370     01000, 00400, 00200, 00100, 00040, 00020, 00010, 00004,
 371     00002, 00001, 00202, 02012, 04042, 00012, 01012, 01006,
 372     00042, 04400, 04200, 04100, 04040, 04020, 04010, 04004,
 373     04002, 04001, 02400, 02200, 02100, 02040, 02020, 02010,
 374     02004, 02002, 02001, 01200, 01100, 01040, 01020, 01010,
 375     01004, 01002, 01001, 05022, 04202, 06022, 02006, 01022,
 376     00402, 05400, 05200, 05100, 05040, 05020, 05010, 05004,
 377     05002, 05001, 06400, 06200, 06100, 06040, 06020, 06010,
 378     06004, 06002, 06001, 03200, 03100, 03040, 03020, 03010,
 379     03004, 03002, 03001, 05000, 04006, 03000, 03400, 00000
 380   };
 381 
 382 static uint16 gcos_table [128] =
 383   {
 384     05403, 04401, 04201, 04101, 00005, 01023, 01013, 01007,
 385     02011, 04021, 02021, 04103, 04043, 04023, 04013, 04007,
 386     06403, 02401, 02201, 02101, 00043, 00023, 00201, 01011,
 387     02003, 02403, 00007, 01005, 02043, 02023, 02013, 02007,
 388     00000, /*02202*/ 01006, /*00006*/ 01012, 00102, 02102, 01042, 04000, /*00022*/ 02006,
 389     04022, 02022, 02042, /*04012*/ 05000, 01102, 02000, 04102, 01400,
 390     01000, 00400, 00200, 00100, 00040, 00020, 00010, 00004,
 391     00002, 00001, /*00202*/ 00022, 02012, /*04042*/ 04012, /*00012*/ 01022, /*01012*/ 00012, /*01006*/ 00006,
 392     00042, 04400, 04200, 04100, 04040, 04020, 04010, 04004,
 393     04002, 04001, 02400, 02200, 02100, 02040, 02020, 02010,
 394     02004, 02002, 02001, 01200, 01100, 01040, 01020, 01010,
 395     01004, 01002, 01001, /*05022*/ 00202, /*04202*/ /* 01201 */ 04006, /*06022*/ 04042, /*02006*/ 03000, /*01022*/ 01202,
 396     00402, 05400, 05200, 05100, 05040, 05020, 05010, 05004,
 397     05002, 05001, 06400, 06200, 06100, 06040, 06020, 06010,
 398     06004, 06002, 06001, 03200, 03100, 03040, 03020, 03010,
 399     03004, 03002, 03001, 05000, 04006, 03000, 03400, 00000
 400   };
 401 
 402 static void asciiToH (char * str, uint * hstr, size_t l)
     /* [previous][next][first][last][top][bottom][index][help] */
 403   {
 404     char * p = str;
 405     for (size_t i = 0; i < l; i ++)
 406     //for (char * p = str; * p; p ++)
 407       {
 408         * hstr ++ = table [(* p) & 0177];
 409         p ++;
 410       }
 411   }
 412 
 413 static void asciiToHGCOS (char * str, uint * hstr, size_t l)
     /* [previous][next][first][last][top][bottom][index][help] */
 414   {
 415     char * p = str;
 416     for (size_t i = 0; i < l; i ++)
 417     //for (char * p = str; * p; p ++)
 418       {
 419         * hstr ++ = gcos_table [(* p) & 0177];
 420         p ++;
 421       }
 422   }
 423 
 424 
 425 
 426 
 427 
 428 
 429 
 430 
 431 
 432 
 433 
 434 
 435 
 436 
 437 
 438 
 439 
 440 
 441 
 442 
 443 
 444 
 445 
 446 
 447 
 448 
 449 
 450 
 451 
 452 static int getCardLine (int fd, unsigned char * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 453   {
 454     uint n = 0;
 455     buffer [n] = 0;
 456     while (1)
 457       {
 458         uint8 ch;
 459         ssize_t rc = read (fd, & ch, 1);
 460         if (rc <= 0) // eof or err
 461           return n == 0;
 462         if (ch == '\n')
 463           return 0;
 464         buffer [n ++] = ch;
 465         buffer [n] = 0;
 466         if (n > 79)
 467           return 0;
 468       }
 469 #if defined(SUNLINT) || !defined(__SUNPRO_C) && !defined(__SUNPRO_CC)
 470     /*NOTREACHED*/ /* unreachable */
 471     return 0;
 472 #endif /* if defined(SUNLINT) || !defined(__SUNPRO_C) && !defined(__SUNPRO_CC) */
 473   }
 474 
 475 static int getCardData (int fd, char * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 476   {
 477     (void)memset (buffer, 0, 80);
 478     ssize_t rc = read (fd, buffer, 80);
 479     if (rc < 0)
 480       return 0;
 481     return (int) rc;
 482   }
 483 
 484 #define rawCardImageBytes (80 * 12 / 8)
 485 static int getRawCardData (int fd, uint8_t * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 486   {
 487     (void)memset (buffer, 0, rawCardImageBytes + 2);
 488     ssize_t rc = read (fd, buffer, rawCardImageBytes);
 489     if (rc < 0)
 490       return 0;
 491     return (int) rc;
 492   }
 493 
 494 #define CARD_COL_COUNT 80
 495 #define NIBBLES_PER_COL 3
 496 #define NIBBLES_PER_CARD (CARD_COL_COUNT * NIBBLES_PER_COL)
 497 #define BYTES_PER_CARD (NIBBLES_PER_CARD / 2)
 498 
 499 typedef struct
 500 {
 501   word12 column[CARD_COL_COUNT];
 502 } card_image_t;
 503 
 504 card_image_t card;
 505 
 506 static int getBinCardData (int fd, card_image_t* card)
     /* [previous][next][first][last][top][bottom][index][help] */
 507   {
 508     uint8 byte_buffer[BYTES_PER_CARD];
 509 
 510     ssize_t bytes_read = read (fd, byte_buffer, BYTES_PER_CARD);
 511     if (bytes_read <= 0)
 512       return 0;
 513 
 514     for (int col = 0; col < CARD_COL_COUNT; col++)
 515       {
 516         card->column[col] = 0;
 517       }
 518 
 519     for (int current_nibble = 0; current_nibble < NIBBLES_PER_CARD; current_nibble++)
 520       {
 521         int byte_offset = current_nibble / 2;
 522         int nibble_offset = (2 - (current_nibble % 3)) * 4;
 523         int col = current_nibble / 3;
 524 
 525         word12 nibble = (current_nibble & 0x1) ? byte_buffer[byte_offset] : (byte_buffer[byte_offset] >> 4);
 526         nibble &= 0x00000F;
 527         card->column[col] |= (nibble << nibble_offset);
 528       }
 529 
 530     return (int) bytes_read;
 531   }
 532 
 533 #if defined(TESTING)
 534 static bool empty = false;
 535 #endif /* if defined(TESTING) */
 536 
 537 static int rdrReadRecord (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
 538 #if defined(TESTING)
 539   cpu_state_t * cpup  = _cpup;
 540   (void)cpup;
 541 #endif
 542   iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
 543   //sim_debug (DBG_NOTIFY, & rdr_dev, "Read binary\r\n");
 544   uint ctlr_unit_idx  = get_ctlr_idx (iomUnitIdx, chan);
 545   uint unitIdx        = cables->urp_to_urd[ctlr_unit_idx][p->IDCW_DEV_CODE].unit_idx;
 546 
 547 
 548 
 549 
 550 
 551 
 552 
 553 
 554 
 555 
 556 
 557 
 558 
 559 
 560 
 561 
 562 
 563 #if defined(TESTING)
 564   sim_printf ("hopper not empty\r\n");
 565   empty = false;
 566 #endif
 567   unsigned char cardImage [80] = "";
 568   uint8_t rawCardImage [rawCardImageBytes + 2 ];
 569   size_t l = 0;
 570   // initialize to quiet compiler
 571   enum deckFormat thisCard = cardDeck;
 572 
 573   static int jobNo = 0;
 574 
 575   switch (rdr_state [unitIdx].deckState) {
 576     case deckStart: {
 577 #if defined(TESTING)
 578   sim_printf ("deckStart: sending ++EOF\r\n");
 579 #endif
 580       strcpy ((char *) cardImage, "++EOF");
 581       l = strlen ((char *) cardImage);
 582       thisCard = cardDeck; //-V1048
 583       rdr_state [unitIdx].deckState = eof1Sent;
 584       jobNo ++;
 585     }
 586     break;
 587 
 588     case eof1Sent: {
 589 #if defined(TESTING)
 590   sim_printf ("eof1Sent: sending ++UID\r\n");
 591 #endif
 592       (void)sprintf ((char *) cardImage, "++UID %d", jobNo);
 593       l = strlen ((char *) cardImage);
 594       thisCard = cardDeck; //-V1048
 595       rdr_state [unitIdx].deckState = uid1Sent;
 596     }
 597     break;
 598 
 599     case uid1Sent: {
 600 #if defined(TESTING)
 601   sim_printf ("uid1Sent: sending data\r\n");
 602 #endif
 603       int rc = getCardLine (rdr_state [unitIdx].deckfd, cardImage);
 604       if (rc) {
 605 #if defined(TESTING)
 606   sim_printf ("uid1Sent: getCardLine returned %d\r\n", rc);
 607 #endif
 608         close (rdr_state [unitIdx].deckfd);
 609         // Windows can't unlink open files; do it now...
 610         rc = unlink (rdr_state [unitIdx].fname);
 611         if (rc)
 612           (void)sir_error ("crdrdr: Failure unlinking card reader deck: %s (Error %d)", xstrerror_l (errno), errno);
 613         rdr_state [unitIdx].deckfd = -1;
 614         rdr_state [unitIdx].deckState = deckStart;
 615         p->stati = 04201; // hopper empty
 616         // No card was read: set initiate so that the DIM doesn't count
 617         // this card slot as read (crz backs up when initiate is set).
 618         p->initiate = true;
 619         return IOM_CMD_DISCONNECT;
 620       }
 621 #if defined(TESTING)
 622   sim_printf ("uid1Sent: getCardLine returned <%s>\r\n", cardImage);
 623 #endif
 624       l = strlen ((char *) cardImage);
 625       thisCard = cardDeck; //-V1048
 626       if (strncasecmp ((char *) cardImage, "++input", 7) == 0) {
 627 #if defined(TESTING)
 628   sim_printf ("uid1Sent: switching to inputSent <%s>\r\n", cardImage);
 629 #endif
 630         rdr_state [unitIdx].deckState = inputSent;
 631       }
 632     }
 633     break;
 634 
 635     // Reading the actual data cards
 636 
 637     case inputSent: {
 638 #if defined(TESTING)
 639   sim_printf ("inputSent: format %d\r\n", rdr_state [unitIdx].deckFormat);
 640 #endif
 641       switch (rdr_state [unitIdx].deckFormat) {
 642         case xDeck:
 643         case gDeck: {
 644 #if defined(TESTING)
 645   sim_printf ("inputSent: xDeck or gDeck\r\n");
 646 #endif
 647           int rc = getCardLine (rdr_state [unitIdx].deckfd, cardImage);
 648           if (rc) {
 649             rdr_state [unitIdx].deckState = deckStart;
 650             close (rdr_state [unitIdx].deckfd);
 651             // Windows can't unlink open files; do it now...
 652             int rc = unlink (rdr_state [unitIdx].fname);
 653             if (rc)
 654               (void)sir_error ("crdrdr: Failure unlinking card reader deck: %s (Error %d)", xstrerror_l (errno), errno);
 655             rdr_state [unitIdx].deckfd = -1;
 656             p->stati = 04201; // hopper empty
 657             // No card was read: set initiate so that the DIM doesn't count
 658             // this card slot as read (crz backs up when initiate is set).
 659             p->initiate = true;
 660             return IOM_CMD_DISCONNECT;
 661           }
 662           l = strlen ((char *) cardImage);
 663         }
 664         thisCard = cardDeck; //-V1048
 665         break;
 666 
 667         case cardDeck: {
 668 #if defined(TESTING)
 669   sim_printf ("inputSent: cardDeck\r\n");
 670 #endif
 671           int rc = getCardLine (rdr_state [unitIdx].deckfd, cardImage);
 672           if (rc) {
 673             strcpy ((char *) cardImage, "++EOF");
 674             rdr_state [unitIdx].deckState = eof2Sent;
 675           }
 676           l = strlen ((char *) cardImage);
 677         }
 678         thisCard = cardDeck; //-V1048
 679         break;
 680 
 681         case streamDeck: {
 682 #if defined(TESTING)
 683   sim_printf ("inputSent: streamDeck\r\n");
 684 #endif
 685           l = (size_t) getCardData (rdr_state [unitIdx].deckfd, (char *) cardImage);
 686           if (l) {
 687             thisCard = streamDeck;
 688           } else {
 689             strcpy ((char *) cardImage, "++EOF");
 690             l = strlen ((char *) cardImage);
 691             rdr_state [unitIdx].deckState = eof2Sent;
 692             thisCard = cardDeck; //-V1048
 693           }
 694 #if defined(TESTING)
 695   sim_printf ("inputSent: getCardLine returned <%s>\r\n", cardImage);
 696 #endif
 697         }
 698         break;
 699 
 700         case bDeck:
 701 #if defined(TESTING)
 702   sim_printf ("inputSent: bDeck\r\n");
 703 #endif
 704         l = (size_t) getBinCardData (rdr_state [unitIdx].deckfd, &card);
 705         if (l) {
 706           thisCard = bDeck;
 707         } else {
 708           rdr_state [unitIdx].deckState = deckStart;
 709           close (rdr_state [unitIdx].deckfd);
 710           // Windows can't unlink open files; do it now...
 711           int rc = unlink (rdr_state [unitIdx].fname);
 712           if (rc)
 713             (void)sir_error ("crdrdr: Failure unlinking card reader deck: %s (Error %d)", xstrerror_l (errno), errno);
 714           rdr_state [unitIdx].deckfd = -1;
 715           p->stati = 04201; // hopper empty
 716           // No card was read: set initiate so that the DIM doesn't count
 717           // this card slot as read (crz backs up when initiate is set).
 718           p->initiate = true;
 719           return IOM_CMD_DISCONNECT;
 720         }
 721         break;
 722 
 723         case sevenDeck: {
 724 #if defined(TESTING)
 725   sim_printf ("inputSent: 7Deck\r\n");
 726 #endif
 727           l = (size_t) getRawCardData (rdr_state [unitIdx].deckfd, rawCardImage);
 728           if (l) {
 729             thisCard = sevenDeck;
 730           } else {
 731             strcpy ((char *) cardImage, "++EOF");
 732             l = strlen ((char *) cardImage);
 733             rdr_state [unitIdx].deckState = eof2Sent;
 734             thisCard = cardDeck; //-V1048
 735           }
 736         }
 737         break;
 738 
 739       } // switch (deckFormat)
 740     } // case inputSent
 741     break;
 742 
 743 // Sending a ++END means that the read_cards command has to be reissued
 744 
 745     case eof2Sent: {
 746 # if defined(TESTING)
 747   sim_printf ("eof2Sent\r\n");
 748 # endif
 749       (void)sprintf ((char *) cardImage, "++UID %d", jobNo);
 750       l = strlen ((char *) cardImage);
 751       thisCard = cardDeck; //-V1048
 752       rdr_state [unitIdx].deckState = deckStart;
 753       close (rdr_state [unitIdx].deckfd);
 754       // Windows can't unlink open files; do it now...
 755       int rc = unlink (rdr_state [unitIdx].fname);
 756       if (rc)
 757         (void)sir_error ("crdrdr: Failure unlinking card reader deck: %s (Error %d)", xstrerror_l (errno), errno);
 758       rdr_state [unitIdx].deckfd = -1;
 759     }
 760     break;
 761 
 762 
 763 
 764 
 765 
 766 
 767 
 768 
 769 
 770 
 771 
 772 
 773 
 774 
 775 
 776 
 777 
 778 
 779 
 780 
 781 
 782 
 783 
 784 
 785 
 786 
 787 
 788 
 789 
 790   }
 791 
 792 
 793 
 794 
 795 
 796     //sim_printf ("card <%s>\r\n", cardImage);
 797 
 798 
 799 
 800 
 801 
 802 
 803 
 804 
 805 
 806 
 807 
 808 
 809 
 810 
 811   word36 buffer [27];
 812   switch (thisCard) {
 813     case bDeck: {
 814         // number of columns
 815         uint l = 80;
 816         // 12 bits / char
 817         uint nbits = (uint) l * 12;
 818         // 36 bits / word
 819         uint tally = (nbits + 35) / 36;
 820 
 821         if (tally > 27) { //-V547
 822             sim_warn ("Impossible rdr tally: %d > 27; truncating.\r\n", tally);
 823             tally = 27;
 824         }
 825 
 826         // Remember that Hollerith for blank is 0, this is really
 827         // filling the buffer with blanks.
 828         (void)memset (buffer, 0, sizeof (buffer));
 829         for (uint col = 0; col < l; col ++) {
 830             uint wordno  = col / 3;
 831             uint fieldno = col % 3;
 832             putbits36_12 (& buffer [wordno], fieldno * 12, (word12) card.column [col]);
 833       }
 834     }
 835     break;
 836     case sevenDeck: {
 837       // This will overread rawCardImage by 12 bits, but that's okay
 838       // because Multics will ignore the last 12 bits.
 839       for (uint i = 0; i < 27; i ++)
 840         buffer [i] = extr36 ((uint8 *) rawCardImage, i);
 841       //sim_printf ("7deck %012"PRIo64" %012"PRIo64" %012"PRIo64" %012"PRIo64"\r\n",
 842       //             buffer [0], buffer [1], buffer [2], buffer [3]);
 843     }
 844     break;
 845 
 846     case streamDeck:
 847 
 848 
 849 
 850 
 851 
 852 
 853 
 854 
 855 
 856 
 857     case xDeck:
 858     case gDeck:
 859     case cardDeck: {
 860       if (l > 80) {
 861         sim_warn ("Whups. rdr l %lu > 80; truncating.\r\n", (unsigned long)l);
 862         l = 80;
 863         //cardImage [l] = 0;
 864       }
 865 
 866       uint hbuf [l + 1];
 867 
 868       if (rdr_state [unitIdx].deckFormat == gDeck) {
 869           asciiToHGCOS ((char *) cardImage, hbuf, l);
 870       }
 871       else {
 872           asciiToH ((char *) cardImage, hbuf, l);
 873       }
 874 
 875       // 12 bits / char
 876       uint nbits = (uint) l * 12;
 877       // 36 bits / word
 878       uint tally = (nbits + 35) / 36;
 879 
 880       if (tally > 27) { //-V547
 881         sim_warn ("Impossible rdr tally: %d > 27; truncating.\r\n", tally);
 882         tally = 27;
 883       }
 884 
 885       // Remember that Hollerith for blank is 0, this is really
 886       // filling the buffer with blanks.
 887       (void)memset (buffer, 0, sizeof (buffer));
 888       for (uint col = 0; col < l; col ++) {
 889         uint wordno  = col / 3;
 890         uint fieldno = col % 3;
 891         putbits36_12 (& buffer [wordno], fieldno * 12, (word12) hbuf [col]);
 892       }
 893     }
 894     break;
 895   }
 896 
 897 
 898 
 899 
 900 
 901 
 902 
 903 
 904 
 905 
 906 
 907   p->stati = 04000;
 908 
 909   // Card images are 80 columns.
 910   uint tally = 27;
 911 
 912   iom_indirect_data_service (iomUnitIdx, chan, buffer, & tally, true);
 913   p->initiate     = false;
 914   p->stati        = 04000; //-V1048  // ok
 915   p->tallyResidue = (word12) tally & MASK12;
 916   p->charPos      = 0;
 917 
 918   return IOM_CMD_PROCEED;
 919 }
 920 
 921 static void submit (enum deckFormat fmt, char * fname, uint16 readerIndex)
     /* [previous][next][first][last][top][bottom][index][help] */
 922   {
 923     if (readerIndex >= N_RDR_UNITS_MAX) {
 924       sim_warn("crdrdr: submit called with invalid reader index %ld\r\n", (long) readerIndex);
 925       return;
 926     }
 927 
 928     int deckfd = open (fname, O_RDONLY);
 929     if (deckfd < 0)
 930       (void)sir_error ("crdrdr: Failure opening card reader deck: %s (Error %d)", xstrerror_l (errno), errno);
 931     // Windows can't unlink open files; save the file name and unlink on close.
 932     // int rc = unlink (fname); // this only works on UNIX
 933 #if defined(TESTING)
 934     sim_printf ("submit %s\r\n", fname);
 935 #endif
 936 #if defined(__GNUC__)
 937 # if !defined(__clang_version__)
 938 #  if __GNUC__ > 7
 939 #   if !defined(__INTEL_COMPILER)
 940 #    pragma GCC diagnostic push
 941 #    pragma GCC diagnostic ignored "-Wstringop-truncation"
 942 #   endif /* if !defined(__INTEL_COMPILER) */
 943 #  endif /* if __GNUC__ > 7 */
 944 # endif /* if !defined(__clang_version__) */
 945 #endif /* if defined(__GNUC__) */
 946     strncpy (rdr_state [readerIndex].fname, fname, sizeof(rdr_state [readerIndex].fname) - 1);
 947     rdr_state [readerIndex].fname [sizeof(rdr_state [readerIndex].fname) - 1] = '\0';
 948 #if defined(__GNUC__)
 949 # if !defined(__clang_version__)
 950 #  if __GNUC__ > 7
 951 #   if !defined(__INTEL_COMPILER)
 952 #    pragma GCC diagnostic pop
 953 #   endif /* if !defined(__INTEL_COMPILER) */
 954 #  endif /* if __GNUC__ > 7 */
 955 # endif /* if !defined(__clang_version__) */
 956 #endif /* if defined(__GNUC__) */
 957     rdr_state [readerIndex].deckfd     = deckfd;
 958     rdr_state [readerIndex].deckState  = deckStart;
 959     rdr_state [readerIndex].deckFormat = fmt;
 960     if (fmt == xDeck || fmt == gDeck || fmt == bDeck) { // xDecks, gDecks, and bDecks just sends the card images
 961       rdr_state [readerIndex].deckState  = inputSent;
 962     }
 963     if (deckfd >= 0)
 964       rdrCardReady (readerIndex);
 965   }
 966 
 967 static void scanForCards(uint16 readerIndex)
     /* [previous][next][first][last][top][bottom][index][help] */
 968   {
 969     char rdr_dir [2 * PATH_MAX + 8];
 970 
 971     if (readerIndex >= N_RDR_UNITS_MAX)
 972       {
 973         (void)sir_error ("crdrdr: scanForCards called with invalid reader index %d", readerIndex);
 974         return;
 975       }
 976 
 977 #if !defined(__MINGW64__) || !defined(__MINGW32__)
 978     const char* r_tmpdir = getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp";
 979 #else
 980     const char* r_tmpdir = getenv("TEMP") ? getenv("TEMP") : \
 981                            getenv("TMP")  ? getenv("TMP")  : ".";
 982 #endif /* if !defined(__MINGW64__) || !defined(__MINGW32__) */
 983     snprintf_truncat(rdr_dir, PATH_MAX + 1, "%s/%s%c",
 984                      r_tmpdir, rdr_name, 'a' + readerIndex);
 985 
 986     if (rdr_path_prefix [0])
 987       {
 988         snprintf_truncat(rdr_dir, PATH_MAX + 1, "%s%s%c",
 989                          rdr_path_prefix, rdr_name, 'a' + readerIndex);
 990       }
 991 
 992     DIR * dp;
 993     dp = opendir (rdr_dir);
 994     if (! dp)
 995       {
 996         if (errno == ENOENT)
 997           {
 998             (void)sir_notice ("crdrdr: Punch reader %s%c input directory '%s' not found; creating it.",
 999                               rdr_name, 'a' + readerIndex, rdr_dir);
1000             if (x_mkdir_p (rdr_dir) == -1)
1001               {
1002                 (void)sir_error ("crdrdr: Failure creating punch reader %s%c input directory '%s': %s (Error %d)",
1003                                  rdr_name, 'a' + readerIndex, rdr_dir, xstrerror_l (errno), errno);
1004                 return;
1005               }
1006           }
1007 
1008         dp = opendir (rdr_dir); /* Second try */
1009         if (! dp)
1010           {
1011             (void)sir_error ("crdrdr: Failure opening punch reader %s%c input directory '%s': %s (Error %d)",
1012                              rdr_name, 'a' + readerIndex, rdr_dir, xstrerror_l (errno), errno);
1013             return;
1014           }
1015       }
1016 
1017     struct dirent * entry;
1018     struct stat info;
1019     char fqname [2 * PATH_MAX + 8];
1020     while ((entry = readdir (dp)))
1021       {
1022         strcpy (fqname, rdr_dir);
1023         strcat (fqname, "/");
1024         strcat (fqname, entry -> d_name);
1025 
1026         if (stat(fqname, &info) != 0)
1027           {
1028             (void)sir_error ("crdrdr: scanForCards stat() failure for %s: %s (Error %d)",
1029                              fqname, xstrerror_l(errno), errno);
1030             continue;
1031           }
1032 
1033         if (S_ISDIR(info.st_mode))
1034           {
1035             // Found a directory so skip it
1036             continue;
1037           }
1038 
1039         if (rdr_state [readerIndex] . deckfd < 0)
1040           {
1041             if (strncmp (entry -> d_name, "xdeck.", 6) == 0)
1042               {
1043                 submit (xDeck, fqname, readerIndex);
1044                 break;
1045               }
1046             if (strncmp (entry -> d_name, "gdeck.", 6) == 0)
1047               {
1048                 submit (gDeck, fqname, readerIndex);
1049                 break;
1050               }
1051             if (strncmp (entry -> d_name, "cdeck.", 6) == 0)
1052               {
1053                 submit (cardDeck, fqname, readerIndex);
1054                 break;
1055               }
1056             if (strncmp (entry -> d_name, "7deck.", 6) == 0)
1057               {
1058                 submit (sevenDeck, fqname, readerIndex);
1059                 break;
1060               }
1061             if (strncmp (entry -> d_name, "sdeck.", 6) == 0)
1062               {
1063                 submit (streamDeck, fqname, readerIndex);
1064                 break;
1065               }
1066             if (strncmp (entry -> d_name, "bdeck.", 6) == 0)
1067               {
1068                 submit (bDeck, fqname, readerIndex);
1069                 break;
1070               }
1071           }
1072         if (strcmp (entry -> d_name, "discard") == 0)
1073           {
1074             // Windows can't unlink open files; do it now...
1075             int rc = unlink (fqname);
1076             if (rc)
1077               (void)sir_error ("crdrdr: Failure unlinking card reader deck: %s (Error %d)", xstrerror_l (errno), errno);
1078             if (rdr_state [readerIndex] . deckfd >= 0)
1079               {
1080                 close (rdr_state [readerIndex] . deckfd);
1081                 rc = unlink (rdr_state [readerIndex] . fname);
1082                 if (rc)
1083                   (void)sir_error ("crdrdr: Failure unlinking card reader deck: %s (Error %d)", xstrerror_l (errno), errno);
1084                 rdr_state [readerIndex] . deckfd = -1;
1085                 rdr_state [readerIndex] . deckState = deckStart;
1086                 break;
1087              }
1088           }
1089       }
1090     closedir (dp);
1091   }
1092 
1093 void rdrProcessEvent (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1094   {
1095     if (rdr_path_prefix [0])
1096       {
1097         // We support multiple card readers when path prefixing is turned on
1098         // so we need to check each possible reader to see if it is active
1099         for (uint16 reader_idx = 0; reader_idx < rdr_dev . numunits; reader_idx++)
1100           {
1101             if (rdr_state [reader_idx] . running)
1102                 scanForCards(reader_idx);
1103           }
1104       }
1105     else
1106       {
1107         // When path prefixing is off we only support a single card reader
1108         // (this is for backwards compatibility)
1109         if (! rdr_state [0] . running)
1110           return;
1111 
1112         scanForCards(0);
1113       }
1114   }
1115 
1116 void rdrCardReady (int unitNum)
     /* [previous][next][first][last][top][bottom][index][help] */
1117   {
1118     uint ctlr_unit_idx = cables->rdr_to_urp [unitNum].ctlr_unit_idx;
1119     uint ctlr_port_num = 0; // Single port device
1120     uint iom_unit_idx  = cables->urp_to_iom[ctlr_unit_idx][ctlr_port_num].iom_unit_idx;
1121     uint chan_num      = cables->urp_to_iom[ctlr_unit_idx][ctlr_port_num].chan_num;
1122     uint dev_code      = cables->rdr_to_urp[unitNum].dev_code;
1123     send_special_interrupt (iom_unit_idx, chan_num, dev_code, 0377, 0377 /* card reader to ready */); //-V536
1124   }
1125 
1126 iom_cmd_rc_t rdr_iom_cmd (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
1127   iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
1128 #if defined(xTESTING)
1129   uint dev_code       = p->IDCW_DEV_CODE;
1130   cpu_state_t * cpup  = _cpup;
1131 
1132   //sim_debug (DBG_TRACE, & rdr_dev, "%s: RDR %c%02o_%02o\r\n",
1133           //__func__, iomChar (iomUnitIdx), chan, dev_code);
1134 #endif
1135 
1136   uint ctlr_unit_idx        = get_ctlr_idx (iomUnitIdx, chan);
1137   uint unitIdx              = cables->urp_to_urd[ctlr_unit_idx][p->IDCW_DEV_CODE].unit_idx;
1138   struct rdr_state * statep = & rdr_state[unitIdx];
1139   statep->running           = true;
1140 
1141   iom_cmd_rc_t rc = IOM_CMD_PROCEED;
1142   // IDCW?
1143   if (IS_IDCW (p)) {
1144     // IDCW
1145     statep->io_mode = rdr_no_mode;
1146 
1147     switch (p->IDCW_DEV_CMD) {
1148       case 000: // CMD 00 Request status
1149         //sim_debug (DBG_DEBUG, & rdr_dev, "%s: Request Status\r\n", __func__);
1150         p->stati = 04000;
1151         // This is controller status, not device status
1152         //if (rdr_state[unitIdx].deckfd < 0)
1153           //p->stati = 04201; // hopper empty
1154 #if defined(TESTING)
1155         sim_printf ("Request status %04o\r\n", p->stati);
1156 #endif
1157         break;
1158 
1159       case 001: // CMD 01 Read binary
1160         //sim_debug (DBG_DEBUG, & rdr_dev, "%s: Read Binary\r\n", __func__);
1161         if (rdr_state [unitIdx].deckfd < 0) {
1162           p->stati        = 04201; // hopper empty
1163           p->tallyResidue = 0;
1164 #if defined(TESTING)
1165           if (! empty)
1166             sim_printf ("hopper empty\r\n");
1167           empty = true;
1168 #endif
1169           return IOM_CMD_DISCONNECT;
1170         }
1171         statep->io_mode = rdr_rd_bin;
1172         p->stati        = 04000;
1173         // This is controller status, not device status
1174         //if (rdr_state[unitIdx].deckfd < 0)
1175           //p->stati = 04201; // hopper empty
1176 #if defined(TESTING)
1177 sim_printf ("read binary %04o\r\n", p->stati);
1178 #endif
1179         break;
1180 
1181       case 040: // CMD 40 Reset status
1182         //sim_debug (DBG_DEBUG, & rdr_dev, "%s: Request Status\r\n", __func__);
1183         p->stati  = 04000;
1184         p->isRead = false;
1185         // This is controller status, not device status
1186         //if (rdr_state[unitIdx].deckfd < 0)
1187           //p->stati = 04201; // hopper empty
1188 #if defined(TESTING)
1189 sim_printf ("reset status %04o\r\n", p->stati);
1190 #endif
1191         break;
1192 
1193       default:
1194 #if defined(TESTING)
1195 sim_printf ("unknown  %o\r\n", p->IDCW_DEV_CMD);
1196 #endif
1197         // ignore the bootload console probe (051) and, in T&D mode, the
1198         // executive's printer survey, which sends a print command (030) to
1199         // every unit record channel: the reader rejects it, as here
1200         if (p->IDCW_DEV_CMD != 051 && ! (tandd_io_mode () && p->IDCW_DEV_CMD == 030))
1201           sim_warn ("%s: RDR unrecognized device command  %02o\r\n", __func__, p->IDCW_DEV_CMD);
1202         p->stati      = 04501; // cmd reject, invalid opcode
1203         p->chanStatus = chanStatIncorrectDCW;
1204         return IOM_CMD_ERROR;
1205     } // switch IDCW_DEV_CMD
1206 
1207     //sim_debug (DBG_DEBUG, & rdr_dev, "%s: stati %04o\r\n", __func__, p->stati);
1208     return IOM_CMD_PROCEED;
1209   } // if IDCW
1210 
1211   // Not IDCW; TDCW are captured in IOM, so must be IOTD, IOTP or IOTNP
1212   switch (statep->io_mode) {
1213     case rdr_no_mode:
1214 #if defined(TESTING)
1215       sim_printf ("%s: Unexpected IOTx\r\n", __func__);
1216 #endif
1217       //sim_warn ("%s: Unexpected IOTx\r\n", __func__);
1218       //return IOM_CMD_ERROR;
1219       break;
1220 
1221     case rdr_rd_bin: {
1222       int rc = rdrReadRecord (iomUnitIdx, chan);
1223 #if defined(TESTING)
1224 sim_printf ("rdrReadRecord returned %d\r\n", rc);
1225 #endif
1226       if (rc)
1227         return IOM_CMD_DISCONNECT;
1228     }
1229     break;
1230 
1231     default:
1232       sim_warn ("%s: Unrecognized io_mode %d\r\n", __func__, statep->io_mode);
1233       return IOM_CMD_ERROR;
1234   }
1235   return rc;
1236 }
1237 
1238 static t_stat rdr_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr, UNUSED int val,
     /* [previous][next][first][last][top][bottom][index][help] */
1239                                UNUSED const void * desc)
1240   {
1241     sim_printf("Number of RDR units in system is %d\r\n", rdr_dev . numunits);
1242     return SCPE_OK;
1243   }
1244 
1245 static t_stat rdr_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value, const char * cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1246                               UNUSED void * desc)
1247   {
1248     if (! cptr)
1249       return SCPE_ARG;
1250     int n = atoi (cptr);
1251     if (n < 1 || n > N_RDR_UNITS_MAX)
1252       return SCPE_ARG;
1253     rdr_dev . numunits = (uint32) n;
1254     return SCPE_OK;
1255   }
1256 
1257 static t_stat rdr_show_device_name (UNUSED FILE * st, UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1258                                     UNUSED int val, UNUSED const void * desc)
1259   {
1260     long n = RDR_UNIT_NUM (uptr);
1261     if (n < 0 || n >= N_RDR_UNITS_MAX)
1262       return SCPE_ARG;
1263     sim_printf("name     : %s", rdr_state [n] . device_name);
1264     return SCPE_OK;
1265   }
1266 
1267 static t_stat rdr_set_device_name (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1268                                    UNUSED const char * cptr, UNUSED void * desc)
1269   {
1270     long n = RDR_UNIT_NUM (uptr);
1271     if (n < 0 || n >= N_RDR_UNITS_MAX)
1272       return SCPE_ARG;
1273     if (cptr)
1274       {
1275         strncpy (rdr_state [n] . device_name, cptr, MAX_DEV_NAME_LEN - 1);
1276         rdr_state [n] . device_name [MAX_DEV_NAME_LEN - 1] = 0;
1277       }
1278     else
1279       rdr_state [n] . device_name [0] = 0;
1280     return SCPE_OK;
1281   }
1282 
1283 static t_stat rdr_set_path (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1284                             const UNUSED char * cptr, UNUSED void * desc)
1285   {
1286     if (! cptr)
1287       return SCPE_ARG;
1288 
1289     size_t len = strlen(cptr);
1290 
1291     // We check for length - (3 + length of rdr_name) to allow for the
1292     // null, a possible '/' being added and "rdrx" being added
1293     if (len >= (sizeof(rdr_path_prefix) - (strlen(rdr_name) + 3)))
1294       return SCPE_ARG;
1295 
1296     strncpy(rdr_path_prefix, cptr, sizeof(rdr_path_prefix) -1);
1297     rdr_path_prefix[sizeof(rdr_path_prefix) - 1] = '\0';
1298     if (len > 0)
1299       {
1300         if (rdr_path_prefix[len - 1] != '/')
1301           {
1302             if (len == sizeof(rdr_path_prefix) - 1)
1303               return SCPE_ARG;
1304             rdr_path_prefix[len++] = '/';
1305             rdr_path_prefix[len] = 0;
1306           }
1307       }
1308     return SCPE_OK;
1309   }
1310 
1311 static t_stat rdr_show_path (UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1312                              UNUSED int val, UNUSED const void * desc)
1313   {
1314     if (rdr_path_prefix [0])
1315       {
1316         sim_printf("\rPath to card reader directories is \"%s\".\r\n", rdr_path_prefix);
1317       }
1318     else
1319       {
1320         sim_printf("\rPath to card reader directories is unset.\r\n");
1321       }
1322     return SCPE_OK;
1323   }

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