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-2023 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 #define snprintf_truncat(dst, size, ...)    \
  61   do                                        \
  62     {                                       \
  63       volatile size_t n = size;             \
  64       (void)snprintf (dst, n, __VA_ARGS__); \
  65     }                                       \
  66   while (0)
  67 
  68 static t_stat rdr_reset (DEVICE * dptr);
  69 static t_stat rdr_show_nunits (FILE *st, UNIT *uptr, int val, const void *desc);
  70 static t_stat rdr_set_nunits (UNIT * uptr, int32 value, const char * cptr, void * desc);
  71 static t_stat rdr_show_device_name (FILE *st, UNIT *uptr, int val, const void *desc);
  72 static t_stat rdr_set_device_name (UNIT * uptr, int32 value, const char * cptr, void * desc);
  73 static t_stat rdr_show_path (UNUSED FILE * st, UNIT * uptr, UNUSED int val,
  74                              UNUSED const void * desc);
  75 static t_stat rdr_set_path (UNUSED UNIT * uptr, UNUSED int32 value, const UNUSED char * cptr,
  76                             UNUSED void * desc);
  77 
  78 #define UNIT_FLAGS ( UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | \
  79                      UNIT_IDLE )
  80 UNIT rdr_unit [N_RDR_UNITS_MAX] =
  81   {
  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     {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   };
  99 
 100 #define RDR_UNIT_NUM(uptr) ((uptr) - rdr_unit)
 101 
 102 static DEBTAB rdr_dt [] =
 103   {
 104     { "NOTIFY", DBG_NOTIFY, NULL },
 105     { "INFO",   DBG_INFO,   NULL },
 106     { "ERR",    DBG_ERR,    NULL },
 107     { "WARN",   DBG_WARN,   NULL },
 108     { "DEBUG",  DBG_DEBUG,  NULL },
 109     { "ALL",    DBG_ALL,    NULL }, // Don't move as it messes up DBG message
 110     { NULL,     0,          NULL }
 111   };
 112 
 113 #define UNIT_WATCH UNIT_V_UF
 114 
 115 static MTAB rdr_mod [] =
 116   {
 117 #ifndef SPEED
 118     { UNIT_WATCH, 1, "WATCH",   "WATCH",   0, 0, NULL, NULL },
 119     { UNIT_WATCH, 0, "NOWATCH", "NOWATCH", 0, 0, NULL, NULL },
 120 #endif
 121     {
 122       MTAB_XTD | MTAB_VDV | \
 123       MTAB_NMO | MTAB_VALR,                 /* Mask               */
 124       0,                                    /* Match              */
 125       "NUNITS",                             /* Print string       */
 126       "NUNITS",                             /* Match string       */
 127       rdr_set_nunits,                       /* Validation routine */
 128       rdr_show_nunits,                      /* Display routine    */
 129       "Number of RDR units in the system",  /* Value descriptor   */
 130       NULL                                  /* Help               */
 131     },
 132     {
 133       MTAB_XTD | MTAB_VUN | \
 134       MTAB_VALR | MTAB_NC,                  /* Mask               */
 135       0,                                    /* Match              */
 136       "NAME",                               /* Print string       */
 137       "NAME",                               /* Match string       */
 138       rdr_set_device_name,                  /* Validation routine */
 139       rdr_show_device_name,                 /* Display routine    */
 140       "Select the boot drive",              /* Value descriptor   */
 141       NULL                                  /* Help               */
 142     },
 143     {
 144       MTAB_XTD | MTAB_VDV | MTAB_NMO | \
 145       MTAB_VALR | MTAB_NC,                  /* Mask               */
 146       0,                                    /* Match              */
 147       "PATH",                               /* Print string       */
 148       "PATH",                               /* Match string       */
 149       rdr_set_path,                         /* Validation routine */
 150       rdr_show_path,                        /* Display routine    */
 151       "Path to card reader directories",    /* Value descriptor   */
 152       NULL                                  /* Help               */
 153     },
 154 
 155     { 0, 0, NULL, NULL, 0, 0, NULL, NULL }
 156   };
 157 
 158 DEVICE rdr_dev = {
 159     "RDR",        /* Name                */
 160     rdr_unit,     /* Units               */
 161     NULL,         /* Registers           */
 162     rdr_mod,      /* Modifiers           */
 163     N_RDR_UNITS,  /* #units              */
 164     10,           /* Address radix       */
 165     24,           /* Address width       */
 166     1,            /* Address increment   */
 167     8,            /* Data radix          */
 168     36,           /* Data width          */
 169     NULL,         /* Examine             */
 170     NULL,         /* Deposit             */
 171     rdr_reset,    /* Reset               */
 172     NULL,         /* Boot                */
 173     NULL,         /* Attach              */
 174     NULL,         /* Detach              */
 175     NULL,         /* Context             */
 176     DEV_DEBUG,    /* Flags               */
 177     0,            /* Debug control flags */
 178     rdr_dt,       /* Debug flag names    */
 179     NULL,         /* Memory size change  */
 180     NULL,         /* Logical name        */
 181     NULL,         /* Help                */
 182     NULL,         /* Attach help         */
 183     NULL,         /* Attach context      */
 184     NULL,         /* Description         */
 185     NULL          /* End                 */
 186 };
 187 
 188 enum deckFormat { sevenDeck, cardDeck, streamDeck };
 189 
 190 // Windows cannot unlink an open file; rework the code to unlink the
 191 // submitted card deck after closing it.
 192 //  -- Add fname tp rdr_state
 193 //  -- Add unlink calls at eof close
 194 static struct rdr_state
 195   {
 196     enum rdr_mode
 197       {
 198         rdr_no_mode, rdr_rd_bin
 199       } io_mode;
 200     int deckfd;
 201     // Sending a ++END means that the read_cards command has to be reissued
 202     //enum { deckStart = 0, eof1Sent, uid1Sent, inputSent, eof2Sent, uid2Sent } deckState;
 203     enum { deckStart = 0, eof1Sent, uid1Sent, inputSent, eof2Sent} deckState;
 204     enum deckFormat deckFormat;
 205     bool running;
 206     char device_name [MAX_DEV_NAME_LEN];
 207     char fname [PATH_MAX+1];
 208   } rdr_state [N_RDR_UNITS_MAX];
 209 
 210 static char* rdr_name = "rdr";
 211 static char rdr_path_prefix[PATH_MAX+1];
 212 
 213 /*
 214  * rdr_init()
 215  */
 216 
 217 // Once-only initialization
 218 
 219 void rdr_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 220   {
 221     memset (rdr_path_prefix, 0, sizeof (rdr_path_prefix));
 222     memset (rdr_state, 0, sizeof (rdr_state));
 223     for (uint i = 0; i < N_RDR_UNITS_MAX; i ++)
 224       rdr_state [i] . deckfd = -1;
 225   }
 226 
 227 static t_stat rdr_reset (UNUSED DEVICE * dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 228   {
 229 
 230 
 231 
 232 
 233 
 234 
 235 
 236     return SCPE_OK;
 237   }
 238 
 239 // General Electric Cards
 240 //
 241 // General Electric used the following collating sequence on their machines,
 242 // including the GE 600 (the machine on which Multics was developed); this is
 243 // largely upward compatible from the IBM 026 commercial character set, and it
 244 // shows strong influence from the IBM 1401 character set while supporting the
 245 // full ASCII character set, with 64 printable characters, as it was understood
 246 // in the 1960's.
 247 //
 248 // GE   &-0123456789ABCDEFGHIJKLMNOPQR/STUVWXYZ[#@:>?+.](<\^$*);'_,%="!
 249 //      ________________________________________________________________
 250 //     /&-0123456789ABCDEFGHIJKLMNOPQR/STUVWXYZ #@:>V .¤(<§ $*);^±,%='"
 251 // 12 / O           OOOOOOOOO                        OOOOOO
 252 // 11|   O                   OOOOOOOOO                     OOOOOO
 253 //  0|    O                           OOOOOOOOO                  OOOOOO
 254 //  1|     O        O        O        O
 255 //  2|      O        O        O        O       O     O     O     O
 256 //  3|       O        O        O        O       O     O     O     O
 257 //  4|        O        O        O        O       O     O     O     O
 258 //  5|         O        O        O        O       O     O     O     O
 259 //  6|          O        O        O        O       O     O     O     O
 260 //  7|           O        O        O        O       O     O     O     O
 261 //  8|            O        O        O        O OOOOOOOOOOOOOOOOOOOOOOOO
 262 //  9|             O        O        O        O
 263 //   |__________________________________________________________________
 264 // In the above, the 0-8-2 punch shown as _ should be printed as an assignment
 265 // arrow, and the 11-8-2 punch shown as ^ should be printed as an up-arrow.
 266 // This conforms to the evolution of of these ASCII symbols from the time GE
 267 // adopted this character set and the present.
 268 
 269 // Sadly, AG91, App. C disagrees
 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 
 357 
 358 
 359 // From card_codes_.alm
 360 
 361 static uint16 table [128] =
 362   {
 363     05403, 04401, 04201, 04101, 00005, 01023, 01013, 01007,
 364     02011, 04021, 02021, 04103, 04043, 04023, 04013, 04007,
 365     06403, 02401, 02201, 02101, 00043, 00023, 00201, 01011,
 366     02003, 02403, 00007, 01005, 02043, 02023, 02013, 02007,
 367     00000, 02202, 00006, 00102, 02102, 01042, 04000, 00022,
 368     04022, 02022, 02042, 04012, 01102, 02000, 04102, 01400,
 369     01000, 00400, 00200, 00100, 00040, 00020, 00010, 00004,
 370     00002, 00001, 00202, 02012, 04042, 00012, 01012, 01006,
 371     00042, 04400, 04200, 04100, 04040, 04020, 04010, 04004,
 372     04002, 04001, 02400, 02200, 02100, 02040, 02020, 02010,
 373     02004, 02002, 02001, 01200, 01100, 01040, 01020, 01010,
 374     01004, 01002, 01001, 05022, 04202, 06022, 02006, 01022,
 375     00402, 05400, 05200, 05100, 05040, 05020, 05010, 05004,
 376     05002, 05001, 06400, 06200, 06100, 06040, 06020, 06010,
 377     06004, 06002, 06001, 03200, 03100, 03040, 03020, 03010,
 378     03004, 03002, 03001, 05000, 04006, 03000, 03400, 00000
 379   };
 380 
 381 static void asciiToH (char * str, uint * hstr, size_t l)
     /* [previous][next][first][last][top][bottom][index][help] */
 382   {
 383     char * p = str;
 384     for (size_t i = 0; i < l; i ++)
 385     //for (char * p = str; * p; p ++)
 386       {
 387         * hstr ++ = table [(* p) & 0177];
 388         p ++;
 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 
 418 
 419 
 420 static int getCardLine (int fd, unsigned char * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 421   {
 422     uint n = 0;
 423     buffer [n] = 0;
 424     while (1)
 425       {
 426         uint8 ch;
 427         ssize_t rc = read (fd, & ch, 1);
 428         if (rc <= 0) // eof or err
 429           return n == 0;
 430         if (ch == '\n')
 431           return 0;
 432         buffer [n ++] = ch;
 433         buffer [n] = 0;
 434         if (n > 79)
 435           return 0;
 436       }
 437     /*NOTREACHED*/ /* unreachable */
 438     return 0;
 439   }
 440 
 441 static int getCardData (int fd, char * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 442   {
 443     memset (buffer, 0, 80);
 444     ssize_t rc = read (fd, buffer, 80);
 445     if (rc < 0)
 446       return 0;
 447     return (int) rc;
 448   }
 449 
 450 #define rawCardImageBytes (80 * 12 / 8)
 451 static int getRawCardData (int fd, uint8_t * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 452   {
 453     memset (buffer, 0, rawCardImageBytes + 2);
 454     ssize_t rc = read (fd, buffer, rawCardImageBytes);
 455     if (rc < 0)
 456       return 0;
 457     return (int) rc;
 458   }
 459 
 460 #ifdef TESTING
 461 static bool empty = false;
 462 #endif
 463 
 464 static int rdrReadRecord (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
 465   iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
 466   sim_debug (DBG_NOTIFY, & rdr_dev, "Read binary\n");
 467   uint ctlr_unit_idx  = get_ctlr_idx (iomUnitIdx, chan);
 468   uint unitIdx        = cables->urp_to_urd[ctlr_unit_idx][p->IDCW_DEV_CODE].unit_idx;
 469 
 470 
 471 
 472 
 473 
 474 
 475 
 476 
 477 
 478 
 479 
 480 
 481 
 482 
 483 
 484 
 485 
 486 #ifdef TESTING
 487   sim_printf ("hopper not empty\r\n");
 488   empty = false;
 489 #endif
 490   unsigned char cardImage [80] = "";
 491   uint8_t rawCardImage [rawCardImageBytes + 2 ];
 492   size_t l = 0;
 493   // initialize to quiet compiler
 494   enum deckFormat thisCard = cardDeck;
 495 
 496   static int jobNo = 0;
 497 
 498   switch (rdr_state [unitIdx].deckState) {
 499 
 500     case deckStart: {
 501 #ifdef TESTING
 502   sim_printf ("deckStart: sending ++EOF\r\n");
 503 #endif
 504       strcpy ((char *) cardImage, "++EOF");
 505       l = strlen ((char *) cardImage);
 506       thisCard = cardDeck; //-V1048
 507       rdr_state [unitIdx].deckState = eof1Sent;
 508       jobNo ++;
 509     }
 510     break;
 511 
 512     case eof1Sent: {
 513 #ifdef TESTING
 514   sim_printf ("eof1Sent: sending ++UID\r\n");
 515 #endif
 516       sprintf ((char *) cardImage, "++UID %d", jobNo);
 517       l = strlen ((char *) cardImage);
 518       thisCard = cardDeck; //-V1048
 519       rdr_state [unitIdx].deckState = uid1Sent;
 520     }
 521     break;
 522 
 523     case uid1Sent: {
 524 #ifdef TESTING
 525   sim_printf ("uid1Sent: sending data\r\n");
 526 #endif
 527       int rc = getCardLine (rdr_state [unitIdx].deckfd, cardImage);
 528       if (rc) {
 529 #ifdef TESTING
 530   sim_printf ("uid1Sent: getCardLine returned %d\r\n", rc);
 531 #endif
 532         close (rdr_state [unitIdx].deckfd);
 533         // Windows can't unlink open files; do it now...
 534         rc = unlink (rdr_state [unitIdx].fname);
 535         if (rc)
 536           perror ("card reader deck unlink\n");
 537         rdr_state [unitIdx].deckfd = -1;
 538         rdr_state [unitIdx].deckState = deckStart;
 539         p->stati = 04201; // hopper empty
 540         return IOM_CMD_DISCONNECT;
 541       }
 542 #ifdef TESTING
 543   sim_printf ("uid1Sent: getCardLine returned <%s>\r\n", cardImage);
 544 #endif
 545       l = strlen ((char *) cardImage);
 546       thisCard = cardDeck; //-V1048
 547       if (strncasecmp ((char *) cardImage, "++input", 7) == 0) {
 548 #ifdef TESTING
 549   sim_printf ("uid1Sent: switching to inputSent <%s>\r\n", cardImage);
 550 #endif
 551         rdr_state [unitIdx].deckState = inputSent;
 552       }
 553     }
 554     break;
 555 
 556     // Reading the actual data cards
 557 
 558     case inputSent: {
 559 #ifdef TESTING
 560   sim_printf ("inputSent: format %d\r\n", rdr_state [unitIdx].deckFormat);
 561 #endif
 562       switch (rdr_state [unitIdx].deckFormat) {
 563         case cardDeck: {
 564 #ifdef TESTING
 565   sim_printf ("inputSent: cardDeck\r\n");
 566 #endif
 567           int rc = getCardLine (rdr_state [unitIdx].deckfd, cardImage);
 568           if (rc) {
 569             strcpy ((char *) cardImage, "++EOF");
 570             rdr_state [unitIdx].deckState = eof2Sent;
 571           }
 572           l = strlen ((char *) cardImage);
 573         }
 574         thisCard = cardDeck; //-V1048
 575         break;
 576 
 577         case streamDeck: {
 578 #ifdef TESTING
 579   sim_printf ("inputSent: streamDeck\r\n");
 580 #endif
 581           l = (size_t) getCardData (rdr_state [unitIdx].deckfd, (char *) cardImage);
 582           if (l) {
 583             thisCard = streamDeck;
 584           } else {
 585             strcpy ((char *) cardImage, "++EOF");
 586             l = strlen ((char *) cardImage);
 587             rdr_state [unitIdx].deckState = eof2Sent;
 588             thisCard = cardDeck; //-V1048
 589           }
 590 #ifdef TESTING
 591   sim_printf ("inputSent: getCardLine returned <%s>\r\n", cardImage);
 592 #endif
 593         }
 594         break;
 595 
 596         case sevenDeck: {
 597 #ifdef TESTING
 598   sim_printf ("inputSent: 7Deck\r\n");
 599 #endif
 600           l = (size_t) getRawCardData (rdr_state [unitIdx].deckfd, rawCardImage);
 601           if (l) {
 602             thisCard = sevenDeck;
 603           } else {
 604             strcpy ((char *) cardImage, "++EOF");
 605             l = strlen ((char *) cardImage);
 606             rdr_state [unitIdx].deckState = eof2Sent;
 607             thisCard = cardDeck; //-V1048
 608           }
 609         }
 610         break;
 611 
 612       } // switch (deckFormat)
 613     } // case inputSent
 614     break;
 615 
 616 // Sending a ++END means that the read_cards command has to be reissued
 617 
 618     case eof2Sent: {
 619 # ifdef TESTING
 620   sim_printf ("eof2Sent\r\n");
 621 # endif
 622       sprintf ((char *) cardImage, "++UID %d", jobNo);
 623       l = strlen ((char *) cardImage);
 624       thisCard = cardDeck; //-V1048
 625       rdr_state [unitIdx].deckState = deckStart;
 626       close (rdr_state [unitIdx].deckfd);
 627       // Windows can't unlink open files; do it now...
 628       int rc = unlink (rdr_state [unitIdx].fname);
 629       if (rc)
 630         perror ("card reader deck unlink\n");
 631       rdr_state [unitIdx].deckfd = -1;
 632     }
 633     break;
 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 
 662 
 663   }
 664 
 665 
 666 
 667 
 668 
 669     //sim_printf ("card <%s>\r\n", cardImage);
 670 #ifdef TESTING
 671   sim_printf ("\r\n");
 672   sim_printf ("\r\n");
 673   for (uint i = 0; i < 80; i ++) {
 674     if (isprint (cardImage [i]))
 675       sim_printf ("%c", cardImage [i]);
 676     else
 677       sim_printf ("\\%03o", cardImage [i]);
 678   }
 679   sim_printf ("\r\n");
 680   sim_printf ("\r\n");
 681 #endif
 682   word36 buffer [27];
 683   switch (thisCard) {
 684 
 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       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 #ifdef 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, strerror(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   uint dev_code       = p->IDCW_DEV_CODE;
 913 
 914   sim_debug (DBG_TRACE, & rdr_dev, "%s: RDR %c%02o_%02o\n",
 915           __func__, iomChar (iomUnitIdx), chan, dev_code);
 916 
 917   uint ctlr_unit_idx        = get_ctlr_idx (iomUnitIdx, chan);
 918   uint unitIdx              = cables->urp_to_urd[ctlr_unit_idx][p->IDCW_DEV_CODE].unit_idx;
 919   struct rdr_state * statep = & rdr_state[unitIdx];
 920   statep->running           = true;
 921 
 922   iom_cmd_rc_t rc = IOM_CMD_PROCEED;
 923   // IDCW?
 924   if (IS_IDCW (p)) {
 925     // IDCW
 926     statep->io_mode = rdr_no_mode;
 927 
 928     switch (p->IDCW_DEV_CMD) {
 929 
 930       case 000: // CMD 00 Request status
 931         sim_debug (DBG_DEBUG, & rdr_dev, "%s: Request Status\n", __func__);
 932         p->stati = 04000;
 933         // This is controller status, not device status
 934         //if (rdr_state[unitIdx].deckfd < 0)
 935           //p->stati = 04201; // hopper empty
 936 #ifdef TESTING
 937         sim_printf ("Request status %04o\r\n", p->stati);
 938 #endif
 939         break;
 940 
 941       case 001: // CMD 01 Read binary
 942         sim_debug (DBG_DEBUG, & rdr_dev, "%s: Read Binary\n", __func__);
 943         if (rdr_state [unitIdx].deckfd < 0) {
 944           p->stati        = 04201; // hopper empty
 945           p->tallyResidue = 0;
 946 #ifdef TESTING
 947           if (! empty)
 948             sim_printf ("hopper empty\r\n");
 949           empty = true;
 950 #endif
 951           return IOM_CMD_DISCONNECT;
 952         }
 953         statep->io_mode = rdr_rd_bin;
 954         p->stati        = 04000;
 955         // This is controller status, not device status
 956         //if (rdr_state[unitIdx].deckfd < 0)
 957           //p->stati = 04201; // hopper empty
 958 #ifdef TESTING
 959 sim_printf ("read binary %04o\r\n", p->stati);
 960 #endif
 961         break;
 962 
 963       case 040: // CMD 40 Reset status
 964         sim_debug (DBG_DEBUG, & rdr_dev, "%s: Request Status\n", __func__);
 965         p->stati  = 04000;
 966         p->isRead = false;
 967         // This is controller status, not device status
 968         //if (rdr_state[unitIdx].deckfd < 0)
 969           //p->stati = 04201; // hopper empty
 970 #ifdef TESTING
 971 sim_printf ("reset status %04o\r\n", p->stati);
 972 #endif
 973         break;
 974 
 975       default:
 976 #ifdef TESTING
 977 sim_printf ("unknown  %o\r\n", p->IDCW_DEV_CMD);
 978 #endif
 979         if (p->IDCW_DEV_CMD != 051) // ignore bootload console probe
 980           sim_warn ("%s: RDR unrecognized device command  %02o\n", __func__, p->IDCW_DEV_CMD);
 981         p->stati      = 04501; // cmd reject, invalid opcode
 982         p->chanStatus = chanStatIncorrectDCW;
 983         return IOM_CMD_ERROR;
 984     } // switch IDCW_DEV_CMD
 985 
 986     sim_debug (DBG_DEBUG, & rdr_dev, "%s: stati %04o\n", __func__, p->stati);
 987     return IOM_CMD_PROCEED;
 988   } // if IDCW
 989 
 990   // Not IDCW; TDCW are captured in IOM, so must be IOTD, IOTP or IOTNP
 991   switch (statep->io_mode) {
 992 
 993     case rdr_no_mode:
 994 #ifdef TESTING
 995       sim_printf ("%s: Unexpected IOTx\r\n", __func__);
 996 #endif
 997       //sim_warn ("%s: Unexpected IOTx\n", __func__);
 998       //return IOM_CMD_ERROR;
 999       break;
1000 
1001     case rdr_rd_bin: {
1002       int rc = rdrReadRecord (iomUnitIdx, chan);
1003 #ifdef TESTING
1004 sim_printf ("rdrReadRecord returned %d\r\n", rc);
1005 #endif
1006       if (rc)
1007         return IOM_CMD_DISCONNECT;
1008     }
1009     break;
1010 
1011     default:
1012       sim_warn ("%s: Unrecognized io_mode %d\n", __func__, statep->io_mode);
1013       return IOM_CMD_ERROR;
1014   }
1015   return rc;
1016 }
1017 
1018 static t_stat rdr_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr, UNUSED int val,
     /* [previous][next][first][last][top][bottom][index][help] */
1019                                UNUSED const void * desc)
1020   {
1021     sim_printf("Number of RDR units in system is %d\n", rdr_dev . numunits);
1022     return SCPE_OK;
1023   }
1024 
1025 static t_stat rdr_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value, const char * cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1026                               UNUSED void * desc)
1027   {
1028     if (! cptr)
1029       return SCPE_ARG;
1030     int n = atoi (cptr);
1031     if (n < 1 || n > N_RDR_UNITS_MAX)
1032       return SCPE_ARG;
1033     rdr_dev . numunits = (uint32) n;
1034     return SCPE_OK;
1035   }
1036 
1037 static t_stat rdr_show_device_name (UNUSED FILE * st, UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1038                                     UNUSED int val, UNUSED const void * desc)
1039   {
1040     long n = RDR_UNIT_NUM (uptr);
1041     if (n < 0 || n >= N_RDR_UNITS_MAX)
1042       return SCPE_ARG;
1043     sim_printf("name     : %s", rdr_state [n] . device_name);
1044     return SCPE_OK;
1045   }
1046 
1047 static t_stat rdr_set_device_name (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1048                                    UNUSED const char * cptr, UNUSED void * desc)
1049   {
1050     long n = RDR_UNIT_NUM (uptr);
1051     if (n < 0 || n >= N_RDR_UNITS_MAX)
1052       return SCPE_ARG;
1053     if (cptr)
1054       {
1055         strncpy (rdr_state [n] . device_name, cptr, MAX_DEV_NAME_LEN - 1);
1056         rdr_state [n] . device_name [MAX_DEV_NAME_LEN - 1] = 0;
1057       }
1058     else
1059       rdr_state [n] . device_name [0] = 0;
1060     return SCPE_OK;
1061   }
1062 
1063 static t_stat rdr_set_path (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1064                             const UNUSED char * cptr, UNUSED void * desc)
1065   {
1066     if (! cptr)
1067       return SCPE_ARG;
1068 
1069     size_t len = strlen(cptr);
1070 
1071     // We check for length - (3 + length of rdr_name) to allow for the
1072     // null, a possible '/' being added and "rdrx" being added
1073     if (len >= (sizeof(rdr_path_prefix) - (strlen(rdr_name) + 3)))
1074       return SCPE_ARG;
1075 
1076     strncpy(rdr_path_prefix, cptr, sizeof(rdr_path_prefix));
1077     if (len > 0)
1078       {
1079         if (rdr_path_prefix[len - 1] != '/')
1080           {
1081             if (len == sizeof(rdr_path_prefix) - 1)
1082               return SCPE_ARG;
1083             rdr_path_prefix[len++] = '/';
1084             rdr_path_prefix[len] = 0;
1085           }
1086       }
1087     return SCPE_OK;
1088   }
1089 
1090 static t_stat rdr_show_path (UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1091                              UNUSED int val, UNUSED const void * desc)
1092   {
1093     if (rdr_path_prefix [0])
1094       {
1095         sim_printf("\rPath to card reader directories is \"%s\".\r\n", rdr_path_prefix);
1096       }
1097     else
1098       {
1099         sim_printf("\rPath to card reader directories is unset.\r\n");
1100       }
1101     return SCPE_OK;
1102   }

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