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);
 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];
 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     strcpy (rdr_state [readerIndex].fname, fname);
 778     rdr_state [readerIndex].deckfd     = deckfd;
 779     rdr_state [readerIndex].deckState  = deckStart;
 780     rdr_state [readerIndex].deckFormat = fmt;
 781     if (deckfd >= 0)
 782       rdrCardReady (readerIndex);
 783   }
 784 
 785 static void scanForCards(uint16 readerIndex)
     /* [previous][next][first][last][top][bottom][index][help] */
 786   {
 787     char rdr_dir [2 * PATH_MAX + 8];
 788 
 789     if (readerIndex >= N_RDR_UNITS_MAX) {
 790       sim_warn("crdrdr: scanForCards called with invalid reader index %d\n", readerIndex);
 791       return;
 792     }
 793 
 794 #if !defined(__MINGW64__) || !defined(__MINGW32__)
 795     const char* r_tmpdir = getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp";
 796 #else
 797     const char* r_tmpdir = getenv("TEMP") ? getenv("TEMP") : \
 798                            getenv("TMP")  ? getenv("TMP")  : ".";
 799 #endif /* if !defined(__MINGW64__) || !defined(__MINGW32__) */
 800     snprintf_truncat(rdr_dir, PATH_MAX + 1, "%s/%s%c",
 801                      r_tmpdir, rdr_name, 'a' + readerIndex);
 802 
 803     if (rdr_path_prefix [0])
 804       {
 805         snprintf_truncat(rdr_dir, PATH_MAX + 1, "%s%s%c",
 806                          rdr_path_prefix, rdr_name, 'a' + readerIndex);
 807       }
 808 
 809     DIR * dp;
 810     dp = opendir (rdr_dir);
 811     if (! dp)
 812       {
 813         sim_warn ("crdrdr opendir '%s' fail.\n", rdr_dir);
 814         perror ("opendir");
 815         return;
 816       }
 817     struct dirent * entry;
 818     struct stat info;
 819     char fqname [2 * PATH_MAX + 8];
 820     while ((entry = readdir (dp)))
 821       {
 822         strcpy (fqname, rdr_dir);
 823         strcat (fqname, "/");
 824         strcat (fqname, entry -> d_name);
 825 
 826         if (stat(fqname, &info) != 0)
 827           {
 828             sim_warn("crdrdr: scanForCards stat() error for %s: %s\n", fqname, xstrerror_l(errno));
 829             continue;
 830           }
 831 
 832         if (S_ISDIR(info.st_mode))
 833           {
 834             // Found a directory so skip it
 835             continue;
 836           }
 837 
 838         if (rdr_state [readerIndex] . deckfd < 0)
 839           {
 840             if (strncmp (entry -> d_name, "cdeck.", 6) == 0)
 841               {
 842                 submit (cardDeck, fqname, readerIndex);
 843                 break;
 844               }
 845             if (strncmp (entry -> d_name, "7deck.", 6) == 0)
 846               {
 847                 submit (sevenDeck, fqname, readerIndex);
 848                 break;
 849               }
 850             if (strncmp (entry -> d_name, "sdeck.", 6) == 0)
 851               {
 852                 submit (streamDeck, fqname, readerIndex);
 853                 break;
 854               }
 855           }
 856         if (strcmp (entry -> d_name, "discard") == 0)
 857           {
 858             // Windows can't unlink open files; do it now...
 859             int rc = unlink (fqname);
 860             if (rc)
 861               perror ("crdrdr discard unlink\n");
 862             if (rdr_state [readerIndex] . deckfd >= 0)
 863               {
 864                 close (rdr_state [readerIndex] . deckfd);
 865                 rc = unlink (rdr_state [readerIndex] . fname);
 866                 if (rc)
 867                   perror ("crdrdr deck unlink\n");
 868                 rdr_state [readerIndex] . deckfd = -1;
 869                 rdr_state [readerIndex] . deckState = deckStart;
 870                 break;
 871              }
 872           }
 873       }
 874     closedir (dp);
 875   }
 876 
 877 void rdrProcessEvent (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 878   {
 879     if (rdr_path_prefix [0])
 880       {
 881         // We support multiple card readers when path prefixing is turned on
 882         // so we need to check each possible reader to see if it is active
 883         for (uint16 reader_idx = 0; reader_idx < rdr_dev . numunits; reader_idx++)
 884           {
 885             if (rdr_state [reader_idx] . running)
 886                 scanForCards(reader_idx);
 887           }
 888       }
 889     else
 890       {
 891         // When path prefixing is off we only support a single card reader
 892         // (this is for backwards compatibility)
 893         if (! rdr_state [0] . running)
 894           return;
 895 
 896         scanForCards(0);
 897       }
 898   }
 899 
 900 void rdrCardReady (int unitNum)
     /* [previous][next][first][last][top][bottom][index][help] */
 901   {
 902     uint ctlr_unit_idx = cables->rdr_to_urp [unitNum].ctlr_unit_idx;
 903     uint ctlr_port_num = 0; // Single port device
 904     uint iom_unit_idx  = cables->urp_to_iom[ctlr_unit_idx][ctlr_port_num].iom_unit_idx;
 905     uint chan_num      = cables->urp_to_iom[ctlr_unit_idx][ctlr_port_num].chan_num;
 906     uint dev_code      = cables->rdr_to_urp[unitNum].dev_code;
 907     send_special_interrupt (iom_unit_idx, chan_num, dev_code, 0377, 0377 /* card reader to ready */); //-V536
 908   }
 909 
 910 iom_cmd_rc_t rdr_iom_cmd (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
 911   iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
 912 #if defined(TESTING)
 913   uint dev_code       = p->IDCW_DEV_CODE;
 914   cpu_state_t * cpup  = _cpup;
 915 
 916   sim_debug (DBG_TRACE, & rdr_dev, "%s: RDR %c%02o_%02o\n",
 917           __func__, iomChar (iomUnitIdx), chan, dev_code);
 918 #endif
 919 
 920   uint ctlr_unit_idx        = get_ctlr_idx (iomUnitIdx, chan);
 921   uint unitIdx              = cables->urp_to_urd[ctlr_unit_idx][p->IDCW_DEV_CODE].unit_idx;
 922   struct rdr_state * statep = & rdr_state[unitIdx];
 923   statep->running           = true;
 924 
 925   iom_cmd_rc_t rc = IOM_CMD_PROCEED;
 926   // IDCW?
 927   if (IS_IDCW (p)) {
 928     // IDCW
 929     statep->io_mode = rdr_no_mode;
 930 
 931     switch (p->IDCW_DEV_CMD) {
 932       case 000: // CMD 00 Request status
 933         sim_debug (DBG_DEBUG, & rdr_dev, "%s: Request Status\n", __func__);
 934         p->stati = 04000;
 935         // This is controller status, not device status
 936         //if (rdr_state[unitIdx].deckfd < 0)
 937           //p->stati = 04201; // hopper empty
 938 #if defined(TESTING)
 939         sim_printf ("Request status %04o\r\n", p->stati);
 940 #endif
 941         break;
 942 
 943       case 001: // CMD 01 Read binary
 944         sim_debug (DBG_DEBUG, & rdr_dev, "%s: Read Binary\n", __func__);
 945         if (rdr_state [unitIdx].deckfd < 0) {
 946           p->stati        = 04201; // hopper empty
 947           p->tallyResidue = 0;
 948 #if defined(TESTING)
 949           if (! empty)
 950             sim_printf ("hopper empty\r\n");
 951           empty = true;
 952 #endif
 953           return IOM_CMD_DISCONNECT;
 954         }
 955         statep->io_mode = rdr_rd_bin;
 956         p->stati        = 04000;
 957         // This is controller status, not device status
 958         //if (rdr_state[unitIdx].deckfd < 0)
 959           //p->stati = 04201; // hopper empty
 960 #if defined(TESTING)
 961 sim_printf ("read binary %04o\r\n", p->stati);
 962 #endif
 963         break;
 964 
 965       case 040: // CMD 40 Reset status
 966         sim_debug (DBG_DEBUG, & rdr_dev, "%s: Request Status\n", __func__);
 967         p->stati  = 04000;
 968         p->isRead = false;
 969         // This is controller status, not device status
 970         //if (rdr_state[unitIdx].deckfd < 0)
 971           //p->stati = 04201; // hopper empty
 972 #if defined(TESTING)
 973 sim_printf ("reset status %04o\r\n", p->stati);
 974 #endif
 975         break;
 976 
 977       default:
 978 #if defined(TESTING)
 979 sim_printf ("unknown  %o\r\n", p->IDCW_DEV_CMD);
 980 #endif
 981         if (p->IDCW_DEV_CMD != 051) // ignore bootload console probe
 982           sim_warn ("%s: RDR unrecognized device command  %02o\n", __func__, p->IDCW_DEV_CMD);
 983         p->stati      = 04501; // cmd reject, invalid opcode
 984         p->chanStatus = chanStatIncorrectDCW;
 985         return IOM_CMD_ERROR;
 986     } // switch IDCW_DEV_CMD
 987 
 988     sim_debug (DBG_DEBUG, & rdr_dev, "%s: stati %04o\n", __func__, p->stati);
 989     return IOM_CMD_PROCEED;
 990   } // if IDCW
 991 
 992   // Not IDCW; TDCW are captured in IOM, so must be IOTD, IOTP or IOTNP
 993   switch (statep->io_mode) {
 994     case rdr_no_mode:
 995 #if defined(TESTING)
 996       sim_printf ("%s: Unexpected IOTx\r\n", __func__);
 997 #endif
 998       //sim_warn ("%s: Unexpected IOTx\n", __func__);
 999       //return IOM_CMD_ERROR;
1000       break;
1001 
1002     case rdr_rd_bin: {
1003       int rc = rdrReadRecord (iomUnitIdx, chan);
1004 #if defined(TESTING)
1005 sim_printf ("rdrReadRecord returned %d\r\n", rc);
1006 #endif
1007       if (rc)
1008         return IOM_CMD_DISCONNECT;
1009     }
1010     break;
1011 
1012     default:
1013       sim_warn ("%s: Unrecognized io_mode %d\n", __func__, statep->io_mode);
1014       return IOM_CMD_ERROR;
1015   }
1016   return rc;
1017 }
1018 
1019 static t_stat rdr_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr, UNUSED int val,
     /* [previous][next][first][last][top][bottom][index][help] */
1020                                UNUSED const void * desc)
1021   {
1022     sim_printf("Number of RDR units in system is %d\n", rdr_dev . numunits);
1023     return SCPE_OK;
1024   }
1025 
1026 static t_stat rdr_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value, const char * cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1027                               UNUSED void * desc)
1028   {
1029     if (! cptr)
1030       return SCPE_ARG;
1031     int n = atoi (cptr);
1032     if (n < 1 || n > N_RDR_UNITS_MAX)
1033       return SCPE_ARG;
1034     rdr_dev . numunits = (uint32) n;
1035     return SCPE_OK;
1036   }
1037 
1038 static t_stat rdr_show_device_name (UNUSED FILE * st, UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1039                                     UNUSED int val, UNUSED const void * desc)
1040   {
1041     long n = RDR_UNIT_NUM (uptr);
1042     if (n < 0 || n >= N_RDR_UNITS_MAX)
1043       return SCPE_ARG;
1044     sim_printf("name     : %s", rdr_state [n] . device_name);
1045     return SCPE_OK;
1046   }
1047 
1048 static t_stat rdr_set_device_name (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1049                                    UNUSED const char * cptr, UNUSED void * desc)
1050   {
1051     long n = RDR_UNIT_NUM (uptr);
1052     if (n < 0 || n >= N_RDR_UNITS_MAX)
1053       return SCPE_ARG;
1054     if (cptr)
1055       {
1056         strncpy (rdr_state [n] . device_name, cptr, MAX_DEV_NAME_LEN - 1);
1057         rdr_state [n] . device_name [MAX_DEV_NAME_LEN - 1] = 0;
1058       }
1059     else
1060       rdr_state [n] . device_name [0] = 0;
1061     return SCPE_OK;
1062   }
1063 
1064 static t_stat rdr_set_path (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1065                             const UNUSED char * cptr, UNUSED void * desc)
1066   {
1067     if (! cptr)
1068       return SCPE_ARG;
1069 
1070     size_t len = strlen(cptr);
1071 
1072     // We check for length - (3 + length of rdr_name) to allow for the
1073     // null, a possible '/' being added and "rdrx" being added
1074     if (len >= (sizeof(rdr_path_prefix) - (strlen(rdr_name) + 3)))
1075       return SCPE_ARG;
1076 
1077     strncpy(rdr_path_prefix, cptr, sizeof(rdr_path_prefix));
1078     if (len > 0)
1079       {
1080         if (rdr_path_prefix[len - 1] != '/')
1081           {
1082             if (len == sizeof(rdr_path_prefix) - 1)
1083               return SCPE_ARG;
1084             rdr_path_prefix[len++] = '/';
1085             rdr_path_prefix[len] = 0;
1086           }
1087       }
1088     return SCPE_OK;
1089   }
1090 
1091 static t_stat rdr_show_path (UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1092                              UNUSED int val, UNUSED const void * desc)
1093   {
1094     if (rdr_path_prefix [0])
1095       {
1096         sim_printf("\rPath to card reader directories is \"%s\".\r\n", rdr_path_prefix);
1097       }
1098     else
1099       {
1100         sim_printf("\rPath to card reader directories is unset.\r\n");
1101       }
1102     return SCPE_OK;
1103   }

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