1 /* sim_tape.c: simulator tape support library
3 Copyright (c) 1993-2007, Robert M Supnik
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
26 Ultimately, this will be a place to hide processing of various tape formats,
27 as well as OS-specific direct hardware access.
29 23-Jan-07 JDB Fixed backspace over gap at BOT
30 22-Jan-07 RMS Fixed bug in P7B format read reclnt rev (found by Rich Cornwell)
31 15-Dec-06 RMS Added support for small capacity tapes
32 30-Aug-06 JDB Added erase gap support
33 14-Feb-06 RMS Added variable tape capacity
34 23-Jan-06 JDB Fixed odd-byte-write problem in sim_tape_wrrecf
35 17-Dec-05 RMS Added write support for Paul Pierce 7b format
36 16-Aug-05 RMS Fixed C++ declaration and cast problems
37 02-May-05 RMS Added support for Pierce 7b format
38 28-Jul-04 RMS Fixed bug in writing error records (found by Dave Bryan)
39 RMS Fixed incorrect error codes (found by Dave Bryan)
40 05-Jan-04 RMS Revised for file I/O library
41 25-Apr-03 RMS Added extended file support
42 28-Mar-03 RMS Added E11 and TPC format support
46 sim_tape_attach attach tape unit
47 sim_tape_detach detach tape unit
48 sim_tape_rdrecf read tape record forward
49 sim_tape_rdrecr read tape record reverse
50 sim_tape_wrrecf write tape record forward
51 sim_tape_sprecf space tape record forward
52 sim_tape_sprecr space tape record reverse
53 sim_tape_wrtmk write tape mark
54 sim_tape_wreom erase remainder of tape
55 sim_tape_wrgap write erase gap
56 sim_tape_rewind rewind
57 sim_tape_reset reset unit
58 sim_tape_bot TRUE if at beginning of tape
59 sim_tape_eot TRUE if at or beyond end of tape
60 sim_tape_wrp TRUE if write protected
61 sim_tape_set_fmt set tape format
62 sim_tape_show_fmt show tape format
63 sim_tape_set_capac set tape capacity
64 sim_tape_show_capac show tape capacity
71 char *name
; /* name */
72 int32 uflags
; /* unit flags */
73 t_addr bot
; /* bot test */
76 static struct sim_tape_fmt fmts
[MTUF_N_FMT
] = {
77 { "SIMH", 0, sizeof (t_mtrlnt
) - 1 },
78 { "E11", 0, sizeof (t_mtrlnt
) - 1 },
79 { "TPC", UNIT_RO
, sizeof (t_tpclnt
) - 1 },
81 /* { "TPF", UNIT_RO, 0 }, */
85 extern int32 sim_switches
;
87 t_stat
sim_tape_ioerr (UNIT
*uptr
);
88 t_stat
sim_tape_wrdata (UNIT
*uptr
, uint32 dat
);
89 uint32
sim_tape_tpc_map (UNIT
*uptr
, t_addr
*map
);
90 t_addr
sim_tape_tpc_fnd (UNIT
*uptr
, t_addr
*map
);
92 /* Attach tape unit */
94 t_stat
sim_tape_attach (UNIT
*uptr
, char *cptr
)
100 if (sim_switches
& SWMASK ('F')) { /* format spec? */
101 cptr
= get_glyph (cptr
, gbuf
, 0); /* get spec */
102 if (*cptr
== 0) return SCPE_2FARG
; /* must be more */
103 if (sim_tape_set_fmt (uptr
, 0, gbuf
, NULL
) != SCPE_OK
)
106 r
= attach_unit (uptr
, cptr
); /* attach unit */
107 if (r
!= SCPE_OK
) return r
; /* error? */
108 switch (MT_GET_FMT (uptr
)) { /* case on format */
110 case MTUF_F_TPC
: /* TPC */
111 objc
= sim_tape_tpc_map (uptr
, NULL
); /* get # objects */
112 if (objc
== 0) { /* tape empty? */
113 sim_tape_detach (uptr
);
114 return SCPE_FMT
; /* yes, complain */
116 uptr
->filebuf
= calloc (objc
+ 1, sizeof (t_addr
));
117 if (uptr
->filebuf
== NULL
) { /* map allocated? */
118 sim_tape_detach (uptr
);
119 return SCPE_MEM
; /* no, complain */
121 uptr
->hwmark
= objc
+ 1; /* save map size */
122 sim_tape_tpc_map (uptr
, (t_addr
*) uptr
->filebuf
); /* fill map */
129 sim_tape_rewind (uptr
);
133 /* Detach tape unit */
135 t_stat
sim_tape_detach (UNIT
*uptr
)
137 uint32 f
= MT_GET_FMT (uptr
);
140 r
= detach_unit (uptr
); /* detach unit */
141 if (r
!= SCPE_OK
) return r
;
142 switch (f
) { /* case on format */
144 case MTUF_F_TPC
: /* TPC */
145 if (uptr
->filebuf
) free (uptr
->filebuf
); /* free map */
146 uptr
->filebuf
= NULL
;
154 sim_tape_rewind (uptr
);
158 /* Read record length forward (internal routine)
161 uptr = pointer to tape unit
162 bc = pointer to returned record length
164 status = operation status
166 exit condition position
168 unit unattached unchanged
169 read error unchanged, PNU set
170 end of file/medium unchanged, PNU set
172 data record updated, sim_fread will read record forward
174 See notes at "sim_tape_wrgap" regarding erase gap implementation.
177 t_stat
sim_tape_rdlntf (UNIT
*uptr
, t_mtrlnt
*bc
)
181 uint32 f
= MT_GET_FMT (uptr
);
186 if ((uptr
->flags
& UNIT_ATT
) == 0) return MTSE_UNATT
; /* not attached? */
187 sim_fseek (uptr
->fileref
, uptr
->pos
, SEEK_SET
); /* set tape pos */
188 switch (f
) { /* switch on fmt */
190 case MTUF_F_STD
: case MTUF_F_E11
:
192 sim_fread (bc
, sizeof (t_mtrlnt
), 1, uptr
->fileref
); /* read rec lnt */
193 sbc
= MTR_L (*bc
); /* save rec lnt */
194 if (ferror (uptr
->fileref
)) { /* error? */
195 MT_SET_PNU (uptr
); /* pos not upd */
196 return sim_tape_ioerr (uptr
);
198 if (feof (uptr
->fileref
) || (*bc
== MTR_EOM
)) { /* eof or eom? */
199 MT_SET_PNU (uptr
); /* pos not upd */
202 uptr
->pos
= uptr
->pos
+ sizeof (t_mtrlnt
); /* spc over rec lnt */
203 if (*bc
== MTR_TMK
) return MTSE_TMK
; /* tape mark? */
204 if (*bc
== MTR_FHGAP
) { /* half gap? */
205 uptr
->pos
= uptr
->pos
+ sizeof (t_mtrlnt
) / 2; /* half space fwd */
206 sim_fseek (uptr
->fileref
, uptr
->pos
, SEEK_SET
); /* resync */
208 else if (*bc
!= MTR_GAP
)
209 uptr
->pos
= uptr
->pos
+ sizeof (t_mtrlnt
) + /* spc over record */
210 ((f
== MTUF_F_STD
)? ((sbc
+ 1) & ~1): sbc
);
212 while ((*bc
== MTR_GAP
) || (*bc
== MTR_FHGAP
));
216 sim_fread (&tpcbc
, sizeof (t_tpclnt
), 1, uptr
->fileref
);
217 *bc
= tpcbc
; /* save rec lnt */
218 if (ferror (uptr
->fileref
)) { /* error? */
219 MT_SET_PNU (uptr
); /* pos not upd */
220 return sim_tape_ioerr (uptr
);
222 if (feof (uptr
->fileref
)) { /* eof? */
223 MT_SET_PNU (uptr
); /* pos not upd */
226 uptr
->pos
= uptr
->pos
+ sizeof (t_tpclnt
); /* spc over reclnt */
227 if (tpcbc
== TPC_TMK
) return MTSE_TMK
; /* tape mark? */
228 uptr
->pos
= uptr
->pos
+ ((tpcbc
+ 1) & ~1); /* spc over record */
232 for (sbc
= 0, all_eof
= 1; ; sbc
++) { /* loop thru record */
233 sim_fread (&c
, sizeof (uint8
), 1, uptr
->fileref
);
234 if (ferror (uptr
->fileref
)) { /* error? */
235 MT_SET_PNU (uptr
); /* pos not upd */
236 return sim_tape_ioerr (uptr
);
238 if (feof (uptr
->fileref
)) { /* eof? */
239 if (sbc
== 0) return MTSE_EOM
; /* no data? eom */
240 break; /* treat like eor */
242 if ((sbc
!= 0) && (c
& P7B_SOR
)) break; /* next record? */
243 if ((c
& P7B_DPAR
) != P7B_EOF
) all_eof
= 0;
245 *bc
= sbc
; /* save rec lnt */
246 sim_fseek (uptr
->fileref
, uptr
->pos
, SEEK_SET
); /* for read */
247 uptr
->pos
= uptr
->pos
+ sbc
; /* spc over record */
248 if (all_eof
) return MTSE_TMK
; /* tape mark? */
258 /* Read record length reverse (internal routine)
261 uptr = pointer to tape unit
262 bc = pointer to returned record length
264 status = operation status
266 exit condition position
268 unit unattached unchanged
269 beginning of tape unchanged
271 end of file unchanged
272 end of medium updated
274 data record updated, sim_fread will read record forward
276 See notes at "sim_tape_wrgap" regarding erase gap implementation.
279 t_stat
sim_tape_rdlntr (UNIT
*uptr
, t_mtrlnt
*bc
)
283 uint32 f
= MT_GET_FMT (uptr
);
289 if ((uptr
->flags
& UNIT_ATT
) == 0) return MTSE_UNATT
; /* not attached? */
290 if (sim_tape_bot (uptr
)) return MTSE_BOT
; /* at BOT? */
291 switch (f
) { /* switch on fmt */
293 case MTUF_F_STD
: case MTUF_F_E11
:
295 sim_fseek (uptr
->fileref
, uptr
->pos
- sizeof (t_mtrlnt
), SEEK_SET
);
296 sim_fread (bc
, sizeof (t_mtrlnt
), 1, uptr
->fileref
); /* read rec lnt */
298 if (ferror (uptr
->fileref
)) /* error? */
299 return sim_tape_ioerr (uptr
);
300 if (feof (uptr
->fileref
)) return MTSE_EOM
; /* eof? */
301 uptr
->pos
= uptr
->pos
- sizeof (t_mtrlnt
); /* spc over rec lnt */
302 if (*bc
== MTR_EOM
) return MTSE_EOM
; /* eom? */
303 if (*bc
== MTR_TMK
) return MTSE_TMK
; /* tape mark? */
304 if ((*bc
& MTR_M_RHGAP
) == MTR_RHGAP
) { /* half gap? */
305 uptr
->pos
= uptr
->pos
+ sizeof (t_mtrlnt
) / 2; /* half space rev */
306 sim_fseek (uptr
->fileref
, uptr
->pos
, SEEK_SET
); /* resync */
308 else if (*bc
!= MTR_GAP
) {
309 uptr
->pos
= uptr
->pos
- sizeof (t_mtrlnt
) - /* spc over record */
310 ((f
== MTUF_F_STD
)? ((sbc
+ 1) & ~1): sbc
);
311 sim_fseek (uptr
->fileref
, uptr
->pos
+ sizeof (t_mtrlnt
), SEEK_SET
);
313 else if (sim_tape_bot (uptr
)) /* backed into BOT? */
316 while ((*bc
== MTR_GAP
) || (*bc
== MTR_RHGAP
));
320 ppos
= sim_tape_tpc_fnd (uptr
, (t_addr
*) uptr
->filebuf
); /* find prev rec */
321 sim_fseek (uptr
->fileref
, ppos
, SEEK_SET
); /* position */
322 sim_fread (&tpcbc
, sizeof (t_tpclnt
), 1, uptr
->fileref
);
323 *bc
= tpcbc
; /* save rec lnt */
324 if (ferror (uptr
->fileref
)) /* error? */
325 return sim_tape_ioerr (uptr
);
326 if (feof (uptr
->fileref
)) return MTSE_EOM
; /* eof? */
327 uptr
->pos
= ppos
; /* spc over record */
328 if (*bc
== MTR_TMK
) return MTSE_TMK
; /* tape mark? */
329 sim_fseek (uptr
->fileref
, uptr
->pos
+ sizeof (t_tpclnt
), SEEK_SET
);
333 for (sbc
= 1, all_eof
= 1; (t_addr
) sbc
<= uptr
->pos
; sbc
++) {
334 sim_fseek (uptr
->fileref
, uptr
->pos
- sbc
, SEEK_SET
);
335 sim_fread (&c
, sizeof (uint8
), 1, uptr
->fileref
);
336 if (ferror (uptr
->fileref
)) /* error? */
337 return sim_tape_ioerr (uptr
);
338 if (feof (uptr
->fileref
)) return MTSE_EOM
; /* eof? */
339 if ((c
& P7B_DPAR
) != P7B_EOF
) all_eof
= 0;
340 if (c
& P7B_SOR
) break; /* start of record? */
342 uptr
->pos
= uptr
->pos
- sbc
; /* update position */
343 *bc
= sbc
; /* save rec lnt */
344 sim_fseek (uptr
->fileref
, uptr
->pos
, SEEK_SET
); /* for read */
345 if (all_eof
) return MTSE_TMK
; /* tape mark? */
355 /* Read record forward
358 uptr = pointer to tape unit
359 buf = pointer to buffer
360 bc = pointer to returned record length
361 max = maximum record size
363 status = operation status
365 exit condition position
367 unit unattached unchanged
368 read error unchanged, PNU set
369 end of file/medium unchanged, PNU set
370 invalid record unchanged, PNU set
373 data record error updated
376 t_stat
sim_tape_rdrecf (UNIT
*uptr
, uint8
*buf
, t_mtrlnt
*bc
, t_mtrlnt max
)
378 uint32 f
= MT_GET_FMT (uptr
);
379 t_mtrlnt i
, tbc
, rbc
;
383 opos
= uptr
->pos
; /* old position */
384 if (st
= sim_tape_rdlntf (uptr
, &tbc
)) return st
; /* read rec lnt */
385 *bc
= rbc
= MTR_L (tbc
); /* strip error flag */
386 if (rbc
> max
) { /* rec out of range? */
391 i
= sim_fread (buf
, sizeof (uint8
), rbc
, uptr
->fileref
); /* read record */
392 if (ferror (uptr
->fileref
)) { /* error? */
395 return sim_tape_ioerr (uptr
);
397 for ( ; i
< rbc
; i
++) buf
[i
] = 0; /* fill with 0's */
398 if (f
== MTUF_F_P7B
) buf
[0] = buf
[0] & P7B_DPAR
; /* p7b? strip SOR */
399 return (MTR_F (tbc
)? MTSE_RECE
: MTSE_OK
);
402 /* Read record reverse
405 uptr = pointer to tape unit
406 buf = pointer to buffer
407 bc = pointer to returned record length
408 max = maximum record size
410 status = operation status
412 exit condition position
414 unit unattached unchanged
416 end of file unchanged
417 end of medium updated
418 invalid record unchanged
421 data record error updated
424 t_stat
sim_tape_rdrecr (UNIT
*uptr
, uint8
*buf
, t_mtrlnt
*bc
, t_mtrlnt max
)
426 uint32 f
= MT_GET_FMT (uptr
);
427 t_mtrlnt i
, rbc
, tbc
;
430 if (st
= sim_tape_rdlntr (uptr
, &tbc
)) return st
; /* read rec lnt */
431 *bc
= rbc
= MTR_L (tbc
); /* strip error flag */
432 if (rbc
> max
) return MTSE_INVRL
; /* rec out of range? */
433 i
= sim_fread (buf
, sizeof (uint8
), rbc
, uptr
->fileref
); /* read record */
434 if (ferror (uptr
->fileref
)) /* error? */
435 return sim_tape_ioerr (uptr
);
436 for ( ; i
< rbc
; i
++) buf
[i
] = 0; /* fill with 0's */
437 if (f
== MTUF_F_P7B
) buf
[0] = buf
[0] & P7B_DPAR
; /* p7b? strip SOR */
438 return (MTR_F (tbc
)? MTSE_RECE
: MTSE_OK
);
441 /* Write record forward
444 uptr = pointer to tape unit
445 buf = pointer to buffer
448 status = operation status
450 exit condition position
452 unit unattached unchanged
453 write protect unchanged
454 write error unchanged, PNU set
458 t_stat
sim_tape_wrrecf (UNIT
*uptr
, uint8
*buf
, t_mtrlnt bc
)
460 uint32 f
= MT_GET_FMT (uptr
);
465 if ((uptr
->flags
& UNIT_ATT
) == 0) return MTSE_UNATT
; /* not attached? */
466 if (sim_tape_wrp (uptr
)) return MTSE_WRP
; /* write prot? */
467 if (sbc
== 0) return MTSE_OK
; /* nothing to do? */
468 sim_fseek (uptr
->fileref
, uptr
->pos
, SEEK_SET
); /* set pos */
469 switch (f
) { /* case on format */
471 case MTUF_F_STD
: /* standard */
472 sbc
= MTR_L ((bc
+ 1) & ~1); /* pad odd length */
473 case MTUF_F_E11
: /* E11 */
474 sim_fwrite (&bc
, sizeof (t_mtrlnt
), 1, uptr
->fileref
);
475 sim_fwrite (buf
, sizeof (uint8
), sbc
, uptr
->fileref
);
476 sim_fwrite (&bc
, sizeof (t_mtrlnt
), 1, uptr
->fileref
);
477 if (ferror (uptr
->fileref
)) { /* error? */
479 return sim_tape_ioerr (uptr
);
481 uptr
->pos
= uptr
->pos
+ sbc
+ (2 * sizeof (t_mtrlnt
)); /* move tape */
484 case MTUF_F_P7B
: /* Pierce 7B */
485 buf
[0] = buf
[0] | P7B_SOR
; /* mark start of rec */
486 sim_fwrite (buf
, sizeof (uint8
), sbc
, uptr
->fileref
);
487 sim_fwrite (buf
, sizeof (uint8
), 1, uptr
->fileref
); /* delimit rec */
488 if (ferror (uptr
->fileref
)) { /* error? */
490 return sim_tape_ioerr (uptr
);
492 uptr
->pos
= uptr
->pos
+ sbc
; /* move tape */
499 /* Write metadata forward (internal routine) */
501 t_stat
sim_tape_wrdata (UNIT
*uptr
, uint32 dat
)
504 if ((uptr
->flags
& UNIT_ATT
) == 0) return MTSE_UNATT
; /* not attached? */
505 if (sim_tape_wrp (uptr
)) return MTSE_WRP
; /* write prot? */
506 sim_fseek (uptr
->fileref
, uptr
->pos
, SEEK_SET
); /* set pos */
507 sim_fwrite (&dat
, sizeof (t_mtrlnt
), 1, uptr
->fileref
);
508 if (ferror (uptr
->fileref
)) { /* error? */
510 return sim_tape_ioerr (uptr
);
512 uptr
->pos
= uptr
->pos
+ sizeof (t_mtrlnt
); /* move tape */
516 /* Write tape mark */
518 t_stat
sim_tape_wrtmk (UNIT
*uptr
)
520 if (MT_GET_FMT (uptr
) == MTUF_F_P7B
) { /* P7B? */
521 uint8 buf
= P7B_EOF
; /* eof mark */
522 return sim_tape_wrrecf (uptr
, &buf
, 1); /* write char */
524 return sim_tape_wrdata (uptr
, MTR_TMK
);
527 /* Write end of medium */
529 t_stat
sim_tape_wreom (UNIT
*uptr
)
531 if (MT_GET_FMT (uptr
) == MTUF_F_P7B
) return MTSE_FMT
; /* cant do P7B */
532 return sim_tape_wrdata (uptr
, MTR_EOM
);
538 uptr = pointer to tape unit
539 gaplen = length of gap in tenths of an inch
540 bpi = current recording density in bytes per inch
543 status = operation status
545 exit condition position
546 ------------------ ------------------
547 unit unattached unchanged
548 unsupported format unchanged
549 write protected unchanged
550 read error unchanged, PNU set
551 write error unchanged, PNU set
555 An erase gap is represented in the tape image file by a special metadata
556 value. This value is chosen so that it is still recognizable even if it has
557 been "cut in half" by a subsequent data overwrite that does not end on a
558 metadatum-sized boundary. In addition, a range of metadata values are
559 reserved for detection in the reverse direction. Erase gaps are supported
560 only in SIMH tape format.
562 This implementation supports erasing gaps in the middle of a populated tape
563 image and will always produce a valid image. It also produces valid images
564 when overwriting gaps with data records, with one exception: a data write
565 that leaves only two bytes of gap remaining will produce an invalid tape.
566 This limitation is deemed acceptable, as it is analogous to the existing
567 limitation that data records cannot overwrite other data records without
568 producing an invalid tape.
570 Because SIMH tape images do not carry physical parameters (e.g., recording
571 density), overwriting a tape image file containing gap metadata is
572 problematic if the density setting is not the same as that used during
573 recording. There is no way to establish a gap of a certain length
574 unequivocally in an image file, so this implementation establishes a gap of a
575 certain number of bytes that reflect the desired gap length at the bpi used
578 To write an erase gap, the implementation uses one of two approaches,
579 depending on whether or not the current tape position is at EOM. Erasing at
580 EOM presents no special difficulties; gap metadata markers are written for
581 the prescribed number of bytes. If the tape is not at EOM, then erasing must
582 take into account the existing record structure to ensure that a valid tape
585 The general approach is to erase for the nominal number of bytes but to
586 increase that length, if necessary, to ensure that a partially overwritten
587 data record at the end of the gap can be altered to maintain validity.
588 Because the smallest legal tape record requires space for two metadata
589 markers plus two data bytes, an erasure that would leave less than that
590 is increased to consume the entire record. Otherwise, the final record is
591 truncated appropriately.
593 When reading in either direction, gap metadata markers are ignored (skipped)
594 until a record length header, EOF marker, EOM marker, or physical EOF is
595 encountered. Thus, tape images containing gap metadata are transparent to
596 the calling simulator.
598 The permissibility of data record lengths that are not multiples of the
599 metadatum size presents a difficulty when reading. If such an "odd length"
600 record is written over a gap, half of a metadata marker will exist
601 immediately after the trailing record length.
603 This condition is detected when reading forward by the appearance of a
604 "reversed" marker. The value appears reversed because the value is made up
605 of half of one marker and half of the next. This is handled by seeking
606 forward two bytes to resync (the stipulation above that the overwrite cannot
607 leave only two bytes of gap means that at least one "whole" metadata marker
608 will follow). Reading in reverse presents a more complex problem, because
609 half of the marker is from the preceding trailing record length marker and
610 therefore could be any of a range of values. However, that range is
611 restricted by the SIMH tape specification requirement that record length
612 metadata values must have bits 30:24 set to zero. This allows unambiguous
613 detection of the condition.
615 The value chosen for gap metadata and the values reserved for "half-gap"
618 0xFFFFFFFE - primary gap value
619 0xFFFEFFFF - reserved (indicates half-gap in forward reads)
620 0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads)
621 0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads)
624 t_stat
sim_tape_wrgap (UNIT
*uptr
, uint32 gaplen
, uint32 bpi
)
627 t_mtrlnt meta
, sbc
, new_len
, rec_size
;
628 t_addr gap_pos
= uptr
->pos
;
629 uint32 file_size
, marker_count
;
630 uint32 format
= MT_GET_FMT (uptr
);
631 uint32 gap_alloc
= 0; /* gap allocated from tape */
632 int32 gap_needed
= (gaplen
* bpi
) / 10; /* gap remainder still needed */
633 const uint32 meta_size
= sizeof (t_mtrlnt
); /* bytes per metadatum */
634 const uint32 min_rec_size
= 2 + sizeof (t_mtrlnt
) * 2; /* smallest data record size */
638 if ((uptr
->flags
& UNIT_ATT
) == 0) /* not attached? */
640 if (format
!= MTUF_F_STD
) /* not SIMH fmt? */
642 if (sim_tape_wrp (uptr
)) /* write protected? */
645 file_size
= sim_fsize (uptr
->fileref
); /* get file size */
646 sim_fseek (uptr
->fileref
, uptr
->pos
, SEEK_SET
); /* position tape */
648 /* Read tape records and allocate to gap until amount required is consumed.
650 Read next metadatum from tape:
651 - EOF or EOM: allocate remainder of bytes needed.
652 - TMK or GAP: allocate sizeof(metadatum) bytes.
653 - Reverse GAP: allocate sizeof(metadatum) / 2 bytes.
654 - Data record: see below.
656 Loop until bytes needed = 0.
660 sim_fread (&meta
, meta_size
, 1, uptr
->fileref
); /* read metadatum */
662 if (ferror (uptr
->fileref
)) { /* read error? */
663 uptr
->pos
= gap_pos
; /* restore original position */
664 MT_SET_PNU (uptr
); /* position not updated */
665 return sim_tape_ioerr (uptr
); /* translate error */
668 uptr
->pos
= uptr
->pos
+ meta_size
; /* move tape over datum */
670 if (feof (uptr
->fileref
) || (meta
== MTR_EOM
)) { /* at eof or eom? */
671 gap_alloc
= gap_alloc
+ gap_needed
; /* allocate remainder */
675 else if ((meta
== MTR_GAP
) || (meta
== MTR_TMK
)) { /* gap or tape mark? */
676 gap_alloc
= gap_alloc
+ meta_size
; /* allocate marker space */
677 gap_needed
= gap_needed
- meta_size
; /* reduce requirement */
680 else if (meta
== MTR_FHGAP
) { /* half gap? */
681 uptr
->pos
= uptr
->pos
- meta_size
/ 2; /* backup to resync */
682 sim_fseek (uptr
->fileref
, uptr
->pos
, SEEK_SET
); /* position tape */
683 gap_alloc
= gap_alloc
+ meta_size
/ 2; /* allocate marker space */
684 gap_needed
= gap_needed
- meta_size
/ 2; /* reduce requirement */
688 MTR_L (meta
) + meta_size
> file_size
) { /* rec len out of range? */
689 gap_alloc
= gap_alloc
+ gap_needed
; /* presume overwritten tape */
690 gap_needed
= 0; /* allocate remainder */
693 /* Allocate a data record:
694 - Determine record size in bytes (including metadata)
695 - If record size - bytes needed < smallest allowed record size,
696 allocate entire record to gap, else allocate needed amount and
697 truncate data record to reflect remainder.
699 else { /* data record */
700 sbc
= MTR_L (meta
); /* get record data length */
701 rec_size
= ((sbc
+ 1) & ~1) + meta_size
* 2; /* overall size in bytes */
703 if (rec_size
< gap_needed
+ min_rec_size
) { /* rec too small? */
704 uptr
->pos
= uptr
->pos
- meta_size
+ rec_size
; /* position past record */
705 sim_fseek (uptr
->fileref
, uptr
->pos
, SEEK_SET
); /* move tape */
706 gap_alloc
= gap_alloc
+ rec_size
; /* allocate record */
707 gap_needed
= gap_needed
- rec_size
; /* reduce requirement */
710 else { /* record size OK */
711 uptr
->pos
= uptr
->pos
- meta_size
+ gap_needed
; /* position to end of gap */
712 new_len
= MTR_F (meta
) | (sbc
- gap_needed
); /* truncate to new len */
713 st
= sim_tape_wrdata (uptr
, new_len
); /* write new rec len */
715 if (st
!= MTSE_OK
) { /* write OK? */
716 uptr
->pos
= gap_pos
; /* restore orig pos */
717 return st
; /* PNU was set by wrdata */
720 uptr
->pos
= uptr
->pos
+ sbc
- gap_needed
; /* position to end of data */
721 st
= sim_tape_wrdata (uptr
, new_len
); /* write new rec len */
723 if (st
!= MTSE_OK
) { /* write OK? */
724 uptr
->pos
= gap_pos
; /* restore orig pos */
725 return st
; /* PNU was set by wrdata */
728 gap_alloc
= gap_alloc
+ gap_needed
; /* allocate remainder */
733 while (gap_needed
> 0);
735 uptr
->pos
= gap_pos
; /* reposition to gap start */
737 if (gap_alloc
& (meta_size
- 1)) { /* gap size "odd?" */
738 st
= sim_tape_wrdata (uptr
, MTR_FHGAP
); /* write half gap marker */
739 if (st
!= MTSE_OK
) { /* write OK? */
740 uptr
->pos
= gap_pos
; /* restore orig pos */
741 return st
; /* PNU was set by wrdata */
743 uptr
->pos
= uptr
->pos
- meta_size
/ 2; /* realign position */
744 gap_alloc
= gap_alloc
- 2; /* decrease gap to write */
747 marker_count
= gap_alloc
/ meta_size
; /* count of gap markers */
750 st
= sim_tape_wrdata (uptr
, MTR_GAP
); /* write gap markers */
751 if (st
!= MTSE_OK
) { /* write OK? */
752 uptr
->pos
= gap_pos
; /* restore orig pos */
753 return st
; /* PNU was set by wrdata */
756 while (--marker_count
> 0);
761 /* Space record forward */
763 t_stat
sim_tape_sprecf (UNIT
*uptr
, t_mtrlnt
*bc
)
767 st
= sim_tape_rdlntf (uptr
, bc
); /* get record length */
772 /* Space record reverse */
774 t_stat
sim_tape_sprecr (UNIT
*uptr
, t_mtrlnt
*bc
)
778 if (MT_TST_PNU (uptr
)) {
783 st
= sim_tape_rdlntr (uptr
, bc
); /* get record length */
790 t_stat
sim_tape_rewind (UNIT
*uptr
)
799 t_stat
sim_tape_reset (UNIT
*uptr
)
807 t_bool
sim_tape_bot (UNIT
*uptr
)
809 uint32 f
= MT_GET_FMT (uptr
);
811 return (uptr
->pos
<= fmts
[f
].bot
)? TRUE
: FALSE
;
814 /* Test for end of tape */
816 t_bool
sim_tape_eot (UNIT
*uptr
)
818 return (uptr
->capac
&& (uptr
->pos
>= uptr
->capac
))? TRUE
: FALSE
;
821 /* Test for write protect */
823 t_bool
sim_tape_wrp (UNIT
*uptr
)
825 return (uptr
->flags
& MTUF_WRP
)? TRUE
: FALSE
;
828 /* Process I/O error */
830 t_stat
sim_tape_ioerr (UNIT
*uptr
)
832 perror ("Magtape library I/O error");
833 clearerr (uptr
->fileref
);
837 /* Set tape format */
839 t_stat
sim_tape_set_fmt (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
843 if (uptr
== NULL
) return SCPE_IERR
;
844 if (cptr
== NULL
) return SCPE_ARG
;
845 for (f
= 0; f
< MTUF_N_FMT
; f
++) {
846 if (fmts
[f
].name
&& (strcmp (cptr
, fmts
[f
].name
) == 0)) {
847 uptr
->flags
= (uptr
->flags
& ~MTUF_FMT
) |
848 (f
<< MTUF_V_FMT
) | fmts
[f
].uflags
;
855 /* Show tape format */
857 t_stat
sim_tape_show_fmt (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
859 int32 f
= MT_GET_FMT (uptr
);
861 if (fmts
[f
].name
) fprintf (st
, "%s format", fmts
[f
].name
);
862 else fprintf (st
, "invalid format");
866 /* Map a TPC format tape image */
868 uint32
sim_tape_tpc_map (UNIT
*uptr
, t_addr
*map
)
874 if ((uptr
== NULL
) || (uptr
->fileref
== NULL
)) return 0;
875 for (objc
= 0, tpos
= 0;; ) {
876 sim_fseek (uptr
->fileref
, tpos
, SEEK_SET
);
877 i
= sim_fread (&bc
, sizeof (t_tpclnt
), 1, uptr
->fileref
);
879 if (map
) map
[objc
] = tpos
;
881 tpos
= tpos
+ ((bc
+ 1) & ~1) + sizeof (t_tpclnt
);
883 if (map
) map
[objc
] = tpos
;
887 /* Find the preceding record in a TPC file */
889 t_addr
sim_tape_tpc_fnd (UNIT
*uptr
, t_addr
*map
)
894 if (map
== NULL
) return 0;
896 hi
= uptr
->hwmark
- 1;
899 if (uptr
->pos
== map
[p
])
900 return ((p
== 0)? map
[p
]: map
[p
- 1]);
901 else if (uptr
->pos
< map
[p
]) hi
= p
- 1;
905 return ((p
== 0)? map
[p
]: map
[p
- 1]);
908 /* Set tape capacity */
910 t_stat
sim_tape_set_capac (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
912 extern uint32 sim_taddr_64
;
916 if ((cptr
== NULL
) || (*cptr
== 0)) return SCPE_ARG
;
917 if (uptr
->flags
& UNIT_ATT
) return SCPE_ALATT
;
918 cap
= (t_addr
) get_uint (cptr
, 10, sim_taddr_64
? 2000000: 2000, &r
);
919 if (r
!= SCPE_OK
) return SCPE_ARG
;
920 uptr
->capac
= cap
* ((t_addr
) 1000000);
924 /* Show tape capacity */
926 t_stat
sim_tape_show_capac (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
929 if (uptr
->capac
>= (t_addr
) 1000000)
930 fprintf (st
, "capacity=%dMB", (uint32
) (uptr
->capac
/ ((t_addr
) 1000000)));
931 else if (uptr
->capac
>= (t_addr
) 1000)
932 fprintf (st
, "capacity=%dKB", (uint32
) (uptr
->capac
/ ((t_addr
) 1000)));
933 else fprintf (st
, "capacity=%dB", (uint32
) uptr
->capac
);
935 else fprintf (st
, "unlimited capacity");