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\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\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\n\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\n\n", dptr->name, (unsigned long)i);
 290         }
 291     else
 292         fprintf (st, "  sim> ATTACH {switches} %s tapefile\n\n", dptr->name);
 293     }
 294 else
 295     fprintf (st, "  sim> ATTACH {switches} %s tapefile\n\n", dptr->name);
 296 fprintf (st, "Attach command switches\n");
 297 fprintf (st, "    -R          Attach Read Only.\n");
 298 fprintf (st, "    -E          Must Exist (if not specified an attempt to create the indicated\n");
 299 fprintf (st, "                virtual tape will be attempted).\n");
 300 fprintf (st, "    -F          Open the indicated tape container in a specific format (default\n");
 301 fprintf (st, "                is SIMH, alternatives are E11, TPC and P7B)\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\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\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\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             *bc = buffer [--bufcntr];                   /* store the metadata marker value */
 686 
 687             uptr->pos = uptr->pos - sizeof (t_mtrlnt);  /* backspace over the marker */
 688 
 689             if (*bc == MTR_TMK) {                       /* if the marker is a tape mark */
 690                 r = MTSE_TMK;                           /*   then quit with tape mark status */
 691                 break;
 692                 }
 693 
 694             else if (*bc == MTR_GAP)                    /* otherwise if the marker is a full gap */
 695                 runaway_counter -= sizeof_gap;          /*   then decrement the gap counter */
 696 
 697             else if ((*bc & MTR_M_RHGAP) == MTR_RHGAP           /* otherwise if the marker */
 698               || *bc == MTR_RRGAP) {                            /*   is a half gap */
 699                 uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2;  /*     then position forward to resync */
 700                 bufcntr = 0;                                    /* mark the buffer as invalid to force a read */
 701 
 702                 *bc = MTR_GAP;                                  /* reset the marker */
 703                 runaway_counter -= sizeof_gap / 2;              /*   and decrement the gap counter */
 704                 }
 705 
 706             else {                                              /* otherwise it's a record marker */
 707                 sbc = MTR_L (*bc);                              /* extract the record length */
 708                 uptr->pos = uptr->pos - sizeof (t_mtrlnt)       /* position to the start */
 709                   - (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc);   /*   of the record */
 710                 (void)sim_fseek (uptr->fileref,                       /* seek to the data area */
 711                            uptr->pos + sizeof (t_mtrlnt), SEEK_SET);
 712                 }
 713             }
 714         while (*bc == MTR_GAP && runaway_counter > 0);  /* continue until data or runaway occurs */
 715 
 716         if (r == MTSE_OK && runaway_counter <= 0)       /* if a tape runaway occurred */
 717             r = MTSE_RUNAWAY;                           /*   then report it */
 718 
 719         break;                                          /* otherwise the operation succeeded */
 720 
 721     case MTUF_F_TPC:
 722         ppos = sim_tape_tpc_fnd (uptr, (t_addr *) uptr->filebuf); /* find prev rec */
 723         (void)sim_fseek (uptr->fileref, ppos, SEEK_SET);      /* position */
 724         (void)sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref);
 725         *bc = tpcbc;                                    /* save rec lnt */
 726         if (ferror (uptr->fileref))                     /* error? */
 727             return sim_tape_ioerr (uptr);
 728         if (feof (uptr->fileref)) {                     /* eof? */
 729             r = MTSE_EOM;
 730             break;
 731             }
 732         uptr->pos = ppos;                               /* spc over record */
 733         if (*bc == MTR_TMK) {                           /* tape mark? */
 734             r = MTSE_TMK;
 735             break;
 736             }
 737         (void)sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET);
 738         break;
 739 
 740     case MTUF_F_P7B:
 741         for (sbc = 1, all_eof = 1; (t_addr) sbc <= uptr->pos ; sbc++) {
 742             (void)sim_fseek (uptr->fileref, uptr->pos - sbc, SEEK_SET);
 743             (void)sim_fread (&c, sizeof (uint8), 1, uptr->fileref);
 744             if (ferror (uptr->fileref))                 /* error? */
 745                 return sim_tape_ioerr (uptr);
 746             if (feof (uptr->fileref)) {                 /* eof? */
 747                 r = MTSE_EOM;
 748                 break;
 749                 }
 750             if ((c & P7B_DPAR) != P7B_EOF)
 751                 all_eof = 0;
 752             if (c & P7B_SOR)                            /* start of record? */
 753                 break;
 754             }
 755         uptr->pos = uptr->pos - sbc;                    /* update position */
 756         *bc = sbc;                                      /* save rec lnt */
 757         (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */
 758         if (all_eof)                                    /* tape mark? */
 759             r = MTSE_TMK;
 760         break;
 761 
 762     default:
 763         return MTSE_FMT;
 764         }
 765 sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %lld, lnt: %lld, pos: %" T_ADDR_FMT "u\n",
 766            (long long)r, (long long)*bc, uptr->pos);
 767 return r;
 768 }
 769 
 770 /* Read record forward
 771 
 772    Inputs:
 773         uptr    =       pointer to tape unit
 774         buf     =       pointer to buffer
 775         bc      =       pointer to returned record length
 776         max     =       maximum record size
 777    Outputs:
 778         status  =       operation status
 779 
 780    exit condition       position
 781 
 782    unit unattached      unchanged
 783    read error           unchanged, PNU set
 784    end of file/medium   unchanged, PNU set
 785    invalid record       unchanged, PNU set
 786    tape mark            updated
 787    data record          updated
 788    data record error    updated
 789 */
 790 
 791 t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max)
     /* [previous][next][first][last][top][bottom][index][help] */
 792 {
 793 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
 794 uint32 f = MT_GET_FMT (uptr);
 795 t_mtrlnt i, tbc, rbc;
 796 t_addr opos;
 797 t_stat st;
 798 
 799 if (ctx == NULL)                                        /* if not properly attached? */
 800     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
 801 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecf(unit=%d, buf=%p, max=%lld)\n",
 802            (int)(uptr-ctx->dptr->units), buf, (long long)max);
 803 
 804 opos = uptr->pos;                                       /* old position */
 805 if (MTSE_OK != (st = sim_tape_rdlntf (uptr, &tbc)))     /* read rec lnt */
 806     return st;
 807 *bc = rbc = MTR_L (tbc);                                /* strip error flag */
 808 if (rbc > max) {                                        /* rec out of range? */
 809     MT_SET_PNU (uptr);
 810     uptr->pos = opos;
 811     return MTSE_INVRL;
 812     }
 813 i = (t_mtrlnt)sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */
 814 if (ferror (uptr->fileref)) {                           /* error? */
 815     MT_SET_PNU (uptr);
 816     uptr->pos = opos;
 817     return sim_tape_ioerr (uptr);
 818     }
 819 for ( ; i < rbc; i++)                                   /* fill with 0's */
 820     buf[i] = 0;
 821 if (f == MTUF_F_P7B)                                    /* p7b? strip SOR */
 822     buf[0] = buf[0] & P7B_DPAR;
 823 sim_tape_data_trace(uptr, buf, rbc, "Record Read", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
 824 return (MTR_F (tbc)? MTSE_RECE: MTSE_OK);
 825 }
 826 
 827 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] */
 828 {
 829 t_stat r = SCPE_OK;
 830     r = sim_tape_rdrecf (uptr, buf, bc, max);
 831 return r;
 832 }
 833 
 834 /* Read record reverse
 835 
 836    Inputs:
 837         uptr    =       pointer to tape unit
 838         buf     =       pointer to buffer
 839         bc      =       pointer to returned record length
 840         max     =       maximum record size
 841    Outputs:
 842         status  =       operation status
 843 
 844    exit condition       position
 845 
 846    unit unattached      unchanged
 847    read error           unchanged
 848    end of file          unchanged
 849    end of medium        updated
 850    invalid record       unchanged
 851    tape mark            updated
 852    data record          updated
 853    data record error    updated
 854 */
 855 
 856 t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max)
     /* [previous][next][first][last][top][bottom][index][help] */
 857 {
 858 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
 859 uint32 f = MT_GET_FMT (uptr);
 860 t_mtrlnt i, rbc, tbc;
 861 t_stat st;
 862 
 863 if (ctx == NULL)                                        /* if not properly attached? */
 864     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
 865 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecr(unit=%d, buf=%p, max=%lld)\n",
 866            (int)(uptr-ctx->dptr->units), buf, (long long)max);
 867 
 868 if (MTSE_OK != (st = sim_tape_rdlntr (uptr, &tbc)))     /* read rec lnt */
 869     return st;
 870 *bc = rbc = MTR_L (tbc);                                /* strip error flag */
 871 if (rbc > max)                                          /* rec out of range? */
 872     return MTSE_INVRL;
 873 i = (t_mtrlnt)sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */
 874 if (ferror (uptr->fileref))                             /* error? */
 875     return sim_tape_ioerr (uptr);
 876 for ( ; i < rbc; i++)                                   /* fill with 0's */
 877     buf[i] = 0;
 878 if (f == MTUF_F_P7B)                                    /* p7b? strip SOR */
 879     buf[0] = buf[0] & P7B_DPAR;
 880 sim_tape_data_trace(uptr, buf, rbc, "Record Read Reverse", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
 881 return (MTR_F (tbc)? MTSE_RECE: MTSE_OK);
 882 }
 883 
 884 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] */
 885 {
 886 t_stat r = SCPE_OK;
 887     r = sim_tape_rdrecr (uptr, buf, bc, max);
 888 return r;
 889 }
 890 
 891 /* Write record forward
 892 
 893    Inputs:
 894         uptr    =       pointer to tape unit
 895         buf     =       pointer to buffer
 896         bc      =       record length
 897    Outputs:
 898         status  =       operation status
 899 
 900    exit condition       position
 901 
 902    unit unattached      unchanged
 903    write protect        unchanged
 904    write error          unchanged, PNU set
 905    data record          updated
 906 */
 907 
 908 t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc)
     /* [previous][next][first][last][top][bottom][index][help] */
 909 {
 910 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
 911 uint32 f = MT_GET_FMT (uptr);
 912 t_mtrlnt sbc;
 913 
 914 if (ctx == NULL)                                        /* if not properly attached? */
 915     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
 916 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrrecf(unit=%d, buf=%p, bc=%lld)\n",
 917            (int)(uptr-ctx->dptr->units), buf, (long long)bc);
 918 
 919 sim_tape_data_trace(uptr, buf, bc, "Record Write", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
 920 MT_CLR_PNU (uptr);
 921 sbc = MTR_L (bc);
 922 if ((uptr->flags & UNIT_ATT) == 0)                      /* not attached? */
 923     return MTSE_UNATT;
 924 if (sim_tape_wrp (uptr))                                /* write prot? */
 925     return MTSE_WRP;
 926 if (sbc == 0)                                           /* nothing to do? */
 927     return MTSE_OK;
 928 (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);         /* set pos */
 929 switch (f) {                                            /* case on format */
 930 
 931     case MTUF_F_STD:                                    /* standard */
 932         sbc = MTR_L ((bc + 1) & ~1);                    /* pad odd length */
 933     /*FALLTHRU*/ /* fall through */ /* fallthrough */
 934     case MTUF_F_E11:                                    /* E11 */
 935         (void)sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref);
 936         (void)sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref);
 937         (void)sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref);
 938         if (ferror (uptr->fileref)) {                   /* error? */
 939             MT_SET_PNU (uptr);
 940             return sim_tape_ioerr (uptr);
 941             }
 942         uptr->pos = uptr->pos + sbc + (2 * sizeof (t_mtrlnt));  /* move tape */
 943         break;
 944 
 945     case MTUF_F_P7B:                                    /* Pierce 7B */
 946         buf[0] = buf[0] | P7B_SOR;                      /* mark start of rec */
 947         (void)sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref);
 948         (void)sim_fwrite (buf, sizeof (uint8), 1, uptr->fileref); /* delimit rec */
 949         if (ferror (uptr->fileref)) {                   /* error? */
 950             MT_SET_PNU (uptr);
 951             return sim_tape_ioerr (uptr);
 952             }
 953         uptr->pos = uptr->pos + sbc;                    /* move tape */
 954         break;
 955         }
 956 sim_tape_data_trace(uptr, buf, sbc, "Record Written", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
 957 return MTSE_OK;
 958 }
 959 
 960 t_stat sim_tape_wrrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt bc, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
 961 {
 962 t_stat r = SCPE_OK;
 963     r = sim_tape_wrrecf (uptr, buf, bc);
 964 return r;
 965 }
 966 
 967 /* Write metadata forward (internal routine) */
 968 
 969 static t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat)
     /* [previous][next][first][last][top][bottom][index][help] */
 970 {
 971 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
 972 
 973 MT_CLR_PNU (uptr);
 974 if ((uptr->flags & UNIT_ATT) == 0)                      /* not attached? */
 975     return MTSE_UNATT;
 976 if (ctx == NULL)                                        /* if not properly attached? */
 977     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
 978 if (sim_tape_wrp (uptr))                                /* write prot? */
 979     return MTSE_WRP;
 980 (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);         /* set pos */
 981 (void)sim_fwrite (&dat, sizeof (uint32_t), 1, uptr->fileref);
 982 if (ferror (uptr->fileref)) {                           /* error? */
 983     MT_SET_PNU (uptr);
 984     return sim_tape_ioerr (uptr);
 985     }
 986 sim_debug (MTSE_DBG_STR, ctx->dptr, "wr_lnt: lnt: %lld, pos: %" T_ADDR_FMT "u\n",
 987            (long long)dat, uptr->pos);
 988 uptr->pos = uptr->pos + sizeof (uint32_t);              /* move tape */
 989 return MTSE_OK;
 990 }
 991 
 992 /* Write tape mark */
 993 
 994 t_stat sim_tape_wrtmk (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 995 {
 996 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
 997 
 998 if (ctx == NULL)                                        /* if not properly attached? */
 999     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1000 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrtmk(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1001 if (MT_GET_FMT (uptr) == MTUF_F_P7B) {                  /* P7B? */
1002     uint8 buf = P7B_EOF;                                /* eof mark */
1003     return sim_tape_wrrecf (uptr, &buf, 1);             /* write char */
1004     }
1005 return sim_tape_wrdata (uptr, MTR_TMK);
1006 }
1007 
1008 t_stat sim_tape_wrtmk_a (UNIT *uptr, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1009 {
1010 t_stat r = MTSE_OK;
1011     r = sim_tape_wrtmk (uptr);
1012 return r;
1013 }
1014 
1015 /* Write end of medium */
1016 
1017 t_stat sim_tape_wreom (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1018 {
1019 t_stat result;
1020 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1021 
1022 if (ctx == NULL)                                        /* if not properly attached? */
1023     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1024 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wreom(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1025 if (MT_GET_FMT (uptr) == MTUF_F_P7B)                    /* can't do P7B */
1026     return MTSE_FMT;
1027 
1028 result = sim_tape_wrdata (uptr, MTR_EOM);               /* write the EOM marker */
1029 
1030 uptr->pos = uptr->pos - sizeof (t_mtrlnt);              /* restore original tape position */
1031 MT_SET_PNU (uptr);                                      /* indicate that position was not updated */
1032 
1033 return result;
1034 }
1035 
1036 t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1037 {
1038 t_stat r = MTSE_OK;
1039     r = sim_tape_wreom (uptr);
1040 return r;
1041 }
1042 
1043 /* Write end of medium-rewind */
1044 
1045 t_stat sim_tape_wreomrw (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1046 {
1047 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1048 t_stat r;
1049 
1050 if (ctx == NULL)                                        /* if not properly attached? */
1051     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1052 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wreomrw(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1053 if (MT_GET_FMT (uptr) == MTUF_F_P7B)                    /* cant do P7B */
1054     return MTSE_FMT;
1055 r = sim_tape_wrdata (uptr, MTR_EOM);
1056 if (r == MTSE_OK)
1057     r = sim_tape_rewind (uptr);
1058 return r;
1059 }
1060 
1061 t_stat sim_tape_wreomrw_a (UNIT *uptr, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1062 {
1063 t_stat r = MTSE_OK;
1064     r = sim_tape_wreomrw (uptr);
1065 return r;
1066 }
1067 
1068 /* Write erase gap
1069 
1070    Inputs:
1071         uptr    = pointer to tape unit
1072         gaplen  = length of gap in tenths of an inch
1073 
1074    Outputs:
1075         status  = operation status
1076 
1077    exit condition       position
1078    ------------------   ------------------
1079    unit unattached      unchanged
1080    unsupported format   unchanged
1081    write protected      unchanged
1082    read error           unchanged, PNU set
1083    write error          unchanged, PNU set
1084    gap written          updated
1085 
1086    An erase gap is represented in the tape image file by a special metadata
1087    value.  This value is chosen so that it is still recognizable even if it has
1088    been "cut in half" by a subsequent data overwrite that does not end on a
1089    metadatum-sized boundary.  In addition, a range of metadata values are
1090    reserved for detection in the reverse direction.  Erase gaps are currently
1091    supported only in SIMH (MTUF_F_STD) tape format.
1092 
1093    This implementation supports erasing gaps in the middle of a populated tape
1094    image and will always produce a valid image.  It also produces valid images
1095    when overwriting gaps with data records, with one exception: a data write
1096    that leaves only two bytes of gap remaining will produce an invalid tape.
1097    This limitation is deemed acceptable, as it is analogous to the existing
1098    limitation that data records cannot overwrite other data records without
1099    producing an invalid tape.
1100 
1101    Because SIMH tape images do not carry physical parameters (e.g., recording
1102    density), overwriting a tape image file containing gap metadata is
1103    problematic if the density setting is not the same as that used during
1104    recording.  There is no way to establish a gap of a certain length
1105    unequivocally in an image file, so this implementation establishes a gap of a
1106    certain number of bytes that reflect the desired gap length at the tape
1107    density in bits per inch used during writing.
1108 
1109    To write an erase gap, the implementation uses one of two approaches,
1110    depending on whether or not the current tape position is at EOM.  Erasing at
1111    EOM presents no special difficulties; gap metadata markers are written for
1112    the prescribed number of bytes.  If the tape is not at EOM, then erasing must
1113    take into account the existing record structure to ensure that a valid tape
1114    image is maintained.
1115 
1116    The general approach is to erase for the nominal number of bytes but to
1117    increase that length, if necessary, to ensure that a partially overwritten
1118    data record at the end of the gap can be altered to maintain validity.
1119    Because the smallest legal tape record requires space for two metadata
1120    markers plus two data bytes, an erasure that would leave less than that
1121    is increased to consume the entire record.  Otherwise, the final record is
1122    truncated appropriately by rewriting the leading and trailing length words
1123    appropriately.
1124 
1125    When reading in either direction, gap metadata markers are ignored (skipped)
1126    until a record length header, EOF marker, EOM marker, or physical EOF is
1127    encountered.  Thus, tape images containing gap metadata are transparent to
1128    the calling simulator (unless tape runaway support is enabled -- see the
1129    notes at "sim_tape_rdlntf" for details).
1130 
1131    The permissibility of data record lengths that are not multiples of the
1132    metadatum size presents a difficulty when reading.  If such an "odd length"
1133    record is written over a gap, half of a metadata marker will exist
1134    immediately after the trailing record length.
1135 
1136    This condition is detected when reading forward by the appearance of a
1137    "reversed" marker.  The value appears reversed because the value is made up
1138    of half of one marker and half of the next.  This is handled by seeking
1139    forward two bytes to resync (the stipulation above that the overwrite cannot
1140    leave only two bytes of gap means that at least one "whole" metadata marker
1141    will follow).  Reading in reverse presents a more complex problem, because
1142    half of the marker is from the preceding trailing record length marker and
1143    therefore could be any of a range of values.  However, that range is
1144    restricted by the SIMH tape specification requirement that record length
1145    metadata values must have bits 30:24 set to zero.  This allows unambiguous
1146    detection of the condition.
1147 
1148    The value chosen for gap metadata and the values reserved for "half-gap"
1149    detection are:
1150 
1151      0xFFFFFFFE            - primary gap value
1152      0xFFFEFFFF            - reserved (indicates half-gap in forward reads)
1153      0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads)
1154      0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads)
1155 
1156    If the tape density has been set via a previous sim_tape_set_dens call, and
1157    the tape format is set to SIMH format, then this routine will write a gap of
1158    the appropriate size.  If the density has not been set, then no action will
1159    be taken, and either MTSE_IOERR or MTSE_OK status will be returned, depending
1160    on whether SIMH or another format is selected, respectively.  A simulator
1161    that calls this routine must set the density beforehand; failure to do so is
1162    an error.  However, calling while another format is enabled is OK and is
1163    treated as a no-operation.  This allows a device simulator that supports
1164    writing erase gaps to use the same code without worrying about the tape
1165    format currently selected by the user.
1166 */
1167 
1168 t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen)
     /* [previous][next][first][last][top][bottom][index][help] */
1169 {
1170 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1171 t_stat st;
1172 t_mtrlnt meta, sbc, new_len, rec_size;
1173 t_addr gap_pos = uptr->pos;
1174 uint32 file_size, marker_count, tape_density;
1175 int32 gap_needed;
1176 uint32 gap_alloc = 0;                                   /* gap currently allocated from the tape */
1177 const uint32 format = MT_GET_FMT (uptr);                /* tape format */
1178 const uint32 meta_size = sizeof (t_mtrlnt);             /* bytes per metadatum */
1179 const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2;  /* smallest data record size */
1180 
1181 if (ctx == NULL)                                        /* if not properly attached? */
1182     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1183 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%u)\n", (int)(uptr-ctx->dptr->units), gaplen);
1184 
1185 MT_CLR_PNU (uptr);
1186 
1187 if ((uptr->flags & UNIT_ATT) == 0)                      /* if the unit is not attached */
1188     return MTSE_UNATT;                                  /*   then we cannot proceed */
1189 
1190 else if (sim_tape_wrp (uptr))                           /* otherwise if the unit is write protected */
1191     return MTSE_WRP;                                    /*   then we cannot write */
1192 
1193 tape_density = bpi [MT_DENS (uptr->dynflags)];          /* get the density of the tape */
1194 
1195 if (format != MTUF_F_STD)                               /* if erase gaps aren't supported by the format */
1196     return MTSE_OK;                                     /*   then take no action */
1197 else if (tape_density == 0)                             /* otherwise if the density is not set */
1198     return MTSE_IOERR;                                  /*   then report an I/O error */
1199 else                                                    /* otherwise */
1200     gap_needed = (gaplen * tape_density) / 10;          /*   determine the gap size needed in bytes */
1201 
1202 file_size = sim_fsize (uptr->fileref);                  /* get file size */
1203 sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);         /* position tape */
1204 
1205 /* Read tape records and allocate to gap until amount required is consumed.
1206 
1207    Read next metadatum from tape:
1208     - EOF or EOM: allocate remainder of bytes needed.
1209     - TMK or GAP: allocate sizeof(metadatum) bytes.
1210     - Reverse GAP: allocate sizeof(metadatum) / 2 bytes.
1211     - Data record: see below.
1212 
1213    Loop until bytes needed = 0.
1214 */
1215 
1216 do {
1217     (void)sim_fread (&meta, meta_size, 1, uptr->fileref);     /* read metadatum */
1218 
1219     if (ferror (uptr->fileref)) {                       /* read error? */
1220         uptr->pos = gap_pos;                            /* restore original position */
1221         MT_SET_PNU (uptr);                              /* position not updated */
1222         return sim_tape_ioerr (uptr);                   /* translate error */
1223         }
1224     else
1225         uptr->pos = uptr->pos + meta_size;              /* move tape over datum */
1226 
1227     if (feof (uptr->fileref) || (meta == MTR_EOM)) {    /* at eof or eom? */
1228         gap_alloc = gap_alloc + gap_needed;             /* allocate remainder */
1229         gap_needed = 0;
1230         }
1231 
1232     else if ((meta == MTR_GAP) || (meta == MTR_TMK)) {  /* gap or tape mark? */
1233         gap_alloc = gap_alloc + meta_size;              /* allocate marker space */
1234         gap_needed = gap_needed - meta_size;            /* reduce requirement */
1235         }
1236 
1237     else if (meta == MTR_FHGAP) {                       /* half gap? */
1238         uptr->pos = uptr->pos - meta_size / 2;          /* backup to resync */
1239         sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */
1240         gap_alloc = gap_alloc + meta_size / 2;          /* allocate marker space */
1241         gap_needed = gap_needed - meta_size / 2;        /* reduce requirement */
1242         }
1243 
1244     else if (uptr->pos +
1245              MTR_L (meta) + meta_size > file_size) {    /* rec len out of range? */
1246         gap_alloc = gap_alloc + gap_needed;             /* presume overwritten tape */
1247         gap_needed = 0;                                 /* allocate remainder */
1248         }
1249 
1250 /* Allocate a data record:
1251     - Determine record size in bytes (including metadata)
1252     - If record size - bytes needed < smallest allowed record size,
1253       allocate entire record to gap, else allocate needed amount and
1254       truncate data record to reflect remainder.
1255 */
1256     else {                                              /* data record */
1257         sbc = MTR_L (meta);                             /* get record data length */
1258         rec_size = ((sbc + 1) & ~1) + meta_size * 2;    /* overall size in bytes */
1259 
1260         if (rec_size < gap_needed + min_rec_size) {         /* rec too small? */
1261             uptr->pos = uptr->pos - meta_size + rec_size;   /* position past record */
1262             sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* move tape */
1263             gap_alloc = gap_alloc + rec_size;               /* allocate record */
1264             gap_needed = gap_needed - rec_size;             /* reduce requirement */
1265             }
1266 
1267         else {                                              /* record size OK */
1268             uptr->pos = uptr->pos - meta_size + gap_needed; /* position to end of gap */
1269             new_len = MTR_F (meta) | (sbc - gap_needed);    /* truncate to new len */
1270             st = sim_tape_wrdata (uptr, new_len);           /* write new rec len */
1271 
1272             if (st != MTSE_OK) {                            /* write OK? */
1273                 uptr->pos = gap_pos;                        /* restore orig pos */
1274                 return st;                                  /* PNU was set by wrdata */
1275                 }
1276 
1277             uptr->pos = uptr->pos + sbc - gap_needed;       /* position to end of data */
1278             st = sim_tape_wrdata (uptr, new_len);           /* write new rec len */
1279 
1280             if (st != MTSE_OK) {                            /* write OK? */
1281                 uptr->pos = gap_pos;                        /* restore orig pos */
1282                 return st;                                  /* PNU was set by wrdata */
1283                 }
1284 
1285             gap_alloc = gap_alloc + gap_needed;             /* allocate remainder */
1286             gap_needed = 0;
1287             }
1288         }
1289     }
1290 while (gap_needed > 0);
1291 
1292 uptr->pos = gap_pos;                                    /* reposition to gap start */
1293 
1294 if (gap_alloc & (meta_size - 1)) {                      /* gap size "odd?" */
1295     st = sim_tape_wrdata (uptr, MTR_FHGAP);             /* write half gap marker */
1296     if (st != MTSE_OK) {                                /* write OK? */
1297         uptr->pos = gap_pos;                            /* restore orig pos */
1298         return st;                                      /* PNU was set by wrdata */
1299         }
1300     uptr->pos = uptr->pos - meta_size / 2;              /* realign position */
1301     gap_alloc = gap_alloc - 2;                          /* decrease gap to write */
1302     }
1303 
1304 marker_count = gap_alloc / meta_size;                   /* count of gap markers */
1305 
1306 do {
1307     st = sim_tape_wrdata (uptr, MTR_GAP);               /* write gap markers */
1308     if (st != MTSE_OK) {                                /* write OK? */
1309         uptr->pos = gap_pos;                            /* restore orig pos */
1310         return st;                                      /* PNU was set by wrdata */
1311         }
1312     }
1313 while (--marker_count > 0);
1314 
1315 return MTSE_OK;
1316 }
1317 
1318 t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1319 {
1320 t_stat r = MTSE_OK;
1321 return r;
1322 }
1323 
1324 /* Space record forward
1325 
1326    Inputs:
1327         uptr    =       pointer to tape unit
1328         bc      =       pointer to size of record skipped
1329    Outputs:
1330         status  =       operation status
1331 
1332    exit condition       position
1333 
1334    unit unattached      unchanged
1335    read error           unchanged, PNU set
1336    end of file/medium   unchanged, PNU set
1337    tape mark            updated
1338    data record          updated
1339    data record error    updated
1340 */
1341 
1342 t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc)
     /* [previous][next][first][last][top][bottom][index][help] */
1343 {
1344 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1345 t_stat st;
1346 
1347 *bc = 0;
1348 if (ctx == NULL)                                        /* if not properly attached? */
1349     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1350 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecf(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1351 
1352 st = sim_tape_rdlntf (uptr, bc);                        /* get record length */
1353 *bc = MTR_L (*bc);
1354 return st;
1355 }
1356 
1357 t_stat sim_tape_sprecf_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1358 {
1359 t_stat r = MTSE_OK;
1360     r = sim_tape_sprecf (uptr, bc);
1361 return r;
1362 }
1363 
1364 /* Space records forward
1365 
1366    Inputs:
1367         uptr    =       pointer to tape unit
1368         count   =       count of records to skip
1369         skipped =       pointer to number of records actually skipped
1370    Outputs:
1371         status  =       operation status
1372 
1373    exit condition       position
1374 
1375    unit unattached      unchanged
1376    read error           unchanged, PNU set
1377    end of file/medium   unchanged, PNU set
1378    tape mark            updated
1379    data record          updated
1380    data record error    updated
1381 */
1382 
1383 t_stat sim_tape_sprecsf (UNIT *uptr, uint32 count, uint32 *skipped)
     /* [previous][next][first][last][top][bottom][index][help] */
1384 {
1385 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1386 t_stat st;
1387 t_mtrlnt tbc;
1388 
1389 if (ctx == NULL)                                        /* if not properly attached? */
1390     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1391 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecsf(unit=%d, count=%lld)\n",
1392            (int)(uptr-ctx->dptr->units), (long long)count);
1393 
1394 *skipped = 0;
1395 while (*skipped < count) {                              /* loopo */
1396     st = sim_tape_sprecf (uptr, &tbc);                  /* spc rec */
1397     if (st != MTSE_OK)
1398         return st;
1399     *skipped = *skipped + 1;                            /* # recs skipped */
1400     }
1401 return MTSE_OK;
1402 }
1403 
1404 t_stat sim_tape_sprecsf_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1405 {
1406 t_stat r = MTSE_OK;
1407     r = sim_tape_sprecsf (uptr, count, skipped);
1408 return r;
1409 }
1410 
1411 /* Space record reverse
1412 
1413    Inputs:
1414         uptr    =       pointer to tape unit
1415         bc      =       pointer to size of records skipped
1416    Outputs:
1417         status  =       operation status
1418 
1419    exit condition       position
1420 
1421    unit unattached      unchanged
1422    beginning of tape    unchanged
1423    read error           unchanged
1424    end of file          unchanged
1425    end of medium        updated
1426    tape mark            updated
1427    data record          updated
1428 */
1429 
1430 t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc)
     /* [previous][next][first][last][top][bottom][index][help] */
1431 {
1432 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1433 t_stat st;
1434 
1435 if (ctx == NULL)                                        /* if not properly attached? */
1436     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1437 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecr(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1438 
1439 if (MT_TST_PNU (uptr)) {
1440     MT_CLR_PNU (uptr);
1441     *bc = 0;
1442     return MTSE_OK;
1443     }
1444 st = sim_tape_rdlntr (uptr, bc);                        /* get record length */
1445 *bc = MTR_L (*bc);
1446 return st;
1447 }
1448 
1449 t_stat sim_tape_sprecr_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1450 {
1451 t_stat r = MTSE_OK;
1452     r = sim_tape_sprecr (uptr, bc);
1453 return r;
1454 }
1455 
1456 /* Space records reverse
1457 
1458    Inputs:
1459         uptr    =       pointer to tape unit
1460         count   =       count of records to skip
1461         skipped =       pointer to number of records actually skipped
1462    Outputs:
1463         status  =       operation status
1464 
1465    exit condition       position
1466 
1467    unit unattached      unchanged
1468    beginning of tape    unchanged
1469    read error           unchanged
1470    end of file          unchanged
1471    end of medium        updated
1472    tape mark            updated
1473    data record          updated
1474 */
1475 
1476 t_stat sim_tape_sprecsr (UNIT *uptr, uint32 count, uint32 *skipped)
     /* [previous][next][first][last][top][bottom][index][help] */
1477 {
1478 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1479 t_stat st;
1480 t_mtrlnt tbc;
1481 
1482 if (ctx == NULL)                                        /* if not properly attached? */
1483     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1484 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecsr(unit=%d, count=%lld)\n",
1485            (int)(uptr-ctx->dptr->units), (long long)count);
1486 
1487 *skipped = 0;
1488 while (*skipped < count) {                              /* loopo */
1489     st = sim_tape_sprecr (uptr, &tbc);                  /* spc rec rev */
1490     if (st != MTSE_OK)
1491         return st;
1492     *skipped = *skipped + 1;                            /* # recs skipped */
1493     }
1494 return MTSE_OK;
1495 }
1496 
1497 t_stat sim_tape_sprecsr_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1498 {
1499 t_stat r = MTSE_OK;
1500     r = sim_tape_sprecsr (uptr, count, skipped);
1501 return r;
1502 }
1503 
1504 /* Space files forward by record
1505 
1506    Inputs:
1507         uptr    =       pointer to tape unit
1508         count   =       count of files to skip
1509         skipped =       pointer to number of files actually skipped
1510         recsskipped =   pointer to number of records skipped
1511         check_leot =    flag to detect and stop skip between two successive tape marks
1512    Outputs:
1513         status  =       operation status
1514 
1515    exit condition       position
1516 
1517    unit unattached      unchanged
1518    read error           unchanged, PNU set
1519    end of file/medium   unchanged, PNU set
1520    tape mark            updated
1521    data record          updated
1522    data record error    updated
1523 */
1524 
1525 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] */
1526 {
1527 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1528 t_stat st;
1529 t_bool last_tapemark = FALSE;
1530 uint32 filerecsskipped = 0;
1531 *skipped = *recsskipped = 0;
1532 if (ctx == NULL)                                        /* if not properly attached? */
1533     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1534 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecf(unit=%d, count=%lld, check_leot=%lld)\n",
1535            (int)(uptr-ctx->dptr->units), (long long)count, (long long)check_leot);
1536 
1537 if (check_leot) {
1538     t_mtrlnt rbc;
1539 
1540     st = sim_tape_rdlntr (uptr, &rbc);
1541     last_tapemark = (MTSE_TMK == st);
1542     if ((st == MTSE_OK) || (st == MTSE_TMK))
1543         sim_tape_rdlntf (uptr, &rbc);
1544     }
1545 *skipped = 0;     //-V1048
1546 *recsskipped = 0; //-V1048
1547 while (*skipped < count) {                              /* loopo */
1548     while (1) {
1549         st = sim_tape_sprecsf (uptr, 0x1ffffff, &filerecsskipped);/* spc recs */
1550         *recsskipped += filerecsskipped;
1551         if (st != MTSE_OK)
1552             break;
1553         }
1554     if (st == MTSE_TMK) {
1555         *skipped = *skipped + 1;                        /* # files skipped */
1556         if (check_leot && (filerecsskipped == 0) && last_tapemark) {
1557             uint32 filefileskipped;
1558             sim_tape_spfilebyrecr (uptr, 1, &filefileskipped, &filerecsskipped);
1559             *skipped = *skipped - 1;                    /* adjust # files skipped */
1560             return MTSE_LEOT;
1561             }
1562         last_tapemark = TRUE;
1563         }
1564     else
1565         return st;
1566     }
1567 return MTSE_OK;
1568 }
1569 
1570 t_stat sim_tape_spfilebyrecf_a \
1571            (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot, TAPE_PCALLBACK callback)
1572 {
1573 t_stat r = MTSE_OK;
1574     r = sim_tape_spfilebyrecf \
1575         (uptr, count, skipped, recsskipped, check_leot);
1576 return r;
1577 }
1578 
1579 /* Space files forward
1580 
1581    Inputs:
1582         uptr    =       pointer to tape unit
1583         count   =       count of files to skip
1584         skipped =       pointer to number of files actually skipped
1585    Outputs:
1586         status  =       operation status
1587 
1588    exit condition       position
1589 
1590    unit unattached      unchanged
1591    read error           unchanged, PNU set
1592    end of file/medium   unchanged, PNU set
1593    tape mark            updated
1594    data record          updated
1595    data record error    updated
1596 */
1597 
1598 t_stat sim_tape_spfilef (UNIT *uptr, uint32 count, uint32 *skipped)
     /* [previous][next][first][last][top][bottom][index][help] */
1599 {
1600 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1601 uint32 totalrecsskipped;
1602 
1603 if (ctx == NULL)                                        /* if not properly attached? */
1604     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1605 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilef(unit=%d, count=%lld)\n",
1606            (int)(uptr-ctx->dptr->units), (long long)count);
1607 
1608 return sim_tape_spfilebyrecf (uptr, count, skipped, &totalrecsskipped, FALSE);
1609 }
1610 
1611 t_stat sim_tape_spfilef_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1612 {
1613 t_stat r = MTSE_OK;
1614     r = sim_tape_spfilef (uptr, count, skipped);
1615 return r;
1616 }
1617 
1618 /* Space files reverse by record
1619 
1620    Inputs:
1621         uptr    =       pointer to tape unit
1622         count   =       count of files to skip
1623         skipped =       pointer to number of files actually skipped
1624         recsskipped =   pointer to number of records skipped
1625    Outputs:
1626         status  =       operation status
1627 
1628    exit condition       position
1629 
1630    unit unattached      unchanged
1631    beginning of tape    unchanged
1632    read error           unchanged
1633    end of file          unchanged
1634    end of medium        updated
1635    tape mark            updated
1636    data record          updated
1637 */
1638 
1639 t_stat sim_tape_spfilebyrecr (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped)
     /* [previous][next][first][last][top][bottom][index][help] */
1640 {
1641 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1642 t_stat st;
1643 uint32 filerecsskipped = 0;
1644 
1645 *skipped = 0;
1646 *recsskipped = 0;
1647 if (ctx == NULL)                                        /* if not properly attached? */
1648     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1649 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecr(unit=%d, count=%lld)\n",
1650            (int)(uptr-ctx->dptr->units), (long long)count);
1651 
1652 while (*skipped < count) {                              /* loopo */
1653     while (1) {
1654         st = sim_tape_sprecsr (uptr, 0x1ffffff, &filerecsskipped);/* spc recs rev */
1655         *recsskipped += filerecsskipped;
1656         if (st != MTSE_OK)
1657             break;
1658         }
1659     if (st == MTSE_TMK)
1660         *skipped = *skipped + 1;                        /* # files skipped */
1661     else
1662         return st;
1663     }
1664 return MTSE_OK;
1665 }
1666 
1667 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] */
1668 {
1669 t_stat r = MTSE_OK;
1670     r = sim_tape_spfilebyrecr (uptr, count, skipped, recsskipped);
1671 return r;
1672 }
1673 
1674 /* Space files reverse
1675 
1676    Inputs:
1677         uptr    =       pointer to tape unit
1678         count   =       count of files to skip
1679         skipped =       pointer to number of files actually skipped
1680    Outputs:
1681         status  =       operation status
1682 
1683    exit condition       position
1684 
1685    unit unattached      unchanged
1686    beginning of tape    unchanged
1687    read error           unchanged
1688    end of file          unchanged
1689    end of medium        updated
1690    tape mark            updated
1691    data record          updated
1692 */
1693 
1694 t_stat sim_tape_spfiler (UNIT *uptr, uint32 count, uint32 *skipped)
     /* [previous][next][first][last][top][bottom][index][help] */
1695 {
1696 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1697 uint32 totalrecsskipped;
1698 
1699 if (ctx == NULL)                                        /* if not properly attached? */
1700     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1701 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfiler(unit=%d, count=%lld)\n",
1702            (int)(uptr-ctx->dptr->units), (long long)count);
1703 
1704 return sim_tape_spfilebyrecr (uptr, count, skipped, &totalrecsskipped);
1705 }
1706 
1707 t_stat sim_tape_spfiler_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1708 {
1709 t_stat r = MTSE_OK;
1710     r = sim_tape_spfiler (uptr, count, skipped);
1711 return r;
1712 }
1713 
1714 /* Rewind tape */
1715 
1716 t_stat sim_tape_rewind (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1717 {
1718 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1719 
1720 if (uptr->flags & UNIT_ATT) {
1721     if (ctx == NULL)                                    /* if not properly attached? */
1722         return sim_messagef (SCPE_IERR, "Bad Attach\n");/*   that's a problem */
1723     sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rewind(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1724     }
1725 uptr->pos = 0;
1726 MT_CLR_PNU (uptr);
1727 return MTSE_OK;
1728 }
1729 
1730 t_stat sim_tape_rewind_a (UNIT *uptr, TAPE_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
1731 {
1732 t_stat r = MTSE_OK;
1733     r = sim_tape_rewind (uptr);
1734 return r;
1735 }
1736 
1737 /* Position Tape */
1738 
1739 t_stat sim_tape_position \
1740            (UNIT *uptr, uint32 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped)
1741 {
1742 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1743 t_stat r = MTSE_OK;
1744 
1745 if (ctx == NULL)                                        /* if not properly attached? */
1746     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1747 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_position(unit=%d, flags=0x%X, recs=%lld, files=%lld)\n",
1748            (int)(uptr-ctx->dptr->units), flags, (long long)recs, (long long)files);
1749 
1750 *recsskipped = *filesskipped = *objectsskipped = 0;
1751 if (flags & MTPOS_M_REW)
1752     r = sim_tape_rewind (uptr);
1753 if (r != MTSE_OK)
1754     return r;
1755 if (flags & MTPOS_M_OBJ) {
1756     uint32 objs = recs;
1757     uint32 skipped;
1758     uint32 objsremaining = objs;
1759 
1760     while (*objectsskipped < objs) {                       /* loopo */
1761         if (flags & MTPOS_M_REV)                        /* reverse? */
1762             r = sim_tape_sprecsr (uptr, objsremaining, &skipped);
1763         else
1764             r = sim_tape_sprecsf (uptr, objsremaining, &skipped);
1765         objsremaining = objsremaining - (skipped + ((r == MTSE_TMK) ? 1 : 0));
1766         if ((r == MTSE_TMK) || (r == MTSE_OK))
1767             *objectsskipped = *objectsskipped + skipped + ((r == MTSE_TMK) ? 1 : 0);
1768         else
1769             return r;
1770         }
1771     r = MTSE_OK;
1772     }
1773 else {
1774     uint32 fileskiprecs;
1775 
1776     if (flags & MTPOS_M_REV)                            /* reverse? */
1777         r = sim_tape_spfilebyrecr (uptr, files, filesskipped, &fileskiprecs);
1778     else
1779         r = sim_tape_spfilebyrecf (uptr, files, filesskipped, &fileskiprecs, (flags & MTPOS_M_DLE));
1780     if (r != MTSE_OK)
1781         return r;
1782     if (flags & MTPOS_M_REV)                            /* reverse? */
1783         r = sim_tape_sprecsr (uptr, recs, recsskipped);
1784     else
1785         r = sim_tape_sprecsf (uptr, recs, recsskipped);
1786     if (r == MTSE_TMK)
1787         *filesskipped = *filesskipped + 1;
1788     *objectsskipped = fileskiprecs + *filesskipped + *recsskipped;
1789     }
1790 return r;
1791 }
1792 
1793 t_stat sim_tape_position_a \
1794            (UNIT *uptr, uint32 flags, uint32 recs, uint32 *recsskipped, uint32 files,
1795             uint32 *filesskipped, uint32 *objectsskipped, TAPE_PCALLBACK callback)
1796 {
1797 t_stat r = MTSE_OK;
1798     r = sim_tape_position (uptr, flags, recs, recsskipped, files, filesskipped, objectsskipped);
1799 return r;
1800 }
1801 
1802 /* Reset tape */
1803 
1804 t_stat sim_tape_reset (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1805 {
1806 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1807 
1808 MT_CLR_PNU (uptr);
1809 if (!(uptr->flags & UNIT_ATT))                          /* attached? */
1810     return SCPE_OK;
1811 
1812 if (ctx == NULL)                                        /* if not properly attached? */
1813     return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
1814 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_reset(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1815 
1816 _sim_tape_io_flush(uptr);
1817 return SCPE_OK;
1818 }
1819 
1820 /* Test for BOT */
1821 
1822 t_bool sim_tape_bot (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1823 {
1824 uint32 f = MT_GET_FMT (uptr);
1825 
1826 return (uptr->pos <= fmts[f].bot)? TRUE: FALSE;
1827 }
1828 
1829 /* Test for end of tape */
1830 
1831 t_bool sim_tape_eot (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1832 {
1833 return (uptr->capac && (uptr->pos >= uptr->capac))? TRUE: FALSE;
1834 }
1835 
1836 /* Test for write protect */
1837 
1838 t_bool sim_tape_wrp (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1839 {
1840 return ((uptr->flags & MTUF_WRP) || (MT_GET_FMT (uptr) == MTUF_F_TPC))? TRUE: FALSE;
1841 }
1842 
1843 /* Process I/O error */
1844 
1845 static t_stat sim_tape_ioerr (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1846 {
1847 sim_printf ("%s: Magtape library I/O error: %s (Error %d)\r\n", sim_uname (uptr), xstrerror_l(errno), errno);
1848 clearerr (uptr->fileref);
1849 return MTSE_IOERR;
1850 }
1851 
1852 /* Set tape format */
1853 
1854 t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
1855 {
1856 uint32 f;
1857 
1858 if (uptr == NULL)
1859     return SCPE_IERR;
1860 if (uptr->flags & UNIT_ATT)
1861     return SCPE_ALATT;
1862 if (cptr == NULL)
1863     return SCPE_ARG;
1864 for (f = 0; f < MTUF_N_FMT; f++) {
1865     if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) {
1866         uptr->flags = (uptr->flags & ~MTUF_FMT) |
1867             (f << MTUF_V_FMT) | fmts[f].uflags;
1868         return SCPE_OK;
1869         }
1870     }
1871 return SCPE_ARG;
1872 }
1873 
1874 /* Show tape format */
1875 
1876 t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
1877 {
1878 int32 f = MT_GET_FMT (uptr);
1879 
1880 if (fmts[f].name)
1881     fprintf (st, "%s format", fmts[f].name);
1882 else fprintf (st, "invalid format");
1883 return SCPE_OK;
1884 }
1885 
1886 /* Map a TPC format tape image */
1887 
1888 static uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map, uint32 mapsize)
     /* [previous][next][first][last][top][bottom][index][help] */
1889 {
1890 t_addr tpos, leot = 0;
1891 t_addr tape_size;
1892 t_tpclnt bc, last_bc = 0xFFFF;
1893 uint32 had_double_tape_mark = 0;
1894 size_t i;
1895 uint32 objc, sizec;
1896 uint32 *countmap = NULL;
1897 uint8 *recbuf = NULL;
1898 DEVICE *dptr = find_dev_from_unit (uptr);
1899 
1900 if ((uptr == NULL) || (uptr->fileref == NULL))
1901     return 0;
1902 countmap = (uint32 *)calloc (65536, sizeof(*countmap));
1903 if (!countmap)
1904   {
1905     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1906              __func__, __FILE__, __LINE__);
1907 #if defined(USE_BACKTRACE)
1908 # if defined(SIGUSR2)
1909     (void)raise(SIGUSR2);
1910     /*NOTREACHED*/ /* unreachable */
1911 # endif /* if defined(SIGUSR2) */
1912 #endif /* if defined(USE_BACKTRACE) */
1913     abort();
1914   }
1915 recbuf = (uint8 *)malloc (65536);
1916 if (!recbuf)
1917   {
1918     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1919              __func__, __FILE__, __LINE__);
1920 #if defined(USE_BACKTRACE)
1921 # if defined(SIGUSR2)
1922     (void)raise(SIGUSR2);
1923     /*NOTREACHED*/ /* unreachable */
1924 # endif /* if defined(SIGUSR2) */
1925 #endif /* if defined(USE_BACKTRACE) */
1926     abort();
1927   }
1928 tape_size = (t_addr)sim_fsize (uptr->fileref);
1929 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: tape_size: %" T_ADDR_FMT "u\n", tape_size);
1930 for (objc = 0, sizec = 0, tpos = 0;; ) {
1931     sim_fseek (uptr->fileref, tpos, SEEK_SET);
1932     i = sim_fread (&bc, sizeof (t_tpclnt), 1, uptr->fileref);
1933     if (i == 0)     /* past or at eof? */
1934         break;
1935     if (countmap[bc] == 0)
1936         sizec++;
1937     ++countmap[bc];
1938     if (map && (objc < mapsize))
1939         map[objc] = tpos;
1940     if (bc) {
1941         sim_debug (MTSE_DBG_STR, dptr, "tpc_map: %lld byte count at pos: %" T_ADDR_FMT "u\n",
1942                    (long long)bc, tpos);
1943         if (sim_deb && (dptr->dctrl & MTSE_DBG_STR)) {
1944             (void)sim_fread (recbuf, 1, bc, uptr->fileref);
1945             sim_data_trace(dptr, uptr, ((dptr->dctrl & MTSE_DBG_DAT) ? recbuf : NULL), "", bc, "Data Record", MTSE_DBG_STR);
1946             }
1947         }
1948     else
1949         sim_debug (MTSE_DBG_STR, dptr, "tpc_map: tape mark at pos: %" T_ADDR_FMT "u\n", tpos);
1950     objc++;
1951     tpos = tpos + ((bc + 1) & ~1) + sizeof (t_tpclnt);
1952     if ((bc == 0) && (last_bc == 0)) {  /* double tape mark? */
1953         had_double_tape_mark = objc;
1954         leot = tpos;
1955         }
1956     last_bc = bc;
1957     }
1958 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: objc: %u, different record sizes: %u\n", objc, sizec);
1959 for (i=0; i<65535; i++) {
1960     if (countmap[i]) {
1961         if (i == 0)
1962             sim_debug (MTSE_DBG_STR, dptr, "tpc_map: summary - %u tape marks\n",
1963                        countmap[i]);
1964         else
1965             sim_debug (MTSE_DBG_STR, dptr, "tpc_map: summary - %u %d byte record%s\n",
1966                        countmap[i], (int)i, (countmap[i] > 1) ? "s" : "");
1967         }
1968     }
1969 if (((last_bc != 0xffff) &&
1970      (tpos > tape_size) &&
1971      (!had_double_tape_mark)) || //-V686
1972     (!had_double_tape_mark) ||
1973     ((objc == countmap[0]) &&
1974      (countmap[0] != 2))) {     /* Unreasonable format? */
1975     if (last_bc != 0xffff)
1976         sim_debug (MTSE_DBG_STR, dptr,
1977                    "tpc_map: ERROR unexpected EOT byte count: %lld\n",
1978                    (long long)last_bc);
1979     if (tpos > tape_size)
1980         sim_debug (MTSE_DBG_STR, dptr,
1981                    "tpc_map: ERROR next record position %" T_ADDR_FMT "u beyond EOT: %" T_ADDR_FMT "u\n",
1982                    tpos, tape_size);
1983     if (objc == countmap[0])
1984         sim_debug (MTSE_DBG_STR, dptr,
1985                    "tpc_map: ERROR tape cnly contains tape marks\n");
1986     FREE (countmap);
1987     FREE (recbuf);
1988     return 0;
1989     }
1990 
1991 if ((last_bc != 0xffff) && (tpos > tape_size)) {
1992     sim_debug (MTSE_DBG_STR, dptr,
1993                "tpc_map: WARNING unexpected EOT byte count: %lld, double tape mark before %" T_ADDR_FMT "u provides logical EOT\n",
1994                (long long)last_bc, leot);
1995     objc = had_double_tape_mark;
1996     tpos = leot;
1997     }
1998 if (map)
1999     map[objc] = tpos;
2000 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: OK objc: %lld\n", (long long)objc);
2001 FREE (countmap);
2002 FREE (recbuf);
2003 return objc;
2004 }
2005 
2006 /* Check the basic structure of a SIMH format tape image */
2007 
2008 static t_stat sim_tape_simh_check (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2009 {
2010 return SCPE_OK;
2011 }
2012 
2013 /* Check the basic structure of a E11 format tape image */
2014 
2015 static t_stat sim_tape_e11_check (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2016 {
2017 return SCPE_OK;
2018 }
2019 
2020 /* Find the preceding record in a TPC file */
2021 
2022 static t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map)
     /* [previous][next][first][last][top][bottom][index][help] */
2023 {
2024 uint32 lo, hi, p;
2025 
2026 if (map == NULL)
2027     return 0;
2028 lo = 0;
2029 hi = uptr->hwmark - 1;
2030 do {
2031     p = (lo + hi) >> 1;
2032     if (uptr->pos == map[p])
2033         return ((p == 0)? map[p]: map[p - 1]);
2034     else if (uptr->pos < map[p])
2035         hi = p - 1;
2036     else lo = p + 1;
2037     }
2038 while (lo <= hi);
2039 return ((p == 0)? map[p]: map[p - 1]);
2040 }
2041 
2042 /* Set tape capacity */
2043 
2044 t_stat sim_tape_set_capac (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
2045 {
2046 t_addr cap;
2047 t_stat r;
2048 
2049 if ((cptr == NULL) || (*cptr == 0))
2050     return SCPE_ARG;
2051 if (uptr->flags & UNIT_ATT)
2052     return SCPE_ALATT;
2053 cap = (t_addr) get_uint (cptr, 10, 2000000, &r);
2054 if (r != SCPE_OK)
2055     return SCPE_ARG;
2056 uptr->capac = cap * ((t_addr) 1000000);
2057 return SCPE_OK;
2058 }
2059 
2060 /* Show tape capacity */
2061 
2062 t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
2063 {
2064 if (uptr->capac) {
2065     if (uptr->capac >= (t_addr) 1000000)
2066         fprintf (st, "capacity : %luMB", (unsigned long)(uptr->capac / ((t_addr) 1000000)));
2067     else {
2068         if (uptr->capac >= (t_addr) 1000)
2069             fprintf (st, "capacity : %luKB", (unsigned long)(uptr->capac / ((t_addr) 1000)));
2070         else
2071             fprintf (st, "capacity : %luB", (unsigned long)uptr->capac);
2072         }
2073     }
2074 else
2075     fprintf (st, "capacity : unlimited");
2076 return SCPE_OK;
2077 }
2078 
2079 /* Set the tape density.
2080 
2081    Set the density of the specified tape unit either to the value supplied or to
2082    the value represented by the supplied character string.
2083 
2084    If "desc" is NULL, then "val" must be set to one of the MT_DENS_* constants
2085    in sim_tape.h other than MT_DENS_NONE; the supplied value is used as the tape
2086    density, and the character string is ignored.  Otherwise, "desc" must point
2087    at an int32 value containing a set of allowed densities constructed as a
2088    bitwise OR of the appropriate MT_*_VALID values.  In this case, the string
2089    pointed to by "cptr" will be parsed for a decimal value corresponding to the
2090    desired density in bits per inch and validated against the set of allowed
2091    values.
2092 
2093    In either case, SCPE_ARG is returned if the density setting is not valid or
2094    allowed.  If the setting is OK, the new density is set into the unit
2095    structure, and SCPE_OK is returned.
2096 */
2097 
2098 t_stat sim_tape_set_dens (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
2099 {
2100 uint32 density, new_bpi;
2101 t_stat result = SCPE_OK;
2102 
2103 if (uptr == NULL)                                               /* if the unit pointer is null */
2104     return SCPE_IERR;                                           /*   then the caller has screwed up */
2105 
2106 else if (desc == NULL)                                          /* otherwise if a validation set was not supplied */
2107     if (val > 0 && val < (int32) BPI_COUNT)                     /*   then if a valid density code was supplied */
2108         uptr->dynflags = (uptr->dynflags & ~MTVF_DENS_MASK)     /*     then insert the code */
2109                            | (val << UNIT_V_DF_TAPE);           /*       in the unit flags */
2110     else                                                        /*   otherwise the code is invalid */
2111         return SCPE_ARG;                                        /*     so report a bad argument */
2112 
2113 else {                                                          /* otherwise a validation set was supplied */
2114     if (cptr == NULL || *cptr == 0)                             /*   but if no value is present */
2115         return SCPE_MISVAL;                                     /*     then report a missing value */
2116 
2117     new_bpi = (uint32) get_uint (cptr, 10, UINT_MAX, &result);  /* convert the string value */
2118 
2119     if (result != SCPE_OK)                                      /* if the conversion failed */
2120         return SCPE_ARG;                                        /*   then report a bad argument */
2121 
2122     else for (density = 0; density < BPI_COUNT; density++)      /* otherwise validate the density */
2123         if (new_bpi == bpi [density]                            /* if it matches a value in the list */
2124           && ((1 << density) & *(const int32 *) desc)) {        /*   and it's an allowed value */
2125             uptr->dynflags = (uptr->dynflags & ~MTVF_DENS_MASK) /*     then store the index of the value */
2126                                | density << UNIT_V_DF_TAPE;     /*       in the unit flags */
2127             return SCPE_OK;                                     /*         and return success */
2128             }
2129 
2130     result = SCPE_ARG;                                          /* if no match, then report a bad argument */
2131     }
2132 
2133 return result;                                                  /* return the result of the operation */
2134 }
2135 
2136 /* Show the tape density */
2137 
2138 t_stat sim_tape_show_dens (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
2139 {
2140 uint32 tape_density;
2141 
2142 if (uptr == NULL)                                       /* if the unit pointer is null */
2143     return SCPE_IERR;                                   /*   then the caller has screwed up */
2144 
2145 else {                                                  /* otherwise get the density */
2146     tape_density = bpi [MT_DENS (uptr->dynflags)];      /*   of the tape from the unit flags */
2147 
2148     if (tape_density)                                   /* if it's set */
2149         fprintf (st, "density=%lu bpi",
2150             (unsigned long)tape_density);               /*   then report it */
2151     else                                                /* otherwise */
2152         fprintf (st, "density not set");                /*   it was never set by the caller */
2153     }
2154 
2155 return SCPE_OK;
2156 }

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