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-2024 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 #define DBG_CTR 1
  55 #define N_RDR_UNITS 1 // default
  56 
  57 #define snprintf_truncat(dst, size, ...)    \
  58   do                                        \
  59     {                                       \
  60       volatile size_t n = size;             \
  61       (void)snprintf (dst, n, __VA_ARGS__); \
  62     }                                       \
  63   while (0)
  64 
  65 static t_stat rdr_reset (DEVICE * dptr);
  66 static t_stat rdr_show_nunits (FILE *st, UNIT *uptr, int val, const void *desc);
  67 static t_stat rdr_set_nunits (UNIT * uptr, int32 value, const char * cptr, void * desc);
  68 static t_stat rdr_show_device_name (FILE *st, UNIT *uptr, int val, const void *desc);
  69 static t_stat rdr_set_device_name (UNIT * uptr, int32 value, const char * cptr, void * desc);
  70 static t_stat rdr_show_path (UNUSED FILE * st, UNIT * uptr, UNUSED int val,
  71                              UNUSED const void * desc);
  72 static t_stat rdr_set_path (UNUSED UNIT * uptr, UNUSED int32 value, const UNUSED char * cptr,
  73                             UNUSED void * desc);
  74 
  75 #define UNIT_FLAGS ( UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | \
  76                      UNIT_IDLE )
  77 UNIT rdr_unit [N_RDR_UNITS_MAX] =
  78   {
  79     {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
  80     {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
  81     {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
  82     {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL},
  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   };
  96 
  97 #define RDR_UNIT_NUM(uptr) ((uptr) - rdr_unit)
  98 
  99 static DEBTAB rdr_dt [] =
 100   {
 101     { "NOTIFY", DBG_NOTIFY, NULL },
 102     { "INFO",   DBG_INFO,   NULL },
 103     { "ERR",    DBG_ERR,    NULL },
 104     { "WARN",   DBG_WARN,   NULL },
 105     { "DEBUG",  DBG_DEBUG,  NULL },
 106     { "ALL",    DBG_ALL,    NULL }, // Don't move as it messes up DBG message
 107     { NULL,     0,          NULL }
 108   };
 109 
 110 #define UNIT_WATCH UNIT_V_UF
 111 
 112 static MTAB rdr_mod [] =
 113   {
 114 #if !defined(SPEED)
 115     { UNIT_WATCH, 1, "WATCH",   "WATCH",   0, 0, NULL, NULL },
 116     { UNIT_WATCH, 0, "NOWATCH", "NOWATCH", 0, 0, NULL, NULL },
 117 #endif /* if !defined(SPEED) */
 118     {
 119       MTAB_XTD | MTAB_VDV | \
 120       MTAB_NMO | MTAB_VALR,                 /* Mask               */
 121       0,                                    /* Match              */
 122       "NUNITS",                             /* Print string       */
 123       "NUNITS",                             /* Match string       */
 124       rdr_set_nunits,                       /* Validation routine */
 125       rdr_show_nunits,                      /* Display routine    */
 126       "Number of RDR units in the system",  /* Value descriptor   */
 127       NULL                                  /* Help               */
 128     },
 129     {
 130       MTAB_XTD | MTAB_VUN | \
 131       MTAB_VALR | MTAB_NC,                  /* Mask               */
 132       0,                                    /* Match              */
 133       "NAME",                               /* Print string       */
 134       "NAME",                               /* Match string       */
 135       rdr_set_device_name,                  /* Validation routine */
 136       rdr_show_device_name,                 /* Display routine    */
 137       "Select the boot drive",              /* Value descriptor   */
 138       NULL                                  /* Help               */
 139     },
 140     {
 141       MTAB_XTD | MTAB_VDV | MTAB_NMO | \
 142       MTAB_VALR | MTAB_NC,                  /* Mask               */
 143       0,                                    /* Match              */
 144       "PATH",                               /* Print string       */
 145       "PATH",                               /* Match string       */
 146       rdr_set_path,                         /* Validation routine */
 147       rdr_show_path,                        /* Display routine    */
 148       "Path to card reader directories",    /* Value descriptor   */
 149       NULL                                  /* Help               */
 150     },
 151 
 152     { 0, 0, NULL, NULL, 0, 0, NULL, NULL }
 153   };
 154 
 155 DEVICE rdr_dev = {
 156     "RDR",        /* Name                */
 157     rdr_unit,     /* Units               */
 158     NULL,         /* Registers           */
 159     rdr_mod,      /* Modifiers           */
 160     N_RDR_UNITS,  /* #units              */
 161     10,           /* Address radix       */
 162     24,           /* Address width       */
 163     1,            /* Address increment   */
 164     8,            /* Data radix          */
 165     36,           /* Data width          */
 166     NULL,         /* Examine             */
 167     NULL,         /* Deposit             */
 168     rdr_reset,    /* Reset               */
 169     NULL,         /* Boot                */
 170     NULL,         /* Attach              */
 171     NULL,         /* Detach              */
 172     NULL,         /* Context             */
 173     DEV_DEBUG,    /* Flags               */
 174     0,            /* Debug control flags */
 175     rdr_dt,       /* Debug flag names    */
 176     NULL,         /* Memory size change  */
 177     NULL,         /* Logical name        */
 178     NULL,         /* Help                */
 179     NULL,         /* Attach help         */
 180     NULL,         /* Attach context      */
 181     NULL,         /* Description         */
 182     NULL          /* End                 */
 183 };
 184 
 185 enum deckFormat { sevenDeck, cardDeck, streamDeck };
 186 
 187 // Windows cannot unlink an open file; rework the code to unlink the
 188 // submitted card deck after closing it.
 189 //  -- Add fname tp rdr_state
 190 //  -- Add unlink calls at eof close
 191 static struct rdr_state
 192   {
 193     enum rdr_mode
 194       {
 195         rdr_no_mode, rdr_rd_bin
 196       } io_mode;
 197     int deckfd;
 198     // Sending a ++END means that the read_cards command has to be reissued
 199     //enum { deckStart = 0, eof1Sent, uid1Sent, inputSent, eof2Sent, uid2Sent } deckState;
 200     enum { deckStart = 0, eof1Sent, uid1Sent, inputSent, eof2Sent} deckState;
 201     enum deckFormat deckFormat;
 202     bool running;
 203     char device_name [MAX_DEV_NAME_LEN];
 204     char fname [PATH_MAX+1];
 205   } rdr_state [N_RDR_UNITS_MAX];
 206 
 207 static char* rdr_name = "rdr";
 208 static char rdr_path_prefix[PATH_MAX+1];
 209 
 210 /*
 211  * rdr_init()
 212  */
 213 
 214 // Once-only initialization
 215 
 216 void rdr_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 217   {
 218     (void)memset (rdr_path_prefix, 0, sizeof (rdr_path_prefix));
 219     (void)memset (rdr_state, 0, sizeof (rdr_state));
 220     for (uint i = 0; i < N_RDR_UNITS_MAX; i ++)
 221       rdr_state [i] . deckfd = -1;
 222   }
 223 
 224 static t_stat rdr_reset (UNUSED DEVICE * dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 225   {
 226 
 227 
 228 
 229 
 230 
 231 
 232 
 233     return SCPE_OK;
 234   }
 235 
 236 // General Electric Cards
 237 //
 238 // General Electric used the following collating sequence on their machines,
 239 // including the GE 600 (the machine on which Multics was developed); this is
 240 // largely upward compatible from the IBM 026 commercial character set, and it
 241 // shows strong influence from the IBM 1401 character set while supporting the
 242 // full ASCII character set, with 64 printable characters, as it was understood
 243 // in the 1960's.
 244 //
 245 // GE   &-0123456789ABCDEFGHIJKLMNOPQR/STUVWXYZ[#@:>?+.](<\^$*);'_,%="!
 246 //      ________________________________________________________________
 247 //     /&-0123456789ABCDEFGHIJKLMNOPQR/STUVWXYZ #@:>V .¤(<§ $*);^±,%='"
 248 // 12 / O           OOOOOOOOO                        OOOOOO
 249 // 11|   O                   OOOOOOOOO                     OOOOOO
 250 //  0|    O                           OOOOOOOOO                  OOOOOO
 251 //  1|     O        O        O        O
 252 //  2|      O        O        O        O       O     O     O     O
 253 //  3|       O        O        O        O       O     O     O     O
 254 //  4|        O        O        O        O       O     O     O     O
 255 //  5|         O        O        O        O       O     O     O     O
 256 //  6|          O        O        O        O       O     O     O     O
 257 //  7|           O        O        O        O       O     O     O     O
 258 //  8|            O        O        O        O OOOOOOOOOOOOOOOOOOOOOOOO
 259 //  9|             O        O        O        O
 260 //   |__________________________________________________________________
 261 // In the above, the 0-8-2 punch shown as _ should be printed as an assignment
 262 // arrow, and the 11-8-2 punch shown as ^ should be printed as an up-arrow.
 263 // This conforms to the evolution of of these ASCII symbols from the time GE
 264 // adopted this character set and the present.
 265 
 266 // Sadly, AG91, App. C disagrees
 267 
 268 
 269 
 270 
 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 // From card_codes_.alm
 357 
 358 static uint16 table [128] =
 359   {
 360     05403, 04401, 04201, 04101, 00005, 01023, 01013, 01007,
 361     02011, 04021, 02021, 04103, 04043, 04023, 04013, 04007,
 362     06403, 02401, 02201, 02101, 00043, 00023, 00201, 01011,
 363     02003, 02403, 00007, 01005, 02043, 02023, 02013, 02007,
 364     00000, 02202, 00006, 00102, 02102, 01042, 04000, 00022,
 365     04022, 02022, 02042, 04012, 01102, 02000, 04102, 01400,
 366     01000, 00400, 00200, 00100, 00040, 00020, 00010, 00004,
 367     00002, 00001, 00202, 02012, 04042, 00012, 01012, 01006,
 368     00042, 04400, 04200, 04100, 04040, 04020, 04010, 04004,
 369     04002, 04001, 02400, 02200, 02100, 02040, 02020, 02010,
 370     02004, 02002, 02001, 01200, 01100, 01040, 01020, 01010,
 371     01004, 01002, 01001, 05022, 04202, 06022, 02006, 01022,
 372     00402, 05400, 05200, 05100, 05040, 05020, 05010, 05004,
 373     05002, 05001, 06400, 06200, 06100, 06040, 06020, 06010,
 374     06004, 06002, 06001, 03200, 03100, 03040, 03020, 03010,
 375     03004, 03002, 03001, 05000, 04006, 03000, 03400, 00000
 376   };
 377 
 378 static void asciiToH (char * str, uint * hstr, size_t l)
     /* [previous][next][first][last][top][bottom][index][help] */
 379   {
 380     char * p = str;
 381     for (size_t i = 0; i < l; i ++)
 382     //for (char * p = str; * p; p ++)
 383       {
 384         * hstr ++ = table [(* p) & 0177];
 385         p ++;
 386       }
 387   }
 388 
 389 
 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 static int getCardLine (int fd, unsigned char * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 418   {
 419     uint n = 0;
 420     buffer [n] = 0;
 421     while (1)
 422       {
 423         uint8 ch;
 424         ssize_t rc = read (fd, & ch, 1);
 425         if (rc <= 0) // eof or err
 426           return n == 0;
 427         if (ch == '\n')
 428           return 0;
 429         buffer [n ++] = ch;
 430         buffer [n] = 0;
 431         if (n > 79)
 432           return 0;
 433       }
 434 #if defined(SUNLINT) || !defined(__SUNPRO_C) && !defined(__SUNPRO_CC)
 435     /*NOTREACHED*/ /* unreachable */
 436     return 0;
 437 #endif /* if defined(SUNLINT) || !defined(__SUNPRO_C) && !defined(__SUNPRO_CC) */
 438   }
 439 
 440 static int getCardData (int fd, char * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 441   {
 442     (void)memset (buffer, 0, 80);
 443     ssize_t rc = read (fd, buffer, 80);
 444     if (rc < 0)
 445       return 0;
 446     return (int) rc;
 447   }
 448 
 449 #define rawCardImageBytes (80 * 12 / 8)
 450 static int getRawCardData (int fd, uint8_t * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 451   {
 452     (void)memset (buffer, 0, rawCardImageBytes + 2);
 453     ssize_t rc = read (fd, buffer, rawCardImageBytes);
 454     if (rc < 0)
 455       return 0;
 456     return (int) rc;
 457   }
 458 
 459 #if defined(TESTING)
 460 static bool empty = false;
 461 #endif /* if defined(TESTING) */
 462 
 463 static int rdrReadRecord (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
 464 #if defined(TESTING)
 465   cpu_state_t * cpup  = _cpup;
 466 #endif
 467   iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
 468   sim_debug (DBG_NOTIFY, & rdr_dev, "Read binary\n");
 469   uint ctlr_unit_idx  = get_ctlr_idx (iomUnitIdx, chan);
 470   uint unitIdx        = cables->urp_to_urd[ctlr_unit_idx][p->IDCW_DEV_CODE].unit_idx;
 471 
 472 
 473 
 474 
 475 
 476 
 477 
 478 
 479 
 480 
 481 
 482 
 483 
 484 
 485 
 486 
 487 
 488 #if defined(TESTING)
 489   sim_printf ("hopper not empty\r\n");
 490   empty = false;
 491 #endif
 492   unsigned char cardImage [80] = "";
 493   uint8_t rawCardImage [rawCardImageBytes + 2 ];
 494   size_t l = 0;
 495   // initialize to quiet compiler
 496   enum deckFormat thisCard = cardDeck;
 497 
 498   static int jobNo = 0;
 499 
 500   switch (rdr_state [unitIdx].deckState) {
 501     case deckStart: {
 502 #if defined(TESTING)
 503   sim_printf ("deckStart: sending ++EOF\r\n");
 504 #endif
 505       strcpy ((char *) cardImage, "++EOF");
 506       l = strlen ((char *) cardImage);
 507       thisCard = cardDeck; //-V1048
 508       rdr_state [unitIdx].deckState = eof1Sent;
 509       jobNo ++;
 510     }
 511     break;
 512 
 513     case eof1Sent: {
 514 #if defined(TESTING)
 515   sim_printf ("eof1Sent: sending ++UID\r\n");
 516 #endif
 517       (void)sprintf ((char *) cardImage, "++UID %d", jobNo);
 518       l = strlen (((char *) cardImage) + 1);
 519       thisCard = cardDeck; //-V1048
 520       rdr_state [unitIdx].deckState = uid1Sent;
 521     }
 522     break;
 523 
 524     case uid1Sent: {
 525 #if defined(TESTING)
 526   sim_printf ("uid1Sent: sending data\r\n");
 527 #endif
 528       int rc = getCardLine (rdr_state [unitIdx].deckfd, cardImage);
 529       if (rc) {
 530 #if defined(TESTING)
 531   sim_printf ("uid1Sent: getCardLine returned %d\r\n", rc);
 532 #endif
 533         close (rdr_state [unitIdx].deckfd);
 534         // Windows can't unlink open files; do it now...
 535         rc = unlink (rdr_state [unitIdx].fname);
 536         if (rc)
 537           perror ("card reader deck unlink\n");
 538         rdr_state [unitIdx].deckfd = -1;
 539         rdr_state [unitIdx].deckState = deckStart;
 540         p->stati = 04201; // hopper empty
 541         return IOM_CMD_DISCONNECT;
 542       }
 543 #if defined(TESTING)
 544   sim_printf ("uid1Sent: getCardLine returned <%s>\r\n", cardImage);
 545 #endif
 546       l = strlen ((char *) cardImage);
 547       thisCard = cardDeck; //-V1048
 548       if (strncasecmp ((char *) cardImage, "++input", 7) == 0) {
 549 #if defined(TESTING)
 550   sim_printf ("uid1Sent: switching to inputSent <%s>\r\n", cardImage);
 551 #endif
 552         rdr_state [unitIdx].deckState = inputSent;
 553       }
 554     }
 555     break;
 556 
 557     // Reading the actual data cards
 558 
 559     case inputSent: {
 560 #if defined(TESTING)
 561   sim_printf ("inputSent: format %d\r\n", rdr_state [unitIdx].deckFormat);
 562 #endif
 563       switch (rdr_state [unitIdx].deckFormat) {
 564         case cardDeck: {
 565 #if defined(TESTING)
 566   sim_printf ("inputSent: cardDeck\r\n");
 567 #endif
 568           int rc = getCardLine (rdr_state [unitIdx].deckfd, cardImage);
 569           if (rc) {
 570             strcpy ((char *) cardImage, "++EOF");
 571             rdr_state [unitIdx].deckState = eof2Sent;
 572           }
 573           l = strlen ((char *) cardImage);
 574         }
 575         thisCard = cardDeck; //-V1048
 576         break;
 577 
 578         case streamDeck: {
 579 #if defined(TESTING)
 580   sim_printf ("inputSent: streamDeck\r\n");
 581 #endif
 582           l = (size_t) getCardData (rdr_state [unitIdx].deckfd, (char *) cardImage);
 583           if (l) {
 584             thisCard = streamDeck;
 585           } else {
 586             strcpy ((char *) cardImage, "++EOF");
 587             l = strlen ((char *) cardImage);
 588             rdr_state [unitIdx].deckState = eof2Sent;
 589             thisCard = cardDeck; //-V1048
 590           }
 591 #if defined(TESTING)
 592   sim_printf ("inputSent: getCardLine returned <%s>\r\n", cardImage);
 593 #endif
 594         }
 595         break;
 596 
 597         case sevenDeck: {
 598 #if defined(TESTING)
 599   sim_printf ("inputSent: 7Deck\r\n");
 600 #endif
 601           l = (size_t) getRawCardData (rdr_state [unitIdx].deckfd, rawCardImage);
 602           if (l) {
 603             thisCard = sevenDeck;
 604           } else {
 605             strcpy ((char *) cardImage, "++EOF");
 606             l = strlen ((char *) cardImage);
 607             rdr_state [unitIdx].deckState = eof2Sent;
 608             thisCard = cardDeck; //-V1048
 609           }
 610         }
 611         break;
 612 
 613       } // switch (deckFormat)
 614     } // case inputSent
 615     break;
 616 
 617 // Sending a ++END means that the read_cards command has to be reissued
 618 
 619     case eof2Sent: {
 620 # if defined(TESTING)
 621   sim_printf ("eof2Sent\r\n");
 622 # endif
 623       (void)sprintf ((char *) cardImage, "++UID %d", jobNo);
 624       l = strlen ((char *) cardImage);
 625       thisCard = cardDeck; //-V1048
 626       rdr_state [unitIdx].deckState = deckStart;
 627       close (rdr_state [unitIdx].deckfd);
 628       // Windows can't unlink open files; do it now...
 629       int rc = unlink (rdr_state [unitIdx].fname);
 630       if (rc)
 631         perror ("card reader deck unlink\n");
 632       rdr_state [unitIdx].deckfd = -1;
 633     }
 634     break;
 635 
 636 
 637 
 638 
 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     //sim_printf ("card <%s>\r\n", cardImage);
 671 #if defined(TESTING)
 672   sim_printf ("\r\n");
 673   sim_printf ("\r\n");
 674   for (uint i = 0; i < 80; i ++) {
 675     if (isprint (cardImage [i]))
 676       sim_printf ("%c", cardImage [i]);
 677     else
 678       sim_printf ("\\%03o", cardImage [i]);
 679   }
 680   sim_printf ("\r\n");
 681   sim_printf ("\r\n");
 682 #endif
 683   word36 buffer [27];
 684   switch (thisCard) {
 685     case sevenDeck: {
 686       // This will overread rawCardImage by 12 bits, but that's okay
 687       // because Multics will ignore the last 12 bits.
 688       for (uint i = 0; i < 27; i ++)
 689         buffer [i] = extr36 ((uint8 *) rawCardImage, i);
 690       //sim_printf ("7deck %012"PRIo64" %012"PRIo64" %012"PRIo64" %012"PRIo64"\r\n",
 691       //             buffer [0], buffer [1], buffer [2], buffer [3]);
 692     }
 693     break;
 694 
 695     case streamDeck:
 696 
 697 
 698 
 699 
 700 
 701 
 702 
 703 
 704 
 705 
 706     case cardDeck: {
 707       if (l > 80) {
 708         sim_warn ("Whups. rdr l %lu > 80; truncating.\n", (unsigned long)l);
 709         l = 80;
 710         //cardImage [l] = 0;
 711       }
 712 
 713       uint hbuf [l + 1];
 714       asciiToH ((char *) cardImage, hbuf, l);
 715 
 716       // 12 bits / char
 717       uint nbits = (uint) l * 12;
 718       // 36 bits / word
 719       uint tally = (nbits + 35) / 36;
 720 
 721       if (tally > 27) { //-V547
 722         sim_warn ("Impossible rdr tally: %d > 27; truncating.\n", tally);
 723         tally = 27;
 724       }
 725 
 726       // Remember that Hollerith for blank is 0, this is really
 727       // filling the buffer with blanks.
 728       (void)memset (buffer, 0, sizeof (buffer));
 729       for (uint col = 0; col < l; col ++) {
 730         uint wordno  = col / 3;
 731         uint fieldno = col % 3;
 732         putbits36_12 (& buffer [wordno], fieldno * 12, (word12) hbuf [col]);
 733       }
 734     }
 735     break;
 736   }
 737 
 738 
 739 
 740 
 741 
 742 
 743 
 744 
 745 
 746 
 747 
 748   p->stati = 04000;
 749 
 750   // Card images are 80 columns.
 751   uint tally = 27;
 752 
 753   iom_indirect_data_service (iomUnitIdx, chan, buffer, & tally, true);
 754   p->initiate     = false;
 755   p->stati        = 04000; //-V1048  // ok
 756   p->tallyResidue = (word12) tally & MASK12;
 757   p->charPos      = 0;
 758 
 759   return IOM_CMD_PROCEED;
 760 }
 761 
 762 static void submit (enum deckFormat fmt, char * fname, uint16 readerIndex)
     /* [previous][next][first][last][top][bottom][index][help] */
 763   {
 764     if (readerIndex >= N_RDR_UNITS_MAX) {
 765       sim_warn("crdrdr: submit called with invalid reader index %ld\n", (long) readerIndex);
 766       return;
 767     }
 768 
 769     int deckfd = open (fname, O_RDONLY);
 770     if (deckfd < 0)
 771       perror ("card reader deck open\n");
 772     // Windows can't unlink open files; save the file name and unlink on close.
 773     // int rc = unlink (fname); // this only works on UNIX
 774 #if defined(TESTING)
 775     sim_printf ("submit %s\r\n", fname);
 776 #endif
 777 #if defined(__GNUC__)
 778 # if !defined(__clang_version__)
 779 #  if __GNUC__ > 7
 780 #   if !defined(__INTEL_COMPILER)
 781 #    pragma GCC diagnostic push
 782 #    pragma GCC diagnostic ignored "-Wstringop-truncation"
 783 #   endif /* if !defined(__INTEL_COMPILER) */
 784 #  endif /* if __GNUC__ > 7 */
 785 # endif /* if !defined(__clang_version__) */
 786 #endif /* if defined(__GNUC__) */
 787     strncpy (rdr_state [readerIndex].fname, fname, sizeof(rdr_state [readerIndex].fname) - 1);
 788     rdr_state [readerIndex].fname [sizeof(rdr_state [readerIndex].fname) - 1] = '\0';
 789 #if defined(__GNUC__)
 790 # if !defined(__clang_version__)
 791 #  if __GNUC__ > 7
 792 #   if !defined(__INTEL_COMPILER)
 793 #    pragma GCC diagnostic pop
 794 #   endif /* if !defined(__INTEL_COMPILER) */
 795 #  endif /* if __GNUC__ > 7 */
 796 # endif /* if !defined(__clang_version__) */
 797 #endif /* if defined(__GNUC__) */
 798     rdr_state [readerIndex].deckfd     = deckfd;
 799     rdr_state [readerIndex].deckState  = deckStart;
 800     rdr_state [readerIndex].deckFormat = fmt;
 801     if (deckfd >= 0)
 802       rdrCardReady (readerIndex);
 803   }
 804 
 805 static void scanForCards(uint16 readerIndex)
     /* [previous][next][first][last][top][bottom][index][help] */
 806   {
 807     char rdr_dir [2 * PATH_MAX + 8];
 808 
 809     if (readerIndex >= N_RDR_UNITS_MAX) {
 810       sim_warn("crdrdr: scanForCards called with invalid reader index %d\n", readerIndex);
 811       return;
 812     }
 813 
 814 #if !defined(__MINGW64__) || !defined(__MINGW32__)
 815     const char* r_tmpdir = getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp";
 816 #else
 817     const char* r_tmpdir = getenv("TEMP") ? getenv("TEMP") : \
 818                            getenv("TMP")  ? getenv("TMP")  : ".";
 819 #endif /* if !defined(__MINGW64__) || !defined(__MINGW32__) */
 820     snprintf_truncat(rdr_dir, PATH_MAX + 1, "%s/%s%c",
 821                      r_tmpdir, rdr_name, 'a' + readerIndex);
 822 
 823     if (rdr_path_prefix [0])
 824       {
 825         snprintf_truncat(rdr_dir, PATH_MAX + 1, "%s%s%c",
 826                          rdr_path_prefix, rdr_name, 'a' + readerIndex);
 827       }
 828 
 829     DIR * dp;
 830     dp = opendir (rdr_dir);
 831     if (! dp)
 832       {
 833         sim_warn ("crdrdr opendir '%s' fail.\n", rdr_dir);
 834         perror ("opendir");
 835         return;
 836       }
 837     struct dirent * entry;
 838     struct stat info;
 839     char fqname [2 * PATH_MAX + 8];
 840     while ((entry = readdir (dp)))
 841       {
 842         strcpy (fqname, rdr_dir);
 843         strcat (fqname, "/");
 844         strcat (fqname, entry -> d_name);
 845 
 846         if (stat(fqname, &info) != 0)
 847           {
 848             sim_warn("crdrdr: scanForCards stat() error for %s: %s\n", fqname, xstrerror_l(errno));
 849             continue;
 850           }
 851 
 852         if (S_ISDIR(info.st_mode))
 853           {
 854             // Found a directory so skip it
 855             continue;
 856           }
 857 
 858         if (rdr_state [readerIndex] . deckfd < 0)
 859           {
 860             if (strncmp (entry -> d_name, "cdeck.", 6) == 0)
 861               {
 862                 submit (cardDeck, fqname, readerIndex);
 863                 break;
 864               }
 865             if (strncmp (entry -> d_name, "7deck.", 6) == 0)
 866               {
 867                 submit (sevenDeck, fqname, readerIndex);
 868                 break;
 869               }
 870             if (strncmp (entry -> d_name, "sdeck.", 6) == 0)
 871               {
 872                 submit (streamDeck, fqname, readerIndex);
 873                 break;
 874               }
 875           }
 876         if (strcmp (entry -> d_name, "discard") == 0)
 877           {
 878             // Windows can't unlink open files; do it now...
 879             int rc = unlink (fqname);
 880             if (rc)
 881               perror ("crdrdr discard unlink\n");
 882             if (rdr_state [readerIndex] . deckfd >= 0)
 883               {
 884                 close (rdr_state [readerIndex] . deckfd);
 885                 rc = unlink (rdr_state [readerIndex] . fname);
 886                 if (rc)
 887                   perror ("crdrdr deck unlink\n");
 888                 rdr_state [readerIndex] . deckfd = -1;
 889                 rdr_state [readerIndex] . deckState = deckStart;
 890                 break;
 891              }
 892           }
 893       }
 894     closedir (dp);
 895   }
 896 
 897 void rdrProcessEvent (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 898   {
 899     if (rdr_path_prefix [0])
 900       {
 901         // We support multiple card readers when path prefixing is turned on
 902         // so we need to check each possible reader to see if it is active
 903         for (uint16 reader_idx = 0; reader_idx < rdr_dev . numunits; reader_idx++)
 904           {
 905             if (rdr_state [reader_idx] . running)
 906                 scanForCards(reader_idx);
 907           }
 908       }
 909     else
 910       {
 911         // When path prefixing is off we only support a single card reader
 912         // (this is for backwards compatibility)
 913         if (! rdr_state [0] . running)
 914           return;
 915 
 916         scanForCards(0);
 917       }
 918   }
 919 
 920 void rdrCardReady (int unitNum)
     /* [previous][next][first][last][top][bottom][index][help] */
 921   {
 922     uint ctlr_unit_idx = cables->rdr_to_urp [unitNum].ctlr_unit_idx;
 923     uint ctlr_port_num = 0; // Single port device
 924     uint iom_unit_idx  = cables->urp_to_iom[ctlr_unit_idx][ctlr_port_num].iom_unit_idx;
 925     uint chan_num      = cables->urp_to_iom[ctlr_unit_idx][ctlr_port_num].chan_num;
 926     uint dev_code      = cables->rdr_to_urp[unitNum].dev_code;
 927     send_special_interrupt (iom_unit_idx, chan_num, dev_code, 0377, 0377 /* card reader to ready */); //-V536
 928   }
 929 
 930 iom_cmd_rc_t rdr_iom_cmd (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
 931   iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
 932 #if defined(TESTING)
 933   uint dev_code       = p->IDCW_DEV_CODE;
 934   cpu_state_t * cpup  = _cpup;
 935 
 936   sim_debug (DBG_TRACE, & rdr_dev, "%s: RDR %c%02o_%02o\n",
 937           __func__, iomChar (iomUnitIdx), chan, dev_code);
 938 #endif
 939 
 940   uint ctlr_unit_idx        = get_ctlr_idx (iomUnitIdx, chan);
 941   uint unitIdx              = cables->urp_to_urd[ctlr_unit_idx][p->IDCW_DEV_CODE].unit_idx;
 942   struct rdr_state * statep = & rdr_state[unitIdx];
 943   statep->running           = true;
 944 
 945   iom_cmd_rc_t rc = IOM_CMD_PROCEED;
 946   // IDCW?
 947   if (IS_IDCW (p)) {
 948     // IDCW
 949     statep->io_mode = rdr_no_mode;
 950 
 951     switch (p->IDCW_DEV_CMD) {
 952       case 000: // CMD 00 Request status
 953         sim_debug (DBG_DEBUG, & rdr_dev, "%s: Request Status\n", __func__);
 954         p->stati = 04000;
 955         // This is controller status, not device status
 956         //if (rdr_state[unitIdx].deckfd < 0)
 957           //p->stati = 04201; // hopper empty
 958 #if defined(TESTING)
 959         sim_printf ("Request status %04o\r\n", p->stati);
 960 #endif
 961         break;
 962 
 963       case 001: // CMD 01 Read binary
 964         sim_debug (DBG_DEBUG, & rdr_dev, "%s: Read Binary\n", __func__);
 965         if (rdr_state [unitIdx].deckfd < 0) {
 966           p->stati        = 04201; // hopper empty
 967           p->tallyResidue = 0;
 968 #if defined(TESTING)
 969           if (! empty)
 970             sim_printf ("hopper empty\r\n");
 971           empty = true;
 972 #endif
 973           return IOM_CMD_DISCONNECT;
 974         }
 975         statep->io_mode = rdr_rd_bin;
 976         p->stati        = 04000;
 977         // This is controller status, not device status
 978         //if (rdr_state[unitIdx].deckfd < 0)
 979           //p->stati = 04201; // hopper empty
 980 #if defined(TESTING)
 981 sim_printf ("read binary %04o\r\n", p->stati);
 982 #endif
 983         break;
 984 
 985       case 040: // CMD 40 Reset status
 986         sim_debug (DBG_DEBUG, & rdr_dev, "%s: Request Status\n", __func__);
 987         p->stati  = 04000;
 988         p->isRead = false;
 989         // This is controller status, not device status
 990         //if (rdr_state[unitIdx].deckfd < 0)
 991           //p->stati = 04201; // hopper empty
 992 #if defined(TESTING)
 993 sim_printf ("reset status %04o\r\n", p->stati);
 994 #endif
 995         break;
 996 
 997       default:
 998 #if defined(TESTING)
 999 sim_printf ("unknown  %o\r\n", p->IDCW_DEV_CMD);
1000 #endif
1001         if (p->IDCW_DEV_CMD != 051) // ignore bootload console probe
1002           sim_warn ("%s: RDR unrecognized device command  %02o\n", __func__, p->IDCW_DEV_CMD);
1003         p->stati      = 04501; // cmd reject, invalid opcode
1004         p->chanStatus = chanStatIncorrectDCW;
1005         return IOM_CMD_ERROR;
1006     } // switch IDCW_DEV_CMD
1007 
1008     sim_debug (DBG_DEBUG, & rdr_dev, "%s: stati %04o\n", __func__, p->stati);
1009     return IOM_CMD_PROCEED;
1010   } // if IDCW
1011 
1012   // Not IDCW; TDCW are captured in IOM, so must be IOTD, IOTP or IOTNP
1013   switch (statep->io_mode) {
1014     case rdr_no_mode:
1015 #if defined(TESTING)
1016       sim_printf ("%s: Unexpected IOTx\r\n", __func__);
1017 #endif
1018       //sim_warn ("%s: Unexpected IOTx\n", __func__);
1019       //return IOM_CMD_ERROR;
1020       break;
1021 
1022     case rdr_rd_bin: {
1023       int rc = rdrReadRecord (iomUnitIdx, chan);
1024 #if defined(TESTING)
1025 sim_printf ("rdrReadRecord returned %d\r\n", rc);
1026 #endif
1027       if (rc)
1028         return IOM_CMD_DISCONNECT;
1029     }
1030     break;
1031 
1032     default:
1033       sim_warn ("%s: Unrecognized io_mode %d\n", __func__, statep->io_mode);
1034       return IOM_CMD_ERROR;
1035   }
1036   return rc;
1037 }
1038 
1039 static t_stat rdr_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr, UNUSED int val,
     /* [previous][next][first][last][top][bottom][index][help] */
1040                                UNUSED const void * desc)
1041   {
1042     sim_printf("Number of RDR units in system is %d\n", rdr_dev . numunits);
1043     return SCPE_OK;
1044   }
1045 
1046 static t_stat rdr_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value, const char * cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1047                               UNUSED void * desc)
1048   {
1049     if (! cptr)
1050       return SCPE_ARG;
1051     int n = atoi (cptr);
1052     if (n < 1 || n > N_RDR_UNITS_MAX)
1053       return SCPE_ARG;
1054     rdr_dev . numunits = (uint32) n;
1055     return SCPE_OK;
1056   }
1057 
1058 static t_stat rdr_show_device_name (UNUSED FILE * st, UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1059                                     UNUSED int val, UNUSED const void * desc)
1060   {
1061     long n = RDR_UNIT_NUM (uptr);
1062     if (n < 0 || n >= N_RDR_UNITS_MAX)
1063       return SCPE_ARG;
1064     sim_printf("name     : %s", rdr_state [n] . device_name);
1065     return SCPE_OK;
1066   }
1067 
1068 static t_stat rdr_set_device_name (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1069                                    UNUSED const char * cptr, UNUSED void * desc)
1070   {
1071     long n = RDR_UNIT_NUM (uptr);
1072     if (n < 0 || n >= N_RDR_UNITS_MAX)
1073       return SCPE_ARG;
1074     if (cptr)
1075       {
1076         strncpy (rdr_state [n] . device_name, cptr, MAX_DEV_NAME_LEN - 1);
1077         rdr_state [n] . device_name [MAX_DEV_NAME_LEN - 1] = 0;
1078       }
1079     else
1080       rdr_state [n] . device_name [0] = 0;
1081     return SCPE_OK;
1082   }
1083 
1084 static t_stat rdr_set_path (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1085                             const UNUSED char * cptr, UNUSED void * desc)
1086   {
1087     if (! cptr)
1088       return SCPE_ARG;
1089 
1090     size_t len = strlen(cptr);
1091 
1092     // We check for length - (3 + length of rdr_name) to allow for the
1093     // null, a possible '/' being added and "rdrx" being added
1094     if (len >= (sizeof(rdr_path_prefix) - (strlen(rdr_name) + 3)))
1095       return SCPE_ARG;
1096 
1097     strncpy(rdr_path_prefix, cptr, sizeof(rdr_path_prefix) -1);
1098     rdr_path_prefix[sizeof(rdr_path_prefix) - 1] = '\0';
1099     if (len > 0)
1100       {
1101         if (rdr_path_prefix[len - 1] != '/')
1102           {
1103             if (len == sizeof(rdr_path_prefix) - 1)
1104               return SCPE_ARG;
1105             rdr_path_prefix[len++] = '/';
1106             rdr_path_prefix[len] = 0;
1107           }
1108       }
1109     return SCPE_OK;
1110   }
1111 
1112 static t_stat rdr_show_path (UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1113                              UNUSED int val, UNUSED const void * desc)
1114   {
1115     if (rdr_path_prefix [0])
1116       {
1117         sim_printf("\rPath to card reader directories is \"%s\".\r\n", rdr_path_prefix);
1118       }
1119     else
1120       {
1121         sim_printf("\rPath to card reader directories is unset.\r\n");
1122       }
1123     return SCPE_OK;
1124   }

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