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

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