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:
504
sim_tape.c
504
sim_tape.c
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user