root/src/dps8/dps8_mt.c

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

DEFINITIONS

This source file includes following definitions.
  1. mtp_show_nunits
  2. mtp_set_nunits
  3. mtp_show_boot_drive
  4. mtp_set_boot_drive
  5. mtp_show_device_name
  6. mtp_set_device_name
  7. mtp_reset
  8. mt_rewind
  9. mt_show_nunits
  10. mt_set_nunits
  11. mt_show_device_name
  12. mt_set_device_name
  13. mt_show_tape_path
  14. mt_set_tape_path
  15. mt_add_tape_search_path
  16. mt_show_tape_search_paths
  17. mt_set_capac
  18. signal_tape
  19. tape_set_ready
  20. mt_reset
  21. deterimeFullTapeFileName
  22. loadTape
  23. unloadTape
  24. mt_init
  25. mt_exit
  26. mtReadRecord
  27. mtReadCtrlMainMem
  28. mtInitRdMem
  29. mtWRCtrlRegs
  30. mtInitWrMem
  31. mtMTPWr
  32. mtWriteRecord
  33. surveyDevices
  34. mt_iom_cmd
  35. simh_tape_msg
  36. attachTape
  37. mount_tape
  38. detachTape

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * SPDX-License-Identifier: Multics
   5  * scspell-id: ae1c781a-f62e-11ec-bd2e-80ee73e9b8e7
   6  *
   7  * ---------------------------------------------------------------------------
   8  *
   9  * Copyright (c) 2007-2013 Michael Mondy
  10  * Copyright (c) 2012-2016 Harry Reed
  11  * Copyright (c) 2013-2018 Charles Anthony
  12  * Copyright (c) 2021 Dean Anderson
  13  * Copyright (c) 2021-2024 The DPS8M Development Team
  14  *
  15  * This software is made available under the terms of the ICU License.
  16  * See the LICENSE.md file at the top-level directory of this distribution.
  17  *
  18  * ---------------------------------------------------------------------------
  19  *
  20  * This source file may contain code comments that adapt, include, and/or
  21  * incorporate Multics program code and/or documentation distributed under
  22  * the Multics License.  In the event of any discrepancy between code
  23  * comments herein and the original Multics materials, the original Multics
  24  * materials should be considered authoritative unless otherwise noted.
  25  * For more details and historical background, see the LICENSE.md file at
  26  * the top-level directory of this distribution.
  27  *
  28  * ---------------------------------------------------------------------------
  29  */
  30 
  31 //-V::536
  32 
  33 #include <stdio.h>
  34 #include <signal.h>
  35 #include <ctype.h>
  36 #include <dirent.h>
  37 
  38 #include "dps8.h"
  39 #include "dps8_sys.h"
  40 #include "dps8_iom.h"
  41 #include "dps8_cable.h"
  42 #include "dps8_cpu.h"
  43 #include "dps8_faults.h"
  44 #include "dps8_scu.h"
  45 #include "dps8_utils.h"
  46 #include "dps8_mt.h"
  47 
  48 #define DBG_CTR 1
  49 
  50 #if defined(FREE)
  51 # undef FREE
  52 #endif /* if defined(FREE) */
  53 #define FREE(p) do  \
  54   {                 \
  55     free((p));      \
  56     (p) = NULL;     \
  57   } while(0)
  58 
  59 /*
  60  * mt.c -- mag tape
  61  * See manual AN87
  62  */
  63 
  64 /*
  65  *
  66  *  COMMENTS ON "CHAN DATA" AND THE T&D TAPE (test and diagnostic tape)
  67  *
  68  * The IOM status service provides the "residue" from the last PCW or
  69  * IDCW as part of the status.  Bootload_tape_label.alm indicates
  70  * that after a read binary operation, the field is interpreted as the
  71  * device number and that a device number of zero is legal.
  72  *
  73  * The IOM boot channel will store an IDCW with a chan-data field of zero.
  74  * AN70, page 2-1 says that when the tape is read in native mode via an
  75  * IOM or IOCC, the tape drive number in the IDCW will be zero.  It says
  76  * that a non-zero tape drive number in the IDCW indicates that BOS is
  77  * being used to simulate an IOM. (Presumably written before BCE replaced
  78  * BOS.)
  79  *
  80  * However...
  81  *
  82  * This seems to imply that an MPC could be connected to a single
  83  * channel and support multiple tape drives by using chan-data as a
  84  * device id.  If so, it seems unlikely that chan-data could ever
  85  * represent anything else such as a count of a number of records to
  86  * back space over (which is hinted at as an example in AN87).
  87  *
  88  * Using chan-data as a device-id that is zero from the IOM also
  89  * implies that Multics could only be cold booted from a tape drive with
  90  * device-id zero.  That doesn't seem to mesh with instructions
  91  * elsewhere... And BCE has to (initially) come from the boot tape...
  92  *
  93  *     Comment by CAC:  From MDD-005-02:
  94  *
  95  *       "     bootload_tape_label  is read  in by  one of  two means.   In
  96  *        native mode, the  IOM or IMU reads it into  absolute location 30,
  97  *        leaving  the PCW,  DCW's, and   other essentials  in locations  0
  98  *        through  5.  The IMU  leaves an indication  of its identity  just
  99  *        after this block of information.
 100  *
 101  *             In  BOS compatibility mode,  the BOS BOOT  command simulates
 102  *        the IOM, leaving the same information.  However, it also leaves a
 103  *        config deck and flagbox (although bce has its own flagbox) in the
 104  *        usual locations.   This allows Bootload Multics to  return to BOS
 105  *        if there is a BOS to return to.  The presence of BOS is indicated
 106  *        by the tape drive number being  non-zero in the idcw in the "IOM"
 107  *        provided information.   (This is normally zero  until firmware is
 108  *        loaded into the bootload tape MPC.)
 109  *
 110  * The T&D tape seems to want to see non-zero residue from the very
 111  * first tape read.  That seems to imply that the T&D tape could not
 112  * be booted by the IOM!  Perhaps the T&D tape requires BCE (which
 113  * replaced BOS) ?
 114  *
 115  *  TODO
 116  *
 117  * When simulating timing, switch to queuing the activity instead
 118  * of queueing the status return.   That may allow us to remove most
 119  * of our state variables and more easily support save/restore.
 120  *
 121  * Convert the rest of the routines to have a chan_devinfo argument.
 122  *
 123  * Allow multiple tapes per channel.
 124  */
 125 
 126 #include "sim_tape.h"
 127 
 128 static DEBTAB mt_dt [] =
 129   {
 130     { "NOTIFY", DBG_NOTIFY, NULL },
 131     { "INFO",   DBG_INFO,   NULL },
 132     { "ERR",    DBG_ERR,    NULL },
 133     { "WARN",   DBG_WARN,   NULL },
 134     { "DEBUG",  DBG_DEBUG,  NULL },
 135     { "TRACE",  DBG_TRACE,  NULL },
 136     { "ALL",    DBG_ALL,    NULL }, // Don't move as it messes up DBG message
 137     { NULL,     0,          NULL }
 138   };
 139 
 140 //////////////
 141 //////////////
 142 //
 143 // MTP
 144 //
 145 
 146 #define MTP_UNIT_IDX(uptr) ((uptr) - mtp_unit)
 147 #define N_MTP_UNITS 1 // default
 148 
 149 static struct mtp_state_s
 150   {
 151     uint boot_drive;
 152     char device_name [MAX_DEV_NAME_LEN];
 153   } mtp_state [N_MTP_UNITS_MAX];
 154 
 155 static t_stat mtp_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
 156                                UNUSED int val, UNUSED const void * desc)
 157   {
 158     sim_printf ("Number of MTP controllers in the system is %d\n",
 159                 mtp_dev.numunits);
 160     return SCPE_OK;
 161   }
 162 
 163 static t_stat mtp_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 164                               const char * cptr, UNUSED void * desc)
 165   {
 166     if (! cptr)
 167       return SCPE_ARG;
 168     int n = atoi (cptr);
 169     if (n < 0 || n > N_MTP_UNITS_MAX)
 170       return SCPE_ARG;
 171     mtp_dev.numunits = (uint32) n;
 172     return SCPE_OK;
 173   }
 174 
 175 static t_stat mtp_show_boot_drive (UNUSED FILE * st, UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
 176                                    UNUSED int val, UNUSED const void * desc)
 177   {
 178     long mtp_unit_idx = MTP_UNIT_IDX (uptr);
 179     if (mtp_unit_idx < 0 || mtp_unit_idx >= N_MTP_UNITS_MAX)
 180       {
 181         sim_printf ("Controller unit number out of range\n");
 182         return SCPE_ARG;
 183       }
 184     sim_printf ("boot     : %u",
 185                 mtp_state[mtp_unit_idx].boot_drive);
 186     return SCPE_OK;
 187   }
 188 
 189 static t_stat mtp_set_boot_drive (UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 190                                  const char * cptr, UNUSED void * desc)
 191   {
 192     long mtp_unit_idx = MTP_UNIT_IDX (uptr);
 193     if (mtp_unit_idx < 0 || mtp_unit_idx >= N_MTP_UNITS_MAX)
 194       {
 195         sim_printf ("Controller unit number out of range\n");
 196         return SCPE_ARG;
 197       }
 198     if (! cptr)
 199       return SCPE_ARG;
 200     int n = (int) atoi (cptr);
 201     if (n < 0 || n >= N_DEV_CODES)
 202       return SCPE_ARG;
 203     mtp_state[mtp_unit_idx].boot_drive = (uint) n;
 204     return SCPE_OK;
 205   }
 206 
 207 UNIT mtp_unit [N_MTP_UNITS_MAX] = {
 208 #if defined(NO_C_ELLIPSIS)
 209   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 210   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 211   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 212   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 213   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 214   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 215   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 216   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 217   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 218   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 219   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 220   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 221   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 222   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 223   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 224   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
 225 #else
 226   [0 ... N_MTP_UNITS_MAX-1] = {
 227     UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
 228   }
 229 #endif
 230 };
 231 
 232 static t_stat mtp_show_device_name (UNUSED FILE * st, UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
 233                                     UNUSED int val, UNUSED const void * desc)
 234   {
 235     int n = (int) MTP_UNIT_IDX (uptr);
 236     if (n < 0 || n >= N_MTP_UNITS_MAX)
 237       return SCPE_ARG;
 238     sim_printf("name     : %s", mtp_state [n].device_name);
 239     return SCPE_OK;
 240   }
 241 
 242 static t_stat mtp_set_device_name (UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 243                                    const char * cptr, UNUSED void * desc)
 244   {
 245     int n = (int) MTP_UNIT_IDX (uptr);
 246     if (n < 0 || n >= N_MTP_UNITS_MAX)
 247       return SCPE_ARG;
 248     if (cptr)
 249       {
 250         strncpy (mtp_state[n].device_name, cptr, MAX_DEV_NAME_LEN-1);
 251         mtp_state[n].device_name[MAX_DEV_NAME_LEN-1] = 0;
 252       }
 253     else
 254       mtp_state[n].device_name[0] = 0;
 255     return SCPE_OK;
 256   }
 257 
 258 static MTAB mtp_mod [] =
 259   {
 260     {
 261       MTAB_XTD | MTAB_VDV | \
 262       MTAB_NMO | MTAB_VALR,                 /* Mask               */
 263       0,                                    /* Match              */
 264       "NUNITS",                             /* Print string       */
 265       "NUNITS",                             /* Match string       */
 266       mtp_set_nunits,                       /* Validation routine */
 267       mtp_show_nunits,                      /* Display routine    */
 268       "Number of TAPE units in the system", /* Value descriptor   */
 269       NULL                                  /* Help               */
 270     },
 271     {
 272       MTAB_XTD | MTAB_VUN | MTAB_VALR,      /* Mask               */
 273       0,                                    /* Match              */
 274       "BOOT_DRIVE",                         /* Print string       */
 275       "BOOT_DRIVE",                         /* Match string       */
 276       mtp_set_boot_drive,                   /* Validation routine */
 277       mtp_show_boot_drive,                  /* Display routine    */
 278       "Select the boot drive",              /* Value descriptor   */
 279       NULL                                  /* Help               */
 280     },
 281     {
 282       MTAB_XTD | MTAB_VUN | \
 283       MTAB_VALR | MTAB_NC,                  /* Mask               */
 284       0,                                    /* Match              */
 285       "NAME",                               /* Print string       */
 286       "NAME",                               /* Match string       */
 287       mtp_set_device_name,                  /* Validation routine */
 288       mtp_show_device_name,                 /* Display routine    */
 289       "Set the device name",                /* Value descriptor   */
 290       NULL                                  /* Help               */
 291     },
 292     { 0, 0, NULL, NULL, NULL, NULL, NULL, NULL }
 293   };
 294 
 295 static t_stat mtp_reset (UNUSED DEVICE * dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 296   {
 297     return SCPE_OK;
 298   }
 299 
 300 DEVICE mtp_dev =
 301   {
 302     "MTP",            /* Name                */
 303     mtp_unit,         /* Units               */
 304     NULL,             /* Registers           */
 305     mtp_mod,          /* Modifiers           */
 306     N_MTP_UNITS,      /* #units              */
 307     10,               /* Address radix       */
 308     31,               /* Address width       */
 309     1,                /* Address increment   */
 310     8,                /* Data radix          */
 311     9,                /* Data width          */
 312     NULL,             /* Examine routine     */
 313     NULL,             /* Deposit routine     */
 314     mtp_reset,        /* Reset routine       */
 315     NULL,             /* Boot routine        */
 316     NULL,             /* Attach routine      */
 317     NULL,             /* Detach routine      */
 318     NULL,             /* Context             */
 319     DEV_DEBUG,        /* Flags               */
 320     0,                /* Debug control flags */
 321     mt_dt,            /* Debug flag names    */
 322     NULL,             /* Memory size change  */
 323     NULL,             /* Logical name        */
 324     NULL,             /* Attach help         */
 325     NULL,             /* Help                */
 326     NULL,             /* Help context        */
 327     NULL,             /* Device description  */
 328     NULL
 329   };
 330 
 331 //////////////
 332 //////////////
 333 //
 334 // tape drive
 335 //
 336 
 337 #define MT_UNIT_NUM(uptr) ((uptr) - mt_unit)
 338 
 339 struct tape_state tape_states [N_MT_UNITS_MAX];
 340 static const char * simh_tape_msg (int code); // hack
 341 // XXX this assumes only one controller, needs to be indexed
 342 static char tape_path_prefix [PATH_MAX+2];
 343 
 344 // Multics RCP limits the volume name in the label to 32 characters
 345 #define LABEL_MAX 32
 346 
 347 #define N_MT_UNITS 1 // default
 348 
 349 UNIT mt_unit [N_MT_UNITS_MAX] = {
 350     // CAC: Looking at SCP source, the only place UNIT_SEQ is used
 351     // by the "run" command's reset sequence; units that have UNIT_SEQ
 352     // set will be issued a rewind on reset.
 353     // Looking at the sim source again... It is used on several of the
 354     // run commands, including CONTINUE. Leave UNIT_SEQ off.
 355 // Unit 0 is the controller
 356 #if defined(NO_C_ELLIPSIS)
 357   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 358   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 359   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 360   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 361   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 362   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 363   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 364   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 365   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 366   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 367   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 368   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 369   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 370   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 371   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 372   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 373   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 374   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 375   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 376   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 377   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 378   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 379   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 380   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 381   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 382   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 383   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 384   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 385   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 386   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 387   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 388   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 389   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 390   { UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
 391 #else
 392   [0 ... N_MT_UNITS_MAX-1] = {
 393     UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
 394   }
 395 #endif
 396 };
 397 
 398 #define UNIT_WATCH (1 << MTUF_V_UF)
 399 
 400 static t_stat mt_rewind (UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 401                          UNUSED const char * cptr, UNUSED void * desc)
 402   {
 403     return sim_tape_rewind (uptr);
 404   }
 405 
 406 static t_stat mt_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
 407                               UNUSED int val, UNUSED const void * desc)
 408   {
 409     sim_printf("Number of TAPE units in system is %d\n", tape_dev . numunits);
 410     return SCPE_OK;
 411   }
 412 
 413 static t_stat mt_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 414                              const char * cptr, UNUSED void * desc)
 415   {
 416     if (! cptr)
 417       return SCPE_ARG;
 418     int n = atoi (cptr);
 419     if (n < 1 || n > N_MT_UNITS_MAX)
 420       return SCPE_ARG;
 421     tape_dev . numunits = (uint32) n;
 422     return SCPE_OK;
 423   }
 424 
 425 static t_stat mt_show_device_name (UNUSED FILE * st, UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
 426                                    UNUSED int val, UNUSED const void * desc)
 427   {
 428     int n = (int) MT_UNIT_NUM (uptr);
 429     if (n < 0 || n >= N_MT_UNITS_MAX)
 430       return SCPE_ARG;
 431     if (tape_states[n].device_name[1] == 0)
 432       sim_printf("name     : default");
 433     else
 434       sim_printf("name     : %s", tape_states[n].device_name);
 435     return SCPE_OK;
 436   }
 437 
 438 static t_stat mt_set_device_name (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 439                                   UNUSED const char * cptr, UNUSED void * desc)
 440   {
 441     int n = (int) MT_UNIT_NUM (uptr);
 442     if (n < 0 || n >= N_MT_UNITS_MAX)
 443       return SCPE_ARG;
 444     if (cptr)
 445       {
 446         strncpy (tape_states [n] . device_name, cptr, MAX_DEV_NAME_LEN - 1);
 447         tape_states [n] . device_name [MAX_DEV_NAME_LEN - 1] = 0;
 448       }
 449     else
 450       tape_states [n] . device_name [0] = 0;
 451     return SCPE_OK;
 452   }
 453 
 454 static t_stat mt_show_tape_path (UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
 455                                  UNUSED int val, UNUSED const void * desc)
 456   {
 457     sim_printf("TAPE DEFAULT_PATH: %s\n", tape_path_prefix);
 458     return SCPE_OK;
 459   }
 460 
 461 typedef struct path_node PATH_ENTRY;
 462 
 463 struct path_node
 464   {
 465       size_t prefix_len;
 466       char label_prefix[LABEL_MAX + 1];
 467       char dir[PATH_MAX + 1];
 468       PATH_ENTRY *next_entry;
 469   };
 470 
 471 static PATH_ENTRY *search_list_head = NULL;
 472 static PATH_ENTRY *search_list_tail = NULL;
 473 
 474 static t_stat mt_set_tape_path (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 475                              const char * cptr, UNUSED void * desc)
 476   {
 477     if (! cptr)
 478       return SCPE_ARG;
 479 
 480     size_t len = strlen(cptr);
 481 
 482     // We check for length - (2 + max label length) to allow for the null,
 483     // a possible '/' being added and the label file name being added
 484     if (len >= (sizeof(tape_path_prefix) - (LABEL_MAX + 2)))
 485       return SCPE_ARG;
 486 
 487     // If any paths have been added, we need to remove them now
 488     PATH_ENTRY *current_entry = search_list_head;
 489     while (current_entry != NULL)
 490       {
 491         PATH_ENTRY *old_entry = current_entry;
 492         current_entry = current_entry->next_entry;
 493         FREE(old_entry);
 494       }
 495 
 496     search_list_head = NULL;
 497     search_list_tail = NULL;
 498 
 499     // Verify the new default path exists
 500     DIR * dp;
 501     dp = opendir (cptr);
 502     if (! dp)
 503       {
 504         sim_warn ("\rInvalid '%s' ", cptr);
 505         perror ("opendir");
 506         sim_warn ("\r\n");
 507         return SCPE_ARG;
 508       }
 509 
 510     closedir(dp);
 511 
 512     // Save the new default path
 513     strncpy (tape_path_prefix, cptr, (sizeof(tape_path_prefix)-1));
 514     if (len > 0)
 515       {
 516         if (tape_path_prefix[len - 1] != '/')
 517           {
 518             tape_path_prefix[len++] = '/';
 519             tape_path_prefix[len] = 0;
 520           }
 521       }
 522 
 523     return SCPE_OK;
 524   }
 525 
 526 static t_stat mt_add_tape_search_path(UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 527                              const char * cptr, UNUSED void * desc)
 528   {
 529     if (! cptr)
 530       return SCPE_ARG;
 531 
 532     if (!tape_path_prefix[0])
 533       {
 534         sim_print("ERROR: DEFAULT_PATH must be set before ADD_PATH is used.\n");
 535         return SCPE_ARG;
 536       }
 537 
 538     size_t len = strlen(cptr);
 539 
 540     char buffer[len + 2];
 541     char prefix[len + 2];
 542     char dir[len + 2];
 543 
 544     strcpy(buffer, cptr);
 545 
 546     // Break up parameter into prefix and directory
 547     char *token = strtok(buffer, "=");
 548     if (token == NULL)
 549       {
 550         return SCPE_ARG;
 551       }
 552 
 553     strcpy(prefix, token);
 554 
 555     token = strtok(NULL, "=");
 556     if (token == NULL)
 557       {
 558         sim_print("ERROR: ADD_PATH parameter must be specified as [prefix]=[dir]\n");
 559         sim_print("   i.e. SET TAPE ADD_PATH=BK=./tapes/backups\n");
 560         return SCPE_ARG;
 561       }
 562 
 563     strcpy(dir, token);
 564 
 565     if (strtok(NULL, "=") != NULL)
 566       {
 567         return SCPE_ARG;
 568       }
 569 
 570     size_t prefix_len = strlen(prefix);
 571     if ((prefix_len > LABEL_MAX) || (prefix_len < 1))
 572       {
 573         return SCPE_ARG;
 574       }
 575 
 576     size_t dir_len = strlen(dir);
 577 
 578     // We check against PATH_MAX - 1 to account for possibly adding a slash at the end of the path
 579     if (dir_len > (PATH_MAX - 1))
 580       {
 581         return SCPE_ARG;
 582       }
 583 
 584     // Verify the new path to add exists
 585     DIR * dp;
 586     dp = opendir (dir);
 587     if (! dp)
 588       {
 589         sim_warn ("\rInvalid '%s' ", dir);
 590         perror ("opendir");
 591         sim_warn ("\r\n");
 592         return SCPE_ARG;
 593       }
 594 
 595     closedir(dp);
 596 
 597     PATH_ENTRY *new_entry = malloc(sizeof(PATH_ENTRY));
 598     if (!new_entry)
 599       {
 600         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 601                        __func__, __FILE__, __LINE__);
 602 #if defined(USE_BACKTRACE)
 603 # if defined(SIGUSR2)
 604         (void)raise(SIGUSR2);
 605         /*NOTREACHED*/ /* unreachable */
 606 # endif /* if defined(SIGUSR2) */
 607 #endif /* if defined(USE_BACKTRACE) */
 608         abort();
 609       }
 610     new_entry->prefix_len = prefix_len;
 611     strcpy(new_entry->label_prefix, prefix);
 612     strcpy(new_entry->dir, dir);
 613 
 614     if (new_entry->dir[dir_len - 1] != '/')
 615       {
 616         new_entry->dir[dir_len++] = '/';
 617         new_entry->dir[dir_len] = 0;
 618       }
 619 
 620     new_entry->next_entry = NULL;
 621     if (search_list_tail == NULL)
 622       {
 623         search_list_head = new_entry;
 624         search_list_tail = new_entry;
 625       }
 626     else
 627       {
 628         search_list_tail->next_entry = new_entry;
 629         search_list_tail = new_entry;
 630       }
 631 
 632     return SCPE_OK;
 633   }
 634 
 635 static t_stat mt_show_tape_search_paths(UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
 636                                  UNUSED int val, UNUSED const void * desc)
 637   {
 638     sim_print("Tape directory search paths:\n");
 639     sim_printf("%-32s %s\n", "Prefix", "Directory");
 640     sim_printf("%-32s %s\n", "------", "---------");
 641     PATH_ENTRY *current_entry = search_list_head;
 642     while (current_entry != NULL)
 643       {
 644           sim_printf("%-32s %s\n", current_entry->label_prefix, current_entry->dir);
 645           current_entry = current_entry->next_entry;
 646       }
 647 
 648     sim_printf("%-32s %s\n", "[default]", tape_path_prefix);
 649 
 650     return SCPE_OK;
 651   }
 652 
 653 static t_stat mt_set_capac (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 654                              const char * cptr, UNUSED void * desc)
 655   {
 656     if (! cptr)
 657       return SCPE_ARG;
 658     t_stat rc;
 659     int i;
 660     // skip the boot tape drive; Multics doesn't use it, and this
 661     // allows setting capacity even though the boot tape is attached.
 662     for (i = 1; i < N_MT_UNITS_MAX; i ++)
 663       {
 664         rc = sim_tape_set_capac (mt_unit + i, value, cptr, desc);
 665         if (rc != SCPE_OK)
 666           return rc;
 667       }
 668     return SCPE_OK;
 669   }
 670 
 671 t_stat signal_tape (uint tap_unit_idx, word8 status0, word8 status1) {
     /* [previous][next][first][last][top][bottom][index][help] */
 672   // if substr (special_status_word, 20, 1) ^= "1"b | substr (special_status_word, 13, 6) ^= "00"b3
 673   // if substr (special_status_word, 34, 3) ^= "001"b
 674   // Note the 34,3 spans 34,35,36; therefore the bits are 1..36, not 0..35
 675   // 20,1 is bit 19
 676   // 13,6, is bits 12..17
 677   // status0 is 19..26
 678   // status1 is 28..35
 679   // so substr (w, 20, 1) is bit 0 of status0
 680   //    substr (w, 13, 6) is the low 6 bits of dev_no
 681   //    substr (w, 34, 3) is the low 3 bits of status 1
 682       //sim_printf ("%s %d %o\n", disk_filename, ro,  mt_unit [tap_unit_idx] . flags);
 683       //sim_printf ("special int %d %o\n", tap_unit_idx, mt_unit [tap_unit_idx] . flags);
 684 
 685   if (! sim_is_running)
 686     return SCPE_OK;
 687 
 688   uint ctlr_unit_idx = cables->tape_to_mtp[tap_unit_idx].ctlr_unit_idx;
 689 
 690 // Sending interrupts to all ports causes error messages in syslog; the additional
 691 // interrupts are "unexpected"
 692 
 693 
 694 
 695 
 696 
 697 
 698 
 699 
 700 
 701 
 702 
 703 
 704 
 705 
 706 
 707 
 708 
 709 
 710 
 711 
 712 
 713   for (uint ctlr_port_num = 0; ctlr_port_num < MAX_CTLR_PORTS; ctlr_port_num ++) {
 714     if (cables->mtp_to_iom[ctlr_unit_idx][ctlr_port_num].in_use) {
 715       uint iom_unit_idx = cables->mtp_to_iom[ctlr_unit_idx][ctlr_port_num].iom_unit_idx;
 716       uint chan_num = cables->mtp_to_iom[ctlr_unit_idx][ctlr_port_num].chan_num;
 717       uint dev_code = cables->tape_to_mtp[tap_unit_idx].dev_code;
 718       send_special_interrupt (iom_unit_idx, chan_num, dev_code, 0, 020 /* tape drive to ready */);
 719       return SCPE_OK;
 720     }
 721   }
 722   sim_warn ("loadTape can't find controller; dropping interrupt\n");
 723   return SCPE_ARG;
 724 
 725 }
 726 
 727 static t_stat tape_set_ready (UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 728                               UNUSED const char * cptr,
 729                               UNUSED void * desc)
 730   {
 731 #if defined(TESTING)
 732     cpu_state_t * cpup = _cpup;
 733 #endif
 734     uint32_t tape_unit_idx = MT_UNIT_NUM (uptr);
 735     if (tape_unit_idx >= N_MT_UNITS_MAX)
 736       {
 737         sim_debug (DBG_ERR, & tape_dev,
 738                    "Tape set ready: Invalid unit number %ld\n", (long) tape_unit_idx);
 739         sim_printf ("error: Invalid unit number %ld\n", (long) tape_unit_idx);
 740         return SCPE_ARG;
 741       }
 742     return signal_tape ((unsigned int) tape_unit_idx, 0, 020 /* tape drive to ready */);
 743   }
 744 
 745 static MTAB mt_mod [] =
 746   {
 747 #if !defined(SPEED)
 748     { UNIT_WATCH, UNIT_WATCH, "WATCH",   "WATCH",   NULL, NULL, NULL, NULL },
 749     { UNIT_WATCH, 0,          "NOWATCH", "NOWATCH", NULL, NULL, NULL, NULL },
 750 #endif /* if !defined(SPEED) */
 751     {
 752        MTAB_XTD | MTAB_VUN | \
 753        MTAB_NC,                                  /* Mask               */
 754       0,                                         /* Match              */
 755       NULL,                                      /* Print string       */
 756       "REWIND",                                  /* Match string       */
 757       mt_rewind,                                 /* Validation routine */
 758       NULL,                                      /* Display routine    */
 759       NULL,                                      /* Value descriptor   */
 760       NULL                                       /* Help               */
 761     },
 762     {
 763       MTAB_XTD | MTAB_VDV | \
 764       MTAB_NMO | MTAB_VALR,                      /* Mask               */
 765       0,                                         /* Match              */
 766       "NUNITS",                                  /* Print string       */
 767       "NUNITS",                                  /* Match string       */
 768       mt_set_nunits,                             /* Validation routine */
 769       mt_show_nunits,                            /* Display routine    */
 770       "Number of TAPE units in the system",      /* Value descriptor   */
 771       NULL                                       /* Help               */
 772     },
 773     {
 774       MTAB_XTD | MTAB_VUN | \
 775       MTAB_VALR | MTAB_NC,                       /* Mask               */
 776       0,                                         /* Match              */
 777       "NAME",                                    /* Print string       */
 778       "NAME",                                    /* Match string       */
 779       mt_set_device_name,                        /* Validation routine */
 780       mt_show_device_name,                       /* Display routine    */
 781       "Set the device name",                     /* Value descriptor   */
 782       NULL                                       /* Help               */
 783     },
 784     {
 785       MTAB_XTD | MTAB_VDV | MTAB_NMO | \
 786       MTAB_VALR | MTAB_NC,                       /* Mask               */
 787       0,                                         /* Match              */
 788       "DEFAULT_PATH",                            /* Print string       */
 789       "DEFAULT_PATH",                            /* Match string       */
 790       mt_set_tape_path,                          /* Validation routine */
 791       mt_show_tape_path,                         /* Display routine    */
 792       "Set the default path to the directory containing tape images (also clear search paths)",
 793       NULL
 794     },
 795     {
 796       MTAB_XTD | MTAB_VDV | MTAB_NMO | \
 797       MTAB_VALR | MTAB_NC,                       /* Mask               */
 798       0,                                         /* Match              */
 799       "ADD_PATH",                                /* Print string       */
 800       "ADD_PATH",                                /* Match string       */
 801       mt_add_tape_search_path,                   /* Validation routine */
 802       mt_show_tape_search_paths,                 /* Display routine    */
 803       "Add a search path for tape directories",  /* Value descriptor   */
 804       NULL                                       /* Help               */
 805     },
 806     {
 807       MTAB_XTD | MTAB_VUN,                       /* Mask               */
 808       0,                                         /* Match              */
 809       "CAPACITY",                                /* Print string       */
 810       "CAPACITY",                                /* Match string       */
 811       sim_tape_set_capac,                        /* Validation routine */
 812       sim_tape_show_capac,                       /* Display routine    */
 813       "Set the device capacity",                 /* Value descriptor   */
 814       NULL                                       /* Help               */
 815     },
 816     {
 817       MTAB_XTD | MTAB_VDV | \
 818       MTAB_NMO | MTAB_VALR,                      /* Mask               */
 819       0,                                         /* Match              */
 820       "CAPACITY_ALL",                            /* Print string       */
 821       "CAPACITY_ALL",                            /* Match string       */
 822       mt_set_capac,                              /* Validation routine */
 823       NULL,                                      /* Display routine    */
 824       "Set the tape capacity of all drives",     /* Value descriptor   */
 825       NULL                                       /* Help               */
 826     },
 827     {
 828       MTAB_XTD | MTAB_VUN | \
 829       MTAB_NMO | MTAB_VALR,                      /* Mask               */
 830       0,                                         /* Match              */
 831       "READY",                                   /* Print string       */
 832       "READY",                                   /* Match string       */
 833       tape_set_ready,                            /* Validation routine */
 834       NULL,                                      /* Display routine    */
 835       NULL,                                      /* Value descriptor   */
 836       NULL                                       /* Help string        */
 837     },
 838     { 0, 0, NULL, NULL, NULL, NULL, NULL, NULL }
 839   };
 840 
 841 static t_stat mt_reset (DEVICE * dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 842   {
 843     for (int i = 0; i < (int) dptr -> numunits; i ++)
 844       {
 845         sim_tape_reset (& mt_unit [i]);
 846         //sim_cancel (& mt_unit [i]);
 847       }
 848     return SCPE_OK;
 849   }
 850 
 851 DEVICE tape_dev =
 852   {
 853     "TAPE",            /* Name                */
 854     mt_unit,           /* Units               */
 855     NULL,              /* Registers           */
 856     mt_mod,            /* Modifiers           */
 857     N_MT_UNITS,        /* #Units              */
 858     10,                /* Address radix       */
 859     31,                /* Address width       */
 860     1,                 /* Address increment   */
 861     8,                 /* Data radix          */
 862     9,                 /* Data width          */
 863     NULL,              /* Examine routine     */
 864     NULL,              /* Deposit routine     */
 865     mt_reset,          /* Reset routine       */
 866     NULL,              /* Boot routine        */
 867     &sim_tape_attach,  /* Attach routine      */
 868     &sim_tape_detach,  /* Detach routine      */
 869     NULL,              /* Context             */
 870     DEV_DEBUG,         /* Flags               */
 871     0,                 /* Debug control flags */
 872     mt_dt,             /* Debug flag names    */
 873     NULL,              /* Memory size change  */
 874     NULL,              /* Logical name        */
 875     NULL,              /* Attach help         */
 876     NULL,              /* Help                */
 877     NULL,              /* Help context        */
 878     NULL,              /* Device description  */
 879     NULL               /* End                 */
 880   };
 881 
 882 static void deterimeFullTapeFileName(char * tapeFileName, char * buffer, int bufferLength)
     /* [previous][next][first][last][top][bottom][index][help] */
 883   {
 884     // If no path prefixing, just return the given tape file name
 885     if (!tape_path_prefix[0])
 886       {
 887           strncpy(buffer, tapeFileName, (unsigned long)bufferLength);
 888           buffer[bufferLength - 1] = 0;
 889           return;
 890       }
 891 
 892     // Prefixing is in effect so now we need to search the additional path list for a prefix match
 893     PATH_ENTRY *current_entry = search_list_head;
 894     while (current_entry != NULL)
 895       {
 896         if (strncmp(current_entry->label_prefix, tapeFileName, current_entry->prefix_len) == 0)
 897           {
 898               break;
 899           }
 900 
 901         current_entry = current_entry->next_entry;
 902       }
 903 
 904     char *selected_path = tape_path_prefix;     // Start with the default path
 905     if (current_entry != NULL)
 906       {
 907         selected_path = current_entry->dir;
 908       }
 909 
 910     // Verify we won't overrun the output buffer
 911     if ((size_t) bufferLength < (strlen(selected_path) + strlen(tapeFileName) + 1))
 912       {
 913           // Bad news, we are going to overrun the buffer so
 914           // we just use as much of the tape file name as we can
 915           strncpy(buffer, tapeFileName, (unsigned long)bufferLength);
 916           buffer[bufferLength - 1] = 0;
 917           return;
 918       }
 919 
 920     // Everything will fit so construct the full tape file name and path
 921     int buffWrote;
 922     buffWrote = snprintf(buffer,
 923                     ((int)((strlen(selected_path)+strlen(tapeFileName))+1)),
 924                         "%s%s", selected_path, tapeFileName);
 925     if (buffWrote < 0)
 926       sim_warn("%s snprintf problem, returned %d\n", __func__, buffWrote);
 927   }
 928 
 929 t_stat loadTape (uint driveNumber, char * tapeFilename, bool ro)
     /* [previous][next][first][last][top][bottom][index][help] */
 930   {
 931     char full_tape_file_name[PATH_MAX + 1];
 932 
 933     if (ro)
 934       mt_unit [driveNumber] . flags |= MTUF_WRP;
 935     else
 936       mt_unit [driveNumber] . flags &= ~ MTUF_WRP;
 937 
 938     deterimeFullTapeFileName(tapeFilename, full_tape_file_name, (sizeof(full_tape_file_name)-1));
 939 
 940     sim_printf("loadTape Attaching drive %u to file %s\n", driveNumber, full_tape_file_name);
 941     t_stat stat = sim_tape_attach (& mt_unit [driveNumber], full_tape_file_name);
 942     if (stat != SCPE_OK)
 943       {
 944         sim_printf ("%s sim_tape_attach returned %d\n", __func__, stat);
 945         return stat;
 946       }
 947     return signal_tape (driveNumber, 0, 020 /* tape drive to ready */);
 948   }
 949 
 950 t_stat unloadTape (uint driveNumber)
     /* [previous][next][first][last][top][bottom][index][help] */
 951   {
 952     if (mt_unit [driveNumber] . flags & UNIT_ATT)
 953       {
 954         t_stat stat = sim_tape_detach (& mt_unit [driveNumber]);
 955         if (stat != SCPE_OK)
 956           {
 957             sim_warn ("%s sim_tape_detach returned %d\n", __func__, stat);
 958             return stat;
 959           }
 960       }
 961     return signal_tape (driveNumber, 0, 040 /* unload complete */);
 962   }
 963 
 964 void mt_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 965   {
 966     (void)memset(tape_states, 0, sizeof(tape_states));
 967     for (int i = 0; i < N_MT_UNITS_MAX; i ++)
 968       {
 969         mt_unit [i] . capac = 40000000;
 970       }
 971   }
 972 
 973 void mt_exit (void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 974   // If any paths have been added, we need to remove them now
 975   PATH_ENTRY * current_entry = search_list_head;
 976   while (current_entry != NULL) {
 977     PATH_ENTRY * old_entry = current_entry;
 978     current_entry = current_entry->next_entry;
 979     FREE (old_entry);
 980   }
 981 }
 982 
 983 static iom_cmd_rc_t mtReadRecord (uint devUnitIdx, uint iomUnitIdx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
 984   {
 985 // If a tape read IDCW has multiple DDCWs, are additional records read?
 986 
 987 #if defined(TESTING)
 988     cpu_state_t * cpup = _cpup;
 989 #endif
 990     iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
 991     UNIT * unitp = & mt_unit[devUnitIdx];
 992     struct tape_state * tape_statep = & tape_states[devUnitIdx];
 993 
 994     //enum { dataOK, noTape, tapeMark, tapeEOM, tapeError } tapeStatus;
 995     sim_debug (DBG_DEBUG, & tape_dev, "%s: Read %s record\n", __func__,
 996                tape_statep->is9 ? "9" : "binary");
 997     // We read the record into the tape controllers memory; the IOT will move it to core
 998     tape_statep->tbc = 0;
 999     if (! (unitp -> flags & UNIT_ATT))
1000       {
1001         p->stati = 04104;
1002         return IOM_CMD_ERROR;
1003       }
1004     t_stat rc = sim_tape_rdrecf (unitp, & tape_statep -> buf [0], & tape_statep -> tbc,
1005                                BUFSZ);
1006     sim_debug (DBG_DEBUG, & tape_dev, "%s: sim_tape_rdrecf returned %d, with tbc %d\n",
1007             __func__, rc, tape_statep -> tbc);
1008     if (rc == MTSE_TMK)
1009        {
1010          tape_statep -> rec_num ++;
1011          sim_debug (DBG_NOTIFY, & tape_dev,
1012                     "%s: EOF: %s\n", __func__, simh_tape_msg (rc));
1013         p -> stati = 04423; // EOF category EOF file mark
1014         if (tape_statep -> tbc != 0)
1015           {
1016             sim_warn ("%s: Read %d bytes with EOF.\n",
1017                         __func__, tape_statep -> tbc);
1018           }
1019         tape_statep -> tbc = 0;
1020         //tapeStatus = tapeMark;
1021         return IOM_CMD_ERROR;
1022       }
1023     if (rc == MTSE_EOM)
1024       {
1025         sim_debug (DBG_NOTIFY, & tape_dev,
1026                     "%s: EOM: %s\n", __func__, simh_tape_msg (rc));
1027         // If the tape is blank, a read should result in '4302' blank tape on read.
1028         if (sim_tape_bot (unitp))
1029           p -> stati = 04302; // blank tape on read
1030         else
1031           p -> stati = 04340; // EOT file mark
1032         if (tape_statep -> tbc != 0)
1033           {
1034             sim_warn ("%s: Read %d bytes with EOM.\n",
1035                         __func__, tape_statep -> tbc);
1036             //return 0;
1037           }
1038         tape_statep -> tbc = 0;
1039         //tapeStatus = tapeEOM;
1040         return IOM_CMD_PROCEED;
1041       }
1042     if (rc != 0)
1043       {
1044         sim_debug (DBG_ERR, & tape_dev,
1045                    "%s: Cannot read tape: %d - %s\n",
1046                    __func__, rc, simh_tape_msg (rc));
1047         sim_debug (DBG_ERR, & tape_dev,
1048                    "%s: Returning arbitrary error code\n",
1049                    __func__);
1050         p -> stati      = 05001; // BUG: arbitrary error code; config switch
1051         p -> chanStatus = chanStatParityErrPeriph;
1052         return IOM_CMD_ERROR;
1053       }
1054     p -> stati = 04000;
1055     if (sim_tape_wrp (unitp))
1056       p -> stati |= 1;
1057     tape_statep -> rec_num ++;
1058 
1059     tape_statep -> words_processed = 0;
1060     if (unitp->flags & UNIT_WATCH)
1061       sim_printf ("Tape %ld reads record %d\n",
1062                   (long) MT_UNIT_NUM (unitp), tape_statep -> rec_num);
1063 
1064     uint tally = p -> DDCW_TALLY;
1065     if (tally == 0)
1066       {
1067         sim_debug (DBG_DEBUG, & tape_dev,
1068                    "%s: Tally of zero interpreted as 010000(4096)\n",
1069                    __func__);
1070         tally = 4096;
1071       }
1072 
1073     sim_debug (DBG_DEBUG, & tape_dev,
1074                "%s: Tally %d (%o)\n", __func__, tally, tally);
1075 
1076     word36 buffer [tally];
1077     uint i;
1078     int rc2;
1079     for (i = 0; i < tally; i ++)
1080       {
1081         if (tape_statep -> is9)
1082           rc2 = extractASCII36FromBuffer (tape_statep -> buf,
1083                   tape_statep -> tbc, & tape_statep -> words_processed, buffer + i);
1084         else
1085           rc2 = extractWord36FromBuffer (tape_statep -> buf,
1086                   tape_statep -> tbc, & tape_statep -> words_processed, buffer + i);
1087         if (rc2)
1088           {
1089              break;
1090           }
1091       }
1092 
1093 
1094 
1095 
1096 
1097 
1098 
1099 
1100 
1101 
1102 
1103 
1104 
1105 
1106 
1107     iom_indirect_data_service (iomUnitIdx, chan, buffer,
1108                                     & tape_statep -> words_processed, true);
1109     if (p -> tallyResidue)
1110       {
1111         sim_debug (DBG_WARN, & tape_dev,
1112                    "%s: Read buffer exhausted on channel %d\n",
1113                        __func__, chan);
1114 
1115       }
1116 // XXX This assumes that the tally was bigger than the record
1117     if (tape_statep -> is9)
1118       p -> charPos = tape_statep -> tbc % 4;
1119     else
1120       p -> charPos = (tape_statep -> tbc * 8) / 9 % 4;
1121     return IOM_CMD_PROCEED;
1122   }
1123 
1124 static void mtReadCtrlMainMem (uint devUnitIdx, uint iomUnitIdx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1125   {
1126     struct tape_state * tape_statep = & tape_states[devUnitIdx];
1127     word36 control[1];
1128     uint count;
1129     iom_indirect_data_service (iomUnitIdx, chan, control, &count, false);
1130     if (count != 1)
1131       sim_warn ("%s: count %d not 1\n", __func__, count);
1132     tape_statep -> cntlrAddress = getbits36_16 (control[0], 0);
1133     tape_statep -> cntlrTally   = getbits36_16 (control[0], 16);
1134   }
1135 
1136 static void mtInitRdMem (uint devUnitIdx, uint iomUnitIdx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1137   {
1138     iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
1139 
1140     uint tally = p -> DDCW_TALLY;
1141     if (tally != 04000)
1142       {
1143         sim_warn ("%s: tape controller read memory expected tally of 04000\n", __func__);
1144         p -> stati      = 04501;
1145         p -> chanStatus = chanStatIncorrectDCW;
1146         return;
1147       }
1148     uint16 mem [04000 * 2];
1149     (void)memset (mem, 0, sizeof (mem));
1150 
1151     const uint charTableOS = 0xE0; // Mtc500 characteristics table at 00E0 (hex)
1152 
1153 // dcl  1 tape_char based (char_ptr) unaligned,
1154 //  0     2 mem_sze bit (16),                                  /* Read/write memory size */
1155 //  1     2 config_sw bit (16),                                /* Configuration switch settings */
1156 //  2     2 trace_tab_p bit (16),                              /* Trace table begin ptr */
1157 //  3     2 trace_tab_size bit (16),                           /* Trace table size */
1158 //  4     2 trace_tab_cur bit (16),                            /* Trace table current entry ptr */
1159 //  5     2 mpc_stat bit (16),                                 /* Mpc statistics table pointer */
1160 //  6     2 dev_stat bit (16),                                 /* Device statistics table pointer */
1161 //  7     2 rev_l_tab bit (16),                                /* Revision level table? */
1162 //  8     2 fw_id bit (16),                                    /* Firmware identification */
1163 //  9     2 fw_rev,                                            /* Firmware revision */
1164 //          3 pad1 bit (4),
1165 //          3 lrev (2) bit (4),                                /* Letter revision */
1166 //          3 srev bit (4),                                    /* Sub revision */
1167 // 10     2 as_date,                                           /* Assembly date */
1168 //          3 month bit (8),
1169 //          3 day bit (8),
1170 // 11     2 pad2 (5) bit (16);
1171 
1172 // For the 501
1173 //  mpc_data.mpc_err_int_ctr_addr = 253;    /* 00FD */
1174 //  mpc_data.mpc_err_data_reg_addr = 254;   /* 00FE */
1175 
1176     mem [charTableOS + 0]  = 4096;          // mem_sze
1177     mem [charTableOS + 1]  = 0;             // config_sw
1178 
1179     // Set the addresses to recognizable values
1180     mem [charTableOS + 2]  = 04000 + 0123;  // trace_tab_p
1181     mem [charTableOS + 3]  = 0;             // trace_tab_size
1182     mem [charTableOS + 4]  = 0;             // trace_tab_cur
1183     mem [charTableOS + 5]  = 04000 + 0234;  // mpc_stat
1184     mem [charTableOS + 6]  = 04000 + 0345;  // dev_stat
1185     mem [charTableOS + 7]  = 04000 + 0456;  // rev_l_tab
1186     mem [charTableOS + 8]  = 012345;        // fw_id
1187 
1188     // Set fw_rev to 0013; I thinks that will xlate as 'A3' (0x01 is A)
1189     mem [charTableOS + 9]  = 0x0013;        // fw_rev
1190 
1191     mem [charTableOS + 10] = 0x1025;        // as_date Oct 27.
1192 
1193     word36 buf [tally];
1194     // Make clang analyzer happy
1195     (void)memset (buf, 0, sizeof (word36) * tally);
1196     for (uint i = 0; i < tally; i ++)
1197       {
1198         putbits36_18 (buf + i,  0, mem [i * 2]);
1199         putbits36_18 (buf + i, 18, mem [i * 2 + 1]);
1200       }
1201     iom_indirect_data_service (iomUnitIdx, chan, buf, & tally, true);
1202     p -> stati = 04000;
1203   }
1204 
1205 static void mtWRCtrlRegs (uint devUnitIdx, uint iomUnitIdx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1206   {
1207     iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
1208     // Fake the indirect data service
1209     p->initiate = false;
1210     return;
1211   }
1212 
1213 static void mtInitWrMem (uint devUnitIdx, uint iomUnitIdx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1214   {
1215     iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
1216     // Fake the indirect data service
1217     p->initiate = false;
1218     return;
1219   }
1220 
1221 static void mtMTPWr (uint devUnitIdx, uint iomUnitIdx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1222   {
1223     iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
1224     struct tape_state * tape_statep = & tape_states [devUnitIdx];
1225 
1226     word36 control[1];
1227     uint count;
1228     iom_indirect_data_service (iomUnitIdx, chan, control, &count, false);
1229     if (count != 1)
1230       sim_warn ("%s: count %d not 1\n", __func__, count);
1231     tape_statep -> cntlrAddress = getbits36_16 (control[0], 0);
1232     tape_statep -> cntlrTally = getbits36_16 (control[0], 16);
1233     p -> stati = 04000;
1234   }
1235 
1236 static int mtWriteRecord (uint devUnitIdx, uint iomUnitIdx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1237   {
1238 // If a tape write IDCW has multiple DDCWs, are additional records written?
1239 
1240 #if defined(TESTING)
1241     cpu_state_t * cpup = _cpup;
1242 #endif
1243     iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
1244     UNIT * unitp = & mt_unit [devUnitIdx];
1245     struct tape_state * tape_statep = & tape_states [devUnitIdx];
1246 
1247     tape_statep -> is9 = p -> IDCW_DEV_CMD == 013;
1248     sim_debug (DBG_DEBUG, & tape_dev, "%s: Write %s record\n", __func__,
1249                tape_statep -> is9 ? "9" : "binary");
1250 
1251     p -> isRead = false;
1252 
1253     uint tally = p -> DDCW_TALLY;
1254     if (tally == 0)
1255       {
1256         sim_debug (DBG_DEBUG, & tape_dev,
1257                    "%s: Tally of zero interpreted as 010000(4096)\n",
1258                    __func__);
1259         tally = 4096;
1260       }
1261 
1262     sim_debug (DBG_DEBUG, & tape_dev,
1263                "%s: Tally %d (%o)\n", __func__, tally, tally);
1264 
1265     // Fetch data from core into buffer
1266 
1267     tape_statep -> words_processed = 0;
1268     word36 buffer [tally];
1269 
1270     iom_indirect_data_service (iomUnitIdx, chan, buffer,
1271                             & tape_statep -> words_processed, false);
1272 
1273 
1274 
1275 
1276 
1277 
1278 
1279 
1280 
1281 
1282 
1283 
1284 
1285 
1286 
1287 // XXX char_pos ??
1288 
1289     if (tape_statep -> is9)
1290       tape_statep -> tbc = tape_statep -> words_processed * 4;
1291     else
1292       tape_statep -> tbc = (tape_statep -> words_processed * 9 + 1) / 2;
1293 
1294     // Pack data from buffer into tape format
1295 
1296     tape_statep -> words_processed = 0;
1297     uint i;
1298     for (i = 0; i < tally; i ++)
1299       {
1300         int rc2;
1301         if (tape_statep -> is9)
1302           {
1303             rc2 = insertASCII36toBuffer (tape_statep -> buf,
1304                                          tape_statep -> tbc,
1305                                          & tape_statep -> words_processed,
1306                                          buffer [i]);
1307           }
1308         else
1309           {
1310             rc2 = insertWord36toBuffer (tape_statep -> buf,
1311                                         tape_statep -> tbc,
1312                                         & tape_statep -> words_processed,
1313                                         buffer [i]);
1314             }
1315         if (rc2)
1316           {
1317             p -> stati = 04000;
1318             if (sim_tape_wrp (unitp))
1319               p -> stati |= 1;
1320             sim_debug (DBG_WARN, & tape_dev,
1321                        "%s: Write buffer exhausted on channel %d\n",
1322                        __func__, chan);
1323             break;
1324           }
1325       }
1326     p -> tallyResidue = (word12) (tally - i);
1327 
1328 // XXX This assumes that the tally was bigger than the record
1329     if (tape_statep -> is9)
1330       p -> charPos = tape_statep -> tbc % 4;
1331     else
1332       p -> charPos = (tape_statep -> tbc * 8) / 9 % 4;
1333 
1334     // Write buf to tape
1335 
1336     if (! (unitp -> flags & UNIT_ATT))
1337       return MTSE_UNATT;
1338 
1339     int ret = sim_tape_wrrecf (unitp, tape_statep -> buf, tape_statep -> tbc);
1340     sim_debug (DBG_DEBUG, & tape_dev, "%s: sim_tape_wrrecf returned %d, with tbc %d\n",
1341                __func__, ret, tape_statep -> tbc);
1342 
1343     if (unitp->io_flush)
1344       unitp->io_flush (unitp);                              /* flush buffered data */
1345     // XXX put unit number in here...
1346 
1347     if (ret != 0)
1348       {
1349         // Actually only returned on read
1350         if (ret == MTSE_EOM)
1351           {
1352             sim_debug (DBG_NOTIFY, & tape_dev,
1353                         "%s: EOM: %s\n", __func__, simh_tape_msg (ret));
1354             p -> stati = 04340; // EOT file mark
1355             if (tape_statep -> tbc != 0)
1356               {
1357                 sim_warn ("%s: Wrote %d bytes with EOM.\n",
1358                            __func__, tape_statep -> tbc);
1359               }
1360             return 0;
1361           }
1362         sim_warn ("%s: Cannot write tape: %d - %s\n",
1363                    __func__, ret, simh_tape_msg(ret));
1364         sim_warn ("%s: Returning arbitrary error code\n",
1365                    __func__);
1366         p -> stati = 05001; // BUG: arbitrary error code; config switch
1367         p -> chanStatus = chanStatParityErrPeriph;
1368         return IOM_CMD_ERROR;
1369       }
1370     tape_statep -> rec_num ++;
1371     if (unitp->flags & UNIT_WATCH)
1372       sim_printf ("Tape %ld writes record %d\n",
1373                   (long) MT_UNIT_NUM (unitp), tape_statep -> rec_num);
1374 
1375     sim_tape_wreom (unitp);
1376     if (unitp->io_flush)
1377       unitp->io_flush (unitp);                              /* flush buffered data */
1378 
1379     p -> stati = 04000;
1380     if (sim_tape_wrp (unitp))
1381       p -> stati |= 1;
1382     if (sim_tape_eot (unitp))
1383       p -> stati |= 04340;
1384 
1385     sim_debug (DBG_INFO, & tape_dev,
1386                "%s: Wrote %d bytes to simulated tape; status %04o\n",
1387                __func__, (int) tape_statep -> tbc, p -> stati);
1388 
1389     return 0;
1390   }
1391 
1392 // 0 ok
1393 // -1 problem
1394 static int surveyDevices (uint iomUnitIdx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1395   {
1396 #if defined(TESTING)
1397     cpu_state_t * cpup = _cpup;
1398 #endif
1399     iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
1400 // According to rcp_tape_survey_.pl1:
1401 //
1402 //       2 survey_data,
1403 //         3 handler (16) unaligned,
1404 //           4 pad1 bit (1),               400000
1405 //           4 reserved bit (1),           200000
1406 //           4 operational bit (1),        100000
1407 //           4 ready bit (1),               40000
1408 //           4 number uns fixed bin (5),    37000
1409 //           4 pad2 bit (1),                  400
1410 //           4 speed uns fixed bin (3),       240
1411 //           4 nine_track bit (1),             20
1412 //           4 density uns fixed bin (4);      17
1413 
1414     sim_debug (DBG_DEBUG, & tape_dev,
1415                "%s: Survey devices\n", __func__);
1416     p -> stati = 04000; // have_status = 1
1417 
1418     uint bufsz = 8;
1419     word36 buffer [bufsz];
1420     uint cnt = 0;
1421     for (uint i = 0; i < bufsz; i ++)
1422       buffer [i] = 0;
1423 
1424     uint ctlr_idx = get_ctlr_idx (iomUnitIdx, chan);
1425     // Walk the device codes
1426     for (uint dev_code = 0; dev_code < N_DEV_CODES; dev_code ++)
1427       {
1428        if (cnt / 2 >= bufsz)
1429           break;
1430         // Which device on the string is connected to that device code
1431         struct ctlr_to_dev_s * p = & cables->mtp_to_tape[ctlr_idx][dev_code];
1432         if (! p -> in_use)
1433           continue;
1434         uint unit_idx = p->unit_idx;
1435 
1436         word18 handler = 0;
1437         handler |= 0100000; // operational
1438         if (mt_unit [unit_idx].filename)
1439           {
1440             handler |= 0040000; // ready
1441           }
1442         handler |= ((word18) dev_code & 037) << 9; // number
1443         handler |= 0000040; // 200 ips
1444         handler |= 0000020; // 9 track
1445         handler |= 0000007; // 800/1600/6250
1446         sim_debug (DBG_DEBUG, & tape_dev,
1447                    "%s: unit %d handler %06o\n", __func__, unit_idx, handler);
1448         if (cnt % 2 == 0)
1449           {
1450             buffer [cnt / 2] = ((word36) handler) << 18;
1451           }
1452         else
1453           {
1454             buffer [cnt / 2] |= handler;
1455           }
1456         cnt ++;
1457       }
1458     iom_indirect_data_service (iomUnitIdx, chan, buffer, & bufsz, true);
1459     p -> stati = 04000; //-V1048
1460     return 0;
1461   }
1462 
1463 // Tally: According to tape_ioi_io.pl1:
1464 //
1465 // backspace file, forward space file, write EOF, erase:
1466 //   tally is always set to 1
1467 // backspace record, forward space record.
1468 //   tally is set to count; tally of 0 means 64.
1469 // density, write control registers
1470 //   tally is set to one.
1471 // request device status
1472 //   don't know
1473 // data security erase, rewind, rewind/unload, tape load, request status,
1474 // reset status, request device status, reset device status, set file permit,
1475 // set file protect, reserve device, release device, read control registers
1476 //   no idcw.
1477 
1478 iom_cmd_rc_t mt_iom_cmd (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
1479   iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
1480 #if defined(TESTING)
1481   cpu_state_t * cpup = _cpup;
1482   if_sim_debug (DBG_TRACE, & tape_dev) dumpDCW (p->DCW, 0);
1483 #endif /* if defined(TESTING) */
1484 // The bootload read command does a read on drive 0; the controller
1485 // recognizes (somehow) a special case for bootload and subs. in
1486 // the boot drive unit set by the controller config. switches
1487 // XXX But controller commands are directed to drive 0, so this
1488 // logic is incorrect. If we just set the boot drive to 0, the
1489 // system will just boot from 0, and ignore it thereafter.
1490 // Although, the install process identifies tapa_00 as a device;
1491 // check the survey code to make sure its not incorrectly
1492 // reporting 0 as a valid device.
1493 
1494 // Simplifying design decision: tapa_00 is hidden, always has the boot tape.
1495 
1496   uint ctlr_unit_idx = get_ctlr_idx (iomUnitIdx, chan);
1497   uint dev_code = p->IDCW_DEV_CODE;
1498 #if defined(TESTING)
1499   if_sim_debug (DBG_TRACE, & tape_dev) dumpDCW (p->DCW, 0);
1500 #endif /* if defined(TESTING) */
1501   if (p->IDCW_DEV_CODE == 0)
1502     dev_code = mtp_state[ctlr_unit_idx].boot_drive;
1503 
1504   sim_debug (DBG_DEBUG, & tape_dev, "%s: Tape %c%02o_%02o\n", __func__, iomChar (iomUnitIdx), chan, dev_code);
1505 
1506   uint devUnitIdx = cables->mtp_to_tape[ctlr_unit_idx][dev_code].unit_idx;
1507   UNIT * unitp = & mt_unit [devUnitIdx];
1508   struct tape_state * tape_statep = & tape_states [devUnitIdx];
1509 
1510   iom_cmd_rc_t rc = IOM_CMD_PROCEED;
1511 
1512   // IDCW?
1513   if (IS_IDCW (p)) {
1514     // IDCW
1515 
1516     // According to poll_mpc.pl1
1517     // Note: XXX should probably be checking these...
1518     //  idcw.chan_cmd = "40"b3; /* Indicate special controller command */
1519     //  idcw.chan_cmd = "41"b3; /* Indicate special controller command */
1520 
1521     // The bootload read command does a read on drive 0; the controller
1522     // recognizes (somehow) a special case for bootload and subs. in
1523     // the boot drive unit set by the controller config. switches
1524     // XXX But controller commands are directed to drive 0, so this
1525     // logic is incorrect. If we just set the boot drive to 0, the
1526     // system will just boot from 0, and ignore it thereafter.
1527     // Although, the install process identifies tapa_00 as a device;
1528     // check the survey code to make sure its not incorrectly
1529     // reporting 0 as a valid device.
1530 
1531     tape_statep->io_mode = tape_no_mode;
1532     sim_debug (DBG_DEBUG, & tape_dev, "%s: IDCW_DEV_CMD %oo %d.\n", __func__, p->IDCW_DEV_CMD, p->IDCW_DEV_CMD);
1533     switch (p->IDCW_DEV_CMD) {
1534       case 000: { // CMD 00 Request status -- controller status, not tape drive
1535           if_sim_debug (DBG_TRACE, & tape_dev) {
1536             sim_printf ("// Tape Request Status\r\n");
1537           }
1538           // If special controller command, then command 0 is 'suspend'
1539           if (p->IDCW_CHAN_CMD == 040) {
1540             sim_debug (DBG_DEBUG, & tape_dev, "%s: controller suspend\n", __func__);
1541             send_special_interrupt (iomUnitIdx, chan, p->IDCW_DEV_CODE, 01, 0 /* suspended */);
1542             p->stati = 04000;
1543           } else {
1544             p->stati = 04000;
1545           }
1546           sim_debug (DBG_DEBUG, & tape_dev, "%s: Request status: %04o control %0o chan_cmd %02o\n", __func__,
1547                      p->stati, p->IDCW_CHAN_CTRL, p->IDCW_CHAN_CMD);
1548         }
1549         break;
1550 
1551 // dcl  1 stat_buf aligned based (workp),             /* The IOI buffer segment */
1552 //        2 idcw1 bit (36),                           /* Will be read controller main memory */
1553 //        2 dcw1 bit (36),                            /* Addr=stat_buf.control, tally=1 */
1554 //        2 idcw2 bit (36),                           /* Will be initiate read data transfer */
1555 //        2 dcw2 bit (36),                            /* Address=stat_buf.mem, tally=rest of segment */
1556 
1557 //        2 control,                                  /* Describes where data is in mpc */
1558 //          3 addr bit (16) unal,                     /* Addr in mpc memory */
1559 //          3 tally bit (16) unal,                    /* Count in mpc words */
1560 //          3 fill bit (4) unal,
1561 //        2 stats (0:83) bit (18) unal;               /* EURC statistics in ASCII */
1562 
1563 //       2 mem (0:mpc_memory_size - 1) bit (18) unal; /* This is the mpc memory */
1564 
1565 //    / * Build read or write (dev stat block) main memory dcw list */
1566 //
1567 //              idcwp = addr (buf.idcw1);             /* First IDCW */
1568 //              buf.idcw1 = "0"b;
1569 //
1570 //              if OP = READ_MPC_MEM
1571 //              then idcw.command = "02"b3;           /* Command is read controller main memory (ASCII) */
1572 //              else idcw.command = "32"b3;           /* Command is write main memory (binary) */
1573 //
1574 //              idcw.code = "111"b;                   /* This makes it an IDCW */
1575 //              idcw.control = "10"b;                 /* Set continue bit */
1576 //              idcw.chan_cmd = "40"b3;               /* Indicate special controller command */
1577 //              dcwp = addr (buf.dcw1);
1578 //              buf.dcw1 = "0"b;
1579 //              dcw.address = rel (addr (buf.control));           /* Get offset to control word */
1580 //              dcw.tally = "000000000001"b;
1581 //              idcwp = addr (buf.idcw2);             /* Second IDCW */
1582 //              buf.idcw2 = "0"b;
1583 //              idcw.code = "111"b;                   /* Code is 111 to make it an idcw */
1584 //              idcw.chan_cmd = "40"b3;               /* Special controller command */
1585 //              dcwp = addr (buf.dcw2);
1586 //              buf.dcw2 = "0"b;
1587 //              dcw.address = rel (addr (buf.mem));   /* Offset to core image */
1588 //              dcw.tally = bit (bin (size (buf) - bin (rel (addr (buf.mem)), 18), 12));
1589 //                                                    /* Rest of seg */
1590 //
1591 //              if OP = READ_MPC_MEM then do;
1592 //                   idcw.command = "06"b3;           /* Command is initiate read data transfer */
1593 //                   buf.addr = "0"b;                 /* Mpc address to start is 0 */
1594 //                   buf.tally = bit (bin (mpc_memory_size, 16), 16);
1595 //                   end;
1596 //
1597 //
1598 // Control word:
1599 //
1600 //
1601 
1602 //      case 001: Unassigned
1603 
1604       case 002:               // CMD 02 -- Read controller main memory (ASCII)
1605         if_sim_debug (DBG_TRACE, & tape_dev) {
1606           sim_printf ("// Tape Read Controller Main Memory\r\n");
1607         }
1608         sim_debug (DBG_DEBUG, & tape_dev, "%s: Read controller main memory\n", __func__);
1609 
1610         tape_statep->io_mode = tape_rd_ctlr;
1611         p->stati = 04000;
1612         break;
1613 
1614       case 003: // CMD 03 -- Read 9 Record
1615         if_sim_debug (DBG_TRACE, & tape_dev) {
1616           sim_printf ("// Tape Read 9 Record\r\n");
1617         }
1618         sim_debug (DBG_DEBUG, & tape_dev, "%s: Read 9 record\n", __func__);
1619         tape_statep->io_mode = tape_rd_9;
1620         tape_statep->is9 = true;
1621         if (! (unitp->flags & UNIT_ATT)) {
1622           p->stati = 04104;
1623           return IOM_CMD_ERROR;
1624         }
1625         p->stati = 04000;
1626         break;
1627 
1628 //          case 004: Read BCD Record
1629 
1630       case 005: // CMD 05 -- Read Binary Record
1631         if_sim_debug (DBG_TRACE, & tape_dev) {
1632           sim_printf ("// Tape Read Binary Record\r\n");
1633         }
1634         sim_debug (DBG_DEBUG, & tape_dev, "%s: Read binary record\n", __func__);
1635         tape_statep->io_mode = tape_rd_bin;
1636         tape_statep->is9 = false;
1637         if (! (unitp->flags & UNIT_ATT)) {
1638           p->stati = 04104;
1639           return IOM_CMD_ERROR;
1640         }
1641         p->stati = 04000;
1642         break;
1643 
1644 // How is the mpc memory sent?
1645 // This is the code from poll_mpc that extracts the memory copy from the buffer and
1646 // repacks it the way it wants"
1647 //
1648 //     2 mem (0:mpc_memory_size - 1) bit (18) unal;         /* This is the mpc memory */
1649 //
1650 //     dcl  mpc_mem_bin (0:4095) bit (16) unal;                    /* mpc mem converted to binary */
1651 //
1652 //     do i = 0 to mpc_memory_size - 1;
1653 //       substr (mpc_mem_bin (i), 1, 8) = substr (buf.mem (i), 2, 8);
1654 //       substr (mpc_mem_bin (i), 9, 8) = substr (buf.mem (i), 11, 8);
1655 //     end;
1656 //
1657 // I interpret that as the 16 bit memory being broken into 8 bit bytes, zero
1658 // extended to 9 bits, and packed 4 to a word.
1659 
1660 // From char_mpc_.pl1, assuming MTP501
1661 //
1662 //      mtc500_char init (0000000011100000b),                  /* Mtc500 characteristics table at 00E0 (hex) */
1663 
1664       case 006:               // CMD 06 -- initiate read data transfer
1665         if_sim_debug (DBG_TRACE, & tape_dev) {
1666           sim_printf ("// Tape Initiate Read Data Transfer\r\n");
1667         }
1668         sim_debug (DBG_DEBUG, & tape_dev, "%s: initiate read data transfer\n", __func__);
1669         tape_statep->io_mode = tape_initiate_rd_mem;
1670         p->stati = 04000;
1671         break;
1672 
1673 //      case 007: Reread Binary Record
1674 
1675 //      case 010: Control Store Overlay
1676 
1677 //      case 011: Main Memory Overlay
1678 
1679 //      case 012: Unassigned
1680 
1681       case 013: // CMD 013 -- Write tape 9
1682         if_sim_debug (DBG_TRACE, & tape_dev) {
1683           sim_printf ("// Tape Write 9 Record\r\n");
1684         }
1685         sim_debug (DBG_DEBUG, & tape_dev, "%s: Write 9 record\n", __func__);
1686         tape_statep->io_mode = tape_wr_9;
1687         if (! (unitp->flags & UNIT_ATT)) {
1688           p->stati = 04104;
1689           return IOM_CMD_ERROR;
1690         }
1691         p->stati = 04000;
1692         break;
1693 
1694 //      case 014: Write BCD Record
1695 
1696       case 015: // CMD 015 -- Write Binary Record
1697         if_sim_debug (DBG_TRACE, & tape_dev) {
1698           sim_printf ("// Tape Write Binary Record\r\n");
1699         }
1700         sim_debug (DBG_DEBUG, & tape_dev, "%s: Write binary record\n", __func__);
1701         tape_statep->io_mode = tape_wr_bin;
1702         if (! (unitp->flags & UNIT_ATT)) {
1703           p->stati = 04104;
1704           return IOM_CMD_ERROR;
1705         }
1706         p->stati = 04000;
1707         break;
1708 
1709       case 016:               // CMD 016 -- Write Control Registers
1710         if_sim_debug (DBG_TRACE, & tape_dev) {
1711           sim_printf ("// Tape Write Control Registers\r\n");
1712         }
1713         sim_debug (DBG_DEBUG, & tape_dev, "%s: Write Control Registers\n", __func__);
1714         tape_statep->io_mode = tape_wr_ctrl_regs;
1715         p->stati = 04000;
1716         break;
1717 
1718 //      case 012: Unassigned
1719 
1720       case 020:  { // CMD 020 -- release controller
1721           if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Release Controller\r\n"); }
1722           if (p->IDCW_CHAN_CMD == 040) { // If special controller command, then command 020 is 'release'
1723             sim_debug (DBG_DEBUG, & tape_dev, "%s: Release controller\n", __func__);
1724             p->stati = 04000; // have_status = 1
1725             sim_debug (DBG_DEBUG, & tape_dev, "%s: Release status: %04o control %0o chan_cmd %02o\n",
1726                        __func__, p->stati, p->IDCW_CHAN_CTRL, p->IDCW_CHAN_CMD);
1727             send_special_interrupt (iomUnitIdx, chan, p->IDCW_DEV_CODE, 02, 0 /* released */);
1728           } else {
1729             p->stati = 04501;
1730             p->chanStatus = chanStatIncorrectDCW;
1731             sim_warn ("%s: Unknown command %02o\n", __func__, p->IDCW_DEV_CMD);
1732           }
1733         }
1734         break;
1735 
1736 //      case 021: Unassigned
1737 
1738 //      case 022: Unassigned
1739 
1740 //      case 023: Unassigned
1741 
1742 //      case 024: Read EBCDIC Record
1743 
1744 //      case 025: Read ASCII/EBCDIC Record
1745 
1746 //      case 026: Read Control Registers
1747 
1748 //      case 027: Read ASCII Record
1749 
1750 //      case 030: Unassigned
1751 
1752 //      case 031: Diagnostic Mode Control
1753 
1754 // 032: MTP write main memory (binary) (poll_mpc.pl1)
1755 
1756       case 032: // CMD 032 -- MTP write main memory (binary)
1757                 //    (poll_mpc.pl1); used to clear device stats.
1758         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Write Main Memory\r\n"); }
1759         sim_debug (DBG_DEBUG, & tape_dev, "%s: Write controller main memory\n", __func__);
1760         tape_statep->io_mode = tape_MTP_wr;
1761         p->stati = 04000;
1762         break;
1763 
1764 //      case 033: Unassigned
1765 
1766 //      case 034: Write EBCDIC Record
1767 
1768 //      case 035: Write ASCII/EBCDIC Record
1769 
1770 //      case 036: Unassigned
1771 
1772 //      case 037: Write ASCII Record
1773 
1774       case 040:               // CMD 040 -- Reset Status
1775         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Reset Status\r\n"); }
1776         sim_debug (DBG_DEBUG, & tape_dev, "%s Reset status\n", __func__);
1777         p->stati = 04000;
1778         p->isRead = false;
1779         // T&D probing
1780         if (dev_code == 077) {
1781           p->stati = 04502; // invalid device code
1782           return IOM_CMD_DISCONNECT;
1783         }
1784         if (sim_tape_wrp (unitp))
1785           p->stati |= 1;
1786         if (sim_tape_bot (unitp))
1787           p->stati |= 2;
1788         //if (sim_tape_eom (unitp))
1789           //p->stati |= 0340;
1790         break;
1791 
1792       case 041:              // CMD 041 -- Set 6250 cpi.
1793         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Set 6259 CPI\r\n"); }
1794         sim_debug (DBG_DEBUG, & tape_dev, "%s: Set 6250 cpi\n", __func__);
1795         p->stati = 04000;
1796         if (sim_tape_wrp (unitp))
1797           p->stati |= 1;
1798         if (sim_tape_bot (unitp))
1799           p->stati |= 2;
1800         //if (sim_tape_eom (unitp))
1801           //p->stati |= 0340;
1802         sim_debug (DBG_DEBUG, & tape_dev, "%s: Set 800 bpi\n", __func__);
1803         break;
1804 
1805       case 042:              // CMD 042 -- Set 800 bpi.
1806       case 060:              // CMD 060 -- Set 800 bpi.
1807         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Set 800 BPI\r\n"); }
1808         p->stati = 04000;
1809         if (sim_tape_wrp (unitp))
1810           p->stati |= 1;
1811         if (sim_tape_bot (unitp))
1812           p->stati |= 2;
1813         //if (sim_tape_eom (unitp))
1814           //p->stati |= 0340;
1815         sim_debug (DBG_DEBUG, & tape_dev, "%s: Set 800 bpi\n", __func__);
1816         break;
1817 
1818       case 043:              // CMD 043 -- Set 556 bpi.
1819       case 061:              // CMD 061 -- Set 556 bpi.
1820         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Set 556 BPI\r\n"); }
1821         p->stati = 04000;
1822         if (sim_tape_wrp (unitp))
1823           p->stati |= 1;
1824         if (sim_tape_bot (unitp))
1825           p->stati |= 2;
1826         //if (sim_tape_eom (unitp))
1827           //p->stati |= 0340;
1828         sim_debug (DBG_DEBUG, & tape_dev, "%s: Set 556 bpi\n", __func__);
1829         break;
1830 
1831       case 044: { // 044 -- Forward Skip One Record
1832         if_sim_debug (DBG_TRACE, & tape_dev) {
1833           sim_printf ("// Tape Forward Skip One Record\r\n");
1834           sim_printf ("//    pos before %d\r\n", unitp->pos);
1835         }
1836 #if defined(TESTING)
1837         hdbgNote ("tape", "Tape Forward Skip One Record pos before %d", unitp->pos);
1838 #endif /* if defined(TESTING) */
1839         if (! (unitp->flags & UNIT_ATT)) {
1840           p->stati = 04104;
1841           return IOM_CMD_ERROR;
1842         }
1843         uint tally = p->IDCW_COUNT;
1844         if (tally == 0) {
1845           sim_debug (DBG_DEBUG, & tape_dev, "%s: Tally of zero interpreted as 64\n", __func__);
1846           tally = 64;
1847         }
1848         sim_debug (DBG_DEBUG, & tape_dev, "%s: Forward skip record %d\n", __func__, tally);
1849 
1850 // sim_tape_sprecsf incorrectly stops on tape marks;
1851 
1852 
1853 
1854 
1855         uint32 skipped = 0;
1856         t_stat ret = MTSE_OK;
1857         while (skipped < tally) {
1858           ret = sim_tape_sprecf (unitp, & tape_statep->tbc);
1859           if (ret != MTSE_OK && ret != MTSE_TMK)
1860             break;
1861           skipped = skipped + 1;
1862         }
1863 
1864         if (ret != MTSE_OK && ret != MTSE_TMK && ret != MTSE_EOM) {
1865           break;
1866         }
1867         if (skipped != tally) {
1868           sim_warn ("skipped %d != tally %d\n", skipped, tally);
1869         }
1870 
1871         tape_statep->rec_num += (int) skipped;
1872         if (unitp->flags & UNIT_WATCH)
1873           sim_printf ("Tape %ld forward skips to record %d\n", (long) MT_UNIT_NUM (unitp), tape_statep->rec_num);
1874 
1875         p->tallyResidue = (word12) (tally - skipped);
1876 
1877         p->stati = 04000;
1878         if (sim_tape_wrp (unitp))
1879           p->stati |= 1;
1880         if (sim_tape_bot (unitp))
1881           p->stati |= 2;
1882         //if (sim_tape_eom (unitp))
1883           //p->stati |= 0340;
1884         if_sim_debug (DBG_TRACE, & tape_dev) {
1885           sim_printf ("//    pos after %d\r\n", unitp->pos);
1886         }
1887 #if defined(TESTING)
1888         hdbgNote ("tape", "Tape Forward Skip One Record pos after %d", unitp->pos);
1889 #endif /* if defined(TESTING) */
1890       }
1891       break;
1892 
1893     case 045: { // CMD 045 -- Forward Skip One File
1894         if_sim_debug (DBG_TRACE, & tape_dev) {
1895           sim_printf ("// Tape Forward Skip One File\r\n");
1896           sim_printf ("//    pos before %d\r\n", unitp->pos);
1897         }
1898 #if defined(TESTING)
1899         hdbgNote ("tape", "Tape Forward Skip One File pos before %d", unitp->pos);
1900 #endif /* if defined(TESTING) */
1901         sim_debug (DBG_DEBUG, & tape_dev, "%s:: Forward Skip File\n", __func__);
1902         if (! (unitp->flags & UNIT_ATT)) {
1903           p->stati = 04104;
1904           return IOM_CMD_ERROR;
1905         }
1906         uint tally = 1;
1907 
1908         uint32 skipped, recsskipped;
1909         t_stat ret = sim_tape_spfilebyrecf (unitp, tally, & skipped, & recsskipped, false);
1910         if_sim_debug (DBG_TRACE, & tape_dev)
1911           {
1912               sim_printf ("// sim_tape_spfilebyrecf ret %d skipped %d recsskipped %d\r\n",
1913                           ret, skipped, recsskipped);
1914           }
1915         if (ret != MTSE_OK && ret != MTSE_TMK && ret != MTSE_LEOT) {
1916           sim_warn ("sim_tape_spfilebyrecf returned %d\n", ret);
1917           break;
1918         }
1919         if (skipped != tally) {
1920           sim_warn ("skipped %d != tally %d\n", skipped, tally);
1921         }
1922 
1923         tape_statep->rec_num += (int) recsskipped;
1924         if (unitp->flags & UNIT_WATCH)
1925           sim_printf ("Tape %ld forward skips to record %d\n", (long) MT_UNIT_NUM (unitp), tape_statep->rec_num);
1926 
1927         p->tallyResidue = (word12) (tally - skipped);
1928         sim_debug (DBG_NOTIFY, & tape_dev, "%s: Forward space %d files\n", __func__, tally);
1929 
1930         p->stati = 04000;
1931         if (sim_tape_wrp (unitp))
1932           p->stati |= 1;
1933         if (sim_tape_bot (unitp))
1934           p->stati |= 2;
1935         //if (sim_tape_eom (unitp))
1936           //p->stati |= 0340;
1937         }
1938         if_sim_debug (DBG_TRACE, & tape_dev) {
1939           sim_printf ("//    pos after %d\r\n", unitp->pos);
1940         }
1941 #if defined(TESTING)
1942         hdbgNote ("tape", "Tape Forward Skip One File pos after %d", unitp->pos);
1943 #endif /* if defined(TESTING) */
1944         break;
1945 
1946       case 046: { // CMD 046 -- Backspace One Record
1947         if_sim_debug (DBG_TRACE, & tape_dev) {
1948           sim_printf ("// Tape Backspace One Record\r\n");
1949           sim_printf ("//    pos before %d\r\n", unitp->pos);
1950         }
1951 #if defined(TESTING)
1952         hdbgNote ("tape", "Tape Backspace One Record pos before %d", unitp->pos);
1953 #endif /* if defined(TESTING) */
1954         sim_debug (DBG_DEBUG, & tape_dev, "%s: Backspace Record\n", __func__);
1955         if (! (unitp->flags & UNIT_ATT)) {
1956           p->stati = 04104;
1957           return IOM_CMD_ERROR;
1958         }
1959 
1960         uint tally = p->IDCW_COUNT;
1961         if (tally == 0) {
1962           sim_debug (DBG_DEBUG, & tape_dev, "%s: Tally of zero interpreted as 64\n", __func__);
1963           tally = 64;
1964         }
1965 
1966         sim_debug (DBG_DEBUG, & tape_dev, "%s: Backspace record tally %d\n", __func__, tally);
1967 
1968         // sim_tape_sprecsr stumbles on tape marks; do our own version...
1969 
1970 
1971 
1972 
1973 
1974 
1975 
1976 
1977         uint32 skipped = 0;
1978         while (skipped < tally) {
1979           t_stat ret = sim_tape_sprecr (unitp, & tape_statep->tbc);
1980           if (ret != MTSE_OK && ret != MTSE_TMK)
1981             break;
1982           skipped ++;
1983         }
1984 
1985         if (skipped != tally) {
1986           sim_warn ("skipped %d != tally %d\n", skipped, tally);
1987         }
1988         tape_statep->rec_num -= (int) skipped;
1989         if (unitp->flags & UNIT_WATCH)
1990           sim_printf ("Tape %ld skip back to record %d\n", (long) MT_UNIT_NUM (unitp), tape_statep->rec_num);
1991 
1992         p->tallyResidue = (word12) (tally - skipped);
1993 
1994         sim_debug (DBG_NOTIFY, & tape_dev, "%s: Backspace %d records\n", __func__, skipped);
1995 
1996         p->stati = 04000;
1997         if (sim_tape_wrp (unitp))
1998           p->stati |= 1;
1999         if (sim_tape_bot (unitp))
2000           p->stati |= 2;
2001         //if (sim_tape_eom (unitp))
2002           //p->stati |= 0340;
2003         }
2004         if_sim_debug (DBG_TRACE, & tape_dev) {
2005           sim_printf ("//    pos after %d\r\n", unitp->pos);
2006         }
2007 #if defined(TESTING)
2008         hdbgNote ("tape", "Tape Backspace One Record pos after %d", unitp->pos);
2009 #endif /* if defined(TESTING) */
2010         break;
2011 
2012       case 047: { // CMD 047 -- Backspace One File
2013         if_sim_debug (DBG_TRACE, & tape_dev) {
2014           sim_printf ("// Tape Backspace One File\r\n");
2015           sim_printf ("//    pos before %d\r\n", unitp->pos);
2016         }
2017         sim_debug (DBG_DEBUG, & tape_dev, "%s: Backspace File\n", __func__);
2018 #if defined(TESTING)
2019         hdbgNote ("tape", "Tape Backspace One File pos before %d", unitp->pos);
2020 #endif /* if defined(TESTING) */
2021         if (! (unitp->flags & UNIT_ATT)) {
2022           p->stati = 04104;
2023           return IOM_CMD_ERROR;
2024         }
2025         uint tally = 1;
2026 
2027 
2028 
2029 
2030 
2031 
2032 
2033 
2034 
2035 
2036 
2037 
2038 
2039 
2040 
2041 
2042 
2043 
2044 
2045 
2046         uint32 skipped, recsskipped;
2047         t_stat ret = sim_tape_spfilebyrecr (unitp, tally, & skipped, & recsskipped);
2048         if (ret != MTSE_OK && ret != MTSE_TMK && ret != MTSE_BOT) {
2049           sim_warn ("sim_tape_spfilebyrecr returned %d\n", ret);
2050           break;
2051         }
2052         if (skipped != tally) {
2053           sim_warn ("skipped %d != tally %d\n", skipped, tally);
2054         }
2055 
2056         tape_statep->rec_num -= (int) recsskipped;
2057         if (unitp->flags & UNIT_WATCH)
2058           sim_printf ("Tape %ld backward skips to record %d\n", (long) MT_UNIT_NUM (unitp), tape_statep->rec_num);
2059 
2060         p->tallyResidue = (word12) (tally - skipped);
2061         sim_debug (DBG_NOTIFY, & tape_dev, "%s: Backspace %d records\n", __func__, tally);
2062 
2063 
2064         p->stati = 04000;
2065         if (sim_tape_wrp (unitp))
2066           p->stati |= 1;
2067         if (sim_tape_bot (unitp))
2068           p->stati |= 2;
2069         //if (sim_tape_eom (unitp))
2070           //p->stati |= 0340;
2071         }
2072         if_sim_debug (DBG_TRACE, & tape_dev) {
2073           sim_printf ("//    pos after %d\r\n", unitp->pos);
2074         }
2075 #if defined(TESTING)
2076         hdbgNote ("tape", "Tape Backspace One File pos after %d", unitp->pos);
2077 #endif /* if defined(TESTING) */
2078         break;
2079 
2080       case 050: {              // CMD 050 -- Request device status
2081           if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Request Device Status\r\n"); }
2082           p->stati = 04000;
2083           if (sim_tape_wrp (unitp))
2084             p->stati |= 1;
2085           if (sim_tape_bot (unitp))
2086             p->stati |= 2;
2087           //if (sim_tape_eom (unitp))
2088             //p->stati |= 0340;
2089           sim_debug (DBG_DEBUG, & tape_dev, "%s: Request device status: %o\n", __func__, p->stati);
2090         }
2091         break;
2092 
2093 // Okay, this is convoluted. Multics locates the console by sending CMD 051
2094 // to devices in the PCW, with the understanding that only the console
2095 // device will "respond", whatever that means.
2096 // But, bootload_tape_label checks for controller firmware loaded
2097 // ("intelligence") by sending a 051 in a IDCW.
2098 // Since it's difficult here to test for PCW/IDCW, assume that the PCW case
2099 // has been filtered out at a higher level
2100       case 051: {              // CMD 051 -- Reset device status
2101           if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Reset Device Status\r\n"); }
2102           if (p->isPCW) {
2103             p->stati = 04501; // cmd reject, invalid opcode
2104             p->chanStatus = chanStatIncorrectDCW;
2105             return IOM_CMD_ERROR;
2106           }
2107           p->stati = 04000;
2108           if (sim_tape_wrp (unitp))
2109             p->stati |= 1;
2110           if (sim_tape_bot (unitp))
2111             p->stati |= 2;
2112           //if (sim_tape_eom (unitp))
2113             //p->stati |= 0340;
2114           sim_debug (DBG_DEBUG, & tape_dev, "%s: Reset device status: %o\n", __func__, p->stati);
2115         }
2116         break;
2117 
2118 //      case 052: Unassigned
2119 
2120 //      case 053: Unassigned
2121 
2122 //      case 054: Erase
2123 
2124       case 055: // CMD 055 -- Write EOF (tape mark);
2125         if_sim_debug (DBG_TRACE, & tape_dev) {
2126           sim_printf ("// Tape Write EOF\r\n");
2127           sim_printf ("//    pos before %d\r\n", unitp->pos);
2128         }
2129         sim_debug (DBG_DEBUG, & tape_dev, "%s: Write tape mark\n", __func__);
2130 #if defined(TESTING)
2131         hdbgNote ("tape", "Tape Write EOF pos before %d", unitp->pos);
2132 #endif /* if defined(TESTING) */
2133 
2134         if (! (unitp->flags & UNIT_ATT)) {
2135           p->stati = 04104;
2136           return IOM_CMD_ERROR;
2137         }
2138         int ret;
2139         ret = sim_tape_wrtmk (unitp);
2140         sim_debug (DBG_DEBUG, & tape_dev, "%s: returned %d\n", __func__, ret);
2141         if (unitp->io_flush)
2142           unitp->io_flush (unitp);                              /* flush buffered data */
2143         if (ret != 0) {
2144           if (ret == MTSE_EOM) {
2145             sim_debug (DBG_NOTIFY, & tape_dev, "%s: EOM: %s\n", __func__, simh_tape_msg (ret));
2146             p->stati = 04340; // EOT file mark
2147             sim_warn ("%s: Wrote tape mark with EOM.\n", __func__);
2148             break;
2149           }
2150           sim_warn ("%s: Cannot write tape mark: %d - %s\n", __func__, ret, simh_tape_msg(ret));
2151           sim_warn ("%s: Returning arbitrary error code\n", __func__);
2152           p->stati = 05001; // BUG: arbitrary error code; config switch
2153           p->chanStatus = chanStatParityErrPeriph;
2154           break;
2155         }
2156 
2157         sim_tape_wreom (unitp);
2158         if (unitp->io_flush)
2159           unitp->io_flush (unitp);                              /* flush buffered data */
2160 
2161         tape_statep->rec_num ++;
2162         if (unitp->flags & UNIT_WATCH)
2163           sim_printf ("Tape %ld writes tape mark %ld\n", (long) MT_UNIT_NUM (unitp), (long) tape_statep->rec_num);
2164 
2165         p->stati = 04000;
2166         if (sim_tape_eot (unitp))
2167           p->stati = 04340;
2168 
2169         sim_debug (DBG_INFO, & tape_dev, "%s: Wrote tape mark; status %04o\n", __func__, p->stati);
2170         if_sim_debug (DBG_TRACE, & tape_dev) {
2171           sim_printf ("//    pos after %d\r\n", unitp->pos);
2172         }
2173 #if defined(TESTING)
2174         hdbgNote ("tape", "Tape Write EOF pos after %d", unitp->pos);
2175 #endif /* if defined(TESTING) */
2176         break;
2177 
2178 //      case 056: Unassigned
2179 
2180       case 057:               // CMD 057 -- Survey devices
2181         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Survey Devices\r\n"); }
2182         sim_debug (DBG_DEBUG, & tape_dev, "%s: survey_devices\n", __func__);
2183         tape_statep->io_mode = tape_survey;
2184         p->stati = 04000;
2185         break;
2186 
2187 //      case 060: Set 800 bpi; see case 042:
2188 
2189 //      case 061: Set 556 bpi; see case 043:
2190 
2191 //      case 062: Set File Protect
2192 
2193       case 063:              // CMD 063 -- Set File Permit.
2194         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Set File Permit\r\n"); }
2195         sim_debug (DBG_WARN, & tape_dev, "%s: Set file permit\n", __func__);
2196         p->stati = 04000;
2197         if (sim_tape_wrp (unitp))
2198           p->stati |= 1;
2199         if (sim_tape_bot (unitp))
2200           p->stati |= 2;
2201         break;
2202 
2203       case 064:              // CMD 064 -- Set 200 bpi.
2204         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Set 200 BPI\r\n"); }
2205         p->stati = 04000;
2206         if (sim_tape_wrp (unitp))
2207           p->stati |= 1;
2208         if (sim_tape_bot (unitp))
2209           p->stati |= 2;
2210         //if (sim_tape_eom (unitp))
2211           //p->stati |= 0340;
2212         sim_debug (DBG_DEBUG, & tape_dev, "%s: Set 200 bpi\n", __func__);
2213         break;
2214 
2215       case 065:              // CMD 064 -- Set 1600 CPI
2216         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Set 1600 CPI\r\n"); }
2217         p->stati = 04000;
2218         if (sim_tape_wrp (unitp))
2219           p->stati |= 1;
2220         if (sim_tape_bot (unitp))
2221           p->stati |= 2;
2222         //if (sim_tape_eom (unitp))
2223           //p->stati |= 0340;
2224         sim_debug (DBG_DEBUG, & tape_dev, "%s: Set 1600 CPI\n", __func__);
2225         break;
2226 
2227 //      case 066: reserve device
2228 
2229 //      case 067: release device
2230 
2231       case 070:              // CMD 070 -- Rewind.
2232         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Rewind\r\n"); }
2233         sim_debug (DBG_DEBUG, & tape_dev, "%s: Rewind\n", __func__);
2234         if (! (unitp->flags & UNIT_ATT)) {
2235           p->stati = 04104;
2236           return IOM_CMD_ERROR;
2237         }
2238         sim_tape_rewind (unitp);
2239 
2240         tape_statep->rec_num = 0;
2241         if (unitp->flags & UNIT_WATCH)
2242           sim_printf ("Tape %ld rewinds\n", (long) MT_UNIT_NUM (unitp));
2243 
2244         p->stati = 04000;
2245         if (sim_tape_wrp (unitp))
2246           p->stati |= 1;
2247         if (sim_tape_bot (unitp))
2248           p->stati |= 2;
2249         //if (sim_tape_eom (unitp))
2250           //p->stati |= 0340;
2251         send_special_interrupt (iomUnitIdx, chan, dev_code, 0, 0100 /* rewind complete */);
2252         break;
2253 
2254 //      case 071: Unassigned
2255 
2256       case 072:              // CMD 072 -- Rewind/Unload.
2257         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Rewind/Unload\r\n"); }
2258         if ((unitp->flags & UNIT_ATT)) {
2259           if (unitp->flags & UNIT_WATCH)
2260             sim_printf ("Tape %ld unloads\n", (long) MT_UNIT_NUM (unitp));
2261           sim_debug (DBG_DEBUG, & tape_dev, "%s: Rewind/unload\n", __func__);
2262           sim_tape_detach (unitp);
2263         }
2264         p->stati = 04000;
2265         // Sending this confuses RCP. When requesting a tape mount, it
2266         // first sends an unload command, and then waits for the operator
2267         // to mount the tape and press ready. This special interrupt
2268         // is being seen as an operator created event and causes RCP
2269         // to request the operator to re-ready the drive.
2270         //send_special_interrupt (iomUnitIdx, chan, dev_code, 0, 0040 /* unload complete */);
2271         break;
2272 
2273 //      case 073: data security erase
2274 
2275 //      case 074: Unassigned
2276 
2277 //      case 075:  tape load
2278 
2279 //      case 076: Unassigned
2280 
2281 //      case 077: set density
2282 
2283       default:
2284         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Unknown %02o\r\n", p->IDCW_DEV_CMD); }
2285         p->stati = 04501;
2286         p->chanStatus = chanStatIncorrectDCW;
2287         sim_warn ("%s: tape unrecognized device command  %02o\n", __func__, p->IDCW_DEV_CMD);
2288         return IOM_CMD_ERROR;
2289 
2290     } // switch IDCW_DEV_CMD
2291 
2292     sim_debug (DBG_DEBUG, & tape_dev, "%s: stati %04o\n", __func__, p->stati);
2293     return rc;
2294   } // if IDCW
2295 
2296   // Not IDCW; TDCW are captured in IOM, so must be IOTD, IOTP or IOTNP
2297   switch (tape_statep->io_mode) {
2298     case tape_no_mode:
2299 // It appears that in some cases Multics builds a dcw list from a generic "single IDCW plus optional DDCW" template.
2300 // That template sets the IDCW channel command to "record", regardless of whether or not the instruction
2301 // needs an DDCW. In particular, disk_ctl pings the disk by sending a "Reset status" with "record" and a apparently
2302 // uninitialized IOTD. The payload channel will send the IOTD because of the "record"; the Reset Status command left
2303 // IO mode at "no mode" so we don't know what to do with it. Since this appears benign, we will assume that the
2304 // original H/W ignored, and so shall we.
2305       //sim_warn ("%s: Unexpected IOTx\n", __func__);
2306       //return IOM_CMD_ERROR;
2307       break;
2308 
2309     case tape_rd_9:
2310     case tape_rd_bin: {
2311         if_sim_debug (DBG_TRACE, & tape_dev) {
2312           sim_printf ("// Tape IOT Read\r\n");
2313           sim_printf ("//    pos before %d\r\n", unitp->pos);
2314         }
2315 #if defined(TESTING)
2316         hdbgNote ("tape", "Tape IOT Read pos before %d", unitp->pos);
2317 #endif /* if defined(TESTING) */
2318         int rc = mtReadRecord (devUnitIdx, iomUnitIdx, chan);
2319         if (rc)
2320           return IOM_CMD_ERROR;
2321         }
2322         if_sim_debug (DBG_TRACE, & tape_dev) {
2323           sim_printf ("//    pos after %d\r\n", unitp->pos);
2324         }
2325 #if defined(TESTING)
2326         hdbgNote ("tape", "Tape IOT Read pos after %d", unitp->pos);
2327 #endif /* if defined(TESTING) */
2328         break;
2329 
2330     case tape_wr_9:
2331     case tape_wr_bin: {
2332         if_sim_debug (DBG_TRACE, & tape_dev) {
2333           sim_printf ("// Tape IOT Write\r\n");
2334           sim_printf ("//    pos before %d\r\n", unitp->pos);
2335         }
2336 #if defined(TESTING)
2337         hdbgNote ("tape", "Tape IOT write pos before %d", unitp->pos);
2338 #endif /* if defined(TESTING) */
2339         int rc = mtWriteRecord (devUnitIdx, iomUnitIdx, chan);
2340         if_sim_debug (DBG_TRACE, & tape_dev) {
2341           sim_printf ("//    pos after %d\r\n", unitp->pos);
2342         }
2343 #if defined(TESTING)
2344         hdbgNote ("tape", "Tape IOT write pos after %d", unitp->pos);
2345 #endif /* if defined(TESTING) */
2346         if (rc)
2347           return IOM_CMD_ERROR;
2348       }
2349       break;
2350 
2351     case tape_rd_ctlr:
2352       if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape IOT Read Memory\r\n"); }
2353       mtReadCtrlMainMem (devUnitIdx, iomUnitIdx, chan);
2354       break;
2355 
2356     case tape_initiate_rd_mem:
2357       if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape IOT Write Memory\r\n"); }
2358       mtInitRdMem (devUnitIdx, iomUnitIdx, chan);
2359       break;
2360 
2361     case tape_initiate_wr_mem:
2362       if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape IOT Initiate Write\r\n"); }
2363       mtInitWrMem (devUnitIdx, iomUnitIdx, chan);
2364       break;
2365 
2366     case tape_MTP_wr:
2367       if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape IOT MTP Write\r\n"); }
2368       mtMTPWr (devUnitIdx, iomUnitIdx, chan);
2369       break;
2370 
2371     case tape_wr_ctrl_regs:
2372       if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape IOT Wr Ctrl Regs\r\n"); }
2373       mtWRCtrlRegs (devUnitIdx, iomUnitIdx, chan);
2374       break;
2375 
2376     case tape_survey:
2377       if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape IOT Survey\r\n"); }
2378       surveyDevices (iomUnitIdx, chan);
2379       break;
2380 
2381     default:
2382       if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape IOT unknown %d\r\n", tape_statep->io_mode); }
2383       sim_warn ("%s: Unrecognized io_mode %d\n", __func__, tape_statep->io_mode);
2384       return IOM_CMD_ERROR;
2385   }
2386 
2387   return rc;
2388 } // mt_iom_cmd
2389 
2390 // 031 read statistics
2391 //  idcw.chan_cmd = "41"b3;  /* Indicate special controller command */
2392 // 006 initiate read data transfer
2393 // 032 write main memory (binary)
2394 // 016 initiate write data transfer
2395 // 000 suspend controller
2396 // 020 release controller
2397 
2398 static const char *simh_tape_msg(int code)
     /* [previous][next][first][last][top][bottom][index][help] */
2399 {
2400     // WARNING: Only selected tape routines return private tape codes
2401     // WARNING: returns static buf
2402     // BUG: Is using a string constant equivalent to using a static buffer?
2403     // static char msg[80];
2404     if (code == MTSE_OK)
2405         return "OK";
2406     else if (code == MTSE_UNATT)
2407         return "Unit not attached to a file";
2408     else if (code == MTSE_FMT)
2409         return "Unit specifies an unsupported tape file format";
2410     else if (code == MTSE_IOERR)
2411         return "Host OS I/O error";
2412     else if (code == MTSE_INVRL)
2413         return "Invalid record length (exceeds maximum allowed)";
2414     else if (code == MTSE_RECE)
2415         return "Record header contains error flag";
2416     else if (code == MTSE_TMK)
2417         return "Tape mark encountered";
2418     else if (code == MTSE_BOT)
2419         return "BOT encountered during reverse operation";
2420     else if (code == MTSE_EOM)
2421         return "End of Medium encountered";
2422     else if (code == MTSE_WRP)
2423         return "Write protected unit during write operation";
2424     else
2425         return "Unknown tape error";
2426   }
2427 
2428 t_stat attachTape (char * label, bool withring, char * drive)
     /* [previous][next][first][last][top][bottom][index][help] */
2429   {
2430     //sim_printf ("%s %s %s\n", label, withring ? "rw" : "ro", drive);
2431     int i;
2432     for (i = 0; i < N_MT_UNITS_MAX; i ++)
2433       {
2434         if (strcmp (drive, tape_states [i] . device_name) == 0)
2435           break;
2436       }
2437     if (i >= N_MT_UNITS_MAX)
2438       {
2439         sim_printf ("can't find device named %s\n", drive);
2440         return SCPE_ARG;
2441       }
2442     sim_printf ("attachTape selected unit %d\n", i);
2443     loadTape ((uint) i, label, ! withring);
2444     return SCPE_OK;
2445   }
2446 
2447 // mount <image.tap> ring|noring <drive>
2448 
2449 t_stat mount_tape (UNUSED int32 arg, const char * buf)
     /* [previous][next][first][last][top][bottom][index][help] */
2450   {
2451     size_t bufl = strlen (buf) + 1;
2452     char fname [bufl];
2453     char ring [bufl];
2454     char drive [bufl];
2455     int nParams = sscanf (buf, "%s %s %s", fname, ring, drive);
2456     if (nParams != 3)
2457       goto usage;
2458     size_t ringl = strlen (ring);
2459     bool withring;
2460     if (strncasecmp ("noring", ring, ringl) == 0)
2461       withring = false;
2462     else if (strncasecmp ("ring", ring, ringl) == 0)
2463       withring = true;
2464     else
2465       goto usage;
2466     return attachTape (fname, withring, drive);
2467 
2468 usage:
2469      sim_printf ("mount <image.tap> ring|noring <drive>\n");
2470      return SCPE_ARG;
2471   }
2472 
2473 #if !defined(QUIET_UNUSED)
2474 t_stat detachTape (char * drive)
     /* [previous][next][first][last][top][bottom][index][help] */
2475   {
2476     //sim_printf ("%s %s %s\n", label, withring ? "rw" : "ro", drive);
2477     int i;
2478     for (i = 0; i < N_MT_UNITS_MAX; i ++)
2479       {
2480         if (strcmp (drive, tape_states [i] . device_name) == 0)
2481           break;
2482       }
2483     if (i >= N_MT_UNITS_MAX)
2484       {
2485         sim_printf ("can't find device named %s\n", drive);
2486         return SCPE_ARG;
2487       }
2488     unloadTape ((uint) i);
2489     return SCPE_OK;
2490   }
2491 #endif /* if !defined(QUIET_UNUSED) */

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