1 /* sim_tape.c: simulator tape support library
2
3 Copyright (c) 1993-2008 Robert M Supnik
4 Copyright (c) 2021 The DPS8M Development Team
5
6 Permission is hereby granted, free of charge, to any person obtaining a
7 copy of this software and associated documentation files (the "Software"),
8 to deal in the Software without restriction, including without limitation
9 the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 and/or sell copies of the Software, and to permit persons to whom the
11 Software is furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23 Except as contained in this notice, the name of Robert M Supnik shall not be
24 used in advertising or otherwise to promote the sale, use or other dealings
25 in this Software without prior written authorization from Robert M Supnik.
26 */
27
28 /*
29 Public routines:
30
31 sim_tape_attach attach tape unit
32 sim_tape_detach detach tape unit
33 sim_tape_attach_help help routine for attaching tapes
34 sim_tape_rdrecf read tape record forward
35 sim_tape_rdrecr read tape record reverse
36 sim_tape_wrrecf write tape record forward
37 sim_tape_sprecf space tape record forward
38 sim_tape_sprecr space tape record reverse
39 sim_tape_wrtmk write tape mark
40 sim_tape_wreom erase remainder of tape
41 sim_tape_wreomrw erase remainder of tape & rewind
42 sim_tape_wrgap write erase gap
43 sim_tape_sprecsf space records forward
44 sim_tape_spfilef space files forward
45 sim_tape_sprecsr space records reverse
46 sim_tape_spfiler space files reverse
47 sim_tape_position generalized position
48 sim_tape_rewind rewind
49 sim_tape_reset reset unit
50 sim_tape_bot TRUE if at beginning of tape
51 sim_tape_eot TRUE if at or beyond end of tape
52 sim_tape_wrp TRUE if write protected
53 sim_tape_set_fmt set tape format
54 sim_tape_show_fmt show tape format
55 sim_tape_set_capac set tape capacity
56 sim_tape_show_capac show tape capacity
57 sim_tape_set_dens set tape density
58 sim_tape_show_dens show tape density
59 */
60
61 #include "sim_defs.h"
62 #include "sim_tape.h"
63 #include <ctype.h>
64
65 struct sim_tape_fmt {
66 const char *name; /* name */
67 int32 uflags; /* unit flags */
68 t_addr bot; /* bot test */
69 };
70
71 static struct sim_tape_fmt fmts[MTUF_N_FMT] = {
72 { "SIMH", 0, sizeof (t_mtrlnt) - 1 },
73 { "E11", 0, sizeof (t_mtrlnt) - 1 },
74 { "TPC", UNIT_RO, sizeof (t_tpclnt) - 1 },
75 { "P7B", 0, 0 },
76 /* { "TPF", UNIT_RO, 0 }, */
77 { NULL, 0, 0 }
78 };
79
80 static const uint32 bpi [] = { /* tape density table, indexed by MT_DENS constants */
81 0, /* 0 = MT_DENS_NONE -- density not set */
82 200, /* 1 = MT_DENS_200 -- 200 bpi NRZI */
83 556, /* 2 = MT_DENS_556 -- 556 bpi NRZI */
84 800, /* 3 = MT_DENS_800 -- 800 bpi NRZI */
85 1600, /* 4 = MT_DENS_1600 -- 1600 bpi PE */
86 6250 /* 5 = MT_DENS_6250 -- 6250 bpi GCR */
87 };
88
89 #define BPI_COUNT (sizeof (bpi) / sizeof (bpi [0])) /* count of density table entries */
90
91 static t_stat sim_tape_ioerr (UNIT *uptr);
92 static t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat);
93 static uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map, uint32 mapsize);
94 static t_stat sim_tape_simh_check (UNIT *uptr);
95 static t_stat sim_tape_e11_check (UNIT *uptr);
96 static t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map);
97 static void sim_tape_data_trace (UNIT *uptr, const uint8 *data, size_t len, const char* txt, int detail, uint32 reason);
98
99
100 struct tape_context {
101 DEVICE *dptr; /* Device for unit (access to debug flags) */
102 uint32 dbit; /* debugging bit for trace */
103 uint32 auto_format; /* Format determined dynamically */
104 };
105 #define tape_ctx up8 /* Field in Unit structure which points to the tape_context */
106
107 /*
108 This routine is called when the simulator stops and any time
109 the asynch mode is changed (enabled or disabled)
110 */
_sim_tape_io_flush(UNIT * uptr)111 static void _sim_tape_io_flush (UNIT *uptr)
112 {
113 fflush (uptr->fileref);
114 }
115
116 /* Attach tape unit */
117
sim_tape_attach(UNIT * uptr,CONST char * cptr)118 t_stat sim_tape_attach (UNIT *uptr, CONST char *cptr)
119 {
120 DEVICE *dptr;
121
122 if ((dptr = find_dev_from_unit (uptr)) == NULL)
123 return SCPE_NOATT;
124 return sim_tape_attach_ex (uptr, cptr, (dptr->flags & DEV_DEBUG) ? 0xFFFFFFFF : 0, 0);
125 }
126
sim_tape_attach_ex(UNIT * uptr,const char * cptr,uint32 dbit,int completion_delay)127 t_stat sim_tape_attach_ex (UNIT *uptr, const char *cptr, uint32 dbit, int completion_delay)
128 {
129 struct tape_context *ctx;
130 uint32 objc;
131 DEVICE *dptr;
132 char gbuf[CBUFSIZE];
133 t_stat r;
134 t_bool auto_format = FALSE;
135
136 if ((dptr = find_dev_from_unit (uptr)) == NULL)
137 return SCPE_NOATT;
138 if (sim_switches & SWMASK ('F')) { /* format spec? */
139 cptr = get_glyph (cptr, gbuf, 0); /* get spec */
140 if (*cptr == 0) /* must be more */
141 return SCPE_2FARG;
142 if (sim_tape_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK)
143 return sim_messagef (SCPE_ARG, "Invalid Tape Format: %s\n", gbuf);
144 sim_switches = sim_switches & ~(SWMASK ('F')); /* Record Format specifier already processed */
145 auto_format = TRUE;
146 }
147 if (MT_GET_FMT (uptr) == MTUF_F_TPC)
148 sim_switches |= SWMASK ('R'); /* Force ReadOnly attach for TPC tapes */
149 r = attach_unit (uptr, (CONST char *)cptr); /* attach unit */
150 if (r != SCPE_OK) /* error? */
151 return sim_messagef (r, "Can't open tape image: %s\n", cptr);
152 switch (MT_GET_FMT (uptr)) { /* case on format */
153
154 case MTUF_F_STD: /* SIMH */
155 if (SCPE_OK != sim_tape_simh_check (uptr)) {
156 sim_tape_detach (uptr);
157 return SCPE_FMT; /* yes, complain */
158 }
159 break;
160
161 case MTUF_F_E11: /* E11 */
162 if (SCPE_OK != sim_tape_e11_check (uptr)) {
163 sim_tape_detach (uptr);
164 return SCPE_FMT; /* yes, complain */
165 }
166 break;
167
168 case MTUF_F_TPC: /* TPC */
169 objc = sim_tape_tpc_map (uptr, NULL, 0); /* get # objects */
170 if (objc == 0) { /* tape empty? */
171 sim_tape_detach (uptr);
172 return SCPE_FMT; /* yes, complain */
173 }
174 uptr->filebuf = calloc (objc + 1, sizeof (t_addr));
175 if (uptr->filebuf == NULL) { /* map allocated? */
176 sim_tape_detach (uptr);
177 return SCPE_MEM; /* no, complain */
178 }
179 uptr->hwmark = objc + 1; /* save map size */
180 sim_tape_tpc_map (uptr, (t_addr *) uptr->filebuf, objc);/* fill map */
181 break;
182
183 default:
184 break;
185 }
186
187 uptr->tape_ctx = ctx = (struct tape_context *)calloc(1, sizeof(struct tape_context));
188 ctx->dptr = dptr; /* save DEVICE pointer */
189 ctx->dbit = dbit; /* save debug bit */
190 ctx->auto_format = auto_format; /* save that we auto selected format */
191
192 sim_tape_rewind (uptr);
193
194 uptr->io_flush = _sim_tape_io_flush;
195
196 return SCPE_OK;
197 }
198
199 /* Detach tape unit */
200
sim_tape_detach(UNIT * uptr)201 t_stat sim_tape_detach (UNIT *uptr)
202 {
203 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
204 uint32 f = 0;
205 t_stat r;
206 t_bool auto_format = FALSE;
207
208 if (uptr != NULL)
209 f = MT_GET_FMT (uptr);
210
211 if ((ctx == NULL) || !(uptr->flags & UNIT_ATT))
212 return SCPE_IERR;
213
214 if (uptr->io_flush)
215 uptr->io_flush (uptr); /* flush buffered data */
216 if (ctx)
217 auto_format = ctx->auto_format;
218
219 r = detach_unit (uptr); /* detach unit */
220 if (r != SCPE_OK)
221 return r;
222 switch (f) { /* case on format */
223
224 case MTUF_F_TPC: /* TPC */
225 if (uptr->filebuf) /* free map */
226 free (uptr->filebuf);
227 uptr->filebuf = NULL;
228 uptr->hwmark = 0;
229 break;
230
231 default:
232 break;
233 }
234
235 sim_tape_rewind (uptr);
236 free (uptr->tape_ctx);
237 uptr->tape_ctx = NULL;
238 uptr->io_flush = NULL;
239 if (auto_format) /* format was determined or specified at attach time? */
240 sim_tape_set_fmt (uptr, 0, "SIMH", NULL); /* restore default format */
241 return SCPE_OK;
242 }
243
sim_tape_attach_help(FILE * st,DEVICE * dptr,const UNIT * uptr,int32 flag,const char * cptr)244 t_stat sim_tape_attach_help(FILE *st, DEVICE *dptr, const UNIT *uptr, int32 flag, const char *cptr)
245 {
246 fprintf (st, "%s Tape Attach Help\n\n", dptr->name);
247 if (0 == (uptr-dptr->units)) {
248 if (dptr->numunits > 1) {
249 uint32 i;
250
251 for (i=0; i < dptr->numunits; ++i)
252 if (dptr->units[i].flags & UNIT_ATTABLE)
253 fprintf (st, " sim> ATTACH {switches} %s%lu tapefile\n\n", dptr->name, (unsigned long)i);
254 }
255 else
256 fprintf (st, " sim> ATTACH {switches} %s tapefile\n\n", dptr->name);
257 }
258 else
259 fprintf (st, " sim> ATTACH {switches} %s tapefile\n\n", dptr->name);
260 fprintf (st, "Attach command switches\n");
261 fprintf (st, " -R Attach Read Only.\n");
262 fprintf (st, " -E Must Exist (if not specified an attempt to create the indicated\n");
263 fprintf (st, " virtual tape will be attempted).\n");
264 fprintf (st, " -F Open the indicated tape container in a specific format (default\n");
265 fprintf (st, " is SIMH, alternatives are E11, TPC and P7B)\n");
266 return SCPE_OK;
267 }
268
sim_tape_data_trace(UNIT * uptr,const uint8 * data,size_t len,const char * txt,int detail,uint32 reason)269 static void sim_tape_data_trace(UNIT *uptr, const uint8 *data, size_t len, const char* txt, int detail, uint32 reason)
270 {
271 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
272
273 if (ctx == NULL)
274 return;
275 if (sim_deb && (ctx->dptr->dctrl & reason))
276 sim_data_trace(ctx->dptr, uptr, (detail ? data : NULL), "", len, txt, reason);
277 }
278
279 /* Read record length forward (internal routine)
280
281 Inputs:
282 uptr = pointer to tape unit
283 bc = pointer to returned record length
284 Outputs:
285 status = operation status
286
287 exit condition tape position
288 ------------------ -----------------------------------------------------
289 unit unattached unchanged
290 read error unchanged, PNU set
291 end of file/medium updated if a gap precedes, else unchanged and PNU set
292 tape mark updated
293 tape runaway updated
294 data record updated, sim_fread will read record forward
295
296 This routine is called to set up a record read or spacing in the forward
297 direction. On return, status is MTSE_OK and the tape is positioned at the
298 first data byte if a record was encountered, or status is an MTSE error code
299 giving the reason that the operation did not succeed and the tape position is
300 as indicated above.
301
302 The ANSI standards for magnetic tape recording (X3.32, X3.39, and X3.54) and
303 the equivalent ECMA standard (ECMA-62) specify a maximum erase gap length of
304 25 feet (7.6 meters). While gaps of any length may be written, gaps longer
305 than this are non-standard and may indicate that an unrecorded or erased tape
306 is being read.
307
308 If the tape density has been set via a previous "sim_tape_set_dens" call,
309 then the length is monitored when skipping over erase gaps. If the length
310 reaches 25 feet, motion is terminated, and MTSE_RUNAWAY status is returned.
311 Runaway status is also returned if an end-of-medium marker or the physical
312 end of file is encountered while spacing over a gap; however, MTSE_EOM is
313 returned if the tape is positioned at the EOM on entry.
314
315 If the density has not been set, then a gap of any length is skipped, and
316 MTSE_RUNAWAY status is never returned. In effect, erase gaps present in the
317 tape image file will be transparent to the caller.
318
319 Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format.
320 Because gaps may be partially overwritten with data records, gap metadata
321 must be examined marker-by-marker. To reduce the number of file read calls,
322 a buffer of metadata elements is used. The buffer size is initially
323 established at 256 elements but may be set to any size desired. To avoid a
324 large read for the typical case where an erase gap is not present, the first
325 read is of a single metadatum marker. If that is a gap marker, then
326 additional buffered reads are performed.
327
328 See the notes at "sim_tape_wrgap" regarding the erase gap implementation.
329
330 Implementation notes:
331
332 1. For programming convenience, erase gap processing is performed for both
333 SIMH standard and E11 tape formats, although the latter will never
334 contain erase gaps, as the "sim_tape_wrgap" call takes no action for the
335 E11 format.
336
337 2. The "feof" call cannot return a non-zero value on the first pass through
338 the loop, because the "sim_fseek" call resets the internal end-of-file
339 indicator. Subsequent passes only occur if an erase gap is present, so
340 a non-zero return indicates an EOF was seen while reading through a gap.
341
342 3. The dynamic start/stop test of the HP 3000 magnetic tape diagnostic
343 heavily exercises the erase gap scanning code. Sample test execution
344 times for various buffer sizes on a 2 GHz host platform are:
345
346 buffer size execution time
347 (elements) (CPU seconds)
348 ----------- --------------
349 1 7200
350 32 783
351 128 237
352 256 203
353 512 186
354 1024 171
355
356 4. Because an erase gap may precede the logical end-of-medium, represented
357 either by the physical end-of-file or by an EOM marker, the "position not
358 updated" flag is set only if the tape is positioned at the EOM when the
359 routine is entered. If at least one gap marker precedes the EOM, then
360 the PNU flag is not set. This ensures that a backspace-and-retry
361 sequence will work correctly in both cases.
362 */
363
sim_tape_rdlntf(UNIT * uptr,t_mtrlnt * bc)364 static t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc)
365 {
366 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
367 uint8 c;
368 t_bool all_eof;
369 uint32 f = MT_GET_FMT (uptr);
370 t_mtrlnt sbc;
371 t_tpclnt tpcbc;
372 t_mtrlnt buffer [256]; /* local tape buffer */
373 uint32 bufcntr, bufcap; /* buffer counter and capacity */
374 int32 runaway_counter, sizeof_gap; /* bytes remaining before runaway and bytes per gap */
375 t_stat r = MTSE_OK;
376
377 MT_CLR_PNU (uptr); /* clear the position-not-updated flag */
378
379 if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */
380 return MTSE_UNATT; /* then quit with an error */
381 if (ctx == NULL) /* if not properly attached? */
382 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
383
384 sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set the initial tape position */
385
386 switch (f) { /* the read method depends on the tape format */
387
388 case MTUF_F_STD:
389 case MTUF_F_E11:
390 runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set the largest legal gap size in bytes */
391
392 if (runaway_counter == 0) { /* if tape density has not been not set */
393 sizeof_gap = 0; /* then disable runaway detection */
394 runaway_counter = INT_MAX; /* to allow gaps of any size */
395 }
396 else /* otherwise */
397 sizeof_gap = sizeof (t_mtrlnt); /* set the size of the gap */
398
399 bufcntr = 0; /* force an initial read */
400 bufcap = 0; /* but of just one metadata marker */
401
402 do { /* loop until a record, gap, or error is seen */
403 if (bufcntr == bufcap) { /* if the buffer is empty then refill it */
404 if (feof (uptr->fileref)) { /* if we hit the EOF while reading a gap */
405 if (sizeof_gap > 0) /* then if detection is enabled */
406 r = MTSE_RUNAWAY; /* then report a tape runaway */
407 else /* otherwise report the physical EOF */
408 r = MTSE_EOM; /* as the end-of-medium */
409 break;
410 }
411
412 else if (bufcap == 0) /* otherwise if this is the initial read */
413 bufcap = 1; /* then start with just one marker */
414
415 else /* otherwise reset the capacity */
416 bufcap = sizeof (buffer) /* to the full size of the buffer */
417 / sizeof (buffer [0]);
418
419 bufcap = sim_fread (buffer, /* fill the buffer */
420 sizeof (t_mtrlnt), /* with tape metadata */
421 bufcap,
422 uptr->fileref);
423
424 if (ferror (uptr->fileref)) { /* if a file I/O error occurred */
425 if (bufcntr == 0) /* then if this is the initial read */
426 MT_SET_PNU (uptr); /* then set position not updated */
427
428 r = sim_tape_ioerr (uptr); /* report the error and quit */
429 break;
430 }
431
432 else if (bufcap == 0 /* otherwise if positioned at the physical EOF */
433 || buffer [0] == MTR_EOM) /* or at the logical EOM */
434 if (bufcntr == 0) { /* then if this is the initial read */
435 MT_SET_PNU (uptr); /* then set position not updated */
436 r = MTSE_EOM; /* and report the end-of-medium and quit */
437 break;
438 }
439
440 else { /* otherwise some gap has already been skipped */
441 if (sizeof_gap > 0) /* so if detection is enabled */
442 r = MTSE_RUNAWAY; /* then report a tape runaway */
443 else /* otherwise report the physical EOF */
444 r = MTSE_EOM; /* as the end-of-medium */
445 break;
446 }
447
448 else /* otherwise reset the index */
449 bufcntr = 0; /* to the start of the buffer */
450 }
451
452 *bc = buffer [bufcntr++]; /* store the metadata marker value */
453
454 if (*bc == MTR_EOM) { /* if an end-of-medium marker is seen */
455 if (sizeof_gap > 0) /* then if detection is enabled */
456 r = MTSE_RUNAWAY; /* then report a tape runaway */
457 else /* otherwise report the physical EOF */
458 r = MTSE_EOM; /* as the end-of-medium */
459 break;
460 }
461
462 uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* space over the marker */
463
464 if (*bc == MTR_TMK) { /* if the value is a tape mark */
465 r = MTSE_TMK; /* then quit with tape mark status */
466 break;
467 }
468
469 else if (*bc == MTR_GAP) /* otherwise if the value is a full gap */
470 runaway_counter -= sizeof_gap; /* then decrement the gap counter */
471
472 else if (*bc == MTR_FHGAP) { /* otherwise if the value if a half gap */
473 uptr->pos = uptr->pos - sizeof (t_mtrlnt) / 2; /* then back up */
474 (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* to resync */
475 bufcntr = bufcap; /* mark the buffer as invalid to force a read */
476
477 *bc = MTR_GAP; /* reset the marker */
478 runaway_counter -= sizeof_gap / 2; /* and decrement the gap counter */
479 }
480
481 else { /* otherwise it's a record marker */
482 if (bufcntr < bufcap) /* if the position is within the buffer */
483 (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* then seek to the data area */
484
485 sbc = MTR_L (*bc); /* extract the record length */
486 uptr->pos = uptr->pos + sizeof (t_mtrlnt) /* position to the start */
487 + (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc); /* of the record */
488 }
489 }
490 while (*bc == MTR_GAP && runaway_counter > 0); /* continue until data or runaway occurs */
491
492 if (r == MTSE_OK && runaway_counter <= 0) /* if a tape runaway occurred */
493 r = MTSE_RUNAWAY; /* then report it */
494
495 break; /* otherwise the operation succeeded */
496
497 case MTUF_F_TPC:
498 (void)sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref);
499 *bc = tpcbc; /* save rec lnt */
500 if (ferror (uptr->fileref)) { /* error? */
501 MT_SET_PNU (uptr); /* pos not upd */
502 return sim_tape_ioerr (uptr);
503 }
504 if (feof (uptr->fileref)) { /* eof? */
505 MT_SET_PNU (uptr); /* pos not upd */
506 r = MTSE_EOM;
507 break;
508 }
509 uptr->pos = uptr->pos + sizeof (t_tpclnt); /* spc over reclnt */
510 if (tpcbc == TPC_TMK) /* tape mark? */
511 r = MTSE_TMK;
512 uptr->pos = uptr->pos + ((tpcbc + 1) & ~1); /* spc over record */
513 break;
514
515 case MTUF_F_P7B:
516 for (sbc = 0, all_eof = 1; ; sbc++) { /* loop thru record */
517 (void)sim_fread (&c, sizeof (uint8), 1, uptr->fileref);
518 if (ferror (uptr->fileref)) { /* error? */
519 MT_SET_PNU (uptr); /* pos not upd */
520 return sim_tape_ioerr (uptr);
521 }
522 if (feof (uptr->fileref)) { /* eof? */
523 if (sbc == 0) /* no data? eom */
524 return MTSE_EOM;
525 break; /* treat like eor */
526 }
527 if ((sbc != 0) && (c & P7B_SOR)) /* next record? */
528 break;
529 if ((c & P7B_DPAR) != P7B_EOF)
530 all_eof = 0;
531 }
532 *bc = sbc; /* save rec lnt */
533 (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */
534 uptr->pos = uptr->pos + sbc; /* spc over record */
535 if (all_eof) /* tape mark? */
536 r = MTSE_TMK;
537 break;
538
539 default:
540 return MTSE_FMT;
541 }
542 sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", r, *bc, uptr->pos);
543 return r;
544 }
545
546 /* Read record length reverse (internal routine)
547
548 Inputs:
549 uptr = pointer to tape unit
550 bc = pointer to returned record length
551 Outputs:
552 status = operation status
553
554 exit condition tape position
555 ------------------ -------------------------------------------
556 unit unattached unchanged
557 beginning of tape unchanged
558 read error unchanged
559 end of file unchanged
560 end of medium updated
561 tape mark updated
562 tape runaway updated
563 data record updated, sim_fread will read record forward
564
565 This routine is called to set up a record read or spacing in the reverse
566 direction. On return, status is MTSE_OK and the tape is positioned at the
567 first data byte if a record was encountered, or status is an MTSE error code
568 giving the reason that the operation did not succeed and the tape position is
569 as indicated above.
570
571 See the notes at "sim_tape_rdlntf" and "sim_tape_wrgap" regarding tape
572 runaway and the erase gap implementation, respectively.
573 */
574
sim_tape_rdlntr(UNIT * uptr,t_mtrlnt * bc)575 static t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc)
576 {
577 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
578 uint8 c;
579 t_bool all_eof;
580 uint32 f = MT_GET_FMT (uptr);
581 t_addr ppos;
582 t_mtrlnt sbc;
583 t_tpclnt tpcbc;
584 t_mtrlnt buffer [256]; /* local tape buffer */
585 uint32 bufcntr, bufcap; /* buffer counter and capacity */
586 int32 runaway_counter, sizeof_gap; /* bytes remaining before runaway and bytes per gap */
587 t_stat r = MTSE_OK;
588
589 MT_CLR_PNU (uptr); /* clear the position-not-updated flag */
590
591 if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */
592 return MTSE_UNATT; /* then quit with an error */
593 if (ctx == NULL) /* if not properly attached? */
594 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
595
596 if (sim_tape_bot (uptr)) /* if the unit is positioned at the BOT */
597 return MTSE_BOT; /* then reading backward is not possible */
598
599 switch (f) { /* the read method depends on the tape format */
600
601 case MTUF_F_STD:
602 case MTUF_F_E11:
603 runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set largest legal gap size in bytes */
604
605 if (runaway_counter == 0) { /* if tape density has not been not set */
606 sizeof_gap = 0; /* then disable runaway detection */
607 runaway_counter = INT_MAX; /* to allow gaps of any size */
608 }
609
610 else /* otherwise */
611 sizeof_gap = sizeof (t_mtrlnt); /* set the size of the gap */
612
613 bufcntr = 0; /* force an initial read */
614 bufcap = 1; /* but of just one metadata marker */
615
616 do { /* loop until a record, gap, or error seen */
617 if (bufcntr == 0) { /* if the buffer is empty then refill it */
618 if (sim_tape_bot (uptr)) { /* if the search has backed into the BOT */
619 r = MTSE_BOT; /* then quit with an error */
620 break;
621 }
622
623 else if (uptr->pos < sizeof (buffer)) /* if less than a full buffer remains */
624 bufcap = (uint32) uptr->pos /* then reduce the capacity accordingly */
625 / sizeof (t_mtrlnt);
626
627 (void)sim_fseek (uptr->fileref, /* seek back to the location */
628 uptr->pos - bufcap * sizeof (t_mtrlnt), /* corresponding to the start */
629 SEEK_SET); /* of the buffer */
630
631 bufcntr = sim_fread (buffer, sizeof (t_mtrlnt), /* fill the buffer */
632 bufcap, uptr->fileref); /* with tape metadata */
633
634 if (ferror (uptr->fileref)) { /* if a file I/O error occurred */
635 MT_SET_PNU (uptr); /* then set position not updated */
636 r = sim_tape_ioerr (uptr); /* report the error and quit */
637 break;
638 }
639
640 else /* otherwise reset the capacity */
641 bufcap = sizeof (buffer) /* to the full size of the buffer */
642 / sizeof (buffer [0]);
643 }
644
645 *bc = buffer [--bufcntr]; /* store the metadata marker value */
646
647 uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* backspace over the marker */
648
649 if (*bc == MTR_TMK) { /* if the marker is a tape mark */
650 r = MTSE_TMK; /* then quit with tape mark status */
651 break;
652 }
653
654 else if (*bc == MTR_GAP) /* otherwise if the marker is a full gap */
655 runaway_counter -= sizeof_gap; /* then decrement the gap counter */
656
657 else if ((*bc & MTR_M_RHGAP) == MTR_RHGAP /* otherwise if the marker */
658 || *bc == MTR_RRGAP) { /* is a half gap */
659 uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* then position forward to resync */
660 bufcntr = 0; /* mark the buffer as invalid to force a read */
661
662 *bc = MTR_GAP; /* reset the marker */
663 runaway_counter -= sizeof_gap / 2; /* and decrement the gap counter */
664 }
665
666 else { /* otherwise it's a record marker */
667 sbc = MTR_L (*bc); /* extract the record length */
668 uptr->pos = uptr->pos - sizeof (t_mtrlnt) /* position to the start */
669 - (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc); /* of the record */
670 (void)sim_fseek (uptr->fileref, /* seek to the data area */
671 uptr->pos + sizeof (t_mtrlnt), SEEK_SET);
672 }
673 }
674 while (*bc == MTR_GAP && runaway_counter > 0); /* continue until data or runaway occurs */
675
676 if (r == MTSE_OK && runaway_counter <= 0) /* if a tape runaway occurred */
677 r = MTSE_RUNAWAY; /* then report it */
678
679 break; /* otherwise the operation succeeded */
680
681 case MTUF_F_TPC:
682 ppos = sim_tape_tpc_fnd (uptr, (t_addr *) uptr->filebuf); /* find prev rec */
683 (void)sim_fseek (uptr->fileref, ppos, SEEK_SET); /* position */
684 (void)sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref);
685 *bc = tpcbc; /* save rec lnt */
686 if (ferror (uptr->fileref)) /* error? */
687 return sim_tape_ioerr (uptr);
688 if (feof (uptr->fileref)) { /* eof? */
689 r = MTSE_EOM;
690 break;
691 }
692 uptr->pos = ppos; /* spc over record */
693 if (*bc == MTR_TMK) { /* tape mark? */
694 r = MTSE_TMK;
695 break;
696 }
697 (void)sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET);
698 break;
699
700 case MTUF_F_P7B:
701 for (sbc = 1, all_eof = 1; (t_addr) sbc <= uptr->pos ; sbc++) {
702 (void)sim_fseek (uptr->fileref, uptr->pos - sbc, SEEK_SET);
703 (void)sim_fread (&c, sizeof (uint8), 1, uptr->fileref);
704 if (ferror (uptr->fileref)) /* error? */
705 return sim_tape_ioerr (uptr);
706 if (feof (uptr->fileref)) { /* eof? */
707 r = MTSE_EOM;
708 break;
709 }
710 if ((c & P7B_DPAR) != P7B_EOF)
711 all_eof = 0;
712 if (c & P7B_SOR) /* start of record? */
713 break;
714 }
715 uptr->pos = uptr->pos - sbc; /* update position */
716 *bc = sbc; /* save rec lnt */
717 (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */
718 if (all_eof) /* tape mark? */
719 r = MTSE_TMK;
720 break;
721
722 default:
723 return MTSE_FMT;
724 }
725 sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", r, *bc, uptr->pos);
726 return r;
727 }
728
729 /* Read record forward
730
731 Inputs:
732 uptr = pointer to tape unit
733 buf = pointer to buffer
734 bc = pointer to returned record length
735 max = maximum record size
736 Outputs:
737 status = operation status
738
739 exit condition position
740
741 unit unattached unchanged
742 read error unchanged, PNU set
743 end of file/medium unchanged, PNU set
744 invalid record unchanged, PNU set
745 tape mark updated
746 data record updated
747 data record error updated
748 */
749
sim_tape_rdrecf(UNIT * uptr,uint8 * buf,t_mtrlnt * bc,t_mtrlnt max)750 t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max)
751 {
752 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
753 uint32 f = MT_GET_FMT (uptr);
754 t_mtrlnt i, tbc, rbc;
755 t_addr opos;
756 t_stat st;
757
758 if (ctx == NULL) /* if not properly attached? */
759 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
760 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecf(unit=%d, buf=%p, max=%d)\n", (int)(uptr-ctx->dptr->units), buf, max);
761
762 opos = uptr->pos; /* old position */
763 if (MTSE_OK != (st = sim_tape_rdlntf (uptr, &tbc))) /* read rec lnt */
764 return st;
765 *bc = rbc = MTR_L (tbc); /* strip error flag */
766 if (rbc > max) { /* rec out of range? */
767 MT_SET_PNU (uptr);
768 uptr->pos = opos;
769 return MTSE_INVRL;
770 }
771 i = (t_mtrlnt)sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */
772 if (ferror (uptr->fileref)) { /* error? */
773 MT_SET_PNU (uptr);
774 uptr->pos = opos;
775 return sim_tape_ioerr (uptr);
776 }
777 for ( ; i < rbc; i++) /* fill with 0's */
778 buf[i] = 0;
779 if (f == MTUF_F_P7B) /* p7b? strip SOR */
780 buf[0] = buf[0] & P7B_DPAR;
781 sim_tape_data_trace(uptr, buf, rbc, "Record Read", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
782 return (MTR_F (tbc)? MTSE_RECE: MTSE_OK);
783 }
784
sim_tape_rdrecf_a(UNIT * uptr,uint8 * buf,t_mtrlnt * bc,t_mtrlnt max,TAPE_PCALLBACK callback)785 t_stat sim_tape_rdrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback)
786 {
787 t_stat r = SCPE_OK;
788 r = sim_tape_rdrecf (uptr, buf, bc, max);
789 return r;
790 }
791
792
793 /* Read record reverse
794
795 Inputs:
796 uptr = pointer to tape unit
797 buf = pointer to buffer
798 bc = pointer to returned record length
799 max = maximum record size
800 Outputs:
801 status = operation status
802
803 exit condition position
804
805 unit unattached unchanged
806 read error unchanged
807 end of file unchanged
808 end of medium updated
809 invalid record unchanged
810 tape mark updated
811 data record updated
812 data record error updated
813 */
814
sim_tape_rdrecr(UNIT * uptr,uint8 * buf,t_mtrlnt * bc,t_mtrlnt max)815 t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max)
816 {
817 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
818 uint32 f = MT_GET_FMT (uptr);
819 t_mtrlnt i, rbc, tbc;
820 t_stat st;
821
822 if (ctx == NULL) /* if not properly attached? */
823 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
824 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecr(unit=%d, buf=%p, max=%d)\n", (int)(uptr-ctx->dptr->units), buf, max);
825
826 if (MTSE_OK != (st = sim_tape_rdlntr (uptr, &tbc))) /* read rec lnt */
827 return st;
828 *bc = rbc = MTR_L (tbc); /* strip error flag */
829 if (rbc > max) /* rec out of range? */
830 return MTSE_INVRL;
831 i = (t_mtrlnt)sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */
832 if (ferror (uptr->fileref)) /* error? */
833 return sim_tape_ioerr (uptr);
834 for ( ; i < rbc; i++) /* fill with 0's */
835 buf[i] = 0;
836 if (f == MTUF_F_P7B) /* p7b? strip SOR */
837 buf[0] = buf[0] & P7B_DPAR;
838 sim_tape_data_trace(uptr, buf, rbc, "Record Read Reverse", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
839 return (MTR_F (tbc)? MTSE_RECE: MTSE_OK);
840 }
841
sim_tape_rdrecr_a(UNIT * uptr,uint8 * buf,t_mtrlnt * bc,t_mtrlnt max,TAPE_PCALLBACK callback)842 t_stat sim_tape_rdrecr_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback)
843 {
844 t_stat r = SCPE_OK;
845 r = sim_tape_rdrecr (uptr, buf, bc, max);
846 return r;
847 }
848
849 /* Write record forward
850
851 Inputs:
852 uptr = pointer to tape unit
853 buf = pointer to buffer
854 bc = record length
855 Outputs:
856 status = operation status
857
858 exit condition position
859
860 unit unattached unchanged
861 write protect unchanged
862 write error unchanged, PNU set
863 data record updated
864 */
865
sim_tape_wrrecf(UNIT * uptr,uint8 * buf,t_mtrlnt bc)866 t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc)
867 {
868 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
869 uint32 f = MT_GET_FMT (uptr);
870 t_mtrlnt sbc;
871
872 if (ctx == NULL) /* if not properly attached? */
873 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
874 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrrecf(unit=%d, buf=%p, bc=%d)\n", (int)(uptr-ctx->dptr->units), buf, bc);
875
876 sim_tape_data_trace(uptr, buf, bc, "Record Write", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
877 MT_CLR_PNU (uptr);
878 sbc = MTR_L (bc);
879 if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
880 return MTSE_UNATT;
881 if (sim_tape_wrp (uptr)) /* write prot? */
882 return MTSE_WRP;
883 if (sbc == 0) /* nothing to do? */
884 return MTSE_OK;
885 (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */
886 switch (f) { /* case on format */
887
888 case MTUF_F_STD: /* standard */
889 sbc = MTR_L ((bc + 1) & ~1); /* pad odd length */
890 case MTUF_F_E11: /* E11 */
891 (void)sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref);
892 (void)sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref);
893 (void)sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref);
894 if (ferror (uptr->fileref)) { /* error? */
895 MT_SET_PNU (uptr);
896 return sim_tape_ioerr (uptr);
897 }
898 uptr->pos = uptr->pos + sbc + (2 * sizeof (t_mtrlnt)); /* move tape */
899 break;
900
901 case MTUF_F_P7B: /* Pierce 7B */
902 buf[0] = buf[0] | P7B_SOR; /* mark start of rec */
903 (void)sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref);
904 (void)sim_fwrite (buf, sizeof (uint8), 1, uptr->fileref); /* delimit rec */
905 if (ferror (uptr->fileref)) { /* error? */
906 MT_SET_PNU (uptr);
907 return sim_tape_ioerr (uptr);
908 }
909 uptr->pos = uptr->pos + sbc; /* move tape */
910 break;
911 }
912 sim_tape_data_trace(uptr, buf, sbc, "Record Written", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
913 return MTSE_OK;
914 }
915
sim_tape_wrrecf_a(UNIT * uptr,uint8 * buf,t_mtrlnt bc,TAPE_PCALLBACK callback)916 t_stat sim_tape_wrrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt bc, TAPE_PCALLBACK callback)
917 {
918 t_stat r = SCPE_OK;
919 r = sim_tape_wrrecf (uptr, buf, bc);
920 return r;
921 }
922
923 /* Write metadata forward (internal routine) */
924
sim_tape_wrdata(UNIT * uptr,uint32 dat)925 static t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat)
926 {
927 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
928
929 MT_CLR_PNU (uptr);
930 if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
931 return MTSE_UNATT;
932 if (ctx == NULL) /* if not properly attached? */
933 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
934 if (sim_tape_wrp (uptr)) /* write prot? */
935 return MTSE_WRP;
936 (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */
937 (void)sim_fwrite (&dat, sizeof (t_mtrlnt), 1, uptr->fileref);
938 if (ferror (uptr->fileref)) { /* error? */
939 MT_SET_PNU (uptr);
940 return sim_tape_ioerr (uptr);
941 }
942 sim_debug (MTSE_DBG_STR, ctx->dptr, "wr_lnt: lnt: %d, pos: %" T_ADDR_FMT "u\n", dat, uptr->pos);
943 uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* move tape */
944 return MTSE_OK;
945 }
946
947 /* Write tape mark */
948
sim_tape_wrtmk(UNIT * uptr)949 t_stat sim_tape_wrtmk (UNIT *uptr)
950 {
951 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
952
953 if (ctx == NULL) /* if not properly attached? */
954 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
955 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrtmk(unit=%d)\n", (int)(uptr-ctx->dptr->units));
956 if (MT_GET_FMT (uptr) == MTUF_F_P7B) { /* P7B? */
957 uint8 buf = P7B_EOF; /* eof mark */
958 return sim_tape_wrrecf (uptr, &buf, 1); /* write char */
959 }
960 return sim_tape_wrdata (uptr, MTR_TMK);
961 }
962
sim_tape_wrtmk_a(UNIT * uptr,TAPE_PCALLBACK callback)963 t_stat sim_tape_wrtmk_a (UNIT *uptr, TAPE_PCALLBACK callback)
964 {
965 t_stat r = MTSE_OK;
966 r = sim_tape_wrtmk (uptr);
967 return r;
968 }
969
970 /* Write end of medium */
971
sim_tape_wreom(UNIT * uptr)972 t_stat sim_tape_wreom (UNIT *uptr)
973 {
974 t_stat result;
975 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
976
977 if (ctx == NULL) /* if not properly attached? */
978 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
979 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wreom(unit=%d)\n", (int)(uptr-ctx->dptr->units));
980 if (MT_GET_FMT (uptr) == MTUF_F_P7B) /* cant do P7B */
981 return MTSE_FMT;
982
983 result = sim_tape_wrdata (uptr, MTR_EOM); /* write the EOM marker */
984
985 uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* restore original tape position */
986 MT_SET_PNU (uptr); /* indicate that position was not updated */
987
988 return result;
989 }
990
sim_tape_wreom_a(UNIT * uptr,TAPE_PCALLBACK callback)991 t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback)
992 {
993 t_stat r = MTSE_OK;
994 r = sim_tape_wreom (uptr);
995 return r;
996 }
997
998 /* Write end of medium-rewind */
999
sim_tape_wreomrw(UNIT * uptr)1000 t_stat sim_tape_wreomrw (UNIT *uptr)
1001 {
1002 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1003 t_stat r;
1004
1005 if (ctx == NULL) /* if not properly attached? */
1006 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
1007 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wreomrw(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1008 if (MT_GET_FMT (uptr) == MTUF_F_P7B) /* cant do P7B */
1009 return MTSE_FMT;
1010 r = sim_tape_wrdata (uptr, MTR_EOM);
1011 if (r == MTSE_OK)
1012 r = sim_tape_rewind (uptr);
1013 return r;
1014 }
1015
sim_tape_wreomrw_a(UNIT * uptr,TAPE_PCALLBACK callback)1016 t_stat sim_tape_wreomrw_a (UNIT *uptr, TAPE_PCALLBACK callback)
1017 {
1018 t_stat r = MTSE_OK;
1019 r = sim_tape_wreomrw (uptr);
1020 return r;
1021 }
1022
1023 /* Write erase gap
1024
1025 Inputs:
1026 uptr = pointer to tape unit
1027 gaplen = length of gap in tenths of an inch
1028
1029 Outputs:
1030 status = operation status
1031
1032 exit condition position
1033 ------------------ ------------------
1034 unit unattached unchanged
1035 unsupported format unchanged
1036 write protected unchanged
1037 read error unchanged, PNU set
1038 write error unchanged, PNU set
1039 gap written updated
1040
1041
1042 An erase gap is represented in the tape image file by a special metadata
1043 value. This value is chosen so that it is still recognizable even if it has
1044 been "cut in half" by a subsequent data overwrite that does not end on a
1045 metadatum-sized boundary. In addition, a range of metadata values are
1046 reserved for detection in the reverse direction. Erase gaps are currently
1047 supported only in SIMH (MTUF_F_STD) tape format.
1048
1049 This implementation supports erasing gaps in the middle of a populated tape
1050 image and will always produce a valid image. It also produces valid images
1051 when overwriting gaps with data records, with one exception: a data write
1052 that leaves only two bytes of gap remaining will produce an invalid tape.
1053 This limitation is deemed acceptable, as it is analogous to the existing
1054 limitation that data records cannot overwrite other data records without
1055 producing an invalid tape.
1056
1057 Because SIMH tape images do not carry physical parameters (e.g., recording
1058 density), overwriting a tape image file containing gap metadata is
1059 problematic if the density setting is not the same as that used during
1060 recording. There is no way to establish a gap of a certain length
1061 unequivocally in an image file, so this implementation establishes a gap of a
1062 certain number of bytes that reflect the desired gap length at the tape
1063 density in bits per inch used during writing.
1064
1065 To write an erase gap, the implementation uses one of two approaches,
1066 depending on whether or not the current tape position is at EOM. Erasing at
1067 EOM presents no special difficulties; gap metadata markers are written for
1068 the prescribed number of bytes. If the tape is not at EOM, then erasing must
1069 take into account the existing record structure to ensure that a valid tape
1070 image is maintained.
1071
1072 The general approach is to erase for the nominal number of bytes but to
1073 increase that length, if necessary, to ensure that a partially overwritten
1074 data record at the end of the gap can be altered to maintain validity.
1075 Because the smallest legal tape record requires space for two metadata
1076 markers plus two data bytes, an erasure that would leave less than that
1077 is increased to consume the entire record. Otherwise, the final record is
1078 truncated appropriately by rewriting the leading and trailing length words
1079 appropriately.
1080
1081 When reading in either direction, gap metadata markers are ignored (skipped)
1082 until a record length header, EOF marker, EOM marker, or physical EOF is
1083 encountered. Thus, tape images containing gap metadata are transparent to
1084 the calling simulator (unless tape runaway support is enabled -- see the
1085 notes at "sim_tape_rdlntf" for details).
1086
1087 The permissibility of data record lengths that are not multiples of the
1088 metadatum size presents a difficulty when reading. If such an "odd length"
1089 record is written over a gap, half of a metadata marker will exist
1090 immediately after the trailing record length.
1091
1092 This condition is detected when reading forward by the appearance of a
1093 "reversed" marker. The value appears reversed because the value is made up
1094 of half of one marker and half of the next. This is handled by seeking
1095 forward two bytes to resync (the stipulation above that the overwrite cannot
1096 leave only two bytes of gap means that at least one "whole" metadata marker
1097 will follow). Reading in reverse presents a more complex problem, because
1098 half of the marker is from the preceding trailing record length marker and
1099 therefore could be any of a range of values. However, that range is
1100 restricted by the SIMH tape specification requirement that record length
1101 metadata values must have bits 30:24 set to zero. This allows unambiguous
1102 detection of the condition.
1103
1104 The value chosen for gap metadata and the values reserved for "half-gap"
1105 detection are:
1106
1107 0xFFFFFFFE - primary gap value
1108 0xFFFEFFFF - reserved (indicates half-gap in forward reads)
1109 0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads)
1110 0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads)
1111
1112 If the tape density has been set via a previous sim_tape_set_dens call, and
1113 the tape format is set to SIMH format, then this routine will write a gap of
1114 the appropriate size. If the density has not been set, then no action will
1115 be taken, and either MTSE_IOERR or MTSE_OK status will be returned, depending
1116 on whether SIMH or another format is selected, respectively. A simulator
1117 that calls this routine must set the density beforehand; failure to do so is
1118 an error. However, calling while another format is enabled is OK and is
1119 treated as a no-operation. This allows a device simulator that supports
1120 writing erase gaps to use the same code without worrying about the tape
1121 format currently selected by the user.
1122 */
1123
sim_tape_wrgap(UNIT * uptr,uint32 gaplen)1124 t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen)
1125 {
1126 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1127 t_stat st;
1128 t_mtrlnt meta, sbc, new_len, rec_size;
1129 t_addr gap_pos = uptr->pos;
1130 uint32 file_size, marker_count, tape_density;
1131 int32 gap_needed;
1132 uint32 gap_alloc = 0; /* gap currently allocated from the tape */
1133 const uint32 format = MT_GET_FMT (uptr); /* tape format */
1134 const uint32 meta_size = sizeof (t_mtrlnt); /* bytes per metadatum */
1135 const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* smallest data record size */
1136
1137 if (ctx == NULL) /* if not properly attached? */
1138 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
1139 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%u)\n", (int)(uptr-ctx->dptr->units), gaplen);
1140
1141 MT_CLR_PNU (uptr);
1142
1143 if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */
1144 return MTSE_UNATT; /* then we cannot proceed */
1145
1146 else if (sim_tape_wrp (uptr)) /* otherwise if the unit is write protected */
1147 return MTSE_WRP; /* then we cannot write */
1148
1149 tape_density = bpi [MT_DENS (uptr->dynflags)]; /* get the density of the tape */
1150
1151 if (format != MTUF_F_STD) /* if erase gaps aren't supported by the format */
1152 return MTSE_OK; /* then take no action */
1153 else if (tape_density == 0) /* otherwise if the density is not set */
1154 return MTSE_IOERR; /* then report an I/O error */
1155 else /* otherwise */
1156 gap_needed = (gaplen * tape_density) / 10; /* determine the gap size needed in bytes */
1157
1158 file_size = sim_fsize (uptr->fileref); /* get file size */
1159 sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */
1160
1161 /* Read tape records and allocate to gap until amount required is consumed.
1162
1163 Read next metadatum from tape:
1164 - EOF or EOM: allocate remainder of bytes needed.
1165 - TMK or GAP: allocate sizeof(metadatum) bytes.
1166 - Reverse GAP: allocate sizeof(metadatum) / 2 bytes.
1167 - Data record: see below.
1168
1169 Loop until bytes needed = 0.
1170 */
1171
1172 do {
1173 (void)sim_fread (&meta, meta_size, 1, uptr->fileref); /* read metadatum */
1174
1175 if (ferror (uptr->fileref)) { /* read error? */
1176 uptr->pos = gap_pos; /* restore original position */
1177 MT_SET_PNU (uptr); /* position not updated */
1178 return sim_tape_ioerr (uptr); /* translate error */
1179 }
1180 else
1181 uptr->pos = uptr->pos + meta_size; /* move tape over datum */
1182
1183 if (feof (uptr->fileref) || (meta == MTR_EOM)) { /* at eof or eom? */
1184 gap_alloc = gap_alloc + gap_needed; /* allocate remainder */
1185 gap_needed = 0;
1186 }
1187
1188 else if ((meta == MTR_GAP) || (meta == MTR_TMK)) { /* gap or tape mark? */
1189 gap_alloc = gap_alloc + meta_size; /* allocate marker space */
1190 gap_needed = gap_needed - meta_size; /* reduce requirement */
1191 }
1192
1193 else if (meta == MTR_FHGAP) { /* half gap? */
1194 uptr->pos = uptr->pos - meta_size / 2; /* backup to resync */
1195 sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */
1196 gap_alloc = gap_alloc + meta_size / 2; /* allocate marker space */
1197 gap_needed = gap_needed - meta_size / 2; /* reduce requirement */
1198 }
1199
1200 else if (uptr->pos +
1201 MTR_L (meta) + meta_size > file_size) { /* rec len out of range? */
1202 gap_alloc = gap_alloc + gap_needed; /* presume overwritten tape */
1203 gap_needed = 0; /* allocate remainder */
1204 }
1205
1206 /* Allocate a data record:
1207 - Determine record size in bytes (including metadata)
1208 - If record size - bytes needed < smallest allowed record size,
1209 allocate entire record to gap, else allocate needed amount and
1210 truncate data record to reflect remainder.
1211 */
1212 else { /* data record */
1213 sbc = MTR_L (meta); /* get record data length */
1214 rec_size = ((sbc + 1) & ~1) + meta_size * 2; /* overall size in bytes */
1215
1216 if (rec_size < gap_needed + min_rec_size) { /* rec too small? */
1217 uptr->pos = uptr->pos - meta_size + rec_size; /* position past record */
1218 sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* move tape */
1219 gap_alloc = gap_alloc + rec_size; /* allocate record */
1220 gap_needed = gap_needed - rec_size; /* reduce requirement */
1221 }
1222
1223 else { /* record size OK */
1224 uptr->pos = uptr->pos - meta_size + gap_needed; /* position to end of gap */
1225 new_len = MTR_F (meta) | (sbc - gap_needed); /* truncate to new len */
1226 st = sim_tape_wrdata (uptr, new_len); /* write new rec len */
1227
1228 if (st != MTSE_OK) { /* write OK? */
1229 uptr->pos = gap_pos; /* restore orig pos */
1230 return st; /* PNU was set by wrdata */
1231 }
1232
1233 uptr->pos = uptr->pos + sbc - gap_needed; /* position to end of data */
1234 st = sim_tape_wrdata (uptr, new_len); /* write new rec len */
1235
1236 if (st != MTSE_OK) { /* write OK? */
1237 uptr->pos = gap_pos; /* restore orig pos */
1238 return st; /* PNU was set by wrdata */
1239 }
1240
1241 gap_alloc = gap_alloc + gap_needed; /* allocate remainder */
1242 gap_needed = 0;
1243 }
1244 }
1245 }
1246 while (gap_needed > 0);
1247
1248 uptr->pos = gap_pos; /* reposition to gap start */
1249
1250 if (gap_alloc & (meta_size - 1)) { /* gap size "odd?" */
1251 st = sim_tape_wrdata (uptr, MTR_FHGAP); /* write half gap marker */
1252 if (st != MTSE_OK) { /* write OK? */
1253 uptr->pos = gap_pos; /* restore orig pos */
1254 return st; /* PNU was set by wrdata */
1255 }
1256 uptr->pos = uptr->pos - meta_size / 2; /* realign position */
1257 gap_alloc = gap_alloc - 2; /* decrease gap to write */
1258 }
1259
1260 marker_count = gap_alloc / meta_size; /* count of gap markers */
1261
1262 do {
1263 st = sim_tape_wrdata (uptr, MTR_GAP); /* write gap markers */
1264 if (st != MTSE_OK) { /* write OK? */
1265 uptr->pos = gap_pos; /* restore orig pos */
1266 return st; /* PNU was set by wrdata */
1267 }
1268 }
1269 while (--marker_count > 0);
1270
1271 return MTSE_OK;
1272 }
1273
sim_tape_wrgap_a(UNIT * uptr,uint32 gaplen,TAPE_PCALLBACK callback)1274 t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, TAPE_PCALLBACK callback)
1275 {
1276 t_stat r = MTSE_OK;
1277 return r;
1278 }
1279
1280 /* Space record forward
1281
1282 Inputs:
1283 uptr = pointer to tape unit
1284 bc = pointer to size of record skipped
1285 Outputs:
1286 status = operation status
1287
1288 exit condition position
1289
1290 unit unattached unchanged
1291 read error unchanged, PNU set
1292 end of file/medium unchanged, PNU set
1293 tape mark updated
1294 data record updated
1295 data record error updated
1296 */
1297
sim_tape_sprecf(UNIT * uptr,t_mtrlnt * bc)1298 t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc)
1299 {
1300 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1301 t_stat st;
1302
1303 if (ctx == NULL) /* if not properly attached? */
1304 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
1305 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecf(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1306
1307 st = sim_tape_rdlntf (uptr, bc); /* get record length */
1308 *bc = MTR_L (*bc);
1309 return st;
1310 }
1311
sim_tape_sprecf_a(UNIT * uptr,t_mtrlnt * bc,TAPE_PCALLBACK callback)1312 t_stat sim_tape_sprecf_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback)
1313 {
1314 t_stat r = MTSE_OK;
1315 r = sim_tape_sprecf (uptr, bc);
1316 return r;
1317 }
1318
1319 /* Space records forward
1320
1321 Inputs:
1322 uptr = pointer to tape unit
1323 count = count of records to skip
1324 skipped = pointer to number of records actually skipped
1325 Outputs:
1326 status = operation status
1327
1328 exit condition position
1329
1330 unit unattached unchanged
1331 read error unchanged, PNU set
1332 end of file/medium unchanged, PNU set
1333 tape mark updated
1334 data record updated
1335 data record error updated
1336 */
1337
sim_tape_sprecsf(UNIT * uptr,uint32 count,uint32 * skipped)1338 t_stat sim_tape_sprecsf (UNIT *uptr, uint32 count, uint32 *skipped)
1339 {
1340 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1341 t_stat st;
1342 t_mtrlnt tbc;
1343
1344 if (ctx == NULL) /* if not properly attached? */
1345 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
1346 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecsf(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);
1347
1348 *skipped = 0;
1349 while (*skipped < count) { /* loopo */
1350 st = sim_tape_sprecf (uptr, &tbc); /* spc rec */
1351 if (st != MTSE_OK)
1352 return st;
1353 *skipped = *skipped + 1; /* # recs skipped */
1354 }
1355 return MTSE_OK;
1356 }
1357
sim_tape_sprecsf_a(UNIT * uptr,uint32 count,uint32 * skipped,TAPE_PCALLBACK callback)1358 t_stat sim_tape_sprecsf_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
1359 {
1360 t_stat r = MTSE_OK;
1361 r = sim_tape_sprecsf (uptr, count, skipped);
1362 return r;
1363 }
1364
1365 /* Space record reverse
1366
1367 Inputs:
1368 uptr = pointer to tape unit
1369 bc = pointer to size of records skipped
1370 Outputs:
1371 status = operation status
1372
1373 exit condition position
1374
1375 unit unattached unchanged
1376 beginning of tape unchanged
1377 read error unchanged
1378 end of file unchanged
1379 end of medium updated
1380 tape mark updated
1381 data record updated
1382 */
1383
sim_tape_sprecr(UNIT * uptr,t_mtrlnt * bc)1384 t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc)
1385 {
1386 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1387 t_stat st;
1388
1389 if (ctx == NULL) /* if not properly attached? */
1390 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
1391 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecr(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1392
1393 if (MT_TST_PNU (uptr)) {
1394 MT_CLR_PNU (uptr);
1395 *bc = 0;
1396 return MTSE_OK;
1397 }
1398 st = sim_tape_rdlntr (uptr, bc); /* get record length */
1399 *bc = MTR_L (*bc);
1400 return st;
1401 }
1402
sim_tape_sprecr_a(UNIT * uptr,t_mtrlnt * bc,TAPE_PCALLBACK callback)1403 t_stat sim_tape_sprecr_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback)
1404 {
1405 t_stat r = MTSE_OK;
1406 r = sim_tape_sprecr (uptr, bc);
1407 return r;
1408 }
1409
1410 /* Space records reverse
1411
1412 Inputs:
1413 uptr = pointer to tape unit
1414 count = count of records to skip
1415 skipped = pointer to number of records actually skipped
1416 Outputs:
1417 status = operation status
1418
1419 exit condition position
1420
1421 unit unattached unchanged
1422 beginning of tape unchanged
1423 read error unchanged
1424 end of file unchanged
1425 end of medium updated
1426 tape mark updated
1427 data record updated
1428 */
1429
sim_tape_sprecsr(UNIT * uptr,uint32 count,uint32 * skipped)1430 t_stat sim_tape_sprecsr (UNIT *uptr, uint32 count, uint32 *skipped)
1431 {
1432 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1433 t_stat st;
1434 t_mtrlnt tbc;
1435
1436 if (ctx == NULL) /* if not properly attached? */
1437 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
1438 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecsr(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);
1439
1440 *skipped = 0;
1441 while (*skipped < count) { /* loopo */
1442 st = sim_tape_sprecr (uptr, &tbc); /* spc rec rev */
1443 if (st != MTSE_OK)
1444 return st;
1445 *skipped = *skipped + 1; /* # recs skipped */
1446 }
1447 return MTSE_OK;
1448 }
1449
sim_tape_sprecsr_a(UNIT * uptr,uint32 count,uint32 * skipped,TAPE_PCALLBACK callback)1450 t_stat sim_tape_sprecsr_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
1451 {
1452 t_stat r = MTSE_OK;
1453 r = sim_tape_sprecsr (uptr, count, skipped);
1454 return r;
1455 }
1456
1457 /* Space files forward by record
1458
1459 Inputs:
1460 uptr = pointer to tape unit
1461 count = count of files to skip
1462 skipped = pointer to number of files actually skipped
1463 recsskipped = pointer to number of records skipped
1464 check_leot = flag to detect and stop skip between two successive tape marks
1465 Outputs:
1466 status = operation status
1467
1468 exit condition position
1469
1470 unit unattached unchanged
1471 read error unchanged, PNU set
1472 end of file/medium unchanged, PNU set
1473 tape mark updated
1474 data record updated
1475 data record error updated
1476 */
1477
sim_tape_spfilebyrecf(UNIT * uptr,uint32 count,uint32 * skipped,uint32 * recsskipped,t_bool check_leot)1478 t_stat sim_tape_spfilebyrecf (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot)
1479 {
1480 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1481 t_stat st;
1482 t_bool last_tapemark = FALSE;
1483 uint32 filerecsskipped;
1484
1485 *skipped = *recsskipped = 0;
1486 if (ctx == NULL) /* if not properly attached? */
1487 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
1488 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecf(unit=%d, count=%d, check_leot=%d)\n", (int)(uptr-ctx->dptr->units), count, check_leot);
1489
1490 if (check_leot) {
1491 t_mtrlnt rbc;
1492
1493 st = sim_tape_rdlntr (uptr, &rbc);
1494 last_tapemark = (MTSE_TMK == st);
1495 if ((st == MTSE_OK) || (st == MTSE_TMK))
1496 sim_tape_rdlntf (uptr, &rbc);
1497 }
1498 *skipped = 0;
1499 *recsskipped = 0;
1500 while (*skipped < count) { /* loopo */
1501 while (1) {
1502 st = sim_tape_sprecsf (uptr, 0x1ffffff, &filerecsskipped);/* spc recs */
1503 *recsskipped += filerecsskipped;
1504 if (st != MTSE_OK)
1505 break;
1506 }
1507 if (st == MTSE_TMK) {
1508 *skipped = *skipped + 1; /* # files skipped */
1509 if (check_leot && (filerecsskipped == 0) && last_tapemark) {
1510 uint32 filefileskipped;
1511 sim_tape_spfilebyrecr (uptr, 1, &filefileskipped, &filerecsskipped);
1512 *skipped = *skipped - 1; /* adjust # files skipped */
1513 return MTSE_LEOT;
1514 }
1515 last_tapemark = TRUE;
1516 }
1517 else
1518 return st;
1519 }
1520 return MTSE_OK;
1521 }
1522
sim_tape_spfilebyrecf_a(UNIT * uptr,uint32 count,uint32 * skipped,uint32 * recsskipped,t_bool check_leot,TAPE_PCALLBACK callback)1523 t_stat sim_tape_spfilebyrecf_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot, TAPE_PCALLBACK callback)
1524 {
1525 t_stat r = MTSE_OK;
1526 r = sim_tape_spfilebyrecf (uptr, count, skipped, recsskipped, check_leot);
1527 return r;
1528 }
1529
1530 /* Space files forward
1531
1532 Inputs:
1533 uptr = pointer to tape unit
1534 count = count of files to skip
1535 skipped = pointer to number of files actually skipped
1536 Outputs:
1537 status = operation status
1538
1539 exit condition position
1540
1541 unit unattached unchanged
1542 read error unchanged, PNU set
1543 end of file/medium unchanged, PNU set
1544 tape mark updated
1545 data record updated
1546 data record error updated
1547 */
1548
sim_tape_spfilef(UNIT * uptr,uint32 count,uint32 * skipped)1549 t_stat sim_tape_spfilef (UNIT *uptr, uint32 count, uint32 *skipped)
1550 {
1551 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1552 uint32 totalrecsskipped;
1553
1554 if (ctx == NULL) /* if not properly attached? */
1555 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
1556 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilef(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);
1557
1558 return sim_tape_spfilebyrecf (uptr, count, skipped, &totalrecsskipped, FALSE);
1559 }
1560
sim_tape_spfilef_a(UNIT * uptr,uint32 count,uint32 * skipped,TAPE_PCALLBACK callback)1561 t_stat sim_tape_spfilef_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
1562 {
1563 t_stat r = MTSE_OK;
1564 r = sim_tape_spfilef (uptr, count, skipped);
1565 return r;
1566 }
1567
1568 /* Space files reverse by record
1569
1570 Inputs:
1571 uptr = pointer to tape unit
1572 count = count of files to skip
1573 skipped = pointer to number of files actually skipped
1574 recsskipped = pointer to number of records skipped
1575 Outputs:
1576 status = operation status
1577
1578 exit condition position
1579
1580 unit unattached unchanged
1581 beginning of tape unchanged
1582 read error unchanged
1583 end of file unchanged
1584 end of medium updated
1585 tape mark updated
1586 data record updated
1587 */
1588
sim_tape_spfilebyrecr(UNIT * uptr,uint32 count,uint32 * skipped,uint32 * recsskipped)1589 t_stat sim_tape_spfilebyrecr (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped)
1590 {
1591 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1592 t_stat st;
1593 uint32 filerecsskipped = 0;
1594
1595 *skipped = 0;
1596 *recsskipped = 0;
1597 if (ctx == NULL) /* if not properly attached? */
1598 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
1599 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecr(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);
1600
1601 while (*skipped < count) { /* loopo */
1602 while (1) {
1603 st = sim_tape_sprecsr (uptr, 0x1ffffff, &filerecsskipped);/* spc recs rev */
1604 *recsskipped += filerecsskipped;
1605 if (st != MTSE_OK)
1606 break;
1607 }
1608 if (st == MTSE_TMK)
1609 *skipped = *skipped + 1; /* # files skipped */
1610 else
1611 return st;
1612 }
1613 return MTSE_OK;
1614 }
1615
sim_tape_spfilebyrecr_a(UNIT * uptr,uint32 count,uint32 * skipped,uint32 * recsskipped,TAPE_PCALLBACK callback)1616 t_stat sim_tape_spfilebyrecr_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, TAPE_PCALLBACK callback)
1617 {
1618 t_stat r = MTSE_OK;
1619 r = sim_tape_spfilebyrecr (uptr, count, skipped, recsskipped);
1620 return r;
1621 }
1622
1623 /* Space files reverse
1624
1625 Inputs:
1626 uptr = pointer to tape unit
1627 count = count of files to skip
1628 skipped = pointer to number of files actually skipped
1629 Outputs:
1630 status = operation status
1631
1632 exit condition position
1633
1634 unit unattached unchanged
1635 beginning of tape unchanged
1636 read error unchanged
1637 end of file unchanged
1638 end of medium updated
1639 tape mark updated
1640 data record updated
1641 */
1642
sim_tape_spfiler(UNIT * uptr,uint32 count,uint32 * skipped)1643 t_stat sim_tape_spfiler (UNIT *uptr, uint32 count, uint32 *skipped)
1644 {
1645 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1646 uint32 totalrecsskipped;
1647
1648 if (ctx == NULL) /* if not properly attached? */
1649 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
1650 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfiler(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);
1651
1652 return sim_tape_spfilebyrecr (uptr, count, skipped, &totalrecsskipped);
1653 }
1654
sim_tape_spfiler_a(UNIT * uptr,uint32 count,uint32 * skipped,TAPE_PCALLBACK callback)1655 t_stat sim_tape_spfiler_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
1656 {
1657 t_stat r = MTSE_OK;
1658 r = sim_tape_spfiler (uptr, count, skipped);
1659 return r;
1660 }
1661
1662 /* Rewind tape */
1663
sim_tape_rewind(UNIT * uptr)1664 t_stat sim_tape_rewind (UNIT *uptr)
1665 {
1666 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1667
1668 if (uptr->flags & UNIT_ATT) {
1669 if (ctx == NULL) /* if not properly attached? */
1670 return sim_messagef (SCPE_IERR, "Bad Attach\n");/* that's a problem */
1671 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rewind(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1672 }
1673 uptr->pos = 0;
1674 MT_CLR_PNU (uptr);
1675 return MTSE_OK;
1676 }
1677
sim_tape_rewind_a(UNIT * uptr,TAPE_PCALLBACK callback)1678 t_stat sim_tape_rewind_a (UNIT *uptr, TAPE_PCALLBACK callback)
1679 {
1680 t_stat r = MTSE_OK;
1681 r = sim_tape_rewind (uptr);
1682 return r;
1683 }
1684
1685 /* Position Tape */
1686
sim_tape_position(UNIT * uptr,uint32 flags,uint32 recs,uint32 * recsskipped,uint32 files,uint32 * filesskipped,uint32 * objectsskipped)1687 t_stat sim_tape_position (UNIT *uptr, uint32 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped)
1688 {
1689 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1690 t_stat r = MTSE_OK;
1691
1692 if (ctx == NULL) /* if not properly attached? */
1693 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
1694 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_position(unit=%d, flags=0x%X, recs=%d, files=%d)\n", (int)(uptr-ctx->dptr->units), flags, recs, files);
1695
1696 *recsskipped = *filesskipped = *objectsskipped = 0;
1697 if (flags & MTPOS_M_REW)
1698 r = sim_tape_rewind (uptr);
1699 if (r != MTSE_OK)
1700 return r;
1701 if (flags & MTPOS_M_OBJ) {
1702 uint32 objs = recs;
1703 uint32 skipped;
1704 uint32 objsremaining = objs;
1705
1706 while (*objectsskipped < objs) { /* loopo */
1707 if (flags & MTPOS_M_REV) /* reverse? */
1708 r = sim_tape_sprecsr (uptr, objsremaining, &skipped);
1709 else
1710 r = sim_tape_sprecsf (uptr, objsremaining, &skipped);
1711 objsremaining = objsremaining - (skipped + ((r == MTSE_TMK) ? 1 : 0));
1712 if ((r == MTSE_TMK) || (r == MTSE_OK))
1713 *objectsskipped = *objectsskipped + skipped + ((r == MTSE_TMK) ? 1 : 0);
1714 else
1715 return r;
1716 }
1717 r = MTSE_OK;
1718 }
1719 else {
1720 uint32 fileskiprecs;
1721
1722 if (flags & MTPOS_M_REV) /* reverse? */
1723 r = sim_tape_spfilebyrecr (uptr, files, filesskipped, &fileskiprecs);
1724 else
1725 r = sim_tape_spfilebyrecf (uptr, files, filesskipped, &fileskiprecs, (flags & MTPOS_M_DLE));
1726 if (r != MTSE_OK)
1727 return r;
1728 if (flags & MTPOS_M_REV) /* reverse? */
1729 r = sim_tape_sprecsr (uptr, recs, recsskipped);
1730 else
1731 r = sim_tape_sprecsf (uptr, recs, recsskipped);
1732 if (r == MTSE_TMK)
1733 *filesskipped = *filesskipped + 1;
1734 *objectsskipped = fileskiprecs + *filesskipped + *recsskipped;
1735 }
1736 return r;
1737 }
1738
sim_tape_position_a(UNIT * uptr,uint32 flags,uint32 recs,uint32 * recsskipped,uint32 files,uint32 * filesskipped,uint32 * objectsskipped,TAPE_PCALLBACK callback)1739 t_stat sim_tape_position_a (UNIT *uptr, uint32 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped, TAPE_PCALLBACK callback)
1740 {
1741 t_stat r = MTSE_OK;
1742 r = sim_tape_position (uptr, flags, recs, recsskipped, files, filesskipped, objectsskipped);
1743 return r;
1744 }
1745
1746 /* Reset tape */
1747
sim_tape_reset(UNIT * uptr)1748 t_stat sim_tape_reset (UNIT *uptr)
1749 {
1750 struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
1751
1752 MT_CLR_PNU (uptr);
1753 if (!(uptr->flags & UNIT_ATT)) /* attached? */
1754 return SCPE_OK;
1755
1756 if (ctx == NULL) /* if not properly attached? */
1757 return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */
1758 sim_debug (ctx->dbit, ctx->dptr, "sim_tape_reset(unit=%d)\n", (int)(uptr-ctx->dptr->units));
1759
1760 _sim_tape_io_flush(uptr);
1761 return SCPE_OK;
1762 }
1763
1764 /* Test for BOT */
1765
sim_tape_bot(UNIT * uptr)1766 t_bool sim_tape_bot (UNIT *uptr)
1767 {
1768 uint32 f = MT_GET_FMT (uptr);
1769
1770 return (uptr->pos <= fmts[f].bot)? TRUE: FALSE;
1771 }
1772
1773 /* Test for end of tape */
1774
sim_tape_eot(UNIT * uptr)1775 t_bool sim_tape_eot (UNIT *uptr)
1776 {
1777 return (uptr->capac && (uptr->pos >= uptr->capac))? TRUE: FALSE;
1778 }
1779
1780 /* Test for write protect */
1781
sim_tape_wrp(UNIT * uptr)1782 t_bool sim_tape_wrp (UNIT *uptr)
1783 {
1784 return ((uptr->flags & MTUF_WRP) || (MT_GET_FMT (uptr) == MTUF_F_TPC))? TRUE: FALSE;
1785 }
1786
1787 /* Process I/O error */
1788
sim_tape_ioerr(UNIT * uptr)1789 static t_stat sim_tape_ioerr (UNIT *uptr)
1790 {
1791 sim_printf ("%s: Magtape library I/O error: %s\n", sim_uname (uptr), strerror (errno));
1792 clearerr (uptr->fileref);
1793 return MTSE_IOERR;
1794 }
1795
1796 /* Set tape format */
1797
sim_tape_set_fmt(UNIT * uptr,int32 val,CONST char * cptr,void * desc)1798 t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
1799 {
1800 uint32 f;
1801
1802 if (uptr->flags & UNIT_ATT)
1803 return SCPE_ALATT;
1804 if (uptr == NULL)
1805 return SCPE_IERR;
1806 if (cptr == NULL)
1807 return SCPE_ARG;
1808 for (f = 0; f < MTUF_N_FMT; f++) {
1809 if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) {
1810 uptr->flags = (uptr->flags & ~MTUF_FMT) |
1811 (f << MTUF_V_FMT) | fmts[f].uflags;
1812 return SCPE_OK;
1813 }
1814 }
1815 return SCPE_ARG;
1816 }
1817
1818 /* Show tape format */
1819
sim_tape_show_fmt(FILE * st,UNIT * uptr,int32 val,CONST void * desc)1820 t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
1821 {
1822 int32 f = MT_GET_FMT (uptr);
1823
1824 if (fmts[f].name)
1825 fprintf (st, "%s format", fmts[f].name);
1826 else fprintf (st, "invalid format");
1827 return SCPE_OK;
1828 }
1829
1830 /* Map a TPC format tape image */
1831
sim_tape_tpc_map(UNIT * uptr,t_addr * map,uint32 mapsize)1832 static uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map, uint32 mapsize)
1833 {
1834 t_addr tpos, leot = 0;
1835 t_addr tape_size;
1836 t_tpclnt bc, last_bc = 0xFFFF;
1837 uint32 had_double_tape_mark = 0;
1838 size_t i;
1839 uint32 objc, sizec;
1840 uint32 *countmap = NULL;
1841 uint8 *recbuf = NULL;
1842 DEVICE *dptr = find_dev_from_unit (uptr);
1843
1844 if ((uptr == NULL) || (uptr->fileref == NULL))
1845 return 0;
1846 countmap = (uint32 *)calloc (65536, sizeof(*countmap));
1847 recbuf = (uint8 *)malloc (65536);
1848 tape_size = (t_addr)sim_fsize (uptr->fileref);
1849 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: tape_size: %" T_ADDR_FMT "u\n", tape_size);
1850 for (objc = 0, sizec = 0, tpos = 0;; ) {
1851 sim_fseek (uptr->fileref, tpos, SEEK_SET);
1852 i = sim_fread (&bc, sizeof (t_tpclnt), 1, uptr->fileref);
1853 if (i == 0) /* past or at eof? */
1854 break;
1855 if (countmap[bc] == 0)
1856 sizec++;
1857 ++countmap[bc];
1858 if (map && (objc < mapsize))
1859 map[objc] = tpos;
1860 if (bc) {
1861 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: %d byte count at pos: %" T_ADDR_FMT "u\n", bc, tpos);
1862 if (sim_deb && (dptr->dctrl & MTSE_DBG_STR)) {
1863 (void)sim_fread (recbuf, 1, bc, uptr->fileref);
1864 sim_data_trace(dptr, uptr, ((dptr->dctrl & MTSE_DBG_DAT) ? recbuf : NULL), "", bc, "Data Record", MTSE_DBG_STR);
1865 }
1866 }
1867 else
1868 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: tape mark at pos: %" T_ADDR_FMT "u\n", tpos);
1869 objc++;
1870 tpos = tpos + ((bc + 1) & ~1) + sizeof (t_tpclnt);
1871 if ((bc == 0) && (last_bc == 0)) { /* double tape mark? */
1872 had_double_tape_mark = objc;
1873 leot = tpos;
1874 }
1875 last_bc = bc;
1876 }
1877 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: objc: %u, different record sizes: %u\n", objc, sizec);
1878 for (i=0; i<65535; i++) {
1879 if (countmap[i]) {
1880 if (i == 0)
1881 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: summary - %u tape marks\n", countmap[i]);
1882 else
1883 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: summary - %u %d byte record%s\n", countmap[i], (int)i, (countmap[i] > 1) ? "s" : "");
1884 }
1885 }
1886 if (((last_bc != 0xffff) &&
1887 (tpos > tape_size) &&
1888 (!had_double_tape_mark)) ||
1889 (!had_double_tape_mark) ||
1890 ((objc == countmap[0]) &&
1891 (countmap[0] != 2))) { /* Unreasonable format? */
1892 if (last_bc != 0xffff)
1893 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: ERROR unexpected EOT byte count: %d\n", last_bc);
1894 if (tpos > tape_size)
1895 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: ERROR next record position %" T_ADDR_FMT "u beyond EOT: %" T_ADDR_FMT "u\n", tpos, tape_size);
1896 if (objc == countmap[0])
1897 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: ERROR tape cnly contains tape marks\n");
1898 free (countmap);
1899 free (recbuf);
1900 return 0;
1901 }
1902
1903 if ((last_bc != 0xffff) && (tpos > tape_size)) {
1904 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: WARNING unexpected EOT byte count: %d, double tape mark before %" T_ADDR_FMT "u provides logical EOT\n", last_bc, leot);
1905 objc = had_double_tape_mark;
1906 tpos = leot;
1907 }
1908 if (map)
1909 map[objc] = tpos;
1910 sim_debug (MTSE_DBG_STR, dptr, "tpc_map: OK objc: %d\n", objc);
1911 free (countmap);
1912 free (recbuf);
1913 return objc;
1914 }
1915
1916 /* Check the basic structure of a SIMH format tape image */
1917
sim_tape_simh_check(UNIT * uptr)1918 static t_stat sim_tape_simh_check (UNIT *uptr)
1919 {
1920 return SCPE_OK;
1921 }
1922
1923 /* Check the basic structure of a E11 format tape image */
1924
sim_tape_e11_check(UNIT * uptr)1925 static t_stat sim_tape_e11_check (UNIT *uptr)
1926 {
1927 return SCPE_OK;
1928 }
1929
1930 /* Find the preceding record in a TPC file */
1931
sim_tape_tpc_fnd(UNIT * uptr,t_addr * map)1932 static t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map)
1933 {
1934 uint32 lo, hi, p;
1935
1936
1937 if (map == NULL)
1938 return 0;
1939 lo = 0;
1940 hi = uptr->hwmark - 1;
1941 do {
1942 p = (lo + hi) >> 1;
1943 if (uptr->pos == map[p])
1944 return ((p == 0)? map[p]: map[p - 1]);
1945 else if (uptr->pos < map[p])
1946 hi = p - 1;
1947 else lo = p + 1;
1948 }
1949 while (lo <= hi);
1950 return ((p == 0)? map[p]: map[p - 1]);
1951 }
1952
1953 /* Set tape capacity */
1954
sim_tape_set_capac(UNIT * uptr,int32 val,CONST char * cptr,void * desc)1955 t_stat sim_tape_set_capac (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
1956 {
1957 t_addr cap;
1958 t_stat r;
1959
1960 if ((cptr == NULL) || (*cptr == 0))
1961 return SCPE_ARG;
1962 if (uptr->flags & UNIT_ATT)
1963 return SCPE_ALATT;
1964 cap = (t_addr) get_uint (cptr, 10, sim_taddr_64? 2000000: 2000, &r);
1965 if (r != SCPE_OK)
1966 return SCPE_ARG;
1967 uptr->capac = cap * ((t_addr) 1000000);
1968 return SCPE_OK;
1969 }
1970
1971 /* Show tape capacity */
1972
sim_tape_show_capac(FILE * st,UNIT * uptr,int32 val,CONST void * desc)1973 t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
1974 {
1975 if (uptr->capac) {
1976 if (uptr->capac >= (t_addr) 1000000)
1977 fprintf (st, "capacity=%luMB", (unsigned long)(uptr->capac / ((t_addr) 1000000)));
1978 else {
1979 if (uptr->capac >= (t_addr) 1000)
1980 fprintf (st, "capacity=%luKB", (unsigned long)(uptr->capac / ((t_addr) 1000)));
1981 else
1982 fprintf (st, "capacity=%luB", (unsigned long)uptr->capac);
1983 }
1984 }
1985 else
1986 fprintf (st, "unlimited capacity");
1987 return SCPE_OK;
1988 }
1989
1990 /* Set the tape density.
1991
1992 Set the density of the specified tape unit either to the value supplied or to
1993 the value represented by the supplied character string.
1994
1995 If "desc" is NULL, then "val" must be set to one of the MT_DENS_* constants
1996 in sim_tape.h other than MT_DENS_NONE; the supplied value is used as the tape
1997 density, and the character string is ignored. Otherwise, "desc" must point
1998 at an int32 value containing a set of allowed densities constructed as a
1999 bitwise OR of the appropriate MT_*_VALID values. In this case, the string
2000 pointed to by "cptr" will be parsed for a decimal value corresponding to the
2001 desired density in bits per inch and validated against the set of allowed
2002 values.
2003
2004 In either case, SCPE_ARG is returned if the density setting is not valid or
2005 allowed. If the setting is OK, the new density is set into the unit
2006 structure, and SCPE_OK is returned.
2007 */
2008
sim_tape_set_dens(UNIT * uptr,int32 val,CONST char * cptr,void * desc)2009 t_stat sim_tape_set_dens (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
2010 {
2011 uint32 density, new_bpi;
2012 t_stat result = SCPE_OK;
2013
2014 if (uptr == NULL) /* if the unit pointer is null */
2015 return SCPE_IERR; /* then the caller has screwed up */
2016
2017 else if (desc == NULL) /* otherwise if a validation set was not supplied */
2018 if (val > 0 && val < (int32) BPI_COUNT) /* then if a valid density code was supplied */
2019 uptr->dynflags = (uptr->dynflags & ~MTVF_DENS_MASK) /* then insert the code */
2020 | (val << UNIT_V_DF_TAPE); /* in the unit flags */
2021 else /* otherwise the code is invalid */
2022 return SCPE_ARG; /* so report a bad argument */
2023
2024 else { /* otherwise a validation set was supplied */
2025 if (cptr == NULL || *cptr == 0) /* but if no value is present */
2026 return SCPE_MISVAL; /* then report a missing value */
2027
2028 new_bpi = (uint32) get_uint (cptr, 10, UINT_MAX, &result); /* convert the string value */
2029
2030 if (result != SCPE_OK) /* if the conversion failed */
2031 return SCPE_ARG; /* then report a bad argument */
2032
2033 else for (density = 0; density < BPI_COUNT; density++) /* otherwise validate the density */
2034 if (new_bpi == bpi [density] /* if it matches a value in the list */
2035 && ((1 << density) & *(const int32 *) desc)) { /* and it's an allowed value */
2036 uptr->dynflags = (uptr->dynflags & ~MTVF_DENS_MASK) /* then store the index of the value */
2037 | density << UNIT_V_DF_TAPE; /* in the unit flags */
2038 return SCPE_OK; /* and return success */
2039 }
2040
2041 result = SCPE_ARG; /* if no match, then report a bad argument */
2042 }
2043
2044 return result; /* return the result of the operation */
2045 }
2046
2047 /* Show the tape density */
2048
sim_tape_show_dens(FILE * st,UNIT * uptr,int32 val,CONST void * desc)2049 t_stat sim_tape_show_dens (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
2050 {
2051 uint32 tape_density;
2052
2053 if (uptr == NULL) /* if the unit pointer is null */
2054 return SCPE_IERR; /* then the caller has screwed up */
2055
2056 else { /* otherwise get the density */
2057 tape_density = bpi [MT_DENS (uptr->dynflags)]; /* of the tape from the unit flags */
2058
2059 if (tape_density) /* if it's set */
2060 fprintf (st, "density=%lu bpi",
2061 (unsigned long)tape_density); /* then report it */
2062 else /* otherwise */
2063 fprintf (st, "density not set"); /* it was never set by the caller */
2064 }
2065
2066 return SCPE_OK;
2067 }
2068