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