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

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