root/src/simh/sim_tape.c

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

DEFINITIONS

This source file includes following definitions.
  1. _sim_tape_io_flush
  2. sim_tape_attach
  3. sim_tape_attach_ex
  4. sim_tape_detach
  5. sim_tape_attach_help
  6. sim_tape_data_trace
  7. sim_tape_rdlntf
  8. sim_tape_rdlntr
  9. sim_tape_rdrecf
  10. sim_tape_rdrecf_a
  11. sim_tape_rdrecr
  12. sim_tape_rdrecr_a
  13. sim_tape_wrrecf
  14. sim_tape_wrrecf_a
  15. sim_tape_wrdata
  16. sim_tape_wrtmk
  17. sim_tape_wrtmk_a
  18. sim_tape_wreom
  19. sim_tape_wreom_a
  20. sim_tape_wreomrw
  21. sim_tape_wreomrw_a
  22. sim_tape_wrgap
  23. sim_tape_wrgap_a
  24. sim_tape_sprecf
  25. sim_tape_sprecf_a
  26. sim_tape_sprecsf
  27. sim_tape_sprecsf_a
  28. sim_tape_sprecr
  29. sim_tape_sprecr_a
  30. sim_tape_sprecsr
  31. sim_tape_sprecsr_a
  32. sim_tape_spfilebyrecf
  33. sim_tape_spfilebyrecf_a
  34. sim_tape_spfilef
  35. sim_tape_spfilef_a
  36. sim_tape_spfilebyrecr
  37. sim_tape_spfilebyrecr_a
  38. sim_tape_spfiler
  39. sim_tape_spfiler_a
  40. sim_tape_rewind
  41. sim_tape_rewind_a
  42. sim_tape_position
  43. sim_tape_position_a
  44. sim_tape_reset
  45. sim_tape_bot
  46. sim_tape_eot
  47. sim_tape_wrp
  48. sim_tape_ioerr
  49. sim_tape_set_fmt
  50. sim_tape_show_fmt
  51. sim_tape_tpc_map
  52. sim_tape_simh_check
  53. sim_tape_e11_check
  54. sim_tape_tpc_fnd
  55. sim_tape_set_capac
  56. sim_tape_show_capac
  57. sim_tape_set_dens
  58. sim_tape_show_dens

   1 /*
   2  * sim_tape.c: simulator tape support library
   3  *
   4  * vim: filetype=c:tabstop=4:ai:expandtab
   5  * SPDX-License-Identifier: MIT
   6  * scspell-id: d4f34561-f62a-11ec-85d5-80ee73e9b8e7
   7  *
   8  * ---------------------------------------------------------------------------
   9  *
  10  * Copyright (c) 1993-2008 Robert M. Supnik
  11  * Copyright (c) 2021-2023 The DPS8M Development Team
  12  *
  13  * Permission is hereby granted, free of charge, to any person obtaining a
  14  * copy of this software and associated documentation files (the "Software"),
  15  * to deal in the Software without restriction, including without limitation
  16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  17  * and/or sell copies of the Software, and to permit persons to whom the
  18  * Software is furnished to do so, subject to the following conditions:
  19  *
  20  * The above copyright notice and this permission notice shall be included
  21  * in all copies or substantial portions of the Software.
  22  *
  23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  24  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  26  * IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR
  27  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  28  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  29  * OTHER DEALINGS IN THE SOFTWARE.
  30  *
  31  * Except as contained in this notice, the name of Robert M. Supnik shall
  32  * not be used in advertising or otherwise to promote the sale, use or other
  33  * dealings in this Software without prior written authorization from
  34  * Robert M. Supnik.
  35  *
  36  * ---------------------------------------------------------------------------
  37  */
  38 
  39 /*
  40  * Public routines:
  41  *
  42  * sim_tape_attach      attach tape unit
  43  * sim_tape_detach      detach tape unit
  44  * sim_tape_attach_help help routine for attaching tapes
  45  * sim_tape_rdrecf      read tape record forward
  46  * sim_tape_rdrecr      read tape record reverse
  47  * sim_tape_wrrecf      write tape record forward
  48  * sim_tape_sprecf      space tape record forward
  49  * sim_tape_sprecr      space tape record reverse
  50  * sim_tape_wrtmk       write tape mark
  51  * sim_tape_wreom       erase remainder of tape
  52  * sim_tape_wreomrw     erase remainder of tape & rewind
  53  * sim_tape_wrgap       write erase gap
  54  * sim_tape_sprecsf     space records forward
  55  * sim_tape_spfilef     space files forward
  56  * sim_tape_sprecsr     space records reverse
  57  * sim_tape_spfiler     space files reverse
  58  * sim_tape_position    generalized position
  59  * sim_tape_rewind      rewind
  60  * sim_tape_reset       reset unit
  61  * sim_tape_bot         TRUE if at beginning of tape
  62  * sim_tape_eot         TRUE if at or beyond end of tape
  63  * sim_tape_wrp         TRUE if write protected
  64  * sim_tape_set_fmt     set tape format
  65  * sim_tape_show_fmt    show tape format
  66  * sim_tape_set_capac   set tape capacity
  67  * sim_tape_show_capac  show tape capacity
  68  * sim_tape_set_dens    set tape density
  69  * sim_tape_show_dens   show tape density
  70  */
  71 
  72 #include "sim_defs.h"
  73 #include "sim_tape.h"
  74 #include <ctype.h>
  75 #include <signal.h>
  76 
  77 #ifdef TESTING
  78 # undef FREE
  79 # define FREE(p) free(p)
  80 #endif /* ifdef TESTING */
  81 
  82 struct sim_tape_fmt {
  83     const char          *name;                          /* name */
  84     int32               uflags;                         /* unit flags */
  85     t_addr              bot;                            /* bot test */
  86     };
  87 
  88 static struct sim_tape_fmt fmts[MTUF_N_FMT] = {
  89     { "SIMH", 0,       sizeof (t_mtrlnt) - 1 },
  90     { "E11",  0,       sizeof (t_mtrlnt) - 1 },
  91     { "TPC",  UNIT_RO, sizeof (t_tpclnt) - 1 },
  92     { "P7B",  0,       0 },
  93 /*  { "TPF",  UNIT_RO, 0 }, */
  94     { NULL,   0,       0 }
  95     };
  96 
  97 static const uint32 bpi [] = {                          /* tape density table, indexed by MT_DENS constants */
  98     0,                                                  /*   0 = MT_DENS_NONE -- density not set */
  99     200,                                                /*   1 = MT_DENS_200  -- 200 bpi NRZI */
 100     556,                                                /*   2 = MT_DENS_556  -- 556 bpi NRZI */
 101     800,                                                /*   3 = MT_DENS_800  -- 800 bpi NRZI */
 102     1600,                                               /*   4 = MT_DENS_1600 -- 1600 bpi PE */
 103     6250                                                /*   5 = MT_DENS_6250 -- 6250 bpi GCR */
 104     };
 105 
 106 #define BPI_COUNT       (sizeof (bpi) / sizeof (bpi [0]))   /* count of density table entries */
 107 
 108 static t_stat sim_tape_ioerr (UNIT *uptr);
 109 static t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat);
 110 static uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map, uint32 mapsize);
 111 static t_stat sim_tape_simh_check (UNIT *uptr);
 112 static t_stat sim_tape_e11_check (UNIT *uptr);
 113 static t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map);
 114 static void sim_tape_data_trace (UNIT *uptr, const uint8 *data, size_t len, const char* txt, int detail, uint32 reason);
 115 
 116 struct tape_context {
 117     DEVICE              *dptr;              /* Device for unit (access to debug flags) */
 118     uint32              dbit;               /* debugging bit for trace */
 119     uint32              auto_format;        /* Format determined dynamically */
 120     };
 121 #define tape_ctx up8                        /* Field in Unit structure which points to the tape_context */
 122 
 123 /*
 124    This routine is called when the simulator stops and any time
 125    the asynch mode is changed (enabled or disabled)
 126 */
 127 static void _sim_tape_io_flush (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 128 {
 129 fflush (uptr->fileref);
 130 }
 131 
 132 /* Attach tape unit */
 133 
 134 t_stat sim_tape_attach (UNIT *uptr, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 135 {
 136 DEVICE *dptr;
 137 
 138 if ((dptr = find_dev_from_unit (uptr)) == NULL)
 139     return SCPE_NOATT;
 140 return sim_tape_attach_ex (uptr, cptr, (dptr->flags & DEV_DEBUG) ? 0xFFFFFFFF : 0, 0);
 141 }
 142 
 143 t_stat sim_tape_attach_ex (UNIT *uptr, const char *cptr, uint32 dbit, int completion_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
 144 {
 145 struct tape_context *ctx;
 146 uint32 objc;
 147 DEVICE *dptr;
 148 char gbuf[CBUFSIZE];
 149 t_stat r;
 150 t_bool auto_format = FALSE;
 151 
 152 if ((dptr = find_dev_from_unit (uptr)) == NULL)
 153     return SCPE_NOATT;
 154 if (sim_switches & SWMASK ('F')) {                      /* format spec? */
 155     cptr = get_glyph (cptr, gbuf, 0);                   /* get spec */
 156     if (*cptr == 0)                                     /* must be more */
 157         return SCPE_2FARG;
 158     if (sim_tape_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK)
 159         return sim_messagef (SCPE_ARG, "Invalid Tape Format: %s\n", gbuf);
 160     sim_switches = sim_switches & ~(SWMASK ('F'));      /* Record Format specifier already processed */
 161     auto_format = TRUE;
 162     }
 163 if (MT_GET_FMT (uptr) == MTUF_F_TPC)
 164     sim_switches |= SWMASK ('R');                       /* Force ReadOnly attach for TPC tapes */
 165 r = attach_unit (uptr, (CONST char *)cptr);             /* attach unit */
 166 if (r != SCPE_OK)                                       /* error? */
 167     return sim_messagef (r, "Can't open tape image: %s\n", cptr);
 168 switch (MT_GET_FMT (uptr)) {                            /* case on format */
 169 
 170     case MTUF_F_STD:                                    /* SIMH */
 171         if (SCPE_OK != sim_tape_simh_check (uptr)) {
 172             sim_tape_detach (uptr);
 173             return SCPE_FMT;                            /* yes, complain */
 174             }
 175         break;
 176 
 177     case MTUF_F_E11:                                    /* E11 */
 178         if (SCPE_OK != sim_tape_e11_check (uptr)) {
 179             sim_tape_detach (uptr);
 180             return SCPE_FMT;                            /* yes, complain */
 181             }
 182         break;
 183 
 184     case MTUF_F_TPC:                                    /* TPC */
 185         objc = sim_tape_tpc_map (uptr, NULL, 0);        /* get # objects */
 186         if (objc == 0) {                                /* tape empty? */
 187             sim_tape_detach (uptr);
 188             return SCPE_FMT;                            /* yes, complain */
 189             }
 190         uptr->filebuf = calloc (objc + 1, sizeof (t_addr));
 191         if (uptr->filebuf == NULL) {                    /* map allocated? */
 192             sim_tape_detach (uptr);
 193             return SCPE_MEM;                            /* no, complain */
 194             }
 195         uptr->hwmark = objc + 1;                        /* save map size */
 196         sim_tape_tpc_map (uptr, (t_addr *) uptr->filebuf, objc);/* fill map */
 197         break;
 198 
 199     default:
 200         break;
 201         }
 202 
 203 uptr->tape_ctx = ctx = (struct tape_context *)calloc(1, sizeof(struct tape_context));
 204 if (!ctx)
 205 {
 206   fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 207           __func__, __FILE__, __LINE__);
 208 #if defined(USE_BACKTRACE)
 209 # ifdef SIGUSR2
 210   (void)raise(SIGUSR2);
 211   /*NOTREACHED*/ /* unreachable */
 212 # endif /* ifdef SIGUSR2 */
 213 #endif /* if defined(USE_BACKTRACE) */
 214   abort();
 215 }
 216 ctx->dptr = dptr;                                       /* save DEVICE pointer */
 217 ctx->dbit = dbit;                                       /* save debug bit */
 218 ctx->auto_format = auto_format;                         /* save that we auto selected format */
 219 
 220 sim_tape_rewind (uptr);
 221 
 222 uptr->io_flush = _sim_tape_io_flush;
 223 
 224 return SCPE_OK;
 225 }
 226 
 227 /* Detach tape unit */
 228 
 229 t_stat sim_tape_detach (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 230 {
 231 struct tape_context *ctx;
 232 uint32 f = 0;
 233 t_stat r;
 234 t_bool auto_format = FALSE;
 235 
 236 if (uptr == NULL)
 237     return SCPE_IERR;
 238 
 239 if (!(uptr->flags & UNIT_ATT))
 240     return SCPE_UNATT;
 241 
 242 ctx = (struct tape_context *)uptr->tape_ctx;
 243 f = MT_GET_FMT (uptr);
 244 
 245 if (uptr->io_flush)
 246     uptr->io_flush (uptr);                              /* flush buffered data */
 247 if (ctx)
 248     auto_format = ctx->auto_format;
 249 
 250 r = detach_unit (uptr);                                 /* detach unit */
 251 if (r != SCPE_OK)
 252     return r;
 253 switch (f) {                                            /* case on format */
 254 
 255     case MTUF_F_TPC:                                    /* TPC */
 256         if (uptr->filebuf)                              /* free map */
 257             FREE (uptr->filebuf);
 258         uptr->filebuf = NULL;
 259         uptr->hwmark = 0;
 260         break;
 261 
 262     default:
 263         break;
 264         }
 265 
 266 sim_tape_rewind (uptr);
 267 FREE (uptr->tape_ctx);
 268 uptr->tape_ctx = NULL;
 269 uptr->io_flush = NULL;
 270 if (auto_format)    /* format was determined or specified at attach time? */
 271     sim_tape_set_fmt (uptr, 0, "SIMH", NULL);   /* restore default format */
 272 return SCPE_OK;
 273 }
 274 
 275 t_stat sim_tape_attach_help(FILE *st, DEVICE *dptr, const UNIT *uptr, int32 flag, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 276 {
 277 fprintf (st, "%s Tape Attach Help\n\n", dptr->name);
 278 if (0 == (uptr-dptr->units)) {
 279     if (dptr->numunits > 1) {
 280         uint32 i;
 281 
 282         for (i=0; i < dptr->numunits; ++i)
 283             if (dptr->units[i].flags & UNIT_ATTABLE)
 284                 fprintf (st, "  sim> ATTACH {switches} %s%lu tapefile\n\n", dptr->name, (unsigned long)i);
 285         }
 286     else
 287         fprintf (st, "  sim> ATTACH {switches} %s tapefile\n\n", dptr->name);
 288     }
 289 else
 290     fprintf (st, "  sim> ATTACH {switches} %s tapefile\n\n", dptr->name);
 291 fprintf (st, "Attach command switches\n");
 292 fprintf (st, "    -R          Attach Read Only.\n");
 293 fprintf (st, "    -E          Must Exist (if not specified an attempt to create the indicated\n");
 294 fprintf (st, "                virtual tape will be attempted).\n");
 295 fprintf (st, "    -F          Open the indicated tape container in a specific format (default\n");
 296 fprintf (st, "                is SIMH, alternatives are E11, TPC and P7B)\n");
 297 return SCPE_OK;
 298 }
 299 
 300 static void sim_tape_data_trace(UNIT *uptr, const uint8 *data, size_t len, const char* txt, int detail, uint32 reason)
     /* [previous][next][first][last][top][bottom][index][help] */
 301 {
 302 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
 303 
 304 if (ctx == NULL)
 305     return;
 306 if (sim_deb && (ctx->dptr->dctrl & reason))
 307     sim_data_trace(ctx->dptr, uptr, (detail ? data : NULL), "", len, txt, reason);
 308 }
 309 
 310 /* Read record length forward (internal routine)
 311 
 312    Inputs:
 313         uptr    =       pointer to tape unit
 314         bc      =       pointer to returned record length
 315    Outputs:
 316         status  =       operation status
 317 
 318    exit condition       tape position
 319    ------------------   -----------------------------------------------------
 320    unit unattached      unchanged
 321    read error           unchanged, PNU set
 322    end of file/medium   updated if a gap precedes, else unchanged and PNU set
 323    tape mark            updated
 324    tape runaway         updated
 325    data record          updated, sim_fread will read record forward
 326 
 327    This routine is called to set up a record read or spacing in the forward
 328    direction.  On return, status is MTSE_OK and the tape is positioned at the
 329    first data byte if a record was encountered, or status is an MTSE error code
 330    giving the reason that the operation did not succeed and the tape position is
 331    as indicated above.
 332 
 333    The ANSI standards for magnetic tape recording (X3.32, X3.39, and X3.54) and
 334    the equivalent ECMA standard (ECMA-62) specify a maximum erase gap length of
 335    25 feet (7.6 meters).  While gaps of any length may be written, gaps longer
 336    than this are non-standard and may indicate that an unrecorded or erased tape
 337    is being read.
 338 
 339    If the tape density has been set via a previous "sim_tape_set_dens" call,
 340    then the length is monitored when skipping over erase gaps.  If the length
 341    reaches 25 feet, motion is terminated, and MTSE_RUNAWAY status is returned.
 342    Runaway status is also returned if an end-of-medium marker or the physical
 343    end of file is encountered while spacing over a gap; however, MTSE_EOM is
 344    returned if the tape is positioned at the EOM on entry.
 345 
 346    If the density has not been set, then a gap of any length is skipped, and
 347    MTSE_RUNAWAY status is never returned.  In effect, erase gaps present in the
 348    tape image file will be transparent to the caller.
 349 
 350    Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format.
 351    Because gaps may be partially overwritten with data records, gap metadata
 352    must be examined marker-by-marker.  To reduce the number of file read calls,
 353    a buffer of metadata elements is used.  The buffer size is initially
 354    established at 256 elements but may be set to any size desired.  To avoid a
 355    large read for the typical case where an erase gap is not present, the first
 356    read is of a single metadatum marker.  If that is a gap marker, then
 357    additional buffered reads are performed.
 358 
 359    See the notes at "sim_tape_wrgap" regarding the erase gap implementation.
 360 
 361    Implementation notes:
 362 
 363     1. For programming convenience, erase gap processing is performed for both
 364        SIMH standard and E11 tape formats, although the latter will never
 365        contain erase gaps, as the "sim_tape_wrgap" call takes no action for the
 366        E11 format.
 367 
 368     2. The "feof" call cannot return a non-zero value on the first pass through
 369        the loop, because the "sim_fseek" call resets the internal end-of-file
 370        indicator.  Subsequent passes only occur if an erase gap is present, so
 371        a non-zero return indicates an EOF was seen while reading through a gap.
 372 
 373     3. The dynamic start/stop test of the HP 3000 magnetic tape diagnostic
 374        heavily exercises the erase gap scanning code.  Sample test execution
 375        times for various buffer sizes on a 2 GHz host platform are:
 376 
 377          buffer size    execution time
 378          (elements)     (CPU seconds)
 379          -----------    --------------
 380                1             7200
 381               32              783
 382              128              237
 383              256              203
 384              512              186
 385             1024              171
 386 
 387     4. Because an erase gap may precede the logical end-of-medium, represented
 388        either by the physical end-of-file or by an EOM marker, the "position not
 389        updated" flag is set only if the tape is positioned at the EOM when the
 390        routine is entered.  If at least one gap marker precedes the EOM, then
 391        the PNU flag is not set.  This ensures that a backspace-and-retry
 392        sequence will work correctly in both cases.
 393 */
 394 
 395 static t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc)
     /* [previous][next][first][last][top][bottom][index][help] */
 396 {
 397 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
 398 uint8 c;
 399 t_bool all_eof;
 400 uint32 f = MT_GET_FMT (uptr);
 401 t_mtrlnt sbc;
 402 t_tpclnt tpcbc;
 403 t_mtrlnt buffer [256];                                  /* local tape buffer */
 404 uint32 bufcntr, bufcap;                                 /* buffer counter and capacity */
 405 int32 runaway_counter, sizeof_gap;                      /* bytes remaining before runaway and bytes per gap */
 406 t_stat r = MTSE_OK;
 407 
 408 MT_CLR_PNU (uptr);                                      /* clear the position-not-updated flag */
 409 
 410 if ((uptr->flags & UNIT_ATT) == 0)                      /* if the unit is not attached */
 411     return MTSE_UNATT;                                  /*   then quit with an error */
 412 if (ctx == NULL)                                        /* if not properly attached? */
 413     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
 414 
 415 sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);         /* set the initial tape position */
 416 
 417 switch (f) {                                            /* the read method depends on the tape format */
 418 
 419     case MTUF_F_STD:
 420     case MTUF_F_E11:
 421         runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set the largest legal gap size in bytes */
 422 
 423         if (runaway_counter == 0) {                     /* if tape density has not been not set */
 424             sizeof_gap = 0;                             /*   then disable runaway detection */
 425             runaway_counter = INT_MAX;                  /*     to allow gaps of any size */
 426             }
 427         else                                            /* otherwise */
 428             sizeof_gap = sizeof (t_mtrlnt);             /*   set the size of the gap */
 429 
 430         bufcntr = 0;                                    /* force an initial read */
 431         bufcap = 0;                                     /*   but of just one metadata marker */
 432 
 433         do {                                            /* loop until a record, gap, or error is seen */
 434             if (bufcntr == bufcap) {                    /* if the buffer is empty then refill it */
 435                 if (feof (uptr->fileref)) {             /* if we hit the EOF while reading a gap */
 436                     if (sizeof_gap > 0)                 /*   then if detection is enabled */
 437                         r = MTSE_RUNAWAY;               /*     then report a tape runaway */
 438                     else                                /*   otherwise report the physical EOF */
 439                         r = MTSE_EOM;                   /*     as the end-of-medium */
 440                     break;
 441                     }
 442 
 443                 else if (bufcap == 0)                   /* otherwise if this is the initial read */
 444                     bufcap = 1;                         /*   then start with just one marker */
 445 
 446                 else                                    /* otherwise reset the capacity */
 447                     bufcap = sizeof (buffer)            /*   to the full size of the buffer */
 448                                / sizeof (buffer [0]);
 449 
 450                 bufcap = sim_fread (buffer,             /* fill the buffer */
 451                                     sizeof (t_mtrlnt),  /*   with tape metadata */
 452                                     bufcap,
 453                                     uptr->fileref);
 454 
 455                 if (ferror (uptr->fileref)) {           /* if a file I/O error occurred */
 456                     if (bufcntr == 0)                   /*   then if this is the initial read */
 457                         MT_SET_PNU (uptr);              /*     then set position not updated */
 458 
 459                     r = sim_tape_ioerr (uptr);          /* report the error and quit */
 460                     break;
 461                     }
 462 
 463                 else if (bufcap == 0                    /* otherwise if positioned at the physical EOF */
 464                   || buffer [0] == MTR_EOM)             /*   or at the logical EOM */
 465                     if (bufcntr == 0) {                 /*     then if this is the initial read */
 466                         MT_SET_PNU (uptr);              /*       then set position not updated */
 467                         r = MTSE_EOM;                   /*         and report the end-of-medium and quit */
 468                         break;
 469                         }
 470 
 471                     else {                              /*     otherwise some gap has already been skipped */
 472                         if (sizeof_gap > 0)             /*       so if detection is enabled */
 473                             r = MTSE_RUNAWAY;           /*         then report a tape runaway */
 474                         else                            /*       otherwise report the physical EOF */
 475                             r = MTSE_EOM;               /*         as the end-of-medium */
 476                         break;
 477                         }
 478 
 479                 else                                    /* otherwise reset the index */
 480                     bufcntr = 0;                        /*   to the start of the buffer */
 481                 }
 482 
 483             *bc = buffer [bufcntr++];                   /* store the metadata marker value */
 484 
 485             if (*bc == MTR_EOM) {                       /* if an end-of-medium marker is seen */
 486                 if (sizeof_gap > 0)                     /*   then if detection is enabled */
 487                     r = MTSE_RUNAWAY;                   /*     then report a tape runaway */
 488                 else                                    /*   otherwise report the physical EOF */
 489                     r = MTSE_EOM;                       /*     as the end-of-medium */
 490                 break;
 491                 }
 492 
 493             uptr->pos = uptr->pos + sizeof (t_mtrlnt);  /* space over the marker */
 494 
 495             if (*bc == MTR_TMK) {                       /* if the value is a tape mark */
 496                 r = MTSE_TMK;                           /*   then quit with tape mark status */
 497                 break;
 498                 }
 499 
 500             else if (*bc == MTR_GAP)                    /* otherwise if the value is a full gap */
 501                 runaway_counter -= sizeof_gap;          /*   then decrement the gap counter */
 502 
 503             else if (*bc == MTR_FHGAP) {                        /* otherwise if the value if a half gap */
 504                 uptr->pos = uptr->pos - sizeof (t_mtrlnt) / 2;  /*   then back up */
 505                 (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /*     to resync */
 506                 bufcntr = bufcap;                               /* mark the buffer as invalid to force a read */
 507 
 508                 *bc = MTR_GAP;                                  /* reset the marker */
 509                 runaway_counter -= sizeof_gap / 2;              /*   and decrement the gap counter */
 510                 }
 511 
 512             else {                                                  /* otherwise it's a record marker */
 513                 if (bufcntr < bufcap)                               /* if the position is within the buffer */
 514                     (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /*   then seek to the data area */
 515 
 516                 sbc = MTR_L (*bc);                                  /* extract the record length */
 517                 uptr->pos = uptr->pos + sizeof (t_mtrlnt)           /* position to the start */
 518                   + (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc);       /*   of the record */
 519                 }
 520             }
 521         while (*bc == MTR_GAP && runaway_counter > 0);  /* continue until data or runaway occurs */
 522 
 523         if (r == MTSE_OK && runaway_counter <= 0)       /* if a tape runaway occurred */
 524             r = MTSE_RUNAWAY;                           /*   then report it */
 525 
 526         break;                                          /* otherwise the operation succeeded */
 527 
 528     case MTUF_F_TPC:
 529         (void)sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref);
 530         *bc = tpcbc;                                    /* save rec lnt */
 531         if (ferror (uptr->fileref)) {                   /* error? */
 532             MT_SET_PNU (uptr);                          /* pos not upd */
 533             return sim_tape_ioerr (uptr);
 534             }
 535         if (feof (uptr->fileref)) {                     /* eof? */
 536             MT_SET_PNU (uptr);                          /* pos not upd */
 537             r = MTSE_EOM;
 538             break;
 539             }
 540         uptr->pos = uptr->pos + sizeof (t_tpclnt);      /* spc over reclnt */
 541         if (tpcbc == TPC_TMK)                           /* tape mark? */
 542             r = MTSE_TMK;
 543         uptr->pos = uptr->pos + ((tpcbc + 1) & ~1);     /* spc over record */
 544         break;
 545 
 546     case MTUF_F_P7B:
 547         for (sbc = 0, all_eof = 1; ; sbc++) {           /* loop thru record */
 548             (void)sim_fread (&c, sizeof (uint8), 1, uptr->fileref);
 549             if (ferror (uptr->fileref)) {               /* error? */
 550                 MT_SET_PNU (uptr);                      /* pos not upd */
 551                 return sim_tape_ioerr (uptr);
 552                 }
 553             if (feof (uptr->fileref)) {                 /* eof? */
 554                 if (sbc == 0)                           /* no data? eom */
 555                     return MTSE_EOM;
 556                 break;                                  /* treat like eor */
 557                 }
 558             if ((sbc != 0) && (c & P7B_SOR))            /* next record? */
 559                 break;
 560             if ((c & P7B_DPAR) != P7B_EOF)
 561                 all_eof = 0;
 562             }
 563         *bc = sbc;                                      /* save rec lnt */
 564         (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */
 565         uptr->pos = uptr->pos + sbc;                    /* spc over record */
 566         if (all_eof)                                    /* tape mark? */
 567             r = MTSE_TMK;
 568         break;
 569 
 570     default:
 571         return MTSE_FMT;
 572         }
 573 #ifdef TESTING
 574 sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", r, *bc, uptr->pos);
 575 #endif /* TESTING */
 576 return r;
 577 }
 578 
 579 /* Read record length reverse (internal routine)
 580 
 581    Inputs:
 582         uptr    =       pointer to tape unit
 583         bc      =       pointer to returned record length
 584    Outputs:
 585         status  =       operation status
 586 
 587    exit condition       tape position
 588    ------------------   -------------------------------------------
 589    unit unattached      unchanged
 590    beginning of tape    unchanged
 591    read error           unchanged
 592    end of file          unchanged
 593    end of medium        updated
 594    tape mark            updated
 595    tape runaway         updated
 596    data record          updated, sim_fread will read record forward
 597 
 598    This routine is called to set up a record read or spacing in the reverse
 599    direction.  On return, status is MTSE_OK and the tape is positioned at the
 600    first data byte if a record was encountered, or status is an MTSE error code
 601    giving the reason that the operation did not succeed and the tape position is
 602    as indicated above.
 603 
 604    See the notes at "sim_tape_rdlntf" and "sim_tape_wrgap" regarding tape
 605    runaway and the erase gap implementation, respectively.
 606 */
 607 
 608 static t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc)
     /* [previous][next][first][last][top][bottom][index][help] */
 609 {
 610 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
 611 uint8 c;
 612 t_bool all_eof;
 613 uint32 f = MT_GET_FMT (uptr);
 614 t_addr ppos;
 615 t_mtrlnt sbc;
 616 t_tpclnt tpcbc;
 617 t_mtrlnt buffer [256];                                  /* local tape buffer */
 618 uint32 bufcntr, bufcap;                                 /* buffer counter and capacity */
 619 int32 runaway_counter, sizeof_gap;                      /* bytes remaining before runaway and bytes per gap */
 620 t_stat r = MTSE_OK;
 621 
 622 MT_CLR_PNU (uptr);                                      /* clear the position-not-updated flag */
 623 *bc = 0;
 624 
 625 if ((uptr->flags & UNIT_ATT) == 0)                      /* if the unit is not attached */
 626     return MTSE_UNATT;                                  /*   then quit with an error */
 627 if (ctx == NULL)                                        /* if not properly attached? */
 628     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
 629 
 630 if (sim_tape_bot (uptr))                                /* if the unit is positioned at the BOT */
 631     return MTSE_BOT;                                    /*   then reading backward is not possible */
 632 
 633 switch (f) {                                            /* the read method depends on the tape format */
 634 
 635     case MTUF_F_STD:
 636     case MTUF_F_E11:
 637         runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set largest legal gap size in bytes */
 638 
 639         if (runaway_counter == 0) {                     /* if tape density has not been not set */
 640             sizeof_gap = 0;                             /*   then disable runaway detection */
 641             runaway_counter = INT_MAX;                  /*     to allow gaps of any size */
 642             }
 643 
 644         else                                            /* otherwise */
 645             sizeof_gap = sizeof (t_mtrlnt);             /*   set the size of the gap */
 646 
 647         bufcntr = 0;                                    /* force an initial read */
 648         bufcap = 1;                                     /*   but of just one metadata marker */
 649 
 650         do {                                            /* loop until a record, gap, or error seen */
 651             if (bufcntr == 0) {                         /* if the buffer is empty then refill it */
 652                 if (sim_tape_bot (uptr)) {              /* if the search has backed into the BOT */
 653                     r = MTSE_BOT;                       /*   then quit with an error */
 654                     break;
 655                     }
 656 
 657                 else if (uptr->pos < sizeof (buffer))   /* if less than a full buffer remains */
 658                     bufcap = (uint32) uptr->pos         /*   then reduce the capacity accordingly */
 659                                / sizeof (t_mtrlnt);
 660 
 661                 (void)sim_fseek (uptr->fileref,                           /* seek back to the location */
 662                            uptr->pos - bufcap * sizeof (t_mtrlnt),  /*   corresponding to the start */
 663                            SEEK_SET);                               /*     of the buffer */
 664 
 665                 bufcntr = sim_fread (buffer, sizeof (t_mtrlnt),     /* fill the buffer */
 666                                      bufcap, uptr->fileref);        /*   with tape metadata */
 667 
 668                 if (ferror (uptr->fileref)) {           /* if a file I/O error occurred */
 669                     MT_SET_PNU (uptr);                  /*   then set position not updated */
 670                     r = sim_tape_ioerr (uptr);          /*     report the error and quit */
 671                     break;
 672                     }
 673 
 674                 else                                    /* otherwise reset the capacity */
 675                     bufcap = sizeof (buffer)            /*   to the full size of the buffer */
 676                                / sizeof (buffer [0]);
 677                 }
 678 
 679             *bc = buffer [--bufcntr];                   /* store the metadata marker value */
 680 
 681             uptr->pos = uptr->pos - sizeof (t_mtrlnt);  /* backspace over the marker */
 682 
 683             if (*bc == MTR_TMK) {                       /* if the marker is a tape mark */
 684                 r = MTSE_TMK;                           /*   then quit with tape mark status */
 685                 break;
 686                 }
 687 
 688             else if (*bc == MTR_GAP)                    /* otherwise if the marker is a full gap */
 689                 runaway_counter -= sizeof_gap;          /*   then decrement the gap counter */
 690 
 691             else if ((*bc & MTR_M_RHGAP) == MTR_RHGAP           /* otherwise if the marker */
 692               || *bc == MTR_RRGAP) {                            /*   is a half gap */
 693                 uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2;  /*     then position forward to resync */
 694                 bufcntr = 0;                                    /* mark the buffer as invalid to force a read */
 695 
 696                 *bc = MTR_GAP;                                  /* reset the marker */
 697                 runaway_counter -= sizeof_gap / 2;              /*   and decrement the gap counter */
 698                 }
 699 
 700             else {                                              /* otherwise it's a record marker */
 701                 sbc = MTR_L (*bc);                              /* extract the record length */
 702                 uptr->pos = uptr->pos - sizeof (t_mtrlnt)       /* position to the start */
 703                   - (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc);   /*   of the record */
 704                 (void)sim_fseek (uptr->fileref,                       /* seek to the data area */
 705                            uptr->pos + sizeof (t_mtrlnt), SEEK_SET);
 706                 }
 707             }
 708         while (*bc == MTR_GAP && runaway_counter > 0);  /* continue until data or runaway occurs */
 709 
 710         if (r == MTSE_OK && runaway_counter <= 0)       /* if a tape runaway occurred */
 711             r = MTSE_RUNAWAY;                           /*   then report it */
 712 
 713         break;                                          /* otherwise the operation succeeded */
 714 
 715     case MTUF_F_TPC:
 716         ppos = sim_tape_tpc_fnd (uptr, (t_addr *) uptr->filebuf); /* find prev rec */
 717         (void)sim_fseek (uptr->fileref, ppos, SEEK_SET);      /* position */
 718         (void)sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref);
 719         *bc = tpcbc;                                    /* save rec lnt */
 720         if (ferror (uptr->fileref))                     /* error? */
 721             return sim_tape_ioerr (uptr);
 722         if (feof (uptr->fileref)) {                     /* eof? */
 723             r = MTSE_EOM;
 724             break;
 725             }
 726         uptr->pos = ppos;                               /* spc over record */
 727         if (*bc == MTR_TMK) {                           /* tape mark? */
 728             r = MTSE_TMK;
 729             break;
 730             }
 731         (void)sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET);
 732         break;
 733 
 734     case MTUF_F_P7B:
 735         for (sbc = 1, all_eof = 1; (t_addr) sbc <= uptr->pos ; sbc++) {
 736             (void)sim_fseek (uptr->fileref, uptr->pos - sbc, SEEK_SET);
 737             (void)sim_fread (&c, sizeof (uint8), 1, uptr->fileref);
 738             if (ferror (uptr->fileref))                 /* error? */
 739                 return sim_tape_ioerr (uptr);
 740             if (feof (uptr->fileref)) {                 /* eof? */
 741                 r = MTSE_EOM;
 742                 break;
 743                 }
 744             if ((c & P7B_DPAR) != P7B_EOF)
 745                 all_eof = 0;
 746             if (c & P7B_SOR)                            /* start of record? */
 747                 break;
 748             }
 749         uptr->pos = uptr->pos - sbc;                    /* update position */
 750         *bc = sbc;                                      /* save rec lnt */
 751         (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */
 752         if (all_eof)                                    /* tape mark? */
 753             r = MTSE_TMK;
 754         break;
 755 
 756     default:
 757         return MTSE_FMT;
 758         }
 759 sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", r, *bc, uptr->pos);
 760 return r;
 761 }
 762 
 763 /* Read record forward
 764 
 765    Inputs:
 766         uptr    =       pointer to tape unit
 767         buf     =       pointer to buffer
 768         bc      =       pointer to returned record length
 769         max     =       maximum record size
 770    Outputs:
 771         status  =       operation status
 772 
 773    exit condition       position
 774 
 775    unit unattached      unchanged
 776    read error           unchanged, PNU set
 777    end of file/medium   unchanged, PNU set
 778    invalid record       unchanged, PNU set
 779    tape mark            updated
 780    data record          updated
 781    data record error    updated
 782 */
 783 
 784 t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max)
     /* [previous][next][first][last][top][bottom][index][help] */
 785 {
 786 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
 787 uint32 f = MT_GET_FMT (uptr);
 788 t_mtrlnt i, tbc, rbc;
 789 t_addr opos;
 790 t_stat st;
 791 
 792 if (ctx == NULL)                                        /* if not properly attached? */
 793     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
 794 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecf(unit=%d, buf=%p, max=%d)\n", (int)(uptr-ctx->dptr->units), buf, max);
 795 
 796 opos = uptr->pos;                                       /* old position */
 797 if (MTSE_OK != (st = sim_tape_rdlntf (uptr, &tbc)))     /* read rec lnt */
 798     return st;
 799 *bc = rbc = MTR_L (tbc);                                /* strip error flag */
 800 if (rbc > max) {                                        /* rec out of range? */
 801     MT_SET_PNU (uptr);
 802     uptr->pos = opos;
 803     return MTSE_INVRL;
 804     }
 805 i = (t_mtrlnt)sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */
 806 if (ferror (uptr->fileref)) {                           /* error? */
 807     MT_SET_PNU (uptr);
 808     uptr->pos = opos;
 809     return sim_tape_ioerr (uptr);
 810     }
 811 for ( ; i < rbc; i++)                                   /* fill with 0's */
 812     buf[i] = 0;
 813 if (f == MTUF_F_P7B)                                    /* p7b? strip SOR */
 814     buf[0] = buf[0] & P7B_DPAR;
 815 sim_tape_data_trace(uptr, buf, rbc, "Record Read", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
 816 return (MTR_F (tbc)? MTSE_RECE: MTSE_OK);
 817 }
 818 
 819 t_stat sim_tape_rdrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
 820 {
 821 t_stat r = SCPE_OK;
 822     r = sim_tape_rdrecf (uptr, buf, bc, max);
 823 return r;
 824 }
 825 
 826 /* Read record reverse
 827 
 828    Inputs:
 829         uptr    =       pointer to tape unit
 830         buf     =       pointer to buffer
 831         bc      =       pointer to returned record length
 832         max     =       maximum record size
 833    Outputs:
 834         status  =       operation status
 835 
 836    exit condition       position
 837 
 838    unit unattached      unchanged
 839    read error           unchanged
 840    end of file          unchanged
 841    end of medium        updated
 842    invalid record       unchanged
 843    tape mark            updated
 844    data record          updated
 845    data record error    updated
 846 */
 847 
 848 t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max)
     /* [previous][next][first][last][top][bottom][index][help] */
 849 {
 850 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
 851 uint32 f = MT_GET_FMT (uptr);
 852 t_mtrlnt i, rbc, tbc;
 853 t_stat st;
 854 
 855 if (ctx == NULL)                                        /* if not properly attached? */
 856     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
 857 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecr(unit=%d, buf=%p, max=%d)\n", (int)(uptr-ctx->dptr->units), buf, max);
 858 
 859 if (MTSE_OK != (st = sim_tape_rdlntr (uptr, &tbc)))     /* read rec lnt */
 860     return st;
 861 *bc = rbc = MTR_L (tbc);                                /* strip error flag */
 862 if (rbc > max)                                          /* rec out of range? */
 863     return MTSE_INVRL;
 864 i = (t_mtrlnt)sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */
 865 if (ferror (uptr->fileref))                             /* error? */
 866     return sim_tape_ioerr (uptr);
 867 for ( ; i < rbc; i++)                                   /* fill with 0's */
 868     buf[i] = 0;
 869 if (f == MTUF_F_P7B)                                    /* p7b? strip SOR */
 870     buf[0] = buf[0] & P7B_DPAR;
 871 sim_tape_data_trace(uptr, buf, rbc, "Record Read Reverse", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
 872 return (MTR_F (tbc)? MTSE_RECE: MTSE_OK);
 873 }
 874 
 875 t_stat sim_tape_rdrecr_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
 876 {
 877 t_stat r = SCPE_OK;
 878     r = sim_tape_rdrecr (uptr, buf, bc, max);
 879 return r;
 880 }
 881 
 882 /* Write record forward
 883 
 884    Inputs:
 885         uptr    =       pointer to tape unit
 886         buf     =       pointer to buffer
 887         bc      =       record length
 888    Outputs:
 889         status  =       operation status
 890 
 891    exit condition       position
 892 
 893    unit unattached      unchanged
 894    write protect        unchanged
 895    write error          unchanged, PNU set
 896    data record          updated
 897 */
 898 
 899 t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc)
     /* [previous][next][first][last][top][bottom][index][help] */
 900 {
 901 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
 902 uint32 f = MT_GET_FMT (uptr);
 903 t_mtrlnt sbc;
 904 
 905 if (ctx == NULL)                                        /* if not properly attached? */
 906     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
 907 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrrecf(unit=%d, buf=%p, bc=%d)\n", (int)(uptr-ctx->dptr->units), buf, bc);
 908 
 909 sim_tape_data_trace(uptr, buf, bc, "Record Write", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
 910 MT_CLR_PNU (uptr);
 911 sbc = MTR_L (bc);
 912 if ((uptr->flags & UNIT_ATT) == 0)                      /* not attached? */
 913     return MTSE_UNATT;
 914 if (sim_tape_wrp (uptr))                                /* write prot? */
 915     return MTSE_WRP;
 916 if (sbc == 0)                                           /* nothing to do? */
 917     return MTSE_OK;
 918 (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);         /* set pos */
 919 switch (f) {                                            /* case on format */
 920 
 921     case MTUF_F_STD:                                    /* standard */
 922         sbc = MTR_L ((bc + 1) & ~1);                    /* pad odd length */
 923     /*FALLTHRU*/ /* fallthrough */
 924     case MTUF_F_E11:                                    /* E11 */
 925         (void)sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref);
 926         (void)sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref);
 927         (void)sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref);
 928         if (ferror (uptr->fileref)) {                   /* error? */
 929             MT_SET_PNU (uptr);
 930             return sim_tape_ioerr (uptr);
 931             }
 932         uptr->pos = uptr->pos + sbc + (2 * sizeof (t_mtrlnt));  /* move tape */
 933         break;
 934 
 935     case MTUF_F_P7B:                                    /* Pierce 7B */
 936         buf[0] = buf[0] | P7B_SOR;                      /* mark start of rec */
 937         (void)sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref);
 938         (void)sim_fwrite (buf, sizeof (uint8), 1, uptr->fileref); /* delimit rec */
 939         if (ferror (uptr->fileref)) {                   /* error? */
 940             MT_SET_PNU (uptr);
 941             return sim_tape_ioerr (uptr);
 942             }
 943         uptr->pos = uptr->pos + sbc;                    /* move tape */
 944         break;
 945         }
 946 sim_tape_data_trace(uptr, buf, sbc, "Record Written", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
 947 return MTSE_OK;
 948 }
 949 
 950 t_stat sim_tape_wrrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt bc, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
 951 {
 952 t_stat r = SCPE_OK;
 953     r = sim_tape_wrrecf (uptr, buf, bc);
 954 return r;
 955 }
 956 
 957 /* Write metadata forward (internal routine) */
 958 
 959 static t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat)
     /* [previous][next][first][last][top][bottom][index][help] */
 960 {
 961 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
 962 
 963 MT_CLR_PNU (uptr);
 964 if ((uptr->flags & UNIT_ATT) == 0)                      /* not attached? */
 965     return MTSE_UNATT;
 966 if (ctx == NULL)                                        /* if not properly attached? */
 967     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
 968 if (sim_tape_wrp (uptr))                                /* write prot? */
 969     return MTSE_WRP;
 970 (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);         /* set pos */
 971 (void)sim_fwrite (&dat, sizeof (t_mtrlnt), 1, uptr->fileref);
 972 if (ferror (uptr->fileref)) {                           /* error? */
 973     MT_SET_PNU (uptr);
 974     return sim_tape_ioerr (uptr);
 975     }
 976 sim_debug (MTSE_DBG_STR, ctx->dptr, "wr_lnt: lnt: %d, pos: %" T_ADDR_FMT "u\n", dat, uptr->pos);
 977 uptr->pos = uptr->pos + sizeof (t_mtrlnt);              /* move tape */
 978 return MTSE_OK;
 979 }
 980 
 981 /* Write tape mark */
 982 
 983 t_stat sim_tape_wrtmk (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 984 {
 985 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
 986 
 987 if (ctx == NULL)                                        /* if not properly attached? */
 988     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
 989 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrtmk(unit=%d)\n", (int)(uptr-ctx->dptr->units));
 990 if (MT_GET_FMT (uptr) == MTUF_F_P7B) {                  /* P7B? */
 991     uint8 buf = P7B_EOF;                                /* eof mark */
 992     return sim_tape_wrrecf (uptr, &buf, 1);             /* write char */
 993     }
 994 return sim_tape_wrdata (uptr, MTR_TMK);
 995 }
 996 
 997 t_stat sim_tape_wrtmk_a (UNIT *uptr, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
 998 {
 999 t_stat r = MTSE_OK;
1000     r = sim_tape_wrtmk (uptr);
1001 return r;
1002 }
1003 
1004 /* Write end of medium */
1005 
1006 t_stat sim_tape_wreom (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1007 {
1008 t_stat result;
1009 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1010 
1011 if (ctx == NULL)                                        /* if not properly attached? */
1012     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1013 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wreom(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1014 if (MT_GET_FMT (uptr) == MTUF_F_P7B)                    /* cant do P7B */
1015     return MTSE_FMT;
1016 
1017 result = sim_tape_wrdata (uptr, MTR_EOM);               /* write the EOM marker */
1018 
1019 uptr->pos = uptr->pos - sizeof (t_mtrlnt);              /* restore original tape position */
1020 MT_SET_PNU (uptr);                                      /* indicate that position was not updated */
1021 
1022 return result;
1023 }
1024 
1025 t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1026 {
1027 t_stat r = MTSE_OK;
1028     r = sim_tape_wreom (uptr);
1029 return r;
1030 }
1031 
1032 /* Write end of medium-rewind */
1033 
1034 t_stat sim_tape_wreomrw (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1035 {
1036 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1037 t_stat r;
1038 
1039 if (ctx == NULL)                                        /* if not properly attached? */
1040     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1041 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wreomrw(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1042 if (MT_GET_FMT (uptr) == MTUF_F_P7B)                    /* cant do P7B */
1043     return MTSE_FMT;
1044 r = sim_tape_wrdata (uptr, MTR_EOM);
1045 if (r == MTSE_OK)
1046     r = sim_tape_rewind (uptr);
1047 return r;
1048 }
1049 
1050 t_stat sim_tape_wreomrw_a (UNIT *uptr, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1051 {
1052 t_stat r = MTSE_OK;
1053     r = sim_tape_wreomrw (uptr);
1054 return r;
1055 }
1056 
1057 /* Write erase gap
1058 
1059    Inputs:
1060         uptr    = pointer to tape unit
1061         gaplen  = length of gap in tenths of an inch
1062 
1063    Outputs:
1064         status  = operation status
1065 
1066    exit condition       position
1067    ------------------   ------------------
1068    unit unattached      unchanged
1069    unsupported format   unchanged
1070    write protected      unchanged
1071    read error           unchanged, PNU set
1072    write error          unchanged, PNU set
1073    gap written          updated
1074 
1075    An erase gap is represented in the tape image file by a special metadata
1076    value.  This value is chosen so that it is still recognizable even if it has
1077    been "cut in half" by a subsequent data overwrite that does not end on a
1078    metadatum-sized boundary.  In addition, a range of metadata values are
1079    reserved for detection in the reverse direction.  Erase gaps are currently
1080    supported only in SIMH (MTUF_F_STD) tape format.
1081 
1082    This implementation supports erasing gaps in the middle of a populated tape
1083    image and will always produce a valid image.  It also produces valid images
1084    when overwriting gaps with data records, with one exception: a data write
1085    that leaves only two bytes of gap remaining will produce an invalid tape.
1086    This limitation is deemed acceptable, as it is analogous to the existing
1087    limitation that data records cannot overwrite other data records without
1088    producing an invalid tape.
1089 
1090    Because SIMH tape images do not carry physical parameters (e.g., recording
1091    density), overwriting a tape image file containing gap metadata is
1092    problematic if the density setting is not the same as that used during
1093    recording.  There is no way to establish a gap of a certain length
1094    unequivocally in an image file, so this implementation establishes a gap of a
1095    certain number of bytes that reflect the desired gap length at the tape
1096    density in bits per inch used during writing.
1097 
1098    To write an erase gap, the implementation uses one of two approaches,
1099    depending on whether or not the current tape position is at EOM.  Erasing at
1100    EOM presents no special difficulties; gap metadata markers are written for
1101    the prescribed number of bytes.  If the tape is not at EOM, then erasing must
1102    take into account the existing record structure to ensure that a valid tape
1103    image is maintained.
1104 
1105    The general approach is to erase for the nominal number of bytes but to
1106    increase that length, if necessary, to ensure that a partially overwritten
1107    data record at the end of the gap can be altered to maintain validity.
1108    Because the smallest legal tape record requires space for two metadata
1109    markers plus two data bytes, an erasure that would leave less than that
1110    is increased to consume the entire record.  Otherwise, the final record is
1111    truncated appropriately by rewriting the leading and trailing length words
1112    appropriately.
1113 
1114    When reading in either direction, gap metadata markers are ignored (skipped)
1115    until a record length header, EOF marker, EOM marker, or physical EOF is
1116    encountered.  Thus, tape images containing gap metadata are transparent to
1117    the calling simulator (unless tape runaway support is enabled -- see the
1118    notes at "sim_tape_rdlntf" for details).
1119 
1120    The permissibility of data record lengths that are not multiples of the
1121    metadatum size presents a difficulty when reading.  If such an "odd length"
1122    record is written over a gap, half of a metadata marker will exist
1123    immediately after the trailing record length.
1124 
1125    This condition is detected when reading forward by the appearance of a
1126    "reversed" marker.  The value appears reversed because the value is made up
1127    of half of one marker and half of the next.  This is handled by seeking
1128    forward two bytes to resync (the stipulation above that the overwrite cannot
1129    leave only two bytes of gap means that at least one "whole" metadata marker
1130    will follow).  Reading in reverse presents a more complex problem, because
1131    half of the marker is from the preceding trailing record length marker and
1132    therefore could be any of a range of values.  However, that range is
1133    restricted by the SIMH tape specification requirement that record length
1134    metadata values must have bits 30:24 set to zero.  This allows unambiguous
1135    detection of the condition.
1136 
1137    The value chosen for gap metadata and the values reserved for "half-gap"
1138    detection are:
1139 
1140      0xFFFFFFFE            - primary gap value
1141      0xFFFEFFFF            - reserved (indicates half-gap in forward reads)
1142      0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads)
1143      0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads)
1144 
1145    If the tape density has been set via a previous sim_tape_set_dens call, and
1146    the tape format is set to SIMH format, then this routine will write a gap of
1147    the appropriate size.  If the density has not been set, then no action will
1148    be taken, and either MTSE_IOERR or MTSE_OK status will be returned, depending
1149    on whether SIMH or another format is selected, respectively.  A simulator
1150    that calls this routine must set the density beforehand; failure to do so is
1151    an error.  However, calling while another format is enabled is OK and is
1152    treated as a no-operation.  This allows a device simulator that supports
1153    writing erase gaps to use the same code without worrying about the tape
1154    format currently selected by the user.
1155 */
1156 
1157 t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen)
     /* [previous][next][first][last][top][bottom][index][help] */
1158 {
1159 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1160 t_stat st;
1161 t_mtrlnt meta, sbc, new_len, rec_size;
1162 t_addr gap_pos = uptr->pos;
1163 uint32 file_size, marker_count, tape_density;
1164 int32 gap_needed;
1165 uint32 gap_alloc = 0;                                   /* gap currently allocated from the tape */
1166 const uint32 format = MT_GET_FMT (uptr);                /* tape format */
1167 const uint32 meta_size = sizeof (t_mtrlnt);             /* bytes per metadatum */
1168 const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2;  /* smallest data record size */
1169 
1170 if (ctx == NULL)                                        /* if not properly attached? */
1171     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1172 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%u)\n", (int)(uptr-ctx->dptr->units), gaplen);
1173 
1174 MT_CLR_PNU (uptr);
1175 
1176 if ((uptr->flags & UNIT_ATT) == 0)                      /* if the unit is not attached */
1177     return MTSE_UNATT;                                  /*   then we cannot proceed */
1178 
1179 else if (sim_tape_wrp (uptr))                           /* otherwise if the unit is write protected */
1180     return MTSE_WRP;                                    /*   then we cannot write */
1181 
1182 tape_density = bpi [MT_DENS (uptr->dynflags)];          /* get the density of the tape */
1183 
1184 if (format != MTUF_F_STD)                               /* if erase gaps aren't supported by the format */
1185     return MTSE_OK;                                     /*   then take no action */
1186 else if (tape_density == 0)                             /* otherwise if the density is not set */
1187     return MTSE_IOERR;                                  /*   then report an I/O error */
1188 else                                                    /* otherwise */
1189     gap_needed = (gaplen * tape_density) / 10;          /*   determine the gap size needed in bytes */
1190 
1191 file_size = sim_fsize (uptr->fileref);                  /* get file size */
1192 sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);         /* position tape */
1193 
1194 /* Read tape records and allocate to gap until amount required is consumed.
1195 
1196    Read next metadatum from tape:
1197     - EOF or EOM: allocate remainder of bytes needed.
1198     - TMK or GAP: allocate sizeof(metadatum) bytes.
1199     - Reverse GAP: allocate sizeof(metadatum) / 2 bytes.
1200     - Data record: see below.
1201 
1202    Loop until bytes needed = 0.
1203 */
1204 
1205 do {
1206     (void)sim_fread (&meta, meta_size, 1, uptr->fileref);     /* read metadatum */
1207 
1208     if (ferror (uptr->fileref)) {                       /* read error? */
1209         uptr->pos = gap_pos;                            /* restore original position */
1210         MT_SET_PNU (uptr);                              /* position not updated */
1211         return sim_tape_ioerr (uptr);                   /* translate error */
1212         }
1213     else
1214         uptr->pos = uptr->pos + meta_size;              /* move tape over datum */
1215 
1216     if (feof (uptr->fileref) || (meta == MTR_EOM)) {    /* at eof or eom? */
1217         gap_alloc = gap_alloc + gap_needed;             /* allocate remainder */
1218         gap_needed = 0;
1219         }
1220 
1221     else if ((meta == MTR_GAP) || (meta == MTR_TMK)) {  /* gap or tape mark? */
1222         gap_alloc = gap_alloc + meta_size;              /* allocate marker space */
1223         gap_needed = gap_needed - meta_size;            /* reduce requirement */
1224         }
1225 
1226     else if (meta == MTR_FHGAP) {                       /* half gap? */
1227         uptr->pos = uptr->pos - meta_size / 2;          /* backup to resync */
1228         sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */
1229         gap_alloc = gap_alloc + meta_size / 2;          /* allocate marker space */
1230         gap_needed = gap_needed - meta_size / 2;        /* reduce requirement */
1231         }
1232 
1233     else if (uptr->pos +
1234              MTR_L (meta) + meta_size > file_size) {    /* rec len out of range? */
1235         gap_alloc = gap_alloc + gap_needed;             /* presume overwritten tape */
1236         gap_needed = 0;                                 /* allocate remainder */
1237         }
1238 
1239 /* Allocate a data record:
1240     - Determine record size in bytes (including metadata)
1241     - If record size - bytes needed < smallest allowed record size,
1242       allocate entire record to gap, else allocate needed amount and
1243       truncate data record to reflect remainder.
1244 */
1245     else {                                              /* data record */
1246         sbc = MTR_L (meta);                             /* get record data length */
1247         rec_size = ((sbc + 1) & ~1) + meta_size * 2;    /* overall size in bytes */
1248 
1249         if (rec_size < gap_needed + min_rec_size) {         /* rec too small? */
1250             uptr->pos = uptr->pos - meta_size + rec_size;   /* position past record */
1251             sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* move tape */
1252             gap_alloc = gap_alloc + rec_size;               /* allocate record */
1253             gap_needed = gap_needed - rec_size;             /* reduce requirement */
1254             }
1255 
1256         else {                                              /* record size OK */
1257             uptr->pos = uptr->pos - meta_size + gap_needed; /* position to end of gap */
1258             new_len = MTR_F (meta) | (sbc - gap_needed);    /* truncate to new len */
1259             st = sim_tape_wrdata (uptr, new_len);           /* write new rec len */
1260 
1261             if (st != MTSE_OK) {                            /* write OK? */
1262                 uptr->pos = gap_pos;                        /* restore orig pos */
1263                 return st;                                  /* PNU was set by wrdata */
1264                 }
1265 
1266             uptr->pos = uptr->pos + sbc - gap_needed;       /* position to end of data */
1267             st = sim_tape_wrdata (uptr, new_len);           /* write new rec len */
1268 
1269             if (st != MTSE_OK) {                            /* write OK? */
1270                 uptr->pos = gap_pos;                        /* restore orig pos */
1271                 return st;                                  /* PNU was set by wrdata */
1272                 }
1273 
1274             gap_alloc = gap_alloc + gap_needed;             /* allocate remainder */
1275             gap_needed = 0;
1276             }
1277         }
1278     }
1279 while (gap_needed > 0);
1280 
1281 uptr->pos = gap_pos;                                    /* reposition to gap start */
1282 
1283 if (gap_alloc & (meta_size - 1)) {                      /* gap size "odd?" */
1284     st = sim_tape_wrdata (uptr, MTR_FHGAP);             /* write half gap marker */
1285     if (st != MTSE_OK) {                                /* write OK? */
1286         uptr->pos = gap_pos;                            /* restore orig pos */
1287         return st;                                      /* PNU was set by wrdata */
1288         }
1289     uptr->pos = uptr->pos - meta_size / 2;              /* realign position */
1290     gap_alloc = gap_alloc - 2;                          /* decrease gap to write */
1291     }
1292 
1293 marker_count = gap_alloc / meta_size;                   /* count of gap markers */
1294 
1295 do {
1296     st = sim_tape_wrdata (uptr, MTR_GAP);               /* write gap markers */
1297     if (st != MTSE_OK) {                                /* write OK? */
1298         uptr->pos = gap_pos;                            /* restore orig pos */
1299         return st;                                      /* PNU was set by wrdata */
1300         }
1301     }
1302 while (--marker_count > 0);
1303 
1304 return MTSE_OK;
1305 }
1306 
1307 t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1308 {
1309 t_stat r = MTSE_OK;
1310 return r;
1311 }
1312 
1313 /* Space record forward
1314 
1315    Inputs:
1316         uptr    =       pointer to tape unit
1317         bc      =       pointer to size of record skipped
1318    Outputs:
1319         status  =       operation status
1320 
1321    exit condition       position
1322 
1323    unit unattached      unchanged
1324    read error           unchanged, PNU set
1325    end of file/medium   unchanged, PNU set
1326    tape mark            updated
1327    data record          updated
1328    data record error    updated
1329 */
1330 
1331 t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc)
     /* [previous][next][first][last][top][bottom][index][help] */
1332 {
1333 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1334 t_stat st;
1335 
1336 *bc = 0;
1337 if (ctx == NULL)                                        /* if not properly attached? */
1338     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1339 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecf(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1340 
1341 st = sim_tape_rdlntf (uptr, bc);                        /* get record length */
1342 *bc = MTR_L (*bc);
1343 return st;
1344 }
1345 
1346 t_stat sim_tape_sprecf_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1347 {
1348 t_stat r = MTSE_OK;
1349     r = sim_tape_sprecf (uptr, bc);
1350 return r;
1351 }
1352 
1353 /* Space records forward
1354 
1355    Inputs:
1356         uptr    =       pointer to tape unit
1357         count   =       count of records to skip
1358         skipped =       pointer to number of records actually skipped
1359    Outputs:
1360         status  =       operation status
1361 
1362    exit condition       position
1363 
1364    unit unattached      unchanged
1365    read error           unchanged, PNU set
1366    end of file/medium   unchanged, PNU set
1367    tape mark            updated
1368    data record          updated
1369    data record error    updated
1370 */
1371 
1372 t_stat sim_tape_sprecsf (UNIT *uptr, uint32 count, uint32 *skipped)
     /* [previous][next][first][last][top][bottom][index][help] */
1373 {
1374 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1375 t_stat st;
1376 t_mtrlnt tbc;
1377 
1378 if (ctx == NULL)                                        /* if not properly attached? */
1379     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1380 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecsf(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);
1381 
1382 *skipped = 0;
1383 while (*skipped < count) {                              /* loopo */
1384     st = sim_tape_sprecf (uptr, &tbc);                  /* spc rec */
1385     if (st != MTSE_OK)
1386         return st;
1387     *skipped = *skipped + 1;                            /* # recs skipped */
1388     }
1389 return MTSE_OK;
1390 }
1391 
1392 t_stat sim_tape_sprecsf_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1393 {
1394 t_stat r = MTSE_OK;
1395     r = sim_tape_sprecsf (uptr, count, skipped);
1396 return r;
1397 }
1398 
1399 /* Space record reverse
1400 
1401    Inputs:
1402         uptr    =       pointer to tape unit
1403         bc      =       pointer to size of records skipped
1404    Outputs:
1405         status  =       operation status
1406 
1407    exit condition       position
1408 
1409    unit unattached      unchanged
1410    beginning of tape    unchanged
1411    read error           unchanged
1412    end of file          unchanged
1413    end of medium        updated
1414    tape mark            updated
1415    data record          updated
1416 */
1417 
1418 t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc)
     /* [previous][next][first][last][top][bottom][index][help] */
1419 {
1420 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1421 t_stat st;
1422 
1423 if (ctx == NULL)                                        /* if not properly attached? */
1424     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1425 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecr(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1426 
1427 if (MT_TST_PNU (uptr)) {
1428     MT_CLR_PNU (uptr);
1429     *bc = 0;
1430     return MTSE_OK;
1431     }
1432 st = sim_tape_rdlntr (uptr, bc);                        /* get record length */
1433 *bc = MTR_L (*bc);
1434 return st;
1435 }
1436 
1437 t_stat sim_tape_sprecr_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1438 {
1439 t_stat r = MTSE_OK;
1440     r = sim_tape_sprecr (uptr, bc);
1441 return r;
1442 }
1443 
1444 /* Space records reverse
1445 
1446    Inputs:
1447         uptr    =       pointer to tape unit
1448         count   =       count of records to skip
1449         skipped =       pointer to number of records actually skipped
1450    Outputs:
1451         status  =       operation status
1452 
1453    exit condition       position
1454 
1455    unit unattached      unchanged
1456    beginning of tape    unchanged
1457    read error           unchanged
1458    end of file          unchanged
1459    end of medium        updated
1460    tape mark            updated
1461    data record          updated
1462 */
1463 
1464 t_stat sim_tape_sprecsr (UNIT *uptr, uint32 count, uint32 *skipped)
     /* [previous][next][first][last][top][bottom][index][help] */
1465 {
1466 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1467 t_stat st;
1468 t_mtrlnt tbc;
1469 
1470 if (ctx == NULL)                                        /* if not properly attached? */
1471     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1472 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecsr(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);
1473 
1474 *skipped = 0;
1475 while (*skipped < count) {                              /* loopo */
1476     st = sim_tape_sprecr (uptr, &tbc);                  /* spc rec rev */
1477     if (st != MTSE_OK)
1478         return st;
1479     *skipped = *skipped + 1;                            /* # recs skipped */
1480     }
1481 return MTSE_OK;
1482 }
1483 
1484 t_stat sim_tape_sprecsr_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1485 {
1486 t_stat r = MTSE_OK;
1487     r = sim_tape_sprecsr (uptr, count, skipped);
1488 return r;
1489 }
1490 
1491 /* Space files forward by record
1492 
1493    Inputs:
1494         uptr    =       pointer to tape unit
1495         count   =       count of files to skip
1496         skipped =       pointer to number of files actually skipped
1497         recsskipped =   pointer to number of records skipped
1498         check_leot =    flag to detect and stop skip between two successive tape marks
1499    Outputs:
1500         status  =       operation status
1501 
1502    exit condition       position
1503 
1504    unit unattached      unchanged
1505    read error           unchanged, PNU set
1506    end of file/medium   unchanged, PNU set
1507    tape mark            updated
1508    data record          updated
1509    data record error    updated
1510 */
1511 
1512 t_stat sim_tape_spfilebyrecf (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot)
     /* [previous][next][first][last][top][bottom][index][help] */
1513 {
1514 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1515 t_stat st;
1516 t_bool last_tapemark = FALSE;
1517 uint32 filerecsskipped = 0;
1518 *skipped = *recsskipped = 0;
1519 if (ctx == NULL)                                        /* if not properly attached? */
1520     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1521 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecf(unit=%d, count=%d, check_leot=%d)\n", (int)(uptr-ctx->dptr->units), count, check_leot);
1522 
1523 if (check_leot) {
1524     t_mtrlnt rbc;
1525 
1526     st = sim_tape_rdlntr (uptr, &rbc);
1527     last_tapemark = (MTSE_TMK == st);
1528     if ((st == MTSE_OK) || (st == MTSE_TMK))
1529         sim_tape_rdlntf (uptr, &rbc);
1530     }
1531 *skipped = 0;     //-V1048
1532 *recsskipped = 0; //-V1048
1533 while (*skipped < count) {                              /* loopo */
1534     while (1) {
1535         st = sim_tape_sprecsf (uptr, 0x1ffffff, &filerecsskipped);/* spc recs */
1536         *recsskipped += filerecsskipped;
1537         if (st != MTSE_OK)
1538             break;
1539         }
1540     if (st == MTSE_TMK) {
1541         *skipped = *skipped + 1;                        /* # files skipped */
1542         if (check_leot && (filerecsskipped == 0) && last_tapemark) {
1543             uint32 filefileskipped;
1544             sim_tape_spfilebyrecr (uptr, 1, &filefileskipped, &filerecsskipped);
1545             *skipped = *skipped - 1;                    /* adjust # files skipped */
1546             return MTSE_LEOT;
1547             }
1548         last_tapemark = TRUE;
1549         }
1550     else
1551         return st;
1552     }
1553 return MTSE_OK;
1554 }
1555 
1556 t_stat sim_tape_spfilebyrecf_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1557 {
1558 t_stat r = MTSE_OK;
1559     r = sim_tape_spfilebyrecf (uptr, count, skipped, recsskipped, check_leot);
1560 return r;
1561 }
1562 
1563 /* Space files forward
1564 
1565    Inputs:
1566         uptr    =       pointer to tape unit
1567         count   =       count of files to skip
1568         skipped =       pointer to number of files actually skipped
1569    Outputs:
1570         status  =       operation status
1571 
1572    exit condition       position
1573 
1574    unit unattached      unchanged
1575    read error           unchanged, PNU set
1576    end of file/medium   unchanged, PNU set
1577    tape mark            updated
1578    data record          updated
1579    data record error    updated
1580 */
1581 
1582 t_stat sim_tape_spfilef (UNIT *uptr, uint32 count, uint32 *skipped)
     /* [previous][next][first][last][top][bottom][index][help] */
1583 {
1584 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1585 uint32 totalrecsskipped;
1586 
1587 if (ctx == NULL)                                        /* if not properly attached? */
1588     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1589 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilef(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);
1590 
1591 return sim_tape_spfilebyrecf (uptr, count, skipped, &totalrecsskipped, FALSE);
1592 }
1593 
1594 t_stat sim_tape_spfilef_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1595 {
1596 t_stat r = MTSE_OK;
1597     r = sim_tape_spfilef (uptr, count, skipped);
1598 return r;
1599 }
1600 
1601 /* Space files reverse by record
1602 
1603    Inputs:
1604         uptr    =       pointer to tape unit
1605         count   =       count of files to skip
1606         skipped =       pointer to number of files actually skipped
1607         recsskipped =   pointer to number of records skipped
1608    Outputs:
1609         status  =       operation status
1610 
1611    exit condition       position
1612 
1613    unit unattached      unchanged
1614    beginning of tape    unchanged
1615    read error           unchanged
1616    end of file          unchanged
1617    end of medium        updated
1618    tape mark            updated
1619    data record          updated
1620 */
1621 
1622 t_stat sim_tape_spfilebyrecr (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped)
     /* [previous][next][first][last][top][bottom][index][help] */
1623 {
1624 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1625 t_stat st;
1626 uint32 filerecsskipped = 0;
1627 
1628 *skipped = 0;
1629 *recsskipped = 0;
1630 if (ctx == NULL)                                        /* if not properly attached? */
1631     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1632 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecr(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);
1633 
1634 while (*skipped < count) {                              /* loopo */
1635     while (1) {
1636         st = sim_tape_sprecsr (uptr, 0x1ffffff, &filerecsskipped);/* spc recs rev */
1637         *recsskipped += filerecsskipped;
1638         if (st != MTSE_OK)
1639             break;
1640         }
1641     if (st == MTSE_TMK)
1642         *skipped = *skipped + 1;                        /* # files skipped */
1643     else
1644         return st;
1645     }
1646 return MTSE_OK;
1647 }
1648 
1649 t_stat sim_tape_spfilebyrecr_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1650 {
1651 t_stat r = MTSE_OK;
1652     r = sim_tape_spfilebyrecr (uptr, count, skipped, recsskipped);
1653 return r;
1654 }
1655 
1656 /* Space files reverse
1657 
1658    Inputs:
1659         uptr    =       pointer to tape unit
1660         count   =       count of files to skip
1661         skipped =       pointer to number of files actually skipped
1662    Outputs:
1663         status  =       operation status
1664 
1665    exit condition       position
1666 
1667    unit unattached      unchanged
1668    beginning of tape    unchanged
1669    read error           unchanged
1670    end of file          unchanged
1671    end of medium        updated
1672    tape mark            updated
1673    data record          updated
1674 */
1675 
1676 t_stat sim_tape_spfiler (UNIT *uptr, uint32 count, uint32 *skipped)
     /* [previous][next][first][last][top][bottom][index][help] */
1677 {
1678 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1679 uint32 totalrecsskipped;
1680 
1681 if (ctx == NULL)                                        /* if not properly attached? */
1682     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1683 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfiler(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);
1684 
1685 return sim_tape_spfilebyrecr (uptr, count, skipped, &totalrecsskipped);
1686 }
1687 
1688 t_stat sim_tape_spfiler_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1689 {
1690 t_stat r = MTSE_OK;
1691     r = sim_tape_spfiler (uptr, count, skipped);
1692 return r;
1693 }
1694 
1695 /* Rewind tape */
1696 
1697 t_stat sim_tape_rewind (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1698 {
1699 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1700 
1701 if (uptr->flags & UNIT_ATT) {
1702     if (ctx == NULL)                                    /* if not properly attached? */
1703         return sim_messagef (SCPE_IERR, "Bad Attach\n");/*   that's a problem */
1704     sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rewind(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1705     }
1706 uptr->pos = 0;
1707 MT_CLR_PNU (uptr);
1708 return MTSE_OK;
1709 }
1710 
1711 t_stat sim_tape_rewind_a (UNIT *uptr, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1712 {
1713 t_stat r = MTSE_OK;
1714     r = sim_tape_rewind (uptr);
1715 return r;
1716 }
1717 
1718 /* Position Tape */
1719 
1720 t_stat sim_tape_position (UNIT *uptr, uint32 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped)
     /* [previous][next][first][last][top][bottom][index][help] */
1721 {
1722 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1723 t_stat r = MTSE_OK;
1724 
1725 if (ctx == NULL)                                        /* if not properly attached? */
1726     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1727 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_position(unit=%d, flags=0x%X, recs=%d, files=%d)\n", (int)(uptr-ctx->dptr->units), flags, recs, files);
1728 
1729 *recsskipped = *filesskipped = *objectsskipped = 0;
1730 if (flags & MTPOS_M_REW)
1731     r = sim_tape_rewind (uptr);
1732 if (r != MTSE_OK)
1733     return r;
1734 if (flags & MTPOS_M_OBJ) {
1735     uint32 objs = recs;
1736     uint32 skipped;
1737     uint32 objsremaining = objs;
1738 
1739     while (*objectsskipped < objs) {                       /* loopo */
1740         if (flags & MTPOS_M_REV)                        /* reverse? */
1741             r = sim_tape_sprecsr (uptr, objsremaining, &skipped);
1742         else
1743             r = sim_tape_sprecsf (uptr, objsremaining, &skipped);
1744         objsremaining = objsremaining - (skipped + ((r == MTSE_TMK) ? 1 : 0));
1745         if ((r == MTSE_TMK) || (r == MTSE_OK))
1746             *objectsskipped = *objectsskipped + skipped + ((r == MTSE_TMK) ? 1 : 0);
1747         else
1748             return r;
1749         }
1750     r = MTSE_OK;
1751     }
1752 else {
1753     uint32 fileskiprecs;
1754 
1755     if (flags & MTPOS_M_REV)                            /* reverse? */
1756         r = sim_tape_spfilebyrecr (uptr, files, filesskipped, &fileskiprecs);
1757     else
1758         r = sim_tape_spfilebyrecf (uptr, files, filesskipped, &fileskiprecs, (flags & MTPOS_M_DLE));
1759     if (r != MTSE_OK)
1760         return r;
1761     if (flags & MTPOS_M_REV)                            /* reverse? */
1762         r = sim_tape_sprecsr (uptr, recs, recsskipped);
1763     else
1764         r = sim_tape_sprecsf (uptr, recs, recsskipped);
1765     if (r == MTSE_TMK)
1766         *filesskipped = *filesskipped + 1;
1767     *objectsskipped = fileskiprecs + *filesskipped + *recsskipped;
1768     }
1769 return r;
1770 }
1771 
1772 t_stat sim_tape_position_a (UNIT *uptr, uint32 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1773 {
1774 t_stat r = MTSE_OK;
1775     r = sim_tape_position (uptr, flags, recs, recsskipped, files, filesskipped, objectsskipped);
1776 return r;
1777 }
1778 
1779 /* Reset tape */
1780 
1781 t_stat sim_tape_reset (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1782 {
1783 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1784 
1785 MT_CLR_PNU (uptr);
1786 if (!(uptr->flags & UNIT_ATT))                          /* attached? */
1787     return SCPE_OK;
1788 
1789 if (ctx == NULL)                                        /* if not properly attached? */
1790     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1791 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_reset(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1792 
1793 _sim_tape_io_flush(uptr);
1794 return SCPE_OK;
1795 }
1796 
1797 /* Test for BOT */
1798 
1799 t_bool sim_tape_bot (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1800 {
1801 uint32 f = MT_GET_FMT (uptr);
1802 
1803 return (uptr->pos <= fmts[f].bot)? TRUE: FALSE;
1804 }
1805 
1806 /* Test for end of tape */
1807 
1808 t_bool sim_tape_eot (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1809 {
1810 return (uptr->capac && (uptr->pos >= uptr->capac))? TRUE: FALSE;
1811 }
1812 
1813 /* Test for write protect */
1814 
1815 t_bool sim_tape_wrp (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1816 {
1817 return ((uptr->flags & MTUF_WRP) || (MT_GET_FMT (uptr) == MTUF_F_TPC))? TRUE: FALSE;
1818 }
1819 
1820 /* Process I/O error */
1821 
1822 static t_stat sim_tape_ioerr (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1823 {
1824 sim_printf ("%s: Magtape library I/O error: %s\n", sim_uname (uptr), strerror (errno));
1825 clearerr (uptr->fileref);
1826 return MTSE_IOERR;
1827 }
1828 
1829 /* Set tape format */
1830 
1831 t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
1832 {
1833 uint32 f;
1834 
1835 if (uptr == NULL)
1836     return SCPE_IERR;
1837 if (uptr->flags & UNIT_ATT)
1838     return SCPE_ALATT;
1839 if (cptr == NULL)
1840     return SCPE_ARG;
1841 for (f = 0; f < MTUF_N_FMT; f++) {
1842     if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) {
1843         uptr->flags = (uptr->flags & ~MTUF_FMT) |
1844             (f << MTUF_V_FMT) | fmts[f].uflags;
1845         return SCPE_OK;
1846         }
1847     }
1848 return SCPE_ARG;
1849 }
1850 
1851 /* Show tape format */
1852 
1853 t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
1854 {
1855 int32 f = MT_GET_FMT (uptr);
1856 
1857 if (fmts[f].name)
1858     fprintf (st, "%s format", fmts[f].name);
1859 else fprintf (st, "invalid format");
1860 return SCPE_OK;
1861 }
1862 
1863 /* Map a TPC format tape image */
1864 
1865 static uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map, uint32 mapsize)
     /* [previous][next][first][last][top][bottom][index][help] */
1866 {
1867 t_addr tpos, leot = 0;
1868 t_addr tape_size;
1869 t_tpclnt bc, last_bc = 0xFFFF;
1870 uint32 had_double_tape_mark = 0;
1871 size_t i;
1872 uint32 objc, sizec;
1873 uint32 *countmap = NULL;
1874 uint8 *recbuf = NULL;
1875 DEVICE *dptr = find_dev_from_unit (uptr);
1876 
1877 if ((uptr == NULL) || (uptr->fileref == NULL))
1878     return 0;
1879 countmap = (uint32 *)calloc (65536, sizeof(*countmap));
1880 if (!countmap)
1881   {
1882     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1883              __func__, __FILE__, __LINE__);
1884 #if defined(USE_BACKTRACE)
1885 # ifdef SIGUSR2
1886     (void)raise(SIGUSR2);
1887     /*NOTREACHED*/ /* unreachable */
1888 # endif /* ifdef SIGUSR2 */
1889 #endif /* if defined(USE_BACKTRACE) */
1890     abort();
1891   }
1892 recbuf = (uint8 *)malloc (65536);
1893 if (!recbuf)
1894   {
1895     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1896              __func__, __FILE__, __LINE__);
1897 #if defined(USE_BACKTRACE)
1898 # ifdef SIGUSR2
1899     (void)raise(SIGUSR2);
1900     /*NOTREACHED*/ /* unreachable */
1901 # endif /* ifdef SIGUSR2 */
1902 #endif /* if defined(USE_BACKTRACE) */
1903     abort();
1904   }
1905 tape_size = (t_addr)sim_fsize (uptr->fileref);
1906 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: tape_size: %" T_ADDR_FMT "u\n", tape_size);
1907 for (objc = 0, sizec = 0, tpos = 0;; ) {
1908     sim_fseek (uptr->fileref, tpos, SEEK_SET);
1909     i = sim_fread (&bc, sizeof (t_tpclnt), 1, uptr->fileref);
1910     if (i == 0)     /* past or at eof? */
1911         break;
1912     if (countmap[bc] == 0)
1913         sizec++;
1914     ++countmap[bc];
1915     if (map && (objc < mapsize))
1916         map[objc] = tpos;
1917     if (bc) {
1918         sim_debug (MTSE_DBG_STR, dptr, "tpc_map: %d byte count at pos: %" T_ADDR_FMT "u\n", bc, tpos);
1919         if (sim_deb && (dptr->dctrl & MTSE_DBG_STR)) {
1920             (void)sim_fread (recbuf, 1, bc, uptr->fileref);
1921             sim_data_trace(dptr, uptr, ((dptr->dctrl & MTSE_DBG_DAT) ? recbuf : NULL), "", bc, "Data Record", MTSE_DBG_STR);
1922             }
1923         }
1924     else
1925         sim_debug (MTSE_DBG_STR, dptr, "tpc_map: tape mark at pos: %" T_ADDR_FMT "u\n", tpos);
1926     objc++;
1927     tpos = tpos + ((bc + 1) & ~1) + sizeof (t_tpclnt);
1928     if ((bc == 0) && (last_bc == 0)) {  /* double tape mark? */
1929         had_double_tape_mark = objc;
1930         leot = tpos;
1931         }
1932     last_bc = bc;
1933     }
1934 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: objc: %u, different record sizes: %u\n", objc, sizec);
1935 for (i=0; i<65535; i++) {
1936     if (countmap[i]) {
1937         if (i == 0)
1938             sim_debug (MTSE_DBG_STR, dptr, "tpc_map: summary - %u tape marks\n", countmap[i]);
1939         else
1940             sim_debug (MTSE_DBG_STR, dptr, "tpc_map: summary - %u %d byte record%s\n", countmap[i], (int)i, (countmap[i] > 1) ? "s" : "");
1941         }
1942     }
1943 if (((last_bc != 0xffff) &&
1944      (tpos > tape_size) &&
1945      (!had_double_tape_mark)) || //-V686
1946     (!had_double_tape_mark) ||
1947     ((objc == countmap[0]) &&
1948      (countmap[0] != 2))) {     /* Unreasonable format? */
1949     if (last_bc != 0xffff)
1950         sim_debug (MTSE_DBG_STR, dptr, "tpc_map: ERROR unexpected EOT byte count: %d\n", last_bc);
1951     if (tpos > tape_size)
1952         sim_debug (MTSE_DBG_STR, dptr, "tpc_map: ERROR next record position %" T_ADDR_FMT "u beyond EOT: %" T_ADDR_FMT "u\n", tpos, tape_size);
1953     if (objc == countmap[0])
1954         sim_debug (MTSE_DBG_STR, dptr, "tpc_map: ERROR tape cnly contains tape marks\n");
1955     FREE (countmap);
1956     FREE (recbuf);
1957     return 0;
1958     }
1959 
1960 if ((last_bc != 0xffff) && (tpos > tape_size)) {
1961     sim_debug (MTSE_DBG_STR, dptr, "tpc_map: WARNING unexpected EOT byte count: %d, double tape mark before %" T_ADDR_FMT "u provides logical EOT\n", last_bc, leot);
1962     objc = had_double_tape_mark;
1963     tpos = leot;
1964     }
1965 if (map)
1966     map[objc] = tpos;
1967 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: OK objc: %d\n", objc);
1968 FREE (countmap);
1969 FREE (recbuf);
1970 return objc;
1971 }
1972 
1973 /* Check the basic structure of a SIMH format tape image */
1974 
1975 static t_stat sim_tape_simh_check (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1976 {
1977 return SCPE_OK;
1978 }
1979 
1980 /* Check the basic structure of a E11 format tape image */
1981 
1982 static t_stat sim_tape_e11_check (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1983 {
1984 return SCPE_OK;
1985 }
1986 
1987 /* Find the preceding record in a TPC file */
1988 
1989 static t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map)
     /* [previous][next][first][last][top][bottom][index][help] */
1990 {
1991 uint32 lo, hi, p;
1992 
1993 if (map == NULL)
1994     return 0;
1995 lo = 0;
1996 hi = uptr->hwmark - 1;
1997 do {
1998     p = (lo + hi) >> 1;
1999     if (uptr->pos == map[p])
2000         return ((p == 0)? map[p]: map[p - 1]);
2001     else if (uptr->pos < map[p])
2002         hi = p - 1;
2003     else lo = p + 1;
2004     }
2005 while (lo <= hi);
2006 return ((p == 0)? map[p]: map[p - 1]);
2007 }
2008 
2009 /* Set tape capacity */
2010 
2011 t_stat sim_tape_set_capac (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
2012 {
2013 t_addr cap;
2014 t_stat r;
2015 
2016 if ((cptr == NULL) || (*cptr == 0))
2017     return SCPE_ARG;
2018 if (uptr->flags & UNIT_ATT)
2019     return SCPE_ALATT;
2020 cap = (t_addr) get_uint (cptr, 10, sim_taddr_64? 2000000: 2000, &r);
2021 if (r != SCPE_OK)
2022     return SCPE_ARG;
2023 uptr->capac = cap * ((t_addr) 1000000);
2024 return SCPE_OK;
2025 }
2026 
2027 /* Show tape capacity */
2028 
2029 t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
2030 {
2031 if (uptr->capac) {
2032     if (uptr->capac >= (t_addr) 1000000)
2033         fprintf (st, "capacity : %luMB", (unsigned long)(uptr->capac / ((t_addr) 1000000)));
2034     else {
2035         if (uptr->capac >= (t_addr) 1000)
2036             fprintf (st, "capacity : %luKB", (unsigned long)(uptr->capac / ((t_addr) 1000)));
2037         else
2038             fprintf (st, "capacity : %luB", (unsigned long)uptr->capac);
2039         }
2040     }
2041 else
2042     fprintf (st, "capacity : unlimited");
2043 return SCPE_OK;
2044 }
2045 
2046 /* Set the tape density.
2047 
2048    Set the density of the specified tape unit either to the value supplied or to
2049    the value represented by the supplied character string.
2050 
2051    If "desc" is NULL, then "val" must be set to one of the MT_DENS_* constants
2052    in sim_tape.h other than MT_DENS_NONE; the supplied value is used as the tape
2053    density, and the character string is ignored.  Otherwise, "desc" must point
2054    at an int32 value containing a set of allowed densities constructed as a
2055    bitwise OR of the appropriate MT_*_VALID values.  In this case, the string
2056    pointed to by "cptr" will be parsed for a decimal value corresponding to the
2057    desired density in bits per inch and validated against the set of allowed
2058    values.
2059 
2060    In either case, SCPE_ARG is returned if the density setting is not valid or
2061    allowed.  If the setting is OK, the new density is set into the unit
2062    structure, and SCPE_OK is returned.
2063 */
2064 
2065 t_stat sim_tape_set_dens (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
2066 {
2067 uint32 density, new_bpi;
2068 t_stat result = SCPE_OK;
2069 
2070 if (uptr == NULL)                                               /* if the unit pointer is null */
2071     return SCPE_IERR;                                           /*   then the caller has screwed up */
2072 
2073 else if (desc == NULL)                                          /* otherwise if a validation set was not supplied */
2074     if (val > 0 && val < (int32) BPI_COUNT)                     /*   then if a valid density code was supplied */
2075         uptr->dynflags = (uptr->dynflags & ~MTVF_DENS_MASK)     /*     then insert the code */
2076                            | (val << UNIT_V_DF_TAPE);           /*       in the unit flags */
2077     else                                                        /*   otherwise the code is invalid */
2078         return SCPE_ARG;                                        /*     so report a bad argument */
2079 
2080 else {                                                          /* otherwise a validation set was supplied */
2081     if (cptr == NULL || *cptr == 0)                             /*   but if no value is present */
2082         return SCPE_MISVAL;                                     /*     then report a missing value */
2083 
2084     new_bpi = (uint32) get_uint (cptr, 10, UINT_MAX, &result);  /* convert the string value */
2085 
2086     if (result != SCPE_OK)                                      /* if the conversion failed */
2087         return SCPE_ARG;                                        /*   then report a bad argument */
2088 
2089     else for (density = 0; density < BPI_COUNT; density++)      /* otherwise validate the density */
2090         if (new_bpi == bpi [density]                            /* if it matches a value in the list */
2091           && ((1 << density) & *(const int32 *) desc)) {        /*   and it's an allowed value */
2092             uptr->dynflags = (uptr->dynflags & ~MTVF_DENS_MASK) /*     then store the index of the value */
2093                                | density << UNIT_V_DF_TAPE;     /*       in the unit flags */
2094             return SCPE_OK;                                     /*         and return success */
2095             }
2096 
2097     result = SCPE_ARG;                                          /* if no match, then report a bad argument */
2098     }
2099 
2100 return result;                                                  /* return the result of the operation */
2101 }
2102 
2103 /* Show the tape density */
2104 
2105 t_stat sim_tape_show_dens (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
2106 {
2107 uint32 tape_density;
2108 
2109 if (uptr == NULL)                                       /* if the unit pointer is null */
2110     return SCPE_IERR;                                   /*   then the caller has screwed up */
2111 
2112 else {                                                  /* otherwise get the density */
2113     tape_density = bpi [MT_DENS (uptr->dynflags)];      /*   of the tape from the unit flags */
2114 
2115     if (tape_density)                                   /* if it's set */
2116         fprintf (st, "density=%lu bpi",
2117             (unsigned long)tape_density);               /*   then report it */
2118     else                                                /* otherwise */
2119         fprintf (st, "density not set");                /*   it was never set by the caller */
2120     }
2121 
2122 return SCPE_OK;
2123 }

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