From fae6a1883536370518831cd3c4747dfb6d66171d Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Fri, 2 Jun 2017 00:54:13 -0700 Subject: [PATCH] TAPE: Update tape gap support (Dave Bryan) With this update, the erase gap operation has been split out of "sim_tape_wrgap" into a separate, internal "tape_erase_fwd" routine that is called from "sim_tape_wrgap" as well as from the new "sim_tape_errecf" routine. There's a corresponding internal "tape_erase_rev" that's called from the new "sim_tape_errecr" routine. I've shimmed "sim_tape_rdlntf" and "sim_tape_rdlntr" to move the tape context and debug stuff out of the routines that I'm maintaining. This will allow me to replace those functions in their entirety with the corresponding functions in my development sources for future updates. It also allows me to keep Bob's version in sync. As my routines are static and only called once from the shims, compilers should optimize away the function calls and instead inline the code, so there'd be no extra call overhead. I'd also like to keep "tape_erase_fwd" and "tape_erase_rev" untouched for the same reason. If you wish to add debug calls to "sim_tape_errecf" and "sim_tape_errecr", that's fine. --- doc/simh.doc | Bin 248832 -> 253952 bytes doc/simh_magtape.doc | Bin 50688 -> 53760 bytes sim_tape.c | 692 ++++++++++++++++++++++++++++++------------- 3 files changed, 481 insertions(+), 211 deletions(-) diff --git a/doc/simh.doc b/doc/simh.doc index 3bdfe5c755d7ff605a24e84154ceda852fd74cb4..964ca7cb6755ce3433fafb8b16f8ab746d40aa66 100644 GIT binary patch delta 47874 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 delta 44410 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 DzY^(= diff --git a/doc/simh_magtape.doc b/doc/simh_magtape.doc index 904f6c192334424776d5562507327472d7d4b9f4..94079bfbd8abba9a3826e138eb511bf209d7d5d3 100644 GIT binary patch delta 5761 zcmb`Ldr(yO701urU3Phks0*&Iiw{sFDBv553m8T*L42mZs4Fb6uc1&j)lh$@7HPG+5ckeE{2HDB@ zgU|lnzjMy-aewFDt{)q_P8tu*mm4S)GKdqR9$~7$wYKF z^Bt}sLUV*fqS|7jblTOjt7YKGz!9}H>LTe>}>ocbl{Q^dQW~HFo6N!h$m$J0_*tjVN zgQCiXm_t1AOtiZ&gXj!8w&kV}okYPw3@|~gGmJ~tZNHR-tTh-bF*eT~hX=j89_yCr zZ8(1enV+KH0>oP;YBjMj%Q;0<5?&L1pxA+V3WkR*6S1_59@F)FusR}dGmTsY9P&c#YkD=;hnpGH>B?QE z*T%-W^0DQSyqIh`UaadJDUKHUU^#@Bp*GhB^I}vL^9sDRH6vq@zPFTCCJ2 z=OwOx+~@E+Wry702smV)v)=1#ki9-R;Mn7ocRPHoa--MR?(j9FjoNURqrR0}Ds7qX zXQXJ&)d73Q7mDYnPprb&KBs?=*VEwgG|2&Ptkgz1(Cn1`dz|&IMwhce_PaWrsxI6z ztdIz1PqQ^S_7sLR3hAs%7oXZ%gHF{bmq#7Y=WKKOU}J*|O#?2kR_fU8-RlX+UI=AV zAB}E>vRv!+yTtzNbn#(Uic&hmYzVFn$S%L^@djj%v)<|VJA55%yx$p6)l zphMo}#B@7l)}QA{v&~;1JN(+*s57F?6Svumwir7yWg(O69d0*tS>tV$m1qku^Sv7sN_SU-Q{-6-un8zKBa8dd#U2qvcC$c zxFoo5EFO9w6{qh2=mtlCJew#76oMB)A2?;0|b> zi>EQ@0N22Ea0}QAh{_A_U|561>wvGW`~Sw@=lIj&JNNJWO9$t7hDoBg?jrG>`1?sw{d)UN1qnCD;35PD7%~a;hvgyh z^zaN{+WGOzN{Esbe?QZ=>}QPXS72mgioh!H-A8FyVw9lx4YPd9e&(otK_eT}1lqwX z;C=8%ur?Fh8H~Jw?%~TBm1!AXGjay4L~YSiOC+L%@Lr|3w{3EiCGpQ(QdGYJ3tMT! zVOt4rCJCO($QDQMS;J`ODtZrv6&$tS2qQV9+cEzibm5VHHGAmWv2vTGrmJ+#B154;3k0q4QBxg*FP?#}%W z4Tpdco?MLVjgZa77BCBhJU=*on(WPx%}!t=XaOGJ1BbxtY8%YAEoRQyWBeKTIcO;) zY6TTVs_d;;@2yzxt!ZM1AsMcf(zNxcWaZjQumjYAweUAD@a^ug7KM&qSHFx(E-=PN6^Tcv~q zOm>2>zmO}A{p5H!cQKSI|Ir8GvXNSB5rYQZ=D>Nb<(LDz_iEtoef;gt&Z~!qE?H<3c2q0yQagUrshSE&rj^)nZRDZ` z8Y^Sd_^1N~9<@^#P55isPX-t?8Qq94?z_} zN`k?JKZ9|okZuQW(}g5WBN}2w5jJ2;H$e#}gm0qNVr;K{Oy(g;B7-Txm|!v+4JOFV z7W;aqt?%y6rn?_P{bsdu`G%JL{VAId@G#?`D5lAbAAD>}ApWXoB4frA50r8VhjKIB z%}}1UFO|gf!!~m%rl^!X$1>4%cxRG2@%Zjp4B->R;Nfh^tYjTIC-wH3jLCQv`~0m5 z%H+P)lC)Gb9?dgZpZI%*=s7xGzIyvQf4DM20PZ+^VbYZ1m=!ZB>s~8Oj|^fJQ%;}A zWRhe7T7~%!>Q{1*SEq6$NMQMca|s0qVf_z)s)*y8yTT8aP8~ z&JXsitM46fr_Z0cQ2nyjgt{&godccHVsUSvCJi%=I|`uj5T;?Kc?B}Xd7|@Nbt>-| zHYZd!+fe8hU!CieHi*4%2hH11LmA?$^Q*;s=ZnNngGuu7=RI-p9lLn$>@#of?2?qO Li(L|4Bf9<%DfpFE delta 4378 zcmchaX>e25701t$WLp^9>;eRsXW@+yFL(tPjD?Kj1#Dx13)C#KEn^wUVk|i^E{w?1 z)=V=A_|gt^0SXBTOefRf&7>i10-4a_u$81ANJBrsOs5IdC6Kn$P6DX^^PZnAizS`u zOs@UY-Ojt`+(G_5B(XFW%W@`v|CA1HcYUV_N<@-%F1QI>A!gWZ}i>uW@Df=pB< zT;`gP2PICrB7JiYy|}M$;OWcklWNl2e=(Kl7Ft%yM7+s*+9eaQliWg{i<~!`(|TEx z8%$HH!!zePj4_f&h8?v_^?pri#3|W6{F6R2J=a%GuVdrxn9^+{Rc#=>u5Z};GK(41 zqMp@faB2Jf8uWt zf1H1M_xj!IS0DF}JwIC+Z)vxDE0G-4h-kxQIMEoby~zCRxGC+E9fLGnPtrbc|?8$rN=>@$XPyJ$zJ}EWZ~)`z^C9Q&`y!CH_jFHjfrB(szhaN?Q2r9ezLN0 zk{qA{Yyd8>J#9$ds?;~`k;J-&L~C5+5j+Z^1_TGe=O9Y=2I%JII7F6sFcNh%-J77BBhWUm z13U}92abSuGasQlGF)AQ;9tOJAWC;DbaV3{h({nBifjmMn(j8}<{(!C>VOk`8+3!+ zstnfK8nxye5%++8P&bdL9%N*zx;wDn9oX-VM6uow-#=hdHh1ioEZmz3mI4PTKs+tX zLDX?5D|!w~EEo0}CraZK~I^ z;@1FcyrX)znE_fS;bhFSlYP zhf_FQtc}OlZ3DWwC$b!i`cuifA!IPb8e>c`2BRsQP1hV^-r-*9332prK7KzroGq3f zS)8H?nqnXrYc!a`2D%nbrKP&MgOWIOB(1^RH;ryzHsIE->b5&OzHjR)c#5Saz(%4^ zV(@j6g7~Oj_e^9Q&z+-NRdKYY80q$S#NdUJsDITOn}Q`s;ct*q@y4s0j96GP;b^`T zqbxi68%fz}&X)RLHyL9M2J!jXDay>(t0bvdv>nSZP5x~CII-v0WI6TMKE9M1F*eM= z?NQUrIoo7ZK0KC_BsLimMh3EoS#KSg$QH2_Ur{+v$xuFe>#yTX{JkZ9JFv*K1dZbE zz>`|blOL2#xreUxXx#uhCs1QUDUT#c*JfsIa~`Ly3YNOb2Rj#v)91`)M1zj| zg185H01sqI7Gb}bA8UftYDs)~p(;^=9};`{(1NAL@CtU}@>nCzTr5lAZr)9G7sn4w zj^V;sMY?oF+PJ8>#Od$uYHGgEFvwH$wo2&}fx_l6ZtW9oocLgB0*j8kddwkx`KuNQ>zc)3*uRdAsZ|U~A zJA*lSxw%C-&7Nkj$G^GDwY7cQkTlEYeywcjqMTuqOB3V8iM{!&I{mgKC@*Ral(f2n zu0YV=<*o|4eKHz6$g9d1+rDcr&n+x;EGWow6c?6f7338y$g&p{7iSgcmgg1aITqRr z<~w%ihPMXTnxzl3rllL>`_}lpwo+CYXlZx*Tn+BF!00OL?(XvNFcl9QX4C@YU{+CM RaedTeO0oI#PF&t8{TKNSzU}}3 diff --git a/sim_tape.c b/sim_tape.c index c238655a..7c2ffe2c 100644 --- a/sim_tape.c +++ b/sim_tape.c @@ -68,6 +68,8 @@ sim_tape_wreom erase remainder of tape sim_tape_wreomrw erase remainder of tape & rewind sim_tape_wrgap write erase gap + sim_tape_errecf erase record forward + sim_tape_errecr erase record reverse sim_tape_sprecsf space records forward sim_tape_spfilef space files forward sim_tape_sprecsr space records reverse @@ -607,7 +609,7 @@ if (sim_deb && (ctx->dptr->dctrl & reason)) sim_data_trace(ctx->dptr, uptr, (detail ? data : NULL), "", len, txt, reason); } -/* Read record length forward (internal routine) +/* Read record length forward (internal routine). Inputs: uptr = pointer to tape unit @@ -630,7 +632,7 @@ if (sim_deb && (ctx->dptr->dctrl & reason)) 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 ANSI standards for magnetic tape recording (X3.22, 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 @@ -641,7 +643,7 @@ if (sim_deb && (ctx->dptr->dctrl & reason)) 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; however, MTSE_EOM is - returned if the tape is positioned at the EOM on entry. + returned if the tape is positioned at the EOM or EOF on entry. 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 @@ -656,13 +658,14 @@ if (sim_deb && (ctx->dptr->dctrl & reason)) 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. + 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 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 + contain erase gaps, as the "tape_erase_fwd" call takes no action for the E11 format. 2. The "feof" call cannot return a non-zero value on the first pass through @@ -670,7 +673,11 @@ if (sim_deb && (ctx->dptr->dctrl & reason)) 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: @@ -684,7 +691,7 @@ if (sim_deb && (ctx->dptr->dctrl & reason)) 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 @@ -694,27 +701,27 @@ if (sim_deb && (ctx->dptr->dctrl & reason)) static t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc) { -struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; -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 */ -t_stat r = MTSE_OK; +uint32 bufcntr, bufcap; /* buffer counter and capacity */ +int32 runaway_counter, sizeof_gap; /* bytes remaining before runaway and bytes per gap */ +t_stat status = MTSE_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 */ -if (ctx == NULL) /* if not properly attached? */ - return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ -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: @@ -724,6 +731,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 */ @@ -734,9 +742,9 @@ switch (f) { /* the read method depen 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 (sizeof_gap > 0) /* then if detection is enabled */ - r = MTSE_RUNAWAY; /* then report a tape runaway */ + status = MTSE_RUNAWAY; /* then report a tape runaway */ else /* otherwise report the physical EOF */ - r = MTSE_EOM; /* as the end-of-medium */ + status = MTSE_EOM; /* as the end-of-medium */ break; } @@ -756,7 +764,7 @@ 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 */ - r = sim_tape_ioerr (uptr); /* report the error and quit */ + status = sim_tape_ioerr (uptr); /* report the error and quit */ break; } @@ -764,15 +772,15 @@ switch (f) { /* the read method depen || 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 */ - r = MTSE_EOM; /* and report the end-of-medium and quit */ + status = MTSE_EOM; /* and report the end-of-medium and quit */ break; } else { /* otherwise some gap has already been skipped */ if (sizeof_gap > 0) /* so if detection is enabled */ - r = MTSE_RUNAWAY; /* then report a tape runaway */ + status = MTSE_RUNAWAY; /* then report a tape runaway */ else /* otherwise report the physical EOF */ - r = MTSE_EOM; /* as the end-of-medium */ + status = MTSE_EOM; /* as the end-of-medium */ break; } @@ -784,16 +792,16 @@ switch (f) { /* the read method depen if (*bc == MTR_EOM) { /* if an end-of-medium marker is seen */ if (sizeof_gap > 0) /* then if detection is enabled */ - r = MTSE_RUNAWAY; /* then report a tape runaway */ + status = MTSE_RUNAWAY; /* then report a tape runaway */ else /* otherwise report the physical EOF */ - r = 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 */ - r = MTSE_TMK; /* then quit with tape mark status */ + status = MTSE_TMK; /* then quit with tape mark status */ break; } @@ -801,80 +809,111 @@ switch (f) { /* the read method depen 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 (r == MTSE_OK && runaway_counter <= 0) /* if a tape runaway occurred */ - r = MTSE_RUNAWAY; /* then report it */ + if (runaway_counter <= 0) /* if a tape runaway occurred */ + 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 */ - r = MTSE_EOM; - break; + 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? */ - r = 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? */ - r = MTSE_TMK; + + if (status == MTSE_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; - } -sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", r, *bc, uptr->pos); -return r; + status = MTSE_FMT; + } + +return status; } -/* Read record length reverse (internal routine) +static t_stat sim_tape_rdrlfwd (UNIT *uptr, t_mtrlnt *bc) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +t_stat status; + +if (ctx == NULL) /* if not properly attached? */ + return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ + +status = sim_tape_rdlntf (uptr, bc); /* read the record length */ + +sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", status, *bc, uptr->pos); + +return status; +} + +/* Read record length reverse (internal routine). Inputs: uptr = pointer to tape unit @@ -899,39 +938,45 @@ return r; 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. + + 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. */ static t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc) { -struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; -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 */ -t_stat r = MTSE_OK; +uint32 bufcntr, bufcap; /* buffer counter and capacity */ +int32 runaway_counter, sizeof_gap; /* bytes remaining before runaway and bytes per gap */ +t_stat status = MTSE_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 */ -if (ctx == NULL) /* if not properly attached? */ - return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ 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 */ @@ -942,35 +987,40 @@ 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 */ - r = MTSE_BOT; /* then quit with an error */ + 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 */ - r = sim_tape_ioerr (uptr); /* report the error and quit */ - break; - } - 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 */ @@ -978,7 +1028,7 @@ switch (f) { /* the read method depen 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 */ + status = MTSE_TMK; /* then quit with tape mark status */ break; } @@ -998,14 +1048,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 (r == MTSE_OK && runaway_counter <= 0) /* if a tape runaway occurred */ - r = MTSE_RUNAWAY; /* then report it */ + if (runaway_counter <= 0) /* if a tape runaway occurred */ + status = MTSE_RUNAWAY; /* then report it */ break; /* otherwise the operation succeeded */ @@ -1014,47 +1069,70 @@ 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? */ - r = MTSE_EOM; - break; + 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); } - uptr->pos = ppos; /* spc over record */ - if (*bc == MTR_TMK) { /* tape mark? */ - r = MTSE_TMK; - break; - } - 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? */ - r = MTSE_EOM; + + if (ferror (uptr->fileref)) { /* error? */ + status = sim_tape_ioerr (uptr); break; } - if ((c & P7B_DPAR) != P7B_EOF) - all_eof = 0; - if (c & P7B_SOR) /* start of record? */ + 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 == MTSE_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? */ - r = MTSE_TMK; break; default: - return MTSE_FMT; + status = MTSE_FMT; } -sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", r, *bc, uptr->pos); -return r; + +return status; +} + +static t_stat sim_tape_rdrlrev (UNIT *uptr, t_mtrlnt *bc) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +t_stat status; + +if (ctx == NULL) /* if not properly attached? */ + return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ + +status = sim_tape_rdlntr (uptr, bc); /* read the record length */ + +sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", status, *bc, uptr->pos); + +return status; } /* Read record forward @@ -1091,7 +1169,8 @@ if (ctx == NULL) /* if not properly attac sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecf(unit=%d, buf=%p, max=%d)\n", (int)(uptr-ctx->dptr->units), buf, max); opos = uptr->pos; /* old position */ -if (MTSE_OK != (st = sim_tape_rdlntf (uptr, &tbc))) /* read rec lnt */ +st = sim_tape_rdrlfwd (uptr, &tbc); /* read rec lnt */ +if (st != MTSE_OK) return st; *bc = rbc = MTR_L (tbc); /* strip error flag */ if (rbc > max) { /* rec out of range? */ @@ -1099,7 +1178,7 @@ if (rbc > max) { /* rec out of range? */ uptr->pos = opos; return MTSE_INVRL; } -i = (t_mtrlnt)sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */ +i = (t_mtrlnt) sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */ if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); uptr->pos = opos; @@ -1156,12 +1235,13 @@ if (ctx == NULL) /* if not properly attac return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecr(unit=%d, buf=%p, max=%d)\n", (int)(uptr-ctx->dptr->units), buf, max); -if (MTSE_OK != (st = sim_tape_rdlntr (uptr, &tbc))) /* read rec lnt */ +st = sim_tape_rdrlrev (uptr, &tbc); /* read rec lnt */ +if (st != MTSE_OK) return st; *bc = rbc = MTR_L (tbc); /* strip error flag */ if (rbc > max) /* rec out of range? */ return MTSE_INVRL; -i = (t_mtrlnt)sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */ +i = (t_mtrlnt) sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */ if (ferror (uptr->fileref)) /* error? */ return sim_tape_ioerr (uptr); for ( ; i < rbc; i++) /* fill with 0's */ @@ -1363,31 +1443,33 @@ AIO_CALL(TOP_WEMR, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback); return r; } -/* Write erase gap - Inputs: - uptr = pointer to tape unit - gaplen = length of gap in tenths of an inch +/* Erase a gap in the forward direction (internal routine). - Outputs: - status = operation status + An erase gap is written in the forward direction on the tape unit specified + by "uptr" for the number of bytes specified by "bc". The status of the + operation is returned, and the file position is altered as follows: - exit condition position - ------------------ ------------------ - unit unattached unchanged - unsupported format unchanged - write protected unchanged - read error unchanged, PNU set - write error unchanged, PNU set - gap written updated + Exit Condition File Position + ------------------ ------------------ + unit unattached unchanged + unsupported format unchanged + write protected unchanged + read error unchanged, PNU set + 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. 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 - metadatum-sized boundary. In addition, a range of metadata values are - reserved for detection in the reverse direction. Erase gaps are currently - supported only in SIMH (MTUF_F_STD) tape format. + 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 + that does not end on a metadatum-sized boundary. In addition, a range of + metadata values are reserved for detection in the reverse direction. 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 @@ -1397,14 +1479,6 @@ return r; limitation that data records cannot overwrite other data records without producing an invalid tape. - Because SIMH tape images do not carry physical parameters (e.g., recording - density), overwriting a tape image file containing gap metadata is - 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 - 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 tape - density in bits per inch used during writing. - 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 EOM presents no special difficulties; gap metadata markers are written for @@ -1418,8 +1492,7 @@ return r; Because the smallest legal tape record requires space for two metadata 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 - truncated appropriately by rewriting the leading and trailing length words - appropriately. + truncated by rewriting the leading and trailing length words appropriately. When reading in either direction, gap metadata markers are ignored (skipped) until a record length header, EOF marker, EOM marker, or physical EOF is @@ -1452,34 +1525,31 @@ return r; 0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads) 0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads) - 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. + If the current tape format supports erase gaps, then this routine will write + 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. A request for an erase gap + of zero length also succeeds with no action taken. + + + Implementation notes: + + 1. Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format. */ -t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen) +static t_stat tape_erase_fwd (UNIT *uptr, t_mtrlnt gap_size) { -struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; -t_stat st; +size_t xfer; +t_stat st; t_mtrlnt meta, sbc, new_len, rec_size; -t_addr gap_pos = uptr->pos; -uint32 file_size, marker_count, tape_density; -int32 gap_needed; -uint32 gap_alloc = 0; /* gap currently allocated from the tape */ -const uint32 format = MT_GET_FMT (uptr); /* tape format */ -const uint32 meta_size = sizeof (t_mtrlnt); /* bytes per metadatum */ -const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* smallest data record size */ - -if (ctx == NULL) /* if not properly attached? */ - return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ -sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%u)\n", (int)(uptr-ctx->dptr->units), gaplen); +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 */ +const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* the smallest data record size */ MT_CLR_PNU (uptr); @@ -1489,39 +1559,45 @@ 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 */ -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 */ +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 */ -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 */ -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 */ MT_SET_PNU (uptr); /* position not updated */ 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 */ @@ -1533,17 +1609,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: @@ -1552,13 +1636,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 */ } @@ -1586,16 +1674,18 @@ 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 */ if (gap_alloc & (meta_size - 1)) { /* gap size "odd?" */ st = sim_tape_wrdata (uptr, MTR_FHGAP); /* write half gap marker */ + if (st != MTSE_OK) { /* write OK? */ uptr->pos = gap_pos; /* restore orig pos */ return st; /* PNU was set by wrdata */ } + uptr->pos = uptr->pos - meta_size / 2; /* realign position */ gap_alloc = gap_alloc - 2; /* decrease gap to write */ } @@ -1604,6 +1694,7 @@ marker_count = gap_alloc / meta_size; /* count of gap markers do { st = sim_tape_wrdata (uptr, MTR_GAP); /* write gap markers */ + if (st != MTSE_OK) { /* write OK? */ uptr->pos = gap_pos; /* restore orig pos */ return st; /* PNU was set by wrdata */ @@ -1614,6 +1705,169 @@ while (--marker_count > 0); return MTSE_OK; } +/* Erase a gap in the reverse direction (internal routine). + + An erase gap is written in the reverse direction on the tape unit specified + by "uptr" for the number of bytes specified by "bc". The status of the + operation is returned, and the file position is altered as follows: + + Exit Condition File Position + ------------------ ------------------ + unit unattached unchanged + unsupported format unchanged + write protected unchanged + read error unchanged, PNU set + 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 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) +{ +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, metadatum; +t_addr gap_pos; +size_t xfer; + +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 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 (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 */ + +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 (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 */ + + sim_fread (&metadatum, meta_size, 1, uptr->fileref); /* read a metadatum */ + + 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 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 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 */ +} + +/* Write an erase gap. + + An erase gap is written in on the tape unit specified by "uptr" for the + length specified by "gap_size" in tenths of an inch, and the status of the + operation is returned. The tape density must have been set via a previous + sim_tape_set_dens call; if it has not, then no action is taken, and + MTSE_IOERR is returned. + + 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 + the density setting is not the same as that used during 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 certain number of bytes that + reflect the desired gap length at the tape density in bits per inch used + during writing. +*/ + +t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +const uint32 density = bpi [MT_DENS (uptr->dynflags)]; /* the tape density in bits per inch */ +const uint32 byte_length = (gaplen * density) / 10; /* the size of the requested gap in bytes */ + +if (ctx == NULL) /* if not properly attached? */ + return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%u)\n", (int)(uptr-ctx->dptr->units), gaplen); + +if (density == 0) /* if the density has not been set */ + return MTSE_IOERR; /* then report an I/O error */ +else /* otherwise */ + return tape_erase_fwd (uptr, byte_length); /* erase the requested gap size in bytes */ +} + t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; @@ -1632,6 +1886,11 @@ return r; 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) @@ -1639,7 +1898,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 MTSE_IOERR; /* stub return */ +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. @@ -1651,6 +1913,11 @@ return MTSE_IOERR; /* stub return */ 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) @@ -1658,7 +1925,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 MTSE_IOERR; /* stub return */ +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 @@ -1688,7 +1958,7 @@ if (ctx == NULL) /* if not properly attac return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecf(unit=%d)\n", (int)(uptr-ctx->dptr->units)); -st = sim_tape_rdlntf (uptr, bc); /* get record length */ +st = sim_tape_rdrlfwd (uptr, bc); /* get record length */ *bc = MTR_L (*bc); return st; } @@ -1783,7 +2053,7 @@ if (MT_TST_PNU (uptr)) { *bc = 0; return MTSE_OK; } -st = sim_tape_rdlntr (uptr, bc); /* get record length */ +st = sim_tape_rdrlrev (uptr, bc); /* get record length */ *bc = MTR_L (*bc); return st; } @@ -1881,10 +2151,10 @@ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecf(unit=%d, count=%d, check if (check_leot) { t_mtrlnt rbc; - st = sim_tape_rdlntr (uptr, &rbc); + st = sim_tape_rdrlrev (uptr, &rbc); last_tapemark = (MTSE_TMK == st); if ((st == MTSE_OK) || (st == MTSE_TMK)) - sim_tape_rdlntf (uptr, &rbc); + sim_tape_rdrlfwd (uptr, &rbc); } *skipped = 0; *recsskipped = 0; @@ -2396,7 +2666,7 @@ return SCPE_OK; 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