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. getCardLine
  5. getCardData
  6. getRawCardData
  7. rdrReadRecord
  8. submit
  9. scanForCards
  10. rdrProcessEvent
  11. rdrCardReady
  12. rdr_iom_cmd
  13. rdr_show_nunits
  14. rdr_set_nunits
  15. rdr_show_device_name
  16. rdr_set_device_name
  17. rdr_set_path
  18. 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 };
 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 void asciiToH (char * str, uint * hstr, size_t l)
     /* [previous][next][first][last][top][bottom][index][help] */
 383   {
 384     char * p = str;
 385     for (size_t i = 0; i < l; i ++)
 386     //for (char * p = str; * p; p ++)
 387       {
 388         * hstr ++ = table [(* p) & 0177];
 389         p ++;
 390       }
 391   }
 392 
 393 
 394 
 395 
 396 
 397 
 398 
 399 
 400 
 401 
 402 
 403 
 404 
 405 
 406 
 407 
 408 
 409 
 410 
 411 
 412 
 413 
 414 
 415 
 416 
 417 
 418 
 419 
 420 
 421 static int getCardLine (int fd, unsigned char * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 422   {
 423     uint n = 0;
 424     buffer [n] = 0;
 425     while (1)
 426       {
 427         uint8 ch;
 428         ssize_t rc = read (fd, & ch, 1);
 429         if (rc <= 0) // eof or err
 430           return n == 0;
 431         if (ch == '\n')
 432           return 0;
 433         buffer [n ++] = ch;
 434         buffer [n] = 0;
 435         if (n > 79)
 436           return 0;
 437       }
 438 #if defined(SUNLINT) || !defined(__SUNPRO_C) && !defined(__SUNPRO_CC)
 439     /*NOTREACHED*/ /* unreachable */
 440     return 0;
 441 #endif /* if defined(SUNLINT) || !defined(__SUNPRO_C) && !defined(__SUNPRO_CC) */
 442   }
 443 
 444 static int getCardData (int fd, char * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 445   {
 446     (void)memset (buffer, 0, 80);
 447     ssize_t rc = read (fd, buffer, 80);
 448     if (rc < 0)
 449       return 0;
 450     return (int) rc;
 451   }
 452 
 453 #define rawCardImageBytes (80 * 12 / 8)
 454 static int getRawCardData (int fd, uint8_t * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 455   {
 456     (void)memset (buffer, 0, rawCardImageBytes + 2);
 457     ssize_t rc = read (fd, buffer, rawCardImageBytes);
 458     if (rc < 0)
 459       return 0;
 460     return (int) rc;
 461   }
 462 
 463 #if defined(TESTING)
 464 static bool empty = false;
 465 #endif /* if defined(TESTING) */
 466 
 467 static int rdrReadRecord (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
 468 #if defined(TESTING)
 469   cpu_state_t * cpup  = _cpup;
 470 #endif
 471   iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
 472   sim_debug (DBG_NOTIFY, & rdr_dev, "Read binary\n");
 473   uint ctlr_unit_idx  = get_ctlr_idx (iomUnitIdx, chan);
 474   uint unitIdx        = cables->urp_to_urd[ctlr_unit_idx][p->IDCW_DEV_CODE].unit_idx;
 475 
 476 
 477 
 478 
 479 
 480 
 481 
 482 
 483 
 484 
 485 
 486 
 487 
 488 
 489 
 490 
 491 
 492 #if defined(TESTING)
 493   sim_printf ("hopper not empty\r\n");
 494   empty = false;
 495 #endif
 496   unsigned char cardImage [80] = "";
 497   uint8_t rawCardImage [rawCardImageBytes + 2 ];
 498   size_t l = 0;
 499   // initialize to quiet compiler
 500   enum deckFormat thisCard = cardDeck;
 501 
 502   static int jobNo = 0;
 503 
 504   switch (rdr_state [unitIdx].deckState) {
 505     case deckStart: {
 506 #if defined(TESTING)
 507   sim_printf ("deckStart: sending ++EOF\r\n");
 508 #endif
 509       strcpy ((char *) cardImage, "++EOF");
 510       l = strlen ((char *) cardImage);
 511       thisCard = cardDeck; //-V1048
 512       rdr_state [unitIdx].deckState = eof1Sent;
 513       jobNo ++;
 514     }
 515     break;
 516 
 517     case eof1Sent: {
 518 #if defined(TESTING)
 519   sim_printf ("eof1Sent: sending ++UID\r\n");
 520 #endif
 521       (void)sprintf ((char *) cardImage, "++UID %d", jobNo);
 522       l = strlen (((char *) cardImage) + 1);
 523       thisCard = cardDeck; //-V1048
 524       rdr_state [unitIdx].deckState = uid1Sent;
 525     }
 526     break;
 527 
 528     case uid1Sent: {
 529 #if defined(TESTING)
 530   sim_printf ("uid1Sent: sending data\r\n");
 531 #endif
 532       int rc = getCardLine (rdr_state [unitIdx].deckfd, cardImage);
 533       if (rc) {
 534 #if defined(TESTING)
 535   sim_printf ("uid1Sent: getCardLine returned %d\r\n", rc);
 536 #endif
 537         close (rdr_state [unitIdx].deckfd);
 538         // Windows can't unlink open files; do it now...
 539         rc = unlink (rdr_state [unitIdx].fname);
 540         if (rc)
 541           perror ("card reader deck unlink\n");
 542         rdr_state [unitIdx].deckfd = -1;
 543         rdr_state [unitIdx].deckState = deckStart;
 544         p->stati = 04201; // hopper empty
 545         return IOM_CMD_DISCONNECT;
 546       }
 547 #if defined(TESTING)
 548   sim_printf ("uid1Sent: getCardLine returned <%s>\r\n", cardImage);
 549 #endif
 550       l = strlen ((char *) cardImage);
 551       thisCard = cardDeck; //-V1048
 552       if (strncasecmp ((char *) cardImage, "++input", 7) == 0) {
 553 #if defined(TESTING)
 554   sim_printf ("uid1Sent: switching to inputSent <%s>\r\n", cardImage);
 555 #endif
 556         rdr_state [unitIdx].deckState = inputSent;
 557       }
 558     }
 559     break;
 560 
 561     // Reading the actual data cards
 562 
 563     case inputSent: {
 564 #if defined(TESTING)
 565   sim_printf ("inputSent: format %d\r\n", rdr_state [unitIdx].deckFormat);
 566 #endif
 567       switch (rdr_state [unitIdx].deckFormat) {
 568         case cardDeck: {
 569 #if defined(TESTING)
 570   sim_printf ("inputSent: cardDeck\r\n");
 571 #endif
 572           int rc = getCardLine (rdr_state [unitIdx].deckfd, cardImage);
 573           if (rc) {
 574             strcpy ((char *) cardImage, "++EOF");
 575             rdr_state [unitIdx].deckState = eof2Sent;
 576           }
 577           l = strlen ((char *) cardImage);
 578         }
 579         thisCard = cardDeck; //-V1048
 580         break;
 581 
 582         case streamDeck: {
 583 #if defined(TESTING)
 584   sim_printf ("inputSent: streamDeck\r\n");
 585 #endif
 586           l = (size_t) getCardData (rdr_state [unitIdx].deckfd, (char *) cardImage);
 587           if (l) {
 588             thisCard = streamDeck;
 589           } else {
 590             strcpy ((char *) cardImage, "++EOF");
 591             l = strlen ((char *) cardImage);
 592             rdr_state [unitIdx].deckState = eof2Sent;
 593             thisCard = cardDeck; //-V1048
 594           }
 595 #if defined(TESTING)
 596   sim_printf ("inputSent: getCardLine returned <%s>\r\n", cardImage);
 597 #endif
 598         }
 599         break;
 600 
 601         case sevenDeck: {
 602 #if defined(TESTING)
 603   sim_printf ("inputSent: 7Deck\r\n");
 604 #endif
 605           l = (size_t) getRawCardData (rdr_state [unitIdx].deckfd, rawCardImage);
 606           if (l) {
 607             thisCard = sevenDeck;
 608           } else {
 609             strcpy ((char *) cardImage, "++EOF");
 610             l = strlen ((char *) cardImage);
 611             rdr_state [unitIdx].deckState = eof2Sent;
 612             thisCard = cardDeck; //-V1048
 613           }
 614         }
 615         break;
 616 
 617       } // switch (deckFormat)
 618     } // case inputSent
 619     break;
 620 
 621 // Sending a ++END means that the read_cards command has to be reissued
 622 
 623     case eof2Sent: {
 624 # if defined(TESTING)
 625   sim_printf ("eof2Sent\r\n");
 626 # endif
 627       (void)sprintf ((char *) cardImage, "++UID %d", jobNo);
 628       l = strlen ((char *) cardImage);
 629       thisCard = cardDeck; //-V1048
 630       rdr_state [unitIdx].deckState = deckStart;
 631       close (rdr_state [unitIdx].deckfd);
 632       // Windows can't unlink open files; do it now...
 633       int rc = unlink (rdr_state [unitIdx].fname);
 634       if (rc)
 635         perror ("card reader deck unlink\n");
 636       rdr_state [unitIdx].deckfd = -1;
 637     }
 638     break;
 639 
 640 
 641 
 642 
 643 
 644 
 645 
 646 
 647 
 648 
 649 
 650 
 651 
 652 
 653 
 654 
 655 
 656 
 657 
 658 
 659 
 660 
 661 
 662 
 663 
 664 
 665 
 666 
 667 
 668   }
 669 
 670 
 671 
 672 
 673 
 674     //sim_printf ("card <%s>\r\n", cardImage);
 675 #if defined(TESTING)
 676   sim_printf ("\r\n");
 677   sim_printf ("\r\n");
 678   for (uint i = 0; i < 80; i ++) {
 679     if (isprint (cardImage [i]))
 680       sim_printf ("%c", cardImage [i]);
 681     else
 682       sim_printf ("\\%03o", cardImage [i]);
 683   }
 684   sim_printf ("\r\n");
 685   sim_printf ("\r\n");
 686 #endif
 687   word36 buffer [27];
 688   switch (thisCard) {
 689     case sevenDeck: {
 690       // This will overread rawCardImage by 12 bits, but that's okay
 691       // because Multics will ignore the last 12 bits.
 692       for (uint i = 0; i < 27; i ++)
 693         buffer [i] = extr36 ((uint8 *) rawCardImage, i);
 694       //sim_printf ("7deck %012"PRIo64" %012"PRIo64" %012"PRIo64" %012"PRIo64"\r\n",
 695       //             buffer [0], buffer [1], buffer [2], buffer [3]);
 696     }
 697     break;
 698 
 699     case streamDeck:
 700 
 701 
 702 
 703 
 704 
 705 
 706 
 707 
 708 
 709 
 710     case cardDeck: {
 711       if (l > 80) {
 712         sim_warn ("Whups. rdr l %lu > 80; truncating.\n", (unsigned long)l);
 713         l = 80;
 714         //cardImage [l] = 0;
 715       }
 716 
 717       uint hbuf [l + 1];
 718       asciiToH ((char *) cardImage, hbuf, l);
 719 
 720       // 12 bits / char
 721       uint nbits = (uint) l * 12;
 722       // 36 bits / word
 723       uint tally = (nbits + 35) / 36;
 724 
 725       if (tally > 27) { //-V547
 726         sim_warn ("Impossible rdr tally: %d > 27; truncating.\n", tally);
 727         tally = 27;
 728       }
 729 
 730       // Remember that Hollerith for blank is 0, this is really
 731       // filling the buffer with blanks.
 732       (void)memset (buffer, 0, sizeof (buffer));
 733       for (uint col = 0; col < l; col ++) {
 734         uint wordno  = col / 3;
 735         uint fieldno = col % 3;
 736         putbits36_12 (& buffer [wordno], fieldno * 12, (word12) hbuf [col]);
 737       }
 738     }
 739     break;
 740   }
 741 
 742 
 743 
 744 
 745 
 746 
 747 
 748 
 749 
 750 
 751 
 752   p->stati = 04000;
 753 
 754   // Card images are 80 columns.
 755   uint tally = 27;
 756 
 757   iom_indirect_data_service (iomUnitIdx, chan, buffer, & tally, true);
 758   p->initiate     = false;
 759   p->stati        = 04000; //-V1048  // ok
 760   p->tallyResidue = (word12) tally & MASK12;
 761   p->charPos      = 0;
 762 
 763   return IOM_CMD_PROCEED;
 764 }
 765 
 766 static void submit (enum deckFormat fmt, char * fname, uint16 readerIndex)
     /* [previous][next][first][last][top][bottom][index][help] */
 767   {
 768     if (readerIndex >= N_RDR_UNITS_MAX) {
 769       sim_warn("crdrdr: submit called with invalid reader index %ld\n", (long) readerIndex);
 770       return;
 771     }
 772 
 773     int deckfd = open (fname, O_RDONLY);
 774     if (deckfd < 0)
 775       perror ("card reader deck open\n");
 776     // Windows can't unlink open files; save the file name and unlink on close.
 777     // int rc = unlink (fname); // this only works on UNIX
 778 #if defined(TESTING)
 779     sim_printf ("submit %s\r\n", fname);
 780 #endif
 781 #if defined(__GNUC__)
 782 # if !defined(__clang_version__)
 783 #  if __GNUC__ > 7
 784 #   if !defined(__INTEL_COMPILER)
 785 #    pragma GCC diagnostic push
 786 #    pragma GCC diagnostic ignored "-Wstringop-truncation"
 787 #   endif /* if !defined(__INTEL_COMPILER) */
 788 #  endif /* if __GNUC__ > 7 */
 789 # endif /* if !defined(__clang_version__) */
 790 #endif /* if defined(__GNUC__) */
 791     strncpy (rdr_state [readerIndex].fname, fname, sizeof(rdr_state [readerIndex].fname) - 1);
 792     rdr_state [readerIndex].fname [sizeof(rdr_state [readerIndex].fname) - 1] = '\0';
 793 #if defined(__GNUC__)
 794 # if !defined(__clang_version__)
 795 #  if __GNUC__ > 7
 796 #   if !defined(__INTEL_COMPILER)
 797 #    pragma GCC diagnostic pop
 798 #   endif /* if !defined(__INTEL_COMPILER) */
 799 #  endif /* if __GNUC__ > 7 */
 800 # endif /* if !defined(__clang_version__) */
 801 #endif /* if defined(__GNUC__) */
 802     rdr_state [readerIndex].deckfd     = deckfd;
 803     rdr_state [readerIndex].deckState  = deckStart;
 804     rdr_state [readerIndex].deckFormat = fmt;
 805     if (deckfd >= 0)
 806       rdrCardReady (readerIndex);
 807   }
 808 
 809 static void scanForCards(uint16 readerIndex)
     /* [previous][next][first][last][top][bottom][index][help] */
 810   {
 811     char rdr_dir [2 * PATH_MAX + 8];
 812 
 813     if (readerIndex >= N_RDR_UNITS_MAX) {
 814       sim_warn("crdrdr: scanForCards called with invalid reader index %d\n", readerIndex);
 815       return;
 816     }
 817 
 818 #if !defined(__MINGW64__) || !defined(__MINGW32__)
 819     const char* r_tmpdir = getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp";
 820 #else
 821     const char* r_tmpdir = getenv("TEMP") ? getenv("TEMP") : \
 822                            getenv("TMP")  ? getenv("TMP")  : ".";
 823 #endif /* if !defined(__MINGW64__) || !defined(__MINGW32__) */
 824     snprintf_truncat(rdr_dir, PATH_MAX + 1, "%s/%s%c",
 825                      r_tmpdir, rdr_name, 'a' + readerIndex);
 826 
 827     if (rdr_path_prefix [0])
 828       {
 829         snprintf_truncat(rdr_dir, PATH_MAX + 1, "%s%s%c",
 830                          rdr_path_prefix, rdr_name, 'a' + readerIndex);
 831       }
 832 
 833     DIR * dp;
 834     dp = opendir (rdr_dir);
 835     if (! dp)
 836       {
 837         sim_warn ("crdrdr opendir '%s' fail.\n", rdr_dir);
 838         perror ("opendir");
 839         return;
 840       }
 841     struct dirent * entry;
 842     struct stat info;
 843     char fqname [2 * PATH_MAX + 8];
 844     while ((entry = readdir (dp)))
 845       {
 846         strcpy (fqname, rdr_dir);
 847         strcat (fqname, "/");
 848         strcat (fqname, entry -> d_name);
 849 
 850         if (stat(fqname, &info) != 0)
 851           {
 852             sim_warn("crdrdr: scanForCards stat() error for %s: %s\n", fqname, xstrerror_l(errno));
 853             continue;
 854           }
 855 
 856         if (S_ISDIR(info.st_mode))
 857           {
 858             // Found a directory so skip it
 859             continue;
 860           }
 861 
 862         if (rdr_state [readerIndex] . deckfd < 0)
 863           {
 864             if (strncmp (entry -> d_name, "cdeck.", 6) == 0)
 865               {
 866                 submit (cardDeck, fqname, readerIndex);
 867                 break;
 868               }
 869             if (strncmp (entry -> d_name, "7deck.", 6) == 0)
 870               {
 871                 submit (sevenDeck, fqname, readerIndex);
 872                 break;
 873               }
 874             if (strncmp (entry -> d_name, "sdeck.", 6) == 0)
 875               {
 876                 submit (streamDeck, fqname, readerIndex);
 877                 break;
 878               }
 879           }
 880         if (strcmp (entry -> d_name, "discard") == 0)
 881           {
 882             // Windows can't unlink open files; do it now...
 883             int rc = unlink (fqname);
 884             if (rc)
 885               perror ("crdrdr discard unlink\n");
 886             if (rdr_state [readerIndex] . deckfd >= 0)
 887               {
 888                 close (rdr_state [readerIndex] . deckfd);
 889                 rc = unlink (rdr_state [readerIndex] . fname);
 890                 if (rc)
 891                   perror ("crdrdr deck unlink\n");
 892                 rdr_state [readerIndex] . deckfd = -1;
 893                 rdr_state [readerIndex] . deckState = deckStart;
 894                 break;
 895              }
 896           }
 897       }
 898     closedir (dp);
 899   }
 900 
 901 void rdrProcessEvent (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 902   {
 903     if (rdr_path_prefix [0])
 904       {
 905         // We support multiple card readers when path prefixing is turned on
 906         // so we need to check each possible reader to see if it is active
 907         for (uint16 reader_idx = 0; reader_idx < rdr_dev . numunits; reader_idx++)
 908           {
 909             if (rdr_state [reader_idx] . running)
 910                 scanForCards(reader_idx);
 911           }
 912       }
 913     else
 914       {
 915         // When path prefixing is off we only support a single card reader
 916         // (this is for backwards compatibility)
 917         if (! rdr_state [0] . running)
 918           return;
 919 
 920         scanForCards(0);
 921       }
 922   }
 923 
 924 void rdrCardReady (int unitNum)
     /* [previous][next][first][last][top][bottom][index][help] */
 925   {
 926     uint ctlr_unit_idx = cables->rdr_to_urp [unitNum].ctlr_unit_idx;
 927     uint ctlr_port_num = 0; // Single port device
 928     uint iom_unit_idx  = cables->urp_to_iom[ctlr_unit_idx][ctlr_port_num].iom_unit_idx;
 929     uint chan_num      = cables->urp_to_iom[ctlr_unit_idx][ctlr_port_num].chan_num;
 930     uint dev_code      = cables->rdr_to_urp[unitNum].dev_code;
 931     send_special_interrupt (iom_unit_idx, chan_num, dev_code, 0377, 0377 /* card reader to ready */); //-V536
 932   }
 933 
 934 iom_cmd_rc_t rdr_iom_cmd (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
 935   iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
 936 #if defined(TESTING)
 937   uint dev_code       = p->IDCW_DEV_CODE;
 938   cpu_state_t * cpup  = _cpup;
 939 
 940   sim_debug (DBG_TRACE, & rdr_dev, "%s: RDR %c%02o_%02o\n",
 941           __func__, iomChar (iomUnitIdx), chan, dev_code);
 942 #endif
 943 
 944   uint ctlr_unit_idx        = get_ctlr_idx (iomUnitIdx, chan);
 945   uint unitIdx              = cables->urp_to_urd[ctlr_unit_idx][p->IDCW_DEV_CODE].unit_idx;
 946   struct rdr_state * statep = & rdr_state[unitIdx];
 947   statep->running           = true;
 948 
 949   iom_cmd_rc_t rc = IOM_CMD_PROCEED;
 950   // IDCW?
 951   if (IS_IDCW (p)) {
 952     // IDCW
 953     statep->io_mode = rdr_no_mode;
 954 
 955     switch (p->IDCW_DEV_CMD) {
 956       case 000: // CMD 00 Request status
 957         sim_debug (DBG_DEBUG, & rdr_dev, "%s: Request Status\n", __func__);
 958         p->stati = 04000;
 959         // This is controller status, not device status
 960         //if (rdr_state[unitIdx].deckfd < 0)
 961           //p->stati = 04201; // hopper empty
 962 #if defined(TESTING)
 963         sim_printf ("Request status %04o\r\n", p->stati);
 964 #endif
 965         break;
 966 
 967       case 001: // CMD 01 Read binary
 968         sim_debug (DBG_DEBUG, & rdr_dev, "%s: Read Binary\n", __func__);
 969         if (rdr_state [unitIdx].deckfd < 0) {
 970           p->stati        = 04201; // hopper empty
 971           p->tallyResidue = 0;
 972 #if defined(TESTING)
 973           if (! empty)
 974             sim_printf ("hopper empty\r\n");
 975           empty = true;
 976 #endif
 977           return IOM_CMD_DISCONNECT;
 978         }
 979         statep->io_mode = rdr_rd_bin;
 980         p->stati        = 04000;
 981         // This is controller status, not device status
 982         //if (rdr_state[unitIdx].deckfd < 0)
 983           //p->stati = 04201; // hopper empty
 984 #if defined(TESTING)
 985 sim_printf ("read binary %04o\r\n", p->stati);
 986 #endif
 987         break;
 988 
 989       case 040: // CMD 40 Reset status
 990         sim_debug (DBG_DEBUG, & rdr_dev, "%s: Request Status\n", __func__);
 991         p->stati  = 04000;
 992         p->isRead = false;
 993         // This is controller status, not device status
 994         //if (rdr_state[unitIdx].deckfd < 0)
 995           //p->stati = 04201; // hopper empty
 996 #if defined(TESTING)
 997 sim_printf ("reset status %04o\r\n", p->stati);
 998 #endif
 999         break;
1000 
1001       default:
1002 #if defined(TESTING)
1003 sim_printf ("unknown  %o\r\n", p->IDCW_DEV_CMD);
1004 #endif
1005         if (p->IDCW_DEV_CMD != 051) // ignore bootload console probe
1006           sim_warn ("%s: RDR unrecognized device command  %02o\n", __func__, p->IDCW_DEV_CMD);
1007         p->stati      = 04501; // cmd reject, invalid opcode
1008         p->chanStatus = chanStatIncorrectDCW;
1009         return IOM_CMD_ERROR;
1010     } // switch IDCW_DEV_CMD
1011 
1012     sim_debug (DBG_DEBUG, & rdr_dev, "%s: stati %04o\n", __func__, p->stati);
1013     return IOM_CMD_PROCEED;
1014   } // if IDCW
1015 
1016   // Not IDCW; TDCW are captured in IOM, so must be IOTD, IOTP or IOTNP
1017   switch (statep->io_mode) {
1018     case rdr_no_mode:
1019 #if defined(TESTING)
1020       sim_printf ("%s: Unexpected IOTx\r\n", __func__);
1021 #endif
1022       //sim_warn ("%s: Unexpected IOTx\n", __func__);
1023       //return IOM_CMD_ERROR;
1024       break;
1025 
1026     case rdr_rd_bin: {
1027       int rc = rdrReadRecord (iomUnitIdx, chan);
1028 #if defined(TESTING)
1029 sim_printf ("rdrReadRecord returned %d\r\n", rc);
1030 #endif
1031       if (rc)
1032         return IOM_CMD_DISCONNECT;
1033     }
1034     break;
1035 
1036     default:
1037       sim_warn ("%s: Unrecognized io_mode %d\n", __func__, statep->io_mode);
1038       return IOM_CMD_ERROR;
1039   }
1040   return rc;
1041 }
1042 
1043 static t_stat rdr_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr, UNUSED int val,
     /* [previous][next][first][last][top][bottom][index][help] */
1044                                UNUSED const void * desc)
1045   {
1046     sim_printf("Number of RDR units in system is %d\n", rdr_dev . numunits);
1047     return SCPE_OK;
1048   }
1049 
1050 static t_stat rdr_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value, const char * cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1051                               UNUSED void * desc)
1052   {
1053     if (! cptr)
1054       return SCPE_ARG;
1055     int n = atoi (cptr);
1056     if (n < 1 || n > N_RDR_UNITS_MAX)
1057       return SCPE_ARG;
1058     rdr_dev . numunits = (uint32) n;
1059     return SCPE_OK;
1060   }
1061 
1062 static t_stat rdr_show_device_name (UNUSED FILE * st, UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1063                                     UNUSED int val, UNUSED const void * desc)
1064   {
1065     long n = RDR_UNIT_NUM (uptr);
1066     if (n < 0 || n >= N_RDR_UNITS_MAX)
1067       return SCPE_ARG;
1068     sim_printf("name     : %s", rdr_state [n] . device_name);
1069     return SCPE_OK;
1070   }
1071 
1072 static t_stat rdr_set_device_name (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1073                                    UNUSED const char * cptr, UNUSED void * desc)
1074   {
1075     long n = RDR_UNIT_NUM (uptr);
1076     if (n < 0 || n >= N_RDR_UNITS_MAX)
1077       return SCPE_ARG;
1078     if (cptr)
1079       {
1080         strncpy (rdr_state [n] . device_name, cptr, MAX_DEV_NAME_LEN - 1);
1081         rdr_state [n] . device_name [MAX_DEV_NAME_LEN - 1] = 0;
1082       }
1083     else
1084       rdr_state [n] . device_name [0] = 0;
1085     return SCPE_OK;
1086   }
1087 
1088 static t_stat rdr_set_path (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1089                             const UNUSED char * cptr, UNUSED void * desc)
1090   {
1091     if (! cptr)
1092       return SCPE_ARG;
1093 
1094     size_t len = strlen(cptr);
1095 
1096     // We check for length - (3 + length of rdr_name) to allow for the
1097     // null, a possible '/' being added and "rdrx" being added
1098     if (len >= (sizeof(rdr_path_prefix) - (strlen(rdr_name) + 3)))
1099       return SCPE_ARG;
1100 
1101     strncpy(rdr_path_prefix, cptr, sizeof(rdr_path_prefix) -1);
1102     rdr_path_prefix[sizeof(rdr_path_prefix) - 1] = '\0';
1103     if (len > 0)
1104       {
1105         if (rdr_path_prefix[len - 1] != '/')
1106           {
1107             if (len == sizeof(rdr_path_prefix) - 1)
1108               return SCPE_ARG;
1109             rdr_path_prefix[len++] = '/';
1110             rdr_path_prefix[len] = 0;
1111           }
1112       }
1113     return SCPE_OK;
1114   }
1115 
1116 static t_stat rdr_show_path (UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1117                              UNUSED int val, UNUSED const void * desc)
1118   {
1119     if (rdr_path_prefix [0])
1120       {
1121         sim_printf("\rPath to card reader directories is \"%s\".\r\n", rdr_path_prefix);
1122       }
1123     else
1124       {
1125         sim_printf("\rPath to card reader directories is unset.\r\n");
1126       }
1127     return SCPE_OK;
1128   }

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