TAPE: Extensive cleanup of tape operations (gap, end of medium, format change, etc.) from Dave Bryan

256. ENHANCEMENT: Add tape runaway support to the simulator tape library.

     OBSERVATION:  The ANSI specifications for NRZI, PE, and GCR tape recording
     mandate a maximum length of 25 feet for erase gaps.  Currently, an erase
     gap of any length is ignored when reading or spacing.  To allow detection
     of non-compliant tape images, the simulator tape library is enhanced to
     halt positioning and return tape runaway status if a gap of 25 feet or more
     is encountered.

     Runaway detection is enabled by calling the tape library to set the tape
     density in bits per inch.  If this call is not made, erase gaps present in
     a tape image are effectively ignored.  Also, with the addition of a
     separate "set density" call, it is no longer necessary to supply the
     density when writing erase gaps.

     RESOLUTION:  Modify "sim_tape_rdlntf" and "sim_tape_rdlntr" (sim_tape.c) to
     detect tape runaway, and add a new MTSE_RUNAWAY status to sim_tape.h.  Add
     new "sim_tape_set_dens" and "sim_tape_show_dens" functions to set and show
     the bits per inch for a unit, respectively, and eliminate the "bpi"
     parameter to "sim_tape_wrgap" in preference to using the density
     established by a previous "sim_tape_set_dens" call.  Add named constants
     to "sim_tape.h" that specify the density.

257. ENHANCEMENT:  Improve performance when reading or spacing over erase gaps.

     OBSERVATION:  Performance when reading or spacing over erase gaps is poor,
     especially in the reverse direction.  Currently, each 4-byte gap marker is
     read individually, and in the reverse direction, each read is preceded by a
     seek to move the file pointer backward.  This combination causes stream
     cache invalidation and a physical disc access for each gap marker.  As a
     single gap consists of over 1000 markers, performance is far worse than if
     a gap was read as a block.

     RESOLUTION:  Modify "sim_tape_rdlntf" and "sim_tape_rdlntr" (sim_tape.c) to
     buffer reads of gap markers.  Using a 128-element buffer, performance
     improves about thirty-fold.

258. PROBLEM:  Writing an end-of-medium positions the tape image after the mark.

     OBSERVATION:  The "sim_tape_wreom" simulator tape library function writes
     an end-of-medium marker on the tape image.  The intent is to erase the
     remainder of the tape.  The "SIMH Magtape Representation and Handling"
     document states that the tape position is not updated by this function.
     However, the function leaves the tape positioned after the marker.

     A subsequent read would stop at the EOM marker.  However, writing a new
     marker over that one would then allow reading of the data following the EOM
     that supposedly had been erased by the original "sim_tape_wreom" call.

     CAUSE:  The tape position is updated by the internal "sim_tape_wrdata" call
     that is used to write the EOM marker, but it is not reset afterward by the
     function.

     RESOLUTION:  Modify "sim_tape_wreom" (sim_tape.c) to reset the tape
     position to point at the EOM marker before returning.  This prevents
     reading past an EOM marker, and a subsequent write will overwrite the
     marker rather than embed it between data records.

259. PROBLEM:  Reading through an erase gap in reverse may return EOM status.

     OBSERVATION:  A reverse read or spacing operation through an erase gap may
     return end-of-medium status.  Reading or spacing forward through the same
     gap works properly.

     CAUSE:  Writing an erase gap over existing records may produce a gap that
     is longer than requested.  This occurs when truncating the last record to
     be overlaid by the gap would leave a record that is shorter than the
     minimum size allowed (eight bytes for the length words plus two bytes for
     the data).  In this case, the gap is lengthened to overlay the entire
     record.  If the new gap size is not evenly divisible by four, a half-gap is
     metadata marker of value 0xFFFF added to the beginning of the gap.

     If a gap that begins with a half-gap marker is written immediately after
     a previous gap, the "seam" between gaps will contain the bytes FE FF FF FF
     ( FF FF ) FE FF FF FF....  Reading forward across this seam will yield a
     metadata value of 0xFFFEFFFF, which is recognized and handled by seeking
     two bytes back to resynchronize reading.  However, reading in reverse will
     yield the value 0xFFFFFFFF, which is interpreted as end-of-medium.

     RESOLUTION:  Modify "sim_tape_rdlntr" (sim_tape.c) to recognize 0xFFFFFFFF
     as a half-gap marker and resynchronize in response.  End of medium cannot
     occur when reading in reverse, as it is impossible to position the tape
     image beyond an EOM marker.  Therefore, any 0xFFFFFFFF value encountered
     must be a half-gap "seam" originating as above.

260. PROBLEM:  sim_tape_wrgap fails when format is changed from SIMH format.

     OBSERVATION:  The HP 2100 magnetic tape simulator supports erase gaps and
     calls sim_tape_wrgap when commanded to write a gap.  However, if a tape
     format other than SIMH format is selected, the call fails with MTSE_FMT.

     CAUSE:  Erase gaps are not supported in formats other than SIMH, but the
     call should not fail.  Instead, the call should be a "no-operation" if the
     underlying format does not support gaps.

     RESOLUTION:  Modify "sim_tape_wrgap" (sim_tape.c) to return MTSE_OK with no
     action performed if a tape format other than SIMH is selected.

261. PROBLEM:  The magnetic tape format of an attached unit may be changed.

     OBSERVATION:  The magnetic tape library supports several tape image
     formats.  The format to use may be specified either by an "ATTACH -F"
     command or by a "SET <unit> FORMAT" command.  The latter calls the
     "sim_tape_set_fmt" function, which allows the format of a file currently
     attached to be changed.  However, the format is an intrinsic property of
     the tape image file, so changing it once the file has been attached makes
     no sense.

     CAUSE:  Oversight.

     RESOLUTION:  Modify "sim_tape_set_fmt" (sim_tape.c) to return an error
     (SCPE_ALATT, "Unit already attached") if the unit is attached.
This commit is contained in:
Mark Pizzolato 2014-12-22 05:48:13 -08:00
parent d5f02124f1
commit afb5d6277d
3 changed files with 435 additions and 104 deletions

View file

@ -530,6 +530,8 @@ struct sim_unit {
#define UNIT_ATTMULT 0000001 /* Allow multiple attach commands */ #define UNIT_ATTMULT 0000001 /* Allow multiple attach commands */
#define UNIT_TM_POLL 0000002 /* TMXR Polling unit */ #define UNIT_TM_POLL 0000002 /* TMXR Polling unit */
#define UNIT_NO_FIO 0000004 /* fileref is NOT a FILE * */ #define UNIT_NO_FIO 0000004 /* fileref is NOT a FILE * */
#define UNIT_V_DF_TAPE 3 /* Bit offset for Tape Density reservation */
#define UNIT_S_DF_TAPE 3 /* Bits Reserved for Tape Density */
struct sim_bitfield { struct sim_bitfield {
char *name; /* field name */ char *name; /* field name */

View file

@ -82,6 +82,8 @@
sim_tape_show_fmt show tape format sim_tape_show_fmt show tape format
sim_tape_set_capac set tape capacity sim_tape_set_capac set tape capacity
sim_tape_show_capac show tape capacity sim_tape_show_capac show tape capacity
sim_tape_set_dens set tape density
sim_tape_show_dens show tape density
sim_tape_set_async enable asynchronous operation sim_tape_set_async enable asynchronous operation
sim_tape_clr_async disable asynchronous operation sim_tape_clr_async disable asynchronous operation
*/ */
@ -109,6 +111,17 @@ static struct sim_tape_fmt fmts[MTUF_N_FMT] = {
{ NULL, 0, 0 } { NULL, 0, 0 }
}; };
static const uint32 bpi [] = { /* tape density table, indexed by MT_DENS constants */
0, /* 0 = MT_DENS_NONE -- density not set */
200, /* 1 = MT_DENS_200 -- 200 bpi NRZI */
556, /* 2 = MT_DENS_556 -- 556 bpi NRZI */
800, /* 3 = MT_DENS_800 -- 800 bpi NRZI */
1600, /* 4 = MT_DENS_1600 -- 1600 bpi PE */
6250 /* 5 = MT_DENS_6250 -- 6250 bpi GCR */
};
#define BPI_COUNT (sizeof (bpi) / sizeof (bpi [0])) /* count of density table entries */
t_stat sim_tape_ioerr (UNIT *uptr); t_stat sim_tape_ioerr (UNIT *uptr);
t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat); t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat);
uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map); uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map);
@ -238,7 +251,7 @@ struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
ctx->io_status = sim_tape_wreomrw (uptr); ctx->io_status = sim_tape_wreomrw (uptr);
break; break;
case TOP_WGAP: case TOP_WGAP:
ctx->io_status = sim_tape_wrgap (uptr, ctx->gaplen, ctx->bpi); ctx->io_status = sim_tape_wrgap (uptr, ctx->gaplen);
break; break;
case TOP_SPRF: case TOP_SPRF:
ctx->io_status = sim_tape_sprecf (uptr, ctx->bc); ctx->io_status = sim_tape_sprecf (uptr, ctx->bc);
@ -561,15 +574,73 @@ if (sim_deb && (ctx->dptr->dctrl & reason))
Outputs: Outputs:
status = operation status status = operation status
exit condition position exit condition tape position
------------------ -------------------------------------------
unit unattached unchanged unit unattached unchanged
read error unchanged, PNU set read error unchanged, PNU set
end of file/medium unchanged, PNU set end of file/medium unchanged, PNU set
tape mark updated tape mark updated
tape runaway updated
data record updated, sim_fread will read record forward data record updated, sim_fread will read record forward
See notes at "sim_tape_wrgap" regarding erase gap implementation. This routine is called to set up a record read or spacing in the forward
direction. On return, status is MTSE_OK and the tape is positioned at the
first data byte if a record was encountered, or status is an MTSE error code
giving the reason that the operation did not succeed and the tape position is
as indicated above.
The ANSI standards for magnetic tape recording (X3.32, X3.39, and X3.54) and
the equivalent ECMA standard (ECMA-62) specify a maximum erase gap length of
25 feet (7.6 meters). While gaps of any length may be written, gaps longer
than this are non-standard and may indicate that an unrecorded or erased tape
is being read.
If the tape density has been set via a previous "sim_tape_set_dens" call,
then the length is monitored when skipping over erase gaps. If the length
reaches 25 feet, motion is terminated, and MTSE_RUNAWAY status is returned.
Runaway status is also returned if an end-of-medium marker or the physical
end of file is encountered while spacing over a gap.
If the density has not been set, then a gap of any length is skipped, and
MTSE_RUNAWAY status is never returned. In effect, erase gaps present in the
tape image file will be transparent to the caller.
Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format.
Because gaps may be partially overwritten with data records, gap metadata
must be examined marker-by-marker. To reduce the number of file read calls,
a buffer of metadata elements is used. The buffer size is initially
established at 256 elements but may be set to any size desired. To avoid a
large read for the typical case where an erase gap is not present, the first
read is of a single metadatum marker. If that is a gap marker, then
additional buffered reads are performed.
See the notes at "sim_tape_wrgap" regarding the erase gap implementation.
Implementation notes:
1. For programming convenience, erase gap processing is performed for both
SIMH standard and E11 tape formats, although the latter will never
contain erase gaps, as the "sim_tape_wrgap" call takes no action for the
E11 format.
2. The "feof" call cannot return a non-zero value on the first pass through
the loop, because the "sim_fseek" call resets the internal end-of-file
indicator. Subsequent passes only occur if an erase gap is present, so
a non-zero return indicates an EOF was seen while reading through a gap.
3. The dynamic start/stop test of the HP 3000 magnetic tape diagnostic
heavily exercises the erase gap scanning code. Sample test execution
times for various buffer sizes on a 2 GHz host platform are:
buffer size execution time
(elements) (CPU seconds)
----------- --------------
1 7200
32 783
128 237
256 203
512 186
1024 171
*/ */
t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc) t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc)
@ -580,42 +651,114 @@ t_bool all_eof;
uint32 f = MT_GET_FMT (uptr); uint32 f = MT_GET_FMT (uptr);
t_mtrlnt sbc; t_mtrlnt sbc;
t_tpclnt tpcbc; t_tpclnt tpcbc;
t_mtrlnt buffer [256]; /* local tape buffer */
uint32 bufcntr, bufcap; /* buffer counter and capacity */
int32 runaway_counter, sizeof_gap; /* bytes remaining before runaway and bytes per gap */
t_stat r = MTSE_OK; t_stat r = MTSE_OK;
MT_CLR_PNU (uptr); MT_CLR_PNU (uptr); /* clear the position-not-updated flag */
if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
return MTSE_UNATT;
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set tape pos */
switch (f) { /* switch on fmt */
case MTUF_F_STD: case MTUF_F_E11: if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */
do { return MTSE_UNATT; /* then quit with an error */
sim_fread (bc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */
sbc = MTR_L (*bc); /* save rec lnt */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set the initial tape position */
if (ferror (uptr->fileref)) { /* error? */
MT_SET_PNU (uptr); /* pos not upd */ switch (f) { /* the read method depends on the tape format */
return sim_tape_ioerr (uptr);
case MTUF_F_STD:
case MTUF_F_E11:
runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set the largest legal gap size in bytes */
if (runaway_counter == 0) { /* if tape density has not been not set */
sizeof_gap = 0; /* then disable runaway detection */
runaway_counter = INT_MAX; /* to allow gaps of any size */
} }
if (feof (uptr->fileref) || (*bc == MTR_EOM)) { /* eof or eom? */ else /* otherwise */
MT_SET_PNU (uptr); /* pos not upd */ sizeof_gap = sizeof (t_mtrlnt); /* set the size of the gap */
r = MTSE_EOM;
bufcntr = 0; /* force an initial read */
bufcap = 0; /* but of just one metadata marker */
do { /* loop until a record, gap, or error seen */
if (bufcntr == bufcap) { /* if the buffer is empty then refill it */
if (feof (uptr->fileref)) { /* if we hit the EOF while reading gaps */
if (sizeof_gap > 0) /* then if detection is enabled */
r = MTSE_RUNAWAY; /* then report a tape runaway */
else /* otherwise report the physical EOF */
r = MTSE_EOM; /* as the end-of-medium */
break; break;
} }
uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* spc over rec lnt */
if (*bc == MTR_TMK) { /* tape mark? */ else if (bufcap == 0) /* otherwise if this is the initial read */
r = MTSE_TMK; bufcap = 1; /* then start with just one marker */
else /* otherwise reset the capacity */
bufcap = sizeof (buffer) /* to the full size of the buffer */
/ sizeof (buffer [0]);
bufcap = sim_fread (buffer, /* fill the buffer */
sizeof (t_mtrlnt), /* with tape metadata */
bufcap,
uptr->fileref);
if (ferror (uptr->fileref)) { /* if a file I/O error occurred */
MT_SET_PNU (uptr); /* then set position not updated */
r = sim_tape_ioerr (uptr); /* report the error and quit */
break; break;
} }
if (*bc == MTR_FHGAP) { /* half gap? */
uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* half space fwd */ else if (bufcap == 0) { /* otherwise if nothing was read */
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* resync */ MT_SET_PNU (uptr); /* then set position not updated */
} r = MTSE_EOM; /* report the end of medium and quit */
else if (*bc != MTR_GAP)
uptr->pos = uptr->pos + sizeof (t_mtrlnt) + /* spc over record */
((f == MTUF_F_STD)? ((sbc + 1) & ~1): sbc);
}
while ((*bc == MTR_GAP) || (*bc == MTR_FHGAP));
break; break;
}
else /* otherwise reset the index */
bufcntr = 0; /* to the start of the buffer */
}
*bc = buffer [bufcntr++]; /* store the metadata marker value */
if (*bc == MTR_EOM) { /* if an end-of-medium marker is seen */
MT_SET_PNU (uptr); /* then set position not updated */
r = MTSE_EOM; /* report the end of medium and quit */
break;
}
uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* space over the marker */
if (*bc == MTR_TMK) { /* if the value is a tape mark */
r = MTSE_TMK; /* then quit with tape mark status */
break;
}
else if (*bc == MTR_GAP) /* otherwise if the value is a full gap */
runaway_counter -= sizeof_gap; /* then decrement the gap counter */
else if (*bc == MTR_FHGAP) { /* otherwise if the value if a half gap */
uptr->pos = uptr->pos - sizeof (t_mtrlnt) / 2; /* then back up */
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* to resync */
bufcntr = bufcap; /* mark the buffer as invalid to force a read */
*bc = MTR_GAP; /* reset the marker */
runaway_counter -= sizeof_gap / 2; /* and decrement the gap counter */
}
else { /* otherwise it's a record marker */
if (bufcntr < bufcap) /* if the position is within the buffer */
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* then seek to the data area */
sbc = MTR_L (*bc); /* extract the record length */
uptr->pos = uptr->pos + sizeof (t_mtrlnt) /* position to the start */
+ (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc); /* of the record */
}
}
while (*bc == MTR_GAP && runaway_counter > 0); /* continue until data or runaway occurs */
if (r == MTSE_OK && runaway_counter <= 0) /* if a tape runaway occurred */
r = MTSE_RUNAWAY; /* then report it */
break; /* otherwise the operation succeeded */
case MTUF_F_TPC: case MTUF_F_TPC:
sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref); sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref);
@ -674,17 +817,25 @@ return r;
Outputs: Outputs:
status = operation status status = operation status
exit condition position exit condition tape position
------------------ -------------------------------------------
unit unattached unchanged unit unattached unchanged
beginning of tape unchanged beginning of tape unchanged
read error unchanged read error unchanged
end of file unchanged end of file unchanged
end of medium updated end of medium updated
tape mark updated tape mark updated
tape runaway updated
data record updated, sim_fread will read record forward data record updated, sim_fread will read record forward
See notes at "sim_tape_wrgap" regarding erase gap implementation. This routine is called to set up a record read or spacing in the reverse
direction. On return, status is MTSE_OK and the tape is positioned at the
first data byte if a record was encountered, or status is an MTSE error code
giving the reason that the operation did not succeed and the tape position is
as indicated above.
See the notes at "sim_tape_rdlntf" and "sim_tape_wrgap" regarding tape
runaway and the erase gap implementation, respectively.
*/ */
t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc) t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc)
@ -696,51 +847,99 @@ uint32 f = MT_GET_FMT (uptr);
t_addr ppos; t_addr ppos;
t_mtrlnt sbc; t_mtrlnt sbc;
t_tpclnt tpcbc; t_tpclnt tpcbc;
t_mtrlnt buffer [256]; /* local tape buffer */
uint32 bufcntr, bufcap; /* buffer counter and capacity */
int32 runaway_counter, sizeof_gap; /* bytes remaining before runaway and bytes per gap */
t_stat r = MTSE_OK; t_stat r = MTSE_OK;
MT_CLR_PNU (uptr); MT_CLR_PNU (uptr); /* clear the position-not-updated flag */
if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
return MTSE_UNATT;
if (sim_tape_bot (uptr)) /* at BOT? */
return MTSE_BOT;
switch (f) { /* switch on fmt */
case MTUF_F_STD: case MTUF_F_E11: if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */
do { return MTSE_UNATT; /* then quit with an error */
sim_fseek (uptr->fileref, uptr->pos - sizeof (t_mtrlnt), SEEK_SET);
sim_fread (bc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */ if (sim_tape_bot (uptr)) /* if the unit is positioned at the BOT */
sbc = MTR_L (*bc); return MTSE_BOT; /* then reading backward is not possible */
if (ferror (uptr->fileref)) /* error? */
return sim_tape_ioerr (uptr); switch (f) { /* the read method depends on the tape format */
if (feof (uptr->fileref)) { /* eof? */
r = MTSE_EOM; case MTUF_F_STD:
case MTUF_F_E11:
runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set largest legal gap size in bytes */
if (runaway_counter == 0) { /* if tape density has not been not set */
sizeof_gap = 0; /* then disable runaway detection */
runaway_counter = INT_MAX; /* to allow gaps of any size */
}
else /* otherwise */
sizeof_gap = sizeof (t_mtrlnt); /* set the size of the gap */
bufcntr = 0; /* force an initial read */
bufcap = 1; /* but of just one metadata marker */
do { /* loop until a record, gap, or error seen */
if (bufcntr == 0) { /* if the buffer is empty then refill it */
if (sim_tape_bot (uptr)) { /* if the search has backed into the BOT */
r = MTSE_BOT; /* then quit with an error */
break; break;
} }
uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* spc over rec lnt */
if (*bc == MTR_EOM) { /* eom? */ else if (uptr->pos < bufcap * sizeof (t_mtrlnt)) /* if less than a full buffer remains */
r = MTSE_EOM; bufcap = (uint32)(uptr->pos / sizeof (t_mtrlnt));/* then reduce the capacity accordingly */
sim_fseek (uptr->fileref, /* seek back to the location */
uptr->pos - bufcap * sizeof (t_mtrlnt), /* corresponding to the start */
SEEK_SET); /* of the buffer */
bufcntr = sim_fread (buffer, sizeof (t_mtrlnt), /* fill the buffer */
bufcap, uptr->fileref); /* with tape metadata */
if (ferror (uptr->fileref)) { /* if a file I/O error occurred */
MT_SET_PNU (uptr); /* then set position not updated */
r = sim_tape_ioerr (uptr); /* report the error and quit */
break; break;
} }
if (*bc == MTR_TMK) { /* tape mark? */
return MTSE_TMK; else /* otherwise reset the capacity */
bufcap = sizeof (buffer) /* to the full size of the buffer */
/ sizeof (buffer [0]);
}
*bc = buffer [--bufcntr]; /* store the metadata marker value */
uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* backspace over the marker */
if (*bc == MTR_TMK) { /* if the marker is a tape mark */
r = MTSE_TMK; /* then quit with tape mark status */
break; break;
} }
if ((*bc & MTR_M_RHGAP) == MTR_RHGAP) { /* half gap? */
uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* half space rev */ else if (*bc == MTR_GAP) /* otherwise if the marker is a full gap */
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* resync */ runaway_counter -= sizeof_gap; /* then decrement the gap counter */
else if ((*bc & MTR_M_RHGAP) == MTR_RHGAP /* otherwise if the marker */
|| *bc == MTR_RRGAP) { /* is a half gap */
uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* then position forward to resync */
bufcntr = 0; /* mark the buffer as invalid to force a read */
*bc = MTR_GAP; /* reset the marker */
runaway_counter -= sizeof_gap / 2; /* and decrement the gap counter */
} }
else if (*bc != MTR_GAP) {
uptr->pos = uptr->pos - sizeof (t_mtrlnt) - /* spc over record */ else { /* otherwise it's a record marker */
((f == MTUF_F_STD)? ((sbc + 1) & ~1): sbc); sbc = MTR_L (*bc); /* extract the record length */
sim_fseek (uptr->fileref, uptr->pos + sizeof (t_mtrlnt), SEEK_SET); uptr->pos = uptr->pos - sizeof (t_mtrlnt) /* position to the start */
} - (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc); /* of the record */
else if (sim_tape_bot (uptr)) { /* backed into BOT? */ sim_fseek (uptr->fileref, /* seek to the data area */
r = MTSE_BOT; uptr->pos + sizeof (t_mtrlnt), SEEK_SET);
break;
} }
} }
while ((*bc == MTR_GAP) || (*bc == MTR_RHGAP)); while (*bc == MTR_GAP && runaway_counter > 0); /* continue until data or runaway occurs */
break;
if (r == MTSE_OK && runaway_counter <= 0) /* if a tape runaway occurred */
r = MTSE_RUNAWAY; /* then report it */
break; /* otherwise the operation succeeded */
case MTUF_F_TPC: case MTUF_F_TPC:
ppos = sim_tape_tpc_fnd (uptr, (t_addr *) uptr->filebuf); /* find prev rec */ ppos = sim_tape_tpc_fnd (uptr, (t_addr *) uptr->filebuf); /* find prev rec */
@ -1033,12 +1232,19 @@ return r;
t_stat sim_tape_wreom (UNIT *uptr) t_stat sim_tape_wreom (UNIT *uptr)
{ {
t_stat result;
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wreom(unit=%d)\n", uptr-ctx->dptr->units); sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wreom(unit=%d)\n", uptr-ctx->dptr->units);
if (MT_GET_FMT (uptr) == MTUF_F_P7B) /* cant do P7B */ if (MT_GET_FMT (uptr) == MTUF_F_P7B) /* cant do P7B */
return MTSE_FMT; return MTSE_FMT;
return sim_tape_wrdata (uptr, MTR_EOM);
result = sim_tape_wrdata (uptr, MTR_EOM); /* write the EOM marker */
uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* restore original tape position */
MT_SET_PNU (uptr); /* indicate that position was not updated */
return result;
} }
t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback) t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback)
@ -1080,7 +1286,6 @@ return r;
Inputs: Inputs:
uptr = pointer to tape unit uptr = pointer to tape unit
gaplen = length of gap in tenths of an inch gaplen = length of gap in tenths of an inch
bpi = current recording density in bytes per inch
Outputs: Outputs:
status = operation status status = operation status
@ -1099,8 +1304,8 @@ return r;
value. This value is chosen so that it is still recognizable even if it has value. This value is chosen so that it is still recognizable even if it has
been "cut in half" by a subsequent data overwrite that does not end on a been "cut in half" by a subsequent data overwrite that does not end on a
metadatum-sized boundary. In addition, a range of metadata values are metadatum-sized boundary. In addition, a range of metadata values are
reserved for detection in the reverse direction. Erase gaps are supported reserved for detection in the reverse direction. Erase gaps are currently
only in SIMH tape format. supported only in SIMH (MTUF_F_STD) tape format.
This implementation supports erasing gaps in the middle of a populated tape This implementation supports erasing gaps in the middle of a populated tape
image and will always produce a valid image. It also produces valid images image and will always produce a valid image. It also produces valid images
@ -1115,8 +1320,8 @@ return r;
problematic if the density setting is not the same as that used during problematic if the density setting is not the same as that used during
recording. There is no way to establish a gap of a certain length recording. There is no way to establish a gap of a certain length
unequivocally in an image file, so this implementation establishes a gap of a unequivocally in an image file, so this implementation establishes a gap of a
certain number of bytes that reflect the desired gap length at the bpi used certain number of bytes that reflect the desired gap length at the tape
during writing. density in bits per inch used during writing.
To write an erase gap, the implementation uses one of two approaches, To write an erase gap, the implementation uses one of two approaches,
depending on whether or not the current tape position is at EOM. Erasing at depending on whether or not the current tape position is at EOM. Erasing at
@ -1131,12 +1336,14 @@ return r;
Because the smallest legal tape record requires space for two metadata Because the smallest legal tape record requires space for two metadata
markers plus two data bytes, an erasure that would leave less than that markers plus two data bytes, an erasure that would leave less than that
is increased to consume the entire record. Otherwise, the final record is is increased to consume the entire record. Otherwise, the final record is
truncated appropriately. truncated appropriately by rewriting the leading and trailing length words
appropriately.
When reading in either direction, gap metadata markers are ignored (skipped) When reading in either direction, gap metadata markers are ignored (skipped)
until a record length header, EOF marker, EOM marker, or physical EOF is until a record length header, EOF marker, EOM marker, or physical EOF is
encountered. Thus, tape images containing gap metadata are transparent to encountered. Thus, tape images containing gap metadata are transparent to
the calling simulator. the calling simulator (unless tape runaway support is enabled -- see the
notes at "sim_tape_rdlntf" for details).
The permissibility of data record lengths that are not multiples of the The permissibility of data record lengths that are not multiples of the
metadatum size presents a difficulty when reading. If such an "odd length" metadatum size presents a difficulty when reading. If such an "odd length"
@ -1162,31 +1369,50 @@ return r;
0xFFFEFFFF - reserved (indicates half-gap in forward reads) 0xFFFEFFFF - reserved (indicates half-gap in forward reads)
0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads) 0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads)
0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads) 0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads)
*/
t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen, uint32 bpi) If the tape density has been set via a previous sim_tape_set_dens call, and
the tape format is set to SIMH format, then this routine will write a gap of
the appropriate size. If the density has not been set, then no action will
be taken, and either MTSE_IOERR or MTSE_OK status will be returned, depending
on whether SIMH or another format is selected, respectively. A simulator
that calls this routine must set the density beforehand; failure to do so is
an error. However, calling while another format is enabled is OK and is
treated as a no-operation. This allows a device simulator that supports
writing erase gaps to use the same code without worrying about the tape
format currently selected by the user.
*/
t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen)
{ {
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
t_stat st; t_stat st;
t_mtrlnt meta, sbc, new_len, rec_size; t_mtrlnt meta, sbc, new_len, rec_size;
t_addr gap_pos = uptr->pos; t_addr gap_pos = uptr->pos;
uint32 file_size, marker_count; uint32 file_size, marker_count, tape_density;
uint32 format = MT_GET_FMT (uptr); int32 gap_needed;
uint32 gap_alloc = 0; /* gap allocated from tape */ uint32 gap_alloc = 0; /* gap currently allocated from the tape */
int32 gap_needed = (gaplen * bpi) / 10; /* gap remainder still needed */ const uint32 format = MT_GET_FMT (uptr); /* tape format */
const uint32 meta_size = sizeof (t_mtrlnt); /* bytes per metadatum */ const uint32 meta_size = sizeof (t_mtrlnt); /* bytes per metadatum */
const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* smallest data record size */ const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* smallest data record size */
sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%p, bpi=%d)\n", uptr-ctx->dptr->units, gaplen, bpi); sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%p)\n", uptr-ctx->dptr->units, gaplen);
MT_CLR_PNU (uptr); MT_CLR_PNU (uptr);
if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */
return MTSE_UNATT; return MTSE_UNATT; /* then we cannot proceed */
if (format != MTUF_F_STD) /* not SIMH fmt? */
return MTSE_FMT; else if (sim_tape_wrp (uptr)) /* otherwise if the unit is write protected */
if (sim_tape_wrp (uptr)) /* write protected? */ return MTSE_WRP; /* then we cannot write */
return MTSE_WRP;
tape_density = bpi [MT_DENS (uptr->dynflags)]; /* get the density of the tape */
if (format != MTUF_F_STD) /* if erase gaps aren't supported by the format */
return MTSE_OK; /* then take no action */
else if (tape_density == 0) /* otherwise if the density is not set */
return MTSE_IOERR; /* then report an I/O error */
else /* otherwise */
gap_needed = (gaplen * tape_density) / 10; /* determine the gap size needed in bytes */
file_size = sim_fsize (uptr->fileref); /* get file size */ file_size = sim_fsize (uptr->fileref); /* get file size */
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */
@ -1304,12 +1530,12 @@ while (--marker_count > 0);
return MTSE_OK; return MTSE_OK;
} }
t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, uint32 bpi, TAPE_PCALLBACK callback) t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, TAPE_PCALLBACK callback)
{ {
t_stat r = MTSE_OK; t_stat r = MTSE_OK;
AIO_CALLSETUP AIO_CALLSETUP
r = sim_tape_wrgap (uptr, gaplen, bpi); r = sim_tape_wrgap (uptr, gaplen);
AIO_CALL(TOP_RDRR, NULL, NULL, NULL, 0, 0, gaplen, bpi, NULL, callback); AIO_CALL(TOP_RDRR, NULL, NULL, NULL, 0, 0, gaplen, 0, NULL, callback);
return r; return r;
} }
@ -1834,6 +2060,8 @@ t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc)
{ {
uint32 f; uint32 f;
if (uptr->flags & UNIT_ATT)
return SCPE_ALATT;
if (uptr == NULL) if (uptr == NULL)
return SCPE_IERR; return SCPE_IERR;
if (cptr == NULL) if (cptr == NULL)
@ -1944,3 +2172,81 @@ else
fprintf (st, "unlimited capacity"); fprintf (st, "unlimited capacity");
return SCPE_OK; return SCPE_OK;
} }
/* Set the tape density.
Set the density of the specified tape unit either to the value supplied or to
the value represented by the supplied character string.
If "desc" is NULL, then "val" must be set to one of the MT_DENS_* constants
in sim_tape.h other than MT_DENS_NONE; the supplied value is used as the tape
density, and the character string is ignored. Otherwise, "desc" must point
at an int32 value containing a set of allowed densities constructed as a
bitwise OR of the appropriate MT_*_VALID values. In this case, the string
pointed to by "cptr" will be parsed for a decimal value corresponding to the
desired density in bits per inch and validated against the set of allowed
values.
In either case, SCPE_ARG is returned if the density setting is not valid or
allowed. If the setting is OK, the new density is set into the unit
structure, and SCPE_OK is returned.
*/
t_stat sim_tape_set_dens (UNIT *uptr, int32 val, char *cptr, void *desc)
{
uint32 density, new_bpi;
t_stat result = SCPE_OK;
if (uptr == NULL) /* if the unit pointer is null */
return SCPE_IERR; /* then the caller has screwed up */
else if (desc == NULL) /* otherwise if a validation set was not supplied */
if (val > 0 && val < (int32) BPI_COUNT) /* then if a valid density code was supplied */
uptr->dynflags = (uptr->dynflags & ~MTVF_DENS_MASK) /* then insert the code */
| (val << UNIT_V_DF_TAPE); /* in the unit flags */
else /* otherwise the code is invalid */
return SCPE_ARG; /* so report a bad argument */
else { /* otherwise a validation set was supplied */
if (cptr == NULL || *cptr == 0) /* but if no value is present */
return SCPE_MISVAL; /* then report a missing value */
new_bpi = (uint32) get_uint (cptr, 10, UINT_MAX, &result); /* convert the string value */
if (result != SCPE_OK) /* if the conversion failed */
result = SCPE_ARG; /* then report a bad argument */
else for (density = 0; density < BPI_COUNT; density++) /* otherwise validate the density */
if (new_bpi == bpi [density] /* if it matches a value in the list */
&& ((1 << density) & *(int32 *) desc)) { /* and it's an allowed value */
uptr->dynflags = (uptr->dynflags & ~MTVF_DENS_MASK) /* then store the index of the value */
| density << UNIT_V_DF_TAPE; /* in the unit flags */
return SCPE_OK; /* and return success */
}
result = SCPE_ARG; /* if no match, then report a bad argument */
}
return result; /* return the result of the operation */
}
/* Show the tape density */
t_stat sim_tape_show_dens (FILE *st, UNIT *uptr, int32 val, void *desc)
{
uint32 tape_density;
if (uptr == NULL) /* if the unit pointer is null */
return SCPE_IERR; /* then the caller has screwed up */
else { /* otherwise get the density */
tape_density = bpi [MT_DENS (uptr->dynflags)]; /* of the tape from the unit flags */
if (tape_density) /* if it's set */
fprintf (st, "density=%d bpi", tape_density); /* then report it */
else /* otherwise */
fprintf (st, "density not set"); /* it was never set by the caller */
}
return SCPE_OK;
}

View file

@ -41,6 +41,7 @@ typedef uint32 t_mtrlnt; /* magtape rec lnt */
#define MTR_TMK 0x00000000 /* tape mark */ #define MTR_TMK 0x00000000 /* tape mark */
#define MTR_EOM 0xFFFFFFFF /* end of medium */ #define MTR_EOM 0xFFFFFFFF /* end of medium */
#define MTR_GAP 0xFFFFFFFE /* primary gap */ #define MTR_GAP 0xFFFFFFFE /* primary gap */
#define MTR_RRGAP 0xFFFFFFFF /* reverse read half gap */
#define MTR_FHGAP 0xFFFEFFFF /* fwd half gap (overwrite) */ #define MTR_FHGAP 0xFFFEFFFF /* fwd half gap (overwrite) */
#define MTR_RHGAP 0xFFFF0000 /* rev half gap (overwrite) */ #define MTR_RHGAP 0xFFFF0000 /* rev half gap (overwrite) */
#define MTR_M_RHGAP (~0x000080FF) /* range mask for rev gap */ #define MTR_M_RHGAP (~0x000080FF) /* range mask for rev gap */
@ -103,6 +104,25 @@ typedef uint16 t_tpclnt; /* magtape rec lnt */
#define MTPOS_V_DLE 4 #define MTPOS_V_DLE 4
#define MTPOS_M_DLE (1u << MTPOS_V_DLE) /* Detect LEOT */ #define MTPOS_M_DLE (1u << MTPOS_V_DLE) /* Detect LEOT */
/* Tape density values */
#define MT_DENS_NONE 0 /* density not set */
#define MT_DENS_200 1 /* 200 bpi NRZI */
#define MT_DENS_556 2 /* 556 bpi NRZI */
#define MT_DENS_800 3 /* 800 bpi NRZI */
#define MT_DENS_1600 4 /* 1600 bpi PE */
#define MT_DENS_6250 5 /* 6250 bpi GCR */
#define MTVF_DENS_MASK (((1u << UNIT_S_DF_TAPE) - 1) << UNIT_V_DF_TAPE)
#define MT_DENS(f) (((f) & MTVF_DENS_MASK) >> UNIT_V_DF_TAPE)
#define MT_NONE_VALID (1u << MT_DENS_NONE) /* density not set is valid */
#define MT_200_VALID (1u << MT_DENS_200) /* 200 bpi is valid */
#define MT_556_VALID (1u << MT_DENS_556) /* 556 bpi is valid */
#define MT_800_VALID (1u << MT_DENS_800) /* 800 bpi is valid */
#define MT_1600_VALID (1u << MT_DENS_1600) /* 1600 bpi is valid */
#define MT_6250_VALID (1u << MT_DENS_6250) /* 6250 bpi is valid */
/* Return status codes */ /* Return status codes */
#define MTSE_OK 0 /* no error */ #define MTSE_OK 0 /* no error */
@ -116,6 +136,7 @@ typedef uint16 t_tpclnt; /* magtape rec lnt */
#define MTSE_RECE 8 /* error in record */ #define MTSE_RECE 8 /* error in record */
#define MTSE_WRP 9 /* write protected */ #define MTSE_WRP 9 /* write protected */
#define MTSE_LEOT 10 /* Logical End Of Tape */ #define MTSE_LEOT 10 /* Logical End Of Tape */
#define MTSE_RUNAWAY 11 /* tape runaway */
typedef void (*TAPE_PCALLBACK)(UNIT *unit, t_stat status); typedef void (*TAPE_PCALLBACK)(UNIT *unit, t_stat status);
@ -143,8 +164,8 @@ t_stat sim_tape_wreom (UNIT *uptr);
t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback); t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback);
t_stat sim_tape_wreomrw (UNIT *uptr); t_stat sim_tape_wreomrw (UNIT *uptr);
t_stat sim_tape_wreomrw_a (UNIT *uptr, TAPE_PCALLBACK callback); t_stat sim_tape_wreomrw_a (UNIT *uptr, TAPE_PCALLBACK callback);
t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen, uint32 bpi); t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen);
t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, uint32 bpi, TAPE_PCALLBACK callback); t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, TAPE_PCALLBACK callback);
t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc); t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc);
t_stat sim_tape_sprecf_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback); t_stat sim_tape_sprecf_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback);
t_stat sim_tape_sprecsf (UNIT *uptr, uint32 count, uint32 *skipped); t_stat sim_tape_sprecsf (UNIT *uptr, uint32 count, uint32 *skipped);
@ -173,6 +194,8 @@ t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat sim_tape_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat sim_tape_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat sim_tape_set_dens (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat sim_tape_show_dens (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat sim_tape_set_asynch (UNIT *uptr, int latency); t_stat sim_tape_set_asynch (UNIT *uptr, int latency);
t_stat sim_tape_clr_asynch (UNIT *uptr); t_stat sim_tape_clr_asynch (UNIT *uptr);
void sim_tape_data_trace (UNIT *uptr, const uint8 *data, size_t len, const char* txt, int detail, uint32 reason); void sim_tape_data_trace (UNIT *uptr, const uint8 *data, size_t len, const char* txt, int detail, uint32 reason);