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