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

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