1
0
mirror of https://github.com/simh/simh.git synced 2026-01-26 04:01:38 +00:00

TAPE: Add support for erasing tape marks and Coverity Fixes (Dave Bryan)

This commit is contained in:
Mark Pizzolato
2017-05-24 09:01:59 -07:00
parent 2f7af39d0b
commit cbef6aa64b
2 changed files with 347 additions and 162 deletions

View File

@@ -1,6 +1,6 @@
/* sim_tape.c: simulator tape support library
Copyright (c) 1993-2016, Robert M Supnik
Copyright (c) 1993-2017, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@@ -26,6 +26,8 @@
Ultimately, this will be a place to hide processing of various tape formats,
as well as OS-specific direct hardware access.
03-May-17 JDB Added support for erasing tape marks to sim_tape_errec[fr]
02-May-17 JDB Added error checks to sim_fseek calls
18-Jul-16 JDB Added sim_tape_errecf, sim_tape_errecr functions
12-Oct-15 JDB Fixed bug in sim_tape_rdlntf if gap buffer read ends at EOM
15-Dec-14 JDB Changed sim_tape_set_dens to check validity of density change
@@ -188,7 +190,7 @@ sim_tape_rewind (uptr);
return SCPE_OK;
}
/* Read record length forward (internal routine)
/* Read record length forward (internal routine).
Inputs:
uptr = pointer to tape unit
@@ -239,6 +241,7 @@ return SCPE_OK;
See the notes at "tape_erase_fwd" regarding the erase gap implementation.
Implementation notes:
1. For programming convenience, erase gap processing is performed for both
@@ -251,7 +254,11 @@ return SCPE_OK;
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
3. The "runaway_counter" cannot decrement to zero (or below) in the presence
of an error that terminates the gap-search loop. Therefore, the test
after the loop exit need not check for error status.
4. 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:
@@ -265,7 +272,7 @@ return SCPE_OK;
512 186
1024 171
4. Because an erase gap may precede the logical end-of-medium, represented
5. Because an erase gap may precede the logical end-of-medium, represented
either by the physical end-of-file or by an EOM marker, the "position not
updated" flag is set only if the tape is positioned at the EOM when the
routine is entered. If at least one gap marker precedes the EOM, then
@@ -273,25 +280,29 @@ return SCPE_OK;
sequence will work correctly in both cases.
*/
t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc)
static t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc)
{
uint8 c;
t_bool all_eof;
uint32 f = MT_GET_FMT (uptr);
uint8 c;
t_bool all_eof;
uint32 f = MT_GET_FMT (uptr);
t_mtrlnt sbc;
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 */
uint32 bufcntr, bufcap; /* buffer counter and capacity */
int32 runaway_counter, sizeof_gap; /* bytes remaining before runaway and bytes per gap */
t_stat status = SCPE_OK;
MT_CLR_PNU (uptr); /* clear the position-not-updated flag */
if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */
return MTSE_UNATT; /* then quit with an error */
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set the initial tape position */
if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) { /* set the initial tape position; if it fails */
MT_SET_PNU (uptr); /* then set position not updated */
status = sim_tape_ioerr (uptr); /* and quit with I/O error status */
}
switch (f) { /* the read method depends on the tape format */
else switch (f) { /* otherwise the read method depends on the tape format */
case MTUF_F_STD:
case MTUF_F_E11:
@@ -301,6 +312,7 @@ switch (f) { /* the read method depen
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 */
@@ -309,11 +321,13 @@ switch (f) { /* the read method depen
do { /* loop until a record, gap, or error is seen */
if (bufcntr == bufcap) { /* if the buffer is empty then refill it */
if (feof (uptr->fileref)) /* if we hit the EOF while reading a gap */
if (feof (uptr->fileref)) { /* if we hit the EOF while reading a gap */
if (sizeof_gap > 0) /* then if detection is enabled */
return MTSE_RUNAWAY; /* then report a tape runaway */
status = MTSE_RUNAWAY; /* then report a tape runaway */
else /* otherwise report the physical EOF */
return MTSE_EOM; /* as the end-of-medium */
status = MTSE_EOM; /* as the end-of-medium */
break;
}
else if (bufcap == 0) /* otherwise if this is the initial read */
bufcap = 1; /* then start with just one marker */
@@ -331,21 +345,25 @@ switch (f) { /* the read method depen
if (bufcntr == 0) /* then if this is the initial read */
MT_SET_PNU (uptr); /* then set position not updated */
return sim_tape_ioerr (uptr); /* report the error and quit */
status = sim_tape_ioerr (uptr); /* report the error and quit */
break;
}
else if (bufcap == 0 /* otherwise if positioned at the physical EOF */
|| buffer [0] == MTR_EOM) /* or at the logical EOM */
if (bufcntr == 0) { /* then if this is the initial read */
MT_SET_PNU (uptr); /* then set position not updated */
return MTSE_EOM; /* and report the end-of-medium and quit */
status = MTSE_EOM; /* and report the end-of-medium and quit */
break;
}
else if (sizeof_gap > 0) /* otherwise if detection is enabled */
return MTSE_RUNAWAY; /* then report a tape runaway */
else /* otherwise report the physical EOF */
return MTSE_EOM; /* as the end-of-medium */
else { /* otherwise some gap has already been skipped */
if (sizeof_gap > 0) /* so if detection is enabled */
status = MTSE_RUNAWAY; /* then report a tape runaway */
else /* otherwise report the physical EOF */
status = MTSE_EOM; /* as the end-of-medium */
break;
}
else /* otherwise reset the index */
bufcntr = 0; /* to the start of the buffer */
@@ -353,94 +371,115 @@ switch (f) { /* the read method depen
*bc = buffer [bufcntr++]; /* store the metadata marker value */
if (*bc == MTR_EOM) /* if an end-of-medium marker is seen */
if (*bc == MTR_EOM) { /* if an end-of-medium marker is seen */
if (sizeof_gap > 0) /* then if detection is enabled */
return MTSE_RUNAWAY; /* then report a tape runaway */
status = MTSE_RUNAWAY; /* then report a tape runaway */
else /* otherwise report the physical EOF */
return MTSE_EOM; /* as the end-of-medium */
status = MTSE_EOM; /* as the end-of-medium */
break;
}
uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* space over the marker */
if (*bc == MTR_TMK) /* if the value is a tape mark */
return MTSE_TMK; /* then quit with tape mark status */
if (*bc == MTR_TMK) { /* if the value is a tape mark */
status = 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 */
uptr->pos = uptr->pos - sizeof (t_mtrlnt) / 2; /* then back up and resync */
*bc = MTR_GAP; /* reset the marker */
runaway_counter -= sizeof_gap / 2; /* and decrement the gap counter */
if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) { /* set the tape position; if it fails */
status = sim_tape_ioerr (uptr); /* then quit with I/O error status */
break;
}
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 */
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; if it fails */
status = sim_tape_ioerr (uptr); /* then quit with I/O error status */
break;
}
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 */
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 (runaway_counter <= 0) /* if a tape runaway occurred */
return MTSE_RUNAWAY; /* then report it */
status = MTSE_RUNAWAY; /* then report it */
break; /* otherwise the operation succeeded */
case MTUF_F_TPC:
sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref);
*bc = tpcbc; /* save rec lnt */
if (ferror (uptr->fileref)) { /* error? */
MT_SET_PNU (uptr); /* pos not upd */
return sim_tape_ioerr (uptr);
status = sim_tape_ioerr (uptr);
}
if (feof (uptr->fileref)) { /* eof? */
else if (feof (uptr->fileref)) { /* eof? */
MT_SET_PNU (uptr); /* pos not upd */
return MTSE_EOM;
status = MTSE_EOM;
}
else {
uptr->pos = uptr->pos + sizeof (t_tpclnt); /* spc over reclnt */
if (tpcbc == TPC_TMK) /* tape mark? */
status = MTSE_TMK;
else
uptr->pos = uptr->pos + ((tpcbc + 1) & ~1); /* spc over record */
}
uptr->pos = uptr->pos + sizeof (t_tpclnt); /* spc over reclnt */
if (tpcbc == TPC_TMK) /* tape mark? */
return MTSE_TMK;
uptr->pos = uptr->pos + ((tpcbc + 1) & ~1); /* spc over record */
break;
case MTUF_F_P7B:
for (sbc = 0, all_eof = 1; ; sbc++) { /* loop thru record */
sim_fread (&c, sizeof (uint8), 1, uptr->fileref);
if (ferror (uptr->fileref)) { /* error? */
MT_SET_PNU (uptr); /* pos not upd */
return sim_tape_ioerr (uptr);
status = sim_tape_ioerr (uptr);
break;
}
if (feof (uptr->fileref)) { /* eof? */
else if (feof (uptr->fileref)) { /* eof? */
if (sbc == 0) /* no data? eom */
return MTSE_EOM;
status = MTSE_EOM;
break; /* treat like eor */
}
if ((sbc != 0) && (c & P7B_SOR)) /* next record? */
else if ((sbc != 0) && (c & P7B_SOR)) /* next record? */
break;
if ((c & P7B_DPAR) != P7B_EOF)
else if ((c & P7B_DPAR) != P7B_EOF)
all_eof = 0;
}
*bc = sbc; /* save rec lnt */
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */
uptr->pos = uptr->pos + sbc; /* spc over record */
if (all_eof) /* tape mark? */
return MTSE_TMK;
if (status == SCPE_OK) {
*bc = sbc; /* save rec lnt */
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */
uptr->pos = uptr->pos + sbc; /* spc over record */
if (all_eof) /* tape mark? */
status = MTSE_TMK;
}
break;
default:
return MTSE_FMT;
}
status = MTSE_FMT;
}
return MTSE_OK;
return status;
}
/* Read record length reverse (internal routine)
/* Read record length reverse (internal routine).
Inputs:
uptr = pointer to tape unit
@@ -465,21 +504,32 @@ return MTSE_OK;
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 "tape_erase_fwd" regarding tape
runaway and the erase gap implementation, respectively.
Implementation notes:
1. The "sim_fread" call cannot return 0 in the absence of an error
condition. The preceding "sim_tape_bot" test ensures that "pos" >= 4, so
"sim_fseek" will back up at least that far, so "sim_fread" will read at
least one element. If the call returns zero, an error must have
occurred, so the "ferror" call must succeed.
2. See the notes at "sim_tape_rdlntf" and "tape_erase_fwd" regarding tape
runaway and the erase gap implementation, respectively.
*/
t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc)
static t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc)
{
uint8 c;
t_bool all_eof;
uint32 f = MT_GET_FMT (uptr);
t_addr ppos;
uint8 c;
t_bool all_eof;
uint32 f = MT_GET_FMT (uptr);
t_addr ppos;
t_mtrlnt sbc;
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 */
uint32 bufcntr, bufcap; /* buffer counter and capacity */
int32 runaway_counter, sizeof_gap; /* bytes remaining before runaway and bytes per gap */
t_stat status = SCPE_OK;
MT_CLR_PNU (uptr); /* clear the position-not-updated flag */
@@ -487,13 +537,13 @@ if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not at
return MTSE_UNATT; /* then quit with an error */
if (sim_tape_bot (uptr)) /* if the unit is positioned at the BOT */
return MTSE_BOT; /* then reading backward is not possible */
status = MTSE_BOT; /* then reading backward is not possible */
switch (f) { /* the read method depends on the tape format */
else switch (f) { /* otherwise the read method depends on the tape format */
case MTUF_F_STD:
case MTUF_F_E11:
runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set largest legal gap size in bytes */
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 */
@@ -504,40 +554,50 @@ switch (f) { /* the read method depen
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 */
bufcap = 0; /* but of just one metadata marker */
do { /* loop until a record, gap, or error seen */
do { /* loop until a record, gap, or error is 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 */
return MTSE_BOT; /* then quit with an error */
if (sim_tape_bot (uptr)) { /* if the search has backed into the BOT */
status = MTSE_BOT; /* then quit with an error */
break;
}
else if (uptr->pos < sizeof (buffer)) /* if less than a full buffer remains */
else if (bufcap == 0) /* otherwise if this is the initial read */
bufcap = 1; /* then start with just one marker */
else if (uptr->pos < sizeof (buffer)) /* otherwise if less than a full buffer remains */
bufcap = (uint32) uptr->pos /* then reduce the capacity accordingly */
/ sizeof (t_mtrlnt);
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 */
return sim_tape_ioerr (uptr); /* report the error and quit */
}
else /* otherwise reset the capacity */
bufcap = sizeof (buffer) /* to the full size of the buffer */
/ sizeof (buffer [0]);
if (sim_fseek (uptr->fileref, /* seek back to the location */
uptr->pos - bufcap * sizeof (t_mtrlnt), /* corresponding to the start */
SEEK_SET)) { /* of the buffer; if it fails */
status = sim_tape_ioerr (uptr); /* and fail with I/O error status */
break;
}
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 */
status = sim_tape_ioerr (uptr); /* then report the error and quit */
break;
}
}
*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 */
return MTSE_TMK; /* then quit with tape mark status */
if (*bc == MTR_TMK) { /* if the marker is a tape mark */
status = MTSE_TMK; /* then quit with tape mark status */
break;
}
else if (*bc == MTR_GAP) /* otherwise if the marker is a full gap */
runaway_counter -= sizeof_gap; /* then decrement the gap counter */
@@ -555,14 +615,19 @@ switch (f) { /* the read method depen
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 */
sim_fseek (uptr->fileref, /* seek to the data area */
uptr->pos + sizeof (t_mtrlnt), SEEK_SET);
if (sim_fseek (uptr->fileref, /* seek to the start of the data area; if it fails */
uptr->pos + sizeof (t_mtrlnt), /* then return with I/O error status */
SEEK_SET)) {
status = sim_tape_ioerr (uptr);
break;
}
}
}
while (*bc == MTR_GAP && runaway_counter > 0); /* continue until data or runaway occurs */
if (runaway_counter <= 0) /* if a tape runaway occurred */
return MTSE_RUNAWAY; /* then report it */
status = MTSE_RUNAWAY; /* then report it */
break; /* otherwise the operation succeeded */
@@ -571,41 +636,55 @@ switch (f) { /* the read method depen
sim_fseek (uptr->fileref, ppos, SEEK_SET); /* position */
sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref);
*bc = tpcbc; /* save rec lnt */
if (ferror (uptr->fileref)) /* error? */
return sim_tape_ioerr (uptr);
if (feof (uptr->fileref)) /* eof? */
return MTSE_EOM;
uptr->pos = ppos; /* spc over record */
if (*bc == MTR_TMK) /* tape mark? */
return MTSE_TMK;
sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET);
status = sim_tape_ioerr (uptr);
else if (feof (uptr->fileref)) /* eof? */
status = MTSE_EOM;
else {
uptr->pos = ppos; /* spc over record */
if (*bc == MTR_TMK) /* tape mark? */
status = MTSE_TMK;
else
sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET);
}
break;
case MTUF_F_P7B:
for (sbc = 1, all_eof = 1; (t_addr) sbc <= uptr->pos ; sbc++) {
sim_fseek (uptr->fileref, uptr->pos - sbc, SEEK_SET);
sim_fread (&c, sizeof (uint8), 1, uptr->fileref);
if (ferror (uptr->fileref)) /* error? */
return sim_tape_ioerr (uptr);
if (feof (uptr->fileref)) /* eof? */
return MTSE_EOM;
if ((c & P7B_DPAR) != P7B_EOF)
all_eof = 0;
if (c & P7B_SOR) /* start of record? */
if (ferror (uptr->fileref)) { /* error? */
status = sim_tape_ioerr (uptr);
break;
}
else if (feof (uptr->fileref)) { /* eof? */
status = MTSE_EOM;
break;
}
else {
if ((c & P7B_DPAR) != P7B_EOF)
all_eof = 0;
if (c & P7B_SOR) /* start of record? */
break;
}
}
if (status == SCPE_OK) {
uptr->pos = uptr->pos - sbc; /* update position */
*bc = sbc; /* save rec lnt */
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */
if (all_eof) /* tape mark? */
status = MTSE_TMK;
}
uptr->pos = uptr->pos - sbc; /* update position */
*bc = sbc; /* save rec lnt */
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */
if (all_eof) /* tape mark? */
return MTSE_TMK;
break;
default:
return MTSE_FMT;
status = MTSE_FMT;
}
return MTSE_OK;
return status;
}
/* Read record forward
@@ -824,6 +903,12 @@ return result;
write error unchanged, PNU set
gap written updated
If the requested byte count equals the metadatum size, then the routine
succeeds only if it can overlay a single metadatum (i.e., a tape mark, an
end-of-medium marker, or an existing erase gap marker); otherwise, the file
position is not altered, PNU is set, and MTSE_INVRL (invalid record length)
status is returned.
An erase gap is represented in the tape image file by a special metadata
value repeated throughout the gap. The value is chosen so that it is still
recognizable even if it has been "cut in half" by a subsequent data overwrite
@@ -888,7 +973,8 @@ return result;
a gap of the requested size. If the format does not, then no action will be
taken, and MTSE_OK status will be returned. 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.
the tape format currently selected by the user. A request for an erase gap
of zero length also succeeds with no action taken.
Implementation notes:
@@ -898,11 +984,12 @@ return result;
static t_stat tape_erase_fwd (UNIT *uptr, t_mtrlnt gap_size)
{
t_stat st;
size_t xfer;
t_stat st;
t_mtrlnt meta, sbc, new_len, rec_size;
uint32 file_size, marker_count;
int32 gap_needed = (int32) gap_size; /* the gap remaining to be allocated from the tape */
uint32 gap_alloc = 0; /* the gap currently allocated from the tape */
uint32 file_size, marker_count;
int32 gap_needed = (int32) gap_size; /* the gap remaining to be allocated from the tape */
uint32 gap_alloc = 0; /* the gap currently allocated from the tape */
const t_addr gap_pos = uptr->pos; /* the file position where the gap will start */
const uint32 format = MT_GET_FMT (uptr); /* the tape format */
const uint32 meta_size = sizeof (t_mtrlnt); /* the number of bytes per metadatum */
@@ -916,25 +1003,30 @@ if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not at
else if (sim_tape_wrp (uptr)) /* otherwise if the unit is write protected */
return MTSE_WRP; /* then we cannot write */
else if (format != MTUF_F_STD) /* if erase gaps aren't supported by the format */
else if (gap_size == 0 || format != MTUF_F_STD) /* otherwise if zero length or gaps aren't supported */
return MTSE_OK; /* then take no action */
file_size = sim_fsize (uptr->fileref); /* get file size */
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */
file_size = sim_fsize (uptr->fileref); /* get the file size */
/* Read tape records and allocate to gap until amount required is consumed.
if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) { /* position the tape; if it fails */
MT_SET_PNU (uptr); /* then set position not updated */
return sim_tape_ioerr (uptr); /* and quit with I/O error status */
}
Read next metadatum from tape:
/* Read tape records and allocate them to the gap until the amount required is
consumed.
Read the next metadatum from tape:
- EOF or EOM: allocate remainder of bytes needed.
- TMK or GAP: allocate sizeof(metadatum) bytes.
- Reverse GAP: allocate sizeof(metadatum) / 2 bytes.
- Data record: see below.
Loop until bytes needed = 0.
Loop until the bytes needed = 0.
*/
do {
sim_fread (&meta, meta_size, 1, uptr->fileref); /* read metadatum */
xfer = sim_fread (&meta, meta_size, 1, uptr->fileref); /* read a metadatum */
if (ferror (uptr->fileref)) { /* read error? */
uptr->pos = gap_pos; /* restore original position */
@@ -942,8 +1034,14 @@ do {
return sim_tape_ioerr (uptr); /* translate error */
}
else
uptr->pos = uptr->pos + meta_size; /* move tape over datum */
else if (xfer != 1 && feof (uptr->fileref) == 0) { /* otherwise if a partial metadatum was read */
uptr->pos = gap_pos; /* then restore the original position */
MT_SET_PNU (uptr); /* set the position-not-updated flag */
return MTSE_INVRL; /* and return an invalid record length error */
}
else /* otherwise we had a good read */
uptr->pos = uptr->pos + meta_size; /* so move the tape over the datum */
if (feof (uptr->fileref) || (meta == MTR_EOM)) { /* at eof or eom? */
gap_alloc = gap_alloc + gap_needed; /* allocate remainder */
@@ -955,17 +1053,25 @@ do {
gap_needed = gap_needed - meta_size; /* reduce requirement */
}
else if (gap_size == meta_size) { /* otherwise if the request is for a single metadatum */
uptr->pos = gap_pos; /* then restore the original position */
MT_SET_PNU (uptr); /* set the position-not-updated flag */
return MTSE_INVRL; /* and return an invalid record length error */
}
else if (meta == MTR_FHGAP) { /* half gap? */
uptr->pos = uptr->pos - meta_size / 2; /* backup to resync */
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */
if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* position the tape; if it fails */
return sim_tape_ioerr (uptr); /* then quit with I/O error status */
gap_alloc = gap_alloc + meta_size / 2; /* allocate marker space */
gap_needed = gap_needed - meta_size / 2; /* reduce requirement */
}
else if (uptr->pos +
MTR_L (meta) + meta_size > file_size) { /* rec len out of range? */
gap_alloc = gap_alloc + gap_needed; /* presume overwritten tape */
gap_needed = 0; /* allocate remainder */
else if (uptr->pos + MTR_L (meta) + meta_size > file_size) { /* rec len out of range? */
gap_alloc = gap_alloc + gap_needed; /* presume overwritten tape */
gap_needed = 0; /* allocate remainder */
}
/* Allocate a data record:
@@ -974,13 +1080,17 @@ do {
allocate entire record to gap, else allocate needed amount and
truncate data record to reflect remainder.
*/
else { /* data record */
sbc = MTR_L (meta); /* get record data length */
rec_size = ((sbc + 1) & ~1) + meta_size * 2; /* overall size in bytes */
if (rec_size < gap_needed + min_rec_size) { /* rec too small? */
uptr->pos = uptr->pos - meta_size + rec_size; /* position past record */
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* move tape */
if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* position the tape; if it fails */
return sim_tape_ioerr (uptr); /* then quit with I/O error status */
gap_alloc = gap_alloc + rec_size; /* allocate record */
gap_needed = gap_needed - rec_size; /* reduce requirement */
}
@@ -1008,7 +1118,7 @@ do {
}
}
}
while (gap_needed > 0);
while (gap_needed > 0); /* loop until all of the gap has been allocated */
uptr->pos = gap_pos; /* reposition to gap start */
@@ -1054,15 +1164,26 @@ return MTSE_OK;
write error unchanged, PNU set
gap written updated
If the requested byte count equals the metadatum size, then the routine
succeeds only if it can overlay a single metadatum (i.e., a tape mark or an
existing erase gap marker); otherwise, the file position is not altered, and
MTSE_INVRL (invalid record length) status is returned.
Implementation notes:
1. Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format.
2. Erasing in the reverse direction currently succeeds only if the gap
requested occupies the same space as the record located immediately
2. Erasing a record in the reverse direction currently succeeds only if the
gap requested occupies the same space as the record located immediately
before the current file position. This limitation may be lifted in a
future update.
3. The "sim_fread" call cannot return 0 in the absence of an error
condition. The preceding "sim_tape_bot" test ensures that "pos" >= 4, so
"sim_fseek" will back up at least that far, so "sim_fread" will read at
least one element. If the call returns zero, an error must have
occurred, so the "ferror" call must succeed.
*/
static t_stat tape_erase_rev (UNIT *uptr, t_mtrlnt gap_size)
@@ -1070,38 +1191,83 @@ static t_stat tape_erase_rev (UNIT *uptr, t_mtrlnt gap_size)
const uint32 format = MT_GET_FMT (uptr); /* the tape format */
const uint32 meta_size = sizeof (t_mtrlnt); /* the number of bytes per metadatum */
t_stat status;
t_mtrlnt rec_size;
t_mtrlnt rec_size, metadatum;
t_addr gap_pos;
size_t xfer;
MT_CLR_PNU (uptr);
MT_CLR_PNU (uptr); /* clear the position-not-updated flag */
if (sim_tape_wrp (uptr)) /* if the unit is write protected */
if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */
return MTSE_UNATT; /* then we cannot proceed */
else if (sim_tape_wrp (uptr)) /* otherwise if the unit is write protected */
return MTSE_WRP; /* then we cannot write */
else if (format != MTUF_F_STD) /* otherwise if erase gaps aren't supported by the format */
else if (gap_size == 0 || format != MTUF_F_STD) /* otherwise if the gap length is zero or unsupported */
return MTSE_OK; /* then take no action */
gap_pos = uptr->pos; /* save the starting position */
status = sim_tape_rdlntr (uptr, &rec_size); /* get the length of the preceding record */
if (gap_size == meta_size) { /* if the request is for a single metadatum */
if (sim_tape_bot (uptr)) /* then if the unit is positioned at the BOT */
return MTSE_BOT; /* then erasing backward is not possible */
else /* otherwise */
uptr->pos -= meta_size; /* back up the file pointer */
if (status == MTSE_OK /* if the read succeeded */
&& gap_size == rec_size + 2 * meta_size) { /* and the gap will exactly overlay the record */
gap_pos = uptr->pos; /* then save the gap start position */
if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* position the tape; if it fails */
return sim_tape_ioerr (uptr); /* then quit with I/O error status */
status = tape_erase_fwd (uptr, gap_size); /* erase the record */
sim_fread (&metadatum, meta_size, 1, uptr->fileref); /* read a metadatum */
if (status == MTSE_OK) /* if the gap write succeeded */
uptr->pos = gap_pos; /* the reposition back to the start of the gap */
if (ferror (uptr->fileref)) /* if a file I/O error occurred */
return sim_tape_ioerr (uptr); /* then report the error and quit */
else if (metadatum == MTR_TMK) /* otherwise if a tape mark is present */
if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* then reposition the tape; if it fails */
return sim_tape_ioerr (uptr); /* then quit with I/O error status */
else { /* otherwise */
metadatum = MTR_GAP; /* replace it with an erase gap marker */
xfer = sim_fwrite (&metadatum, meta_size, /* write the gap marker */
1, uptr->fileref);
if (ferror (uptr->fileref) || xfer == 0) /* if a file I/O error occurred */
return sim_tape_ioerr (uptr); /* report the error and quit */
else /* otherwise the write succeeded */
status = MTSE_OK; /* so return success */
}
else if (metadatum == MTR_GAP) /* otherwise if a gap already exists */
status = MTSE_OK; /* then take no additional action */
else { /* otherwise a data record is present */
uptr->pos = gap_pos; /* so restore the starting position */
return MTSE_INVRL; /* and fail with invalid record length status */
}
}
else { /* otherwise the read failed or is the wrong size */
uptr->pos = gap_pos; /* so restore the starting position */
else { /* otherwise it's an erase record request */
status = sim_tape_rdlntr (uptr, &rec_size); /* so get the length of the preceding record */
if (status != MTSE_OK) /* if the record was not found */
return status; /* then return the failure reason */
else /* otherwise the record is the wrong size */
return MTSE_INVRL; /* so report an invalid record length */
if (status == MTSE_OK /* if the read succeeded */
&& gap_size == rec_size + 2 * meta_size) { /* and the gap will exactly overlay the record */
gap_pos = uptr->pos; /* then save the gap start position */
status = tape_erase_fwd (uptr, gap_size); /* erase the record */
if (status == MTSE_OK) /* if the gap write succeeded */
uptr->pos = gap_pos; /* the reposition back to the start of the gap */
}
else { /* otherwise the read failed or is the wrong size */
uptr->pos = gap_pos; /* so restore the starting position */
if (status != MTSE_OK) /* if the record was not found */
return status; /* then return the failure reason */
else /* otherwise the record is the wrong size */
return MTSE_INVRL; /* so report an invalid record length */
}
}
return status; /* return the status of the erase operation */
@@ -1115,10 +1281,10 @@ return status; /* return the status of
sim_tape_set_dens call; if it has not, then no action is taken, and
MTSE_IOERR is returned.
If the tape format currently selected does not support erase gaps, the call
succeeds with no action taken. 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.
If the requested gap length is zero, or the tape format currently selected
does not support erase gaps, the call succeeds with no action taken. 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.
Because SIMH tape images do not carry physical parameters (e.g., recording
density), overwriting a tape image file containing a gap is problematic if
@@ -1149,6 +1315,11 @@ else /* otherwise */
metadata. This function may be used to erase a record of length "n" in place
by requesting a gap of length "n". After erasure, the tape will be
positioned at the end of the gap.
If a length of 0 is specified, then the metadatum marker at the current tape
position will be erased. If the tape is not positioned at a metadatum
marker, the routine fails with MTSE_INVRL, and the tape position is
unchanged.
*/
t_stat sim_tape_errecf (UNIT *uptr, t_mtrlnt bc)
@@ -1156,7 +1327,10 @@ t_stat sim_tape_errecf (UNIT *uptr, t_mtrlnt bc)
const t_mtrlnt meta_size = sizeof (t_mtrlnt); /* the number of bytes per metadatum */
const t_mtrlnt gap_size = bc + 2 * meta_size; /* the requested gap size in bytes */
return tape_erase_fwd (uptr, gap_size); /* erase the requested gap */
if (bc == 0) /* if a zero-length erase is requested */
return tape_erase_fwd (uptr, meta_size); /* then erase a metadatum marker */
else /* otherwise */
return tape_erase_fwd (uptr, gap_size); /* erase the requested gap */
}
/* Erase a record reverse.
@@ -1168,6 +1342,11 @@ return tape_erase_fwd (uptr, gap_size); /* erase the requested g
metadata. This function may be used to erase a record of length "n" in place
by requesting a gap of length "n". After erasure, the tape will be
positioned at the start of the gap.
If a length of 0 is specified, then the metadatum marker preceding the
current tape position will be erased. If the tape is not positioned after a
metadatum marker, the routine fails with MTSE_INVRL, and the tape position is
unchanged.
*/
t_stat sim_tape_errecr (UNIT *uptr, t_mtrlnt bc)
@@ -1175,7 +1354,10 @@ t_stat sim_tape_errecr (UNIT *uptr, t_mtrlnt bc)
const t_mtrlnt meta_size = sizeof (t_mtrlnt); /* the number of bytes per metadatum */
const t_mtrlnt gap_size = bc + 2 * meta_size; /* the requested gap size in bytes */
return tape_erase_rev (uptr, gap_size); /* erase the requested gap */
if (bc == 0) /* if a zero-length erase is requested */
return tape_erase_rev (uptr, meta_size); /* then erase a metadatum marker */
else /* otherwise */
return tape_erase_rev (uptr, gap_size); /* erase the requested gap */
}
/* Space record forward */