mirror of
https://github.com/simh/simh.git
synced 2026-01-28 12:49:21 +00:00
Notes For V3.7-0
1. New Features 1.1 3.7-0 1.1.1 SCP - Added SET THROTTLE and SET NOTHROTTLE commands to regulate simulator execution rate and host resource utilization. - Added idle support (based on work by Mark Pizzolato). - Added -e to control error processing in nested DO commands (from Dave Bryan). 1.1.2 HP2100 - Added Double Integer instructions, 1000-F CPU, and Floating Point Processor (from Dave Bryan). - Added 2114 and 2115 CPUs, 12607B and 12578A DMA controllers, and 21xx binary loader protection (from Dave Bryan). 1.1.3 Interdata - Added SET IDLE and SET NOIDLE commands to idle the simulator in wait state. 1.1.4 PDP-11 - Added SET IDLE and SET NOIDLE commands to idle the simulator in wait state (WAIT instruction executed). - Added TA11/TU60 cassette support. 1.1.5 PDP-8 - Added SET IDLE and SET NOIDLE commands to idle the simulator in wait state (keyboard poll loop or jump-to-self). - Added TA8E/TU60 cassette support. 1.1.6 PDP-1 - Added support for 16-channel sequence break system. - Added support for PDP-1D extended features and timesharing clock. - Added support for Type 630 data communications subsystem. 1.1.6 PDP-4/7/9/15 - Added SET IDLE and SET NOIDLE commands to idle the simulator in wait state (keyboard poll loop or jump-to-self). 1.1.7 VAX, VAX780 - Added SET IDLE and SET NOIDLE commands to idle the simulator in wait state (more than 200 cycles at IPL's 0, 1, or 3 in kernel mode). 1.1.8 PDP-10 - Added SET IDLE and SET NOIDLE commands to idle the simulator in wait state (operating system dependent). - Added CD20 (CD11) support. 2. Bugs Fixed Please see the revision history on http://simh.trailing-edge.com or in the source module sim_rev.h.
This commit is contained in:
committed by
Mark Pizzolato
parent
15919a2dd7
commit
53d02f7fa7
318
sim_tape.c
318
sim_tape.c
@@ -1,6 +1,6 @@
|
||||
/* sim_tape.c: simulator tape support library
|
||||
|
||||
Copyright (c) 1993-2006, Robert M Supnik
|
||||
Copyright (c) 1993-2007, Robert M Supnik
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
@@ -26,6 +26,10 @@
|
||||
Ultimately, this will be a place to hide processing of various tape formats,
|
||||
as well as OS-specific direct hardware access.
|
||||
|
||||
23-Jan-07 JDB Fixed backspace over gap at BOT
|
||||
22-Jan-07 RMS Fixed bug in P7B format read reclnt rev (found by Rich Cornwell)
|
||||
15-Dec-06 RMS Added support for small capacity tapes
|
||||
30-Aug-06 JDB Added erase gap support
|
||||
14-Feb-06 RMS Added variable tape capacity
|
||||
23-Jan-06 JDB Fixed odd-byte-write problem in sim_tape_wrrecf
|
||||
17-Dec-05 RMS Added write support for Paul Pierce 7b format
|
||||
@@ -48,6 +52,7 @@
|
||||
sim_tape_sprecr space tape record reverse
|
||||
sim_tape_wrtmk write tape mark
|
||||
sim_tape_wreom erase remainder of tape
|
||||
sim_tape_wrgap write erase gap
|
||||
sim_tape_rewind rewind
|
||||
sim_tape_reset reset unit
|
||||
sim_tape_bot TRUE if at beginning of tape
|
||||
@@ -165,6 +170,8 @@ return SCPE_OK;
|
||||
end of file/medium unchanged, PNU set
|
||||
tape mark updated
|
||||
data record updated, sim_fread will read record forward
|
||||
|
||||
See notes at "sim_tape_wrgap" regarding erase gap implementation.
|
||||
*/
|
||||
|
||||
t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc)
|
||||
@@ -181,20 +188,28 @@ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set tape pos */
|
||||
switch (f) { /* switch on fmt */
|
||||
|
||||
case MTUF_F_STD: case MTUF_F_E11:
|
||||
sim_fread (bc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */
|
||||
sbc = MTR_L (*bc); /* save rec lnt */
|
||||
if (ferror (uptr->fileref)) { /* error? */
|
||||
MT_SET_PNU (uptr); /* pos not upd */
|
||||
return sim_tape_ioerr (uptr);
|
||||
do {
|
||||
sim_fread (bc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */
|
||||
sbc = MTR_L (*bc); /* save rec lnt */
|
||||
if (ferror (uptr->fileref)) { /* error? */
|
||||
MT_SET_PNU (uptr); /* pos not upd */
|
||||
return sim_tape_ioerr (uptr);
|
||||
}
|
||||
if (feof (uptr->fileref) || (*bc == MTR_EOM)) { /* eof or eom? */
|
||||
MT_SET_PNU (uptr); /* pos not upd */
|
||||
return MTSE_EOM;
|
||||
}
|
||||
uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* spc over rec lnt */
|
||||
if (*bc == MTR_TMK) return MTSE_TMK; /* tape mark? */
|
||||
if (*bc == MTR_FHGAP) { /* half gap? */
|
||||
uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* half space fwd */
|
||||
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* resync */
|
||||
}
|
||||
else if (*bc != MTR_GAP)
|
||||
uptr->pos = uptr->pos + sizeof (t_mtrlnt) + /* spc over record */
|
||||
((f == MTUF_F_STD)? ((sbc + 1) & ~1): sbc);
|
||||
}
|
||||
if (feof (uptr->fileref) || (*bc == MTR_EOM)) { /* eof or eom? */
|
||||
MT_SET_PNU (uptr); /* pos not upd */
|
||||
return MTSE_EOM;
|
||||
}
|
||||
uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* spc over rec lnt */
|
||||
if (*bc == MTR_TMK) return MTSE_TMK; /* tape mark? */
|
||||
uptr->pos = uptr->pos + sizeof (t_mtrlnt) + /* spc over record */
|
||||
((f == MTUF_F_STD)? ((sbc + 1) & ~1): sbc);
|
||||
while ((*bc == MTR_GAP) || (*bc == MTR_FHGAP));
|
||||
break;
|
||||
|
||||
case MTUF_F_TPC:
|
||||
@@ -257,6 +272,8 @@ return MTSE_OK;
|
||||
end of medium updated
|
||||
tape mark updated
|
||||
data record updated, sim_fread will read record forward
|
||||
|
||||
See notes at "sim_tape_wrgap" regarding erase gap implementation.
|
||||
*/
|
||||
|
||||
t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc)
|
||||
@@ -274,18 +291,29 @@ if (sim_tape_bot (uptr)) return MTSE_BOT; /* at BOT? */
|
||||
switch (f) { /* switch on fmt */
|
||||
|
||||
case MTUF_F_STD: case MTUF_F_E11:
|
||||
sim_fseek (uptr->fileref, uptr->pos - sizeof (t_mtrlnt), SEEK_SET);
|
||||
sim_fread (bc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */
|
||||
sbc = MTR_L (*bc);
|
||||
if (ferror (uptr->fileref)) /* error? */
|
||||
return sim_tape_ioerr (uptr);
|
||||
if (feof (uptr->fileref)) return MTSE_EOM; /* eof? */
|
||||
uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* spc over rec lnt */
|
||||
if (*bc == MTR_EOM) return MTSE_EOM; /* eom? */
|
||||
if (*bc == MTR_TMK) return MTSE_TMK; /* tape mark? */
|
||||
uptr->pos = uptr->pos - sizeof (t_mtrlnt) - /* spc over record */
|
||||
((f == MTUF_F_STD)? ((sbc + 1) & ~1): sbc);
|
||||
sim_fseek (uptr->fileref, uptr->pos + sizeof (t_mtrlnt), SEEK_SET);
|
||||
do {
|
||||
sim_fseek (uptr->fileref, uptr->pos - sizeof (t_mtrlnt), SEEK_SET);
|
||||
sim_fread (bc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */
|
||||
sbc = MTR_L (*bc);
|
||||
if (ferror (uptr->fileref)) /* error? */
|
||||
return sim_tape_ioerr (uptr);
|
||||
if (feof (uptr->fileref)) return MTSE_EOM; /* eof? */
|
||||
uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* spc over rec lnt */
|
||||
if (*bc == MTR_EOM) return MTSE_EOM; /* eom? */
|
||||
if (*bc == MTR_TMK) return MTSE_TMK; /* tape mark? */
|
||||
if ((*bc & MTR_M_RHGAP) == MTR_RHGAP) { /* half gap? */
|
||||
uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* half space rev */
|
||||
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* resync */
|
||||
}
|
||||
else if (*bc != MTR_GAP) {
|
||||
uptr->pos = uptr->pos - sizeof (t_mtrlnt) - /* spc over record */
|
||||
((f == MTUF_F_STD)? ((sbc + 1) & ~1): sbc);
|
||||
sim_fseek (uptr->fileref, uptr->pos + sizeof (t_mtrlnt), SEEK_SET);
|
||||
}
|
||||
else if (sim_tape_bot (uptr)) /* backed into BOT? */
|
||||
return MTSE_BOT;
|
||||
}
|
||||
while ((*bc == MTR_GAP) || (*bc == MTR_RHGAP));
|
||||
break;
|
||||
|
||||
case MTUF_F_TPC:
|
||||
@@ -312,6 +340,7 @@ switch (f) { /* switch on fmt */
|
||||
if (c & P7B_SOR) break; /* start of record? */
|
||||
}
|
||||
uptr->pos = uptr->pos - sbc; /* update position */
|
||||
*bc = sbc; /* save rec lnt */
|
||||
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */
|
||||
if (all_eof) return MTSE_TMK; /* tape mark? */
|
||||
break;
|
||||
@@ -432,9 +461,10 @@ uint32 f = MT_GET_FMT (uptr);
|
||||
t_mtrlnt sbc;
|
||||
|
||||
MT_CLR_PNU (uptr);
|
||||
sbc = MTR_L (bc);
|
||||
if ((uptr->flags & UNIT_ATT) == 0) return MTSE_UNATT; /* not attached? */
|
||||
if (sim_tape_wrp (uptr)) return MTSE_WRP; /* write prot? */
|
||||
sbc = MTR_L (bc);
|
||||
if (sbc == 0) return MTSE_OK; /* nothing to do? */
|
||||
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */
|
||||
switch (f) { /* case on format */
|
||||
|
||||
@@ -502,6 +532,232 @@ if (MT_GET_FMT (uptr) == MTUF_F_P7B) return MTSE_FMT; /* cant do P7B */
|
||||
return sim_tape_wrdata (uptr, MTR_EOM);
|
||||
}
|
||||
|
||||
/* Write erase gap
|
||||
|
||||
Inputs:
|
||||
uptr = pointer to tape unit
|
||||
gaplen = length of gap in tenths of an inch
|
||||
bpi = current recording density in bytes per inch
|
||||
|
||||
Outputs:
|
||||
status = operation status
|
||||
|
||||
exit condition position
|
||||
------------------ ------------------
|
||||
unit unattached unchanged
|
||||
unsupported format unchanged
|
||||
write protected unchanged
|
||||
read error unchanged, PNU set
|
||||
write error unchanged, PNU set
|
||||
gap written updated
|
||||
|
||||
|
||||
An erase gap is represented in the tape image file by a special metadata
|
||||
value. This value is chosen so that it is still recognizable even if it has
|
||||
been "cut in half" by a subsequent data overwrite that does not end on a
|
||||
metadatum-sized boundary. In addition, a range of metadata values are
|
||||
reserved for detection in the reverse direction. Erase gaps are supported
|
||||
only in SIMH tape format.
|
||||
|
||||
This implementation supports erasing gaps in the middle of a populated tape
|
||||
image and will always produce a valid image. It also produces valid images
|
||||
when overwriting gaps with data records, with one exception: a data write
|
||||
that leaves only two bytes of gap remaining will produce an invalid tape.
|
||||
This limitation is deemed acceptable, as it is analogous to the existing
|
||||
limitation that data records cannot overwrite other data records without
|
||||
producing an invalid tape.
|
||||
|
||||
Because SIMH tape images do not carry physical parameters (e.g., recording
|
||||
density), overwriting a tape image file containing gap metadata is
|
||||
problematic if the density setting is not the same as that used during
|
||||
recording. There is no way to establish a gap of a certain length
|
||||
unequivocally in an image file, so this implementation establishes a gap of a
|
||||
certain number of bytes that reflect the desired gap length at the bpi used
|
||||
during writing.
|
||||
|
||||
To write an erase gap, the implementation uses one of two approaches,
|
||||
depending on whether or not the current tape position is at EOM. Erasing at
|
||||
EOM presents no special difficulties; gap metadata markers are written for
|
||||
the prescribed number of bytes. If the tape is not at EOM, then erasing must
|
||||
take into account the existing record structure to ensure that a valid tape
|
||||
image is maintained.
|
||||
|
||||
The general approach is to erase for the nominal number of bytes but to
|
||||
increase that length, if necessary, to ensure that a partially overwritten
|
||||
data record at the end of the gap can be altered to maintain validity.
|
||||
Because the smallest legal tape record requires space for two metadata
|
||||
markers plus two data bytes, an erasure that would leave less than that
|
||||
is increased to consume the entire record. Otherwise, the final record is
|
||||
truncated appropriately.
|
||||
|
||||
When reading in either direction, gap metadata markers are ignored (skipped)
|
||||
until a record length header, EOF marker, EOM marker, or physical EOF is
|
||||
encountered. Thus, tape images containing gap metadata are transparent to
|
||||
the calling simulator.
|
||||
|
||||
The permissibility of data record lengths that are not multiples of the
|
||||
metadatum size presents a difficulty when reading. If such an "odd length"
|
||||
record is written over a gap, half of a metadata marker will exist
|
||||
immediately after the trailing record length.
|
||||
|
||||
This condition is detected when reading forward by the appearance of a
|
||||
"reversed" marker. The value appears reversed because the value is made up
|
||||
of half of one marker and half of the next. This is handled by seeking
|
||||
forward two bytes to resync (the stipulation above that the overwrite cannot
|
||||
leave only two bytes of gap means that at least one "whole" metadata marker
|
||||
will follow). Reading in reverse presents a more complex problem, because
|
||||
half of the marker is from the preceding trailing record length marker and
|
||||
therefore could be any of a range of values. However, that range is
|
||||
restricted by the SIMH tape specification requirement that record length
|
||||
metadata values must have bits 30:24 set to zero. This allows unambiguous
|
||||
detection of the condition.
|
||||
|
||||
The value chosen for gap metadata and the values reserved for "half-gap"
|
||||
detection are:
|
||||
|
||||
0xFFFFFFFE - primary gap value
|
||||
0xFFFEFFFF - reserved (indicates half-gap in forward reads)
|
||||
0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads)
|
||||
0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads)
|
||||
*/
|
||||
|
||||
t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen, uint32 bpi)
|
||||
{
|
||||
t_stat st;
|
||||
t_mtrlnt meta, sbc, new_len, rec_size;
|
||||
t_addr gap_pos = uptr->pos;
|
||||
uint32 file_size, marker_count;
|
||||
uint32 format = MT_GET_FMT (uptr);
|
||||
uint32 gap_alloc = 0; /* gap allocated from tape */
|
||||
int32 gap_needed = (gaplen * bpi) / 10; /* gap remainder still needed */
|
||||
const uint32 meta_size = sizeof (t_mtrlnt); /* bytes per metadatum */
|
||||
const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* smallest data record size */
|
||||
|
||||
MT_CLR_PNU (uptr);
|
||||
|
||||
if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
|
||||
return MTSE_UNATT;
|
||||
if (format != MTUF_F_STD) /* not SIMH fmt? */
|
||||
return MTSE_FMT;
|
||||
if (sim_tape_wrp (uptr)) /* write protected? */
|
||||
return MTSE_WRP;
|
||||
|
||||
file_size = sim_fsize (uptr->fileref); /* get file size */
|
||||
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */
|
||||
|
||||
/* Read tape records and allocate to gap until amount required is consumed.
|
||||
|
||||
Read next metadatum from tape:
|
||||
- EOF or EOM: allocate remainder of bytes needed.
|
||||
- TMK or GAP: allocate sizeof(metadatum) bytes.
|
||||
- Reverse GAP: allocate sizeof(metadatum) / 2 bytes.
|
||||
- Data record: see below.
|
||||
|
||||
Loop until bytes needed = 0.
|
||||
*/
|
||||
|
||||
do {
|
||||
sim_fread (&meta, meta_size, 1, uptr->fileref); /* read metadatum */
|
||||
|
||||
if (ferror (uptr->fileref)) { /* read error? */
|
||||
uptr->pos = gap_pos; /* restore original position */
|
||||
MT_SET_PNU (uptr); /* position not updated */
|
||||
return sim_tape_ioerr (uptr); /* translate error */
|
||||
}
|
||||
else
|
||||
uptr->pos = uptr->pos + meta_size; /* move tape over datum */
|
||||
|
||||
if (feof (uptr->fileref) || (meta == MTR_EOM)) { /* at eof or eom? */
|
||||
gap_alloc = gap_alloc + gap_needed; /* allocate remainder */
|
||||
gap_needed = 0;
|
||||
}
|
||||
|
||||
else if ((meta == MTR_GAP) || (meta == MTR_TMK)) { /* gap or tape mark? */
|
||||
gap_alloc = gap_alloc + meta_size; /* allocate marker space */
|
||||
gap_needed = gap_needed - meta_size; /* reduce requirement */
|
||||
}
|
||||
|
||||
else if (meta == MTR_FHGAP) { /* half gap? */
|
||||
uptr->pos = uptr->pos - meta_size / 2; /* backup to resync */
|
||||
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */
|
||||
gap_alloc = gap_alloc + meta_size / 2; /* allocate marker space */
|
||||
gap_needed = gap_needed - meta_size / 2; /* reduce requirement */
|
||||
}
|
||||
|
||||
else if (uptr->pos +
|
||||
MTR_L (meta) + meta_size > file_size) { /* rec len out of range? */
|
||||
gap_alloc = gap_alloc + gap_needed; /* presume overwritten tape */
|
||||
gap_needed = 0; /* allocate remainder */
|
||||
}
|
||||
|
||||
/* Allocate a data record:
|
||||
- Determine record size in bytes (including metadata)
|
||||
- If record size - bytes needed < smallest allowed record size,
|
||||
allocate entire record to gap, else allocate needed amount and
|
||||
truncate data record to reflect remainder.
|
||||
*/
|
||||
else { /* data record */
|
||||
sbc = MTR_L (meta); /* get record data length */
|
||||
rec_size = ((sbc + 1) & ~1) + meta_size * 2; /* overall size in bytes */
|
||||
|
||||
if (rec_size < gap_needed + min_rec_size) { /* rec too small? */
|
||||
uptr->pos = uptr->pos - meta_size + rec_size; /* position past record */
|
||||
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* move tape */
|
||||
gap_alloc = gap_alloc + rec_size; /* allocate record */
|
||||
gap_needed = gap_needed - rec_size; /* reduce requirement */
|
||||
}
|
||||
|
||||
else { /* record size OK */
|
||||
uptr->pos = uptr->pos - meta_size + gap_needed; /* position to end of gap */
|
||||
new_len = MTR_F (meta) | (sbc - gap_needed); /* truncate to new len */
|
||||
st = sim_tape_wrdata (uptr, new_len); /* write new rec len */
|
||||
|
||||
if (st != MTSE_OK) { /* write OK? */
|
||||
uptr->pos = gap_pos; /* restore orig pos */
|
||||
return st; /* PNU was set by wrdata */
|
||||
}
|
||||
|
||||
uptr->pos = uptr->pos + sbc - gap_needed; /* position to end of data */
|
||||
st = sim_tape_wrdata (uptr, new_len); /* write new rec len */
|
||||
|
||||
if (st != MTSE_OK) { /* write OK? */
|
||||
uptr->pos = gap_pos; /* restore orig pos */
|
||||
return st; /* PNU was set by wrdata */
|
||||
}
|
||||
|
||||
gap_alloc = gap_alloc + gap_needed; /* allocate remainder */
|
||||
gap_needed = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (gap_needed > 0);
|
||||
|
||||
uptr->pos = gap_pos; /* reposition to gap start */
|
||||
|
||||
if (gap_alloc & (meta_size - 1)) { /* gap size "odd?" */
|
||||
st = sim_tape_wrdata (uptr, MTR_FHGAP); /* write half gap marker */
|
||||
if (st != MTSE_OK) { /* write OK? */
|
||||
uptr->pos = gap_pos; /* restore orig pos */
|
||||
return st; /* PNU was set by wrdata */
|
||||
}
|
||||
uptr->pos = uptr->pos - meta_size / 2; /* realign position */
|
||||
gap_alloc = gap_alloc - 2; /* decrease gap to write */
|
||||
}
|
||||
|
||||
marker_count = gap_alloc / meta_size; /* count of gap markers */
|
||||
|
||||
do {
|
||||
st = sim_tape_wrdata (uptr, MTR_GAP); /* write gap markers */
|
||||
if (st != MTSE_OK) { /* write OK? */
|
||||
uptr->pos = gap_pos; /* restore orig pos */
|
||||
return st; /* PNU was set by wrdata */
|
||||
}
|
||||
}
|
||||
while (--marker_count > 0);
|
||||
|
||||
return MTSE_OK;
|
||||
}
|
||||
|
||||
/* Space record forward */
|
||||
|
||||
t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc)
|
||||
@@ -669,7 +925,13 @@ return SCPE_OK;
|
||||
|
||||
t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, void *desc)
|
||||
{
|
||||
if (uptr->capac) fprintf (st, "capacity=%dMB", (uint32) (uptr->capac / ((t_addr) 1000000)));
|
||||
if (uptr->capac) {
|
||||
if (uptr->capac >= (t_addr) 1000000)
|
||||
fprintf (st, "capacity=%dMB", (uint32) (uptr->capac / ((t_addr) 1000000)));
|
||||
else if (uptr->capac >= (t_addr) 1000)
|
||||
fprintf (st, "capacity=%dKB", (uint32) (uptr->capac / ((t_addr) 1000)));
|
||||
else fprintf (st, "capacity=%dB", (uint32) uptr->capac);
|
||||
}
|
||||
else fprintf (st, "unlimited capacity");
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user