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

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