diff --git a/sim_defs.h b/sim_defs.h index 08908db..a92433d 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -654,7 +654,7 @@ struct UNIT { #define UNIT_NO_FIO 0000004 /* fileref is NOT a FILE * */ #define UNIT_DISK_CHK 0000010 /* disk data debug checking (sim_disk) */ #define UNIT_TMR_UNIT 0000200 /* Unit registered as a calibrated timer */ -#define UNIT_TAPE_MRK 0000400 /* Tape Unit AWS Tapemark */ +#define UNIT_TAPE_MRK 0000400 /* Tape Unit Tapemark */ #define UNIT_TAPE_PNU 0001000 /* Tape Unit Position Not Updated */ #define UNIT_V_DF_TAPE 10 /* Bit offset for Tape Density reservation */ #define UNIT_S_DF_TAPE 3 /* Bits Reserved for Tape Density */ diff --git a/sim_tape.c b/sim_tape.c index 1f5cb0e..9ade108 100644 --- a/sim_tape.c +++ b/sim_tape.c @@ -113,6 +113,7 @@ static struct sim_tape_fmt { { "AWS", 0, 0, 0 }, { "TAR", UNIT_RO, 0, 0 }, { "ANSI", UNIT_RO, 0, 0 }, + { "FIXED", UNIT_RO, 0, 0 }, { NULL, 0, 0, 0 } }; @@ -441,15 +442,15 @@ typedef struct TAPE_RECORD { uint8 data[1]; } TAPE_RECORD; -typedef struct ANSI_TAPE { - uint32 ansi_type; /* ANSI-VMS, ANSI-RT11, ANSI-RSTS, ANSI-RSX11 */ +typedef struct MEMORY_TAPE { + uint32 ansi_type; /* ANSI-VMS, ANSI-RT11, ANSI-RSTS, ANSI-RSX11, etc. */ uint32 file_count; /* number of labeled files */ uint32 record_count; /* number of entries in the record array */ uint32 array_size; /* allocated size of records array */ uint32 block_size; /* tape block size */ TAPE_RECORD **records; VOL1 vol1; - } ANSI_TAPE; + } MEMORY_TAPE; const char HDR3_RMS_STREAM[] = "HDR3020002040000" "0000000100000000" @@ -498,23 +499,27 @@ static struct ansi_tape_parameters { t_bool zero_record_length; char record_format; char carriage_control; - } ansi_args[] = { /* code nohdr2 nohdr3 fixed_text lvl hdr3 fir fuxed hdr3 fir lf hdr3 for crlf */ - {"ANSI-VMS" , "DECFILE11A", FALSE, FALSE, FALSE, '3', HDR3_RMS_FIXED, HDR3_RMS_STMLF, HDR3_RMS_STREAM, 0, 0, FALSE, FALSE, 0, 0}, - {"ANSI-RSX11" , "DECFILE11A", FALSE, FALSE, FALSE, '4', HDR3_RMS_FIXRSX, HDR3_RMS_VARRSX, HDR3_RMS_VARRSX, 1, 2, FALSE, FALSE, 0, 0}, - {"ANSI-RT11" , "DECRT11A", TRUE, TRUE, TRUE, '3', NULL, NULL, NULL, 0, 0, FALSE, FALSE, 0, 0}, - {"ANSI-RSTS" , "DECRSTS/E", FALSE, TRUE, TRUE, '3', NULL, NULL, NULL, 0, 0, TRUE, TRUE, 'U', 'M'}, + } ansi_args[] = { /* code nohdr2 nohdr3 fixed_text lvl hdr3 fir fuxed hdr3 fir lf hdr3 for crlf skLF CRLF Y2KDT 0RecLnt RFM CC*/ + {"ANSI-VMS" , "DECFILE11A", FALSE, FALSE, FALSE, '3', HDR3_RMS_FIXED, HDR3_RMS_STMLF, HDR3_RMS_STREAM, 0, 0, FALSE, FALSE, 0, 0}, + {"ANSI-RSX11" , "DECFILE11A", FALSE, FALSE, FALSE, '4', HDR3_RMS_FIXRSX, HDR3_RMS_VARRSX, HDR3_RMS_VARRSX, 1, 2, FALSE, FALSE, 0, 0}, + {"ANSI-RT11" , "DECRT11A", TRUE, TRUE, TRUE, '3', NULL, NULL, NULL, 0, 0, FALSE, FALSE, 0, 0}, + {"ANSI-RSTS" , "DECRSTS/E", FALSE, TRUE, TRUE, '3', NULL, NULL, NULL, 0, 0, TRUE, TRUE, 'U', 'M'}, + {"ANSI-VAR" , "DECRSTS/E", FALSE, TRUE, FALSE, '3', NULL, NULL, NULL, 1, 2, TRUE, FALSE, 'D', ' '}, {NULL} }; -static ANSI_TAPE *ansi_create_tape (const char *label, uint32 block_size, uint32 ansi_type); -static int ansi_free_tape (void *vtape); +static MEMORY_TAPE *ansi_create_tape (const char *label, uint32 block_size, uint32 ansi_type); +static MEMORY_TAPE *memory_create_tape (void); +static void memory_free_tape (void *vtape); static void sim_tape_add_ansi_entry (const char *directory, const char *filename, t_offset FileSize, const struct stat *filestat, void *context); -static t_bool ansi_tape_add_block (ANSI_TAPE *tape, uint8 *block, uint32 size); +static t_bool memory_tape_add_block (MEMORY_TAPE *tape, uint8 *block, uint32 size); +static t_stat sim_export_tape (UNIT *uptr, const char *export_file); +static int tape_classify_file_contents (FILE *f, size_t *max_record_size, t_bool *lf_line_endings, t_bool *crlf_line_endings); /* Enable asynchronous operation */ @@ -610,12 +615,14 @@ struct tape_context *ctx; uint32 objc; DEVICE *dptr; char gbuf[CBUFSIZE]; +char export_file[CBUFSIZE] = ""; uint32 recsize = 0; t_stat r; t_bool auto_format = FALSE; t_bool had_debug = (sim_deb != NULL); uint32 starting_dctrl = uptr->dctrl; int32 saved_switches = sim_switches; +MEMORY_TAPE *tape = NULL; if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_NOATT; @@ -638,51 +645,165 @@ if (sim_switches & SWMASK ('B')) { /* Record Size (blocking fac uptr->recsize = recsize; sim_switches = sim_switches & ~(SWMASK ('B')); /* Record Blocking Factor */ } +else { + if ((MT_GET_FMT (uptr) == MTUF_F_TAR) && (uptr->recsize == 0)) + uptr->recsize = TAR_DFLT_RECSIZE; + } +if (sim_switches & SWMASK ('X')) + cptr = get_glyph_nc (cptr, export_file, 0); /* get spec */ if ((MT_GET_FMT (uptr) == MTUF_F_TPC) || (MT_GET_FMT (uptr) == MTUF_F_TAR) || - (MT_GET_FMT (uptr) == MTUF_F_ANSI)) + (MT_GET_FMT (uptr) >= MTUF_F_ANSI)) sim_switches |= SWMASK ('R'); /* Force ReadOnly attach for TPC, TAR and ANSI tapes */ -if (MT_GET_FMT (uptr) == MTUF_F_ANSI) { - const char *ocptr = cptr; - char label[CBUFSIZE] = "simh"; - ANSI_TAPE *tape; +if (sim_switches & SWMASK ('X')) + cptr = get_glyph_nc (cptr, export_file, 0); /* get export file spec */ +switch (MT_GET_FMT (uptr)) { + case MTUF_F_ANSI: + if (1) { + const char *ocptr = cptr; + char label[CBUFSIZE] = "simh"; - if ((MT_GET_ANSI_TYP (uptr) == MTAT_F_RT11) || - (MT_GET_ANSI_TYP (uptr) == MTAT_F_RSX11) || - (MT_GET_ANSI_TYP (uptr) == MTAT_F_RSTS)) - uptr->recsize = 512; - if (uptr->recsize == 0) - uptr->recsize = 2048; - else { - if ((uptr->recsize < 512) || (uptr->recsize % 512)) - return sim_messagef (SCPE_ARG, "Block size of %u is below or not a multiple of the required minimum ANSI size of 512.\n", uptr->recsize); - } - tape = ansi_create_tape (label, uptr->recsize, MT_GET_ANSI_TYP (uptr)); - uptr->fileref = (FILE *)tape; - if (!uptr->fileref) - return SCPE_MEM; - while (*cptr != 0) { /* do all mods */ - cptr = get_glyph_nc (cptr, gbuf, ','); /* get filename */ - sim_dir_scan (gbuf, sim_tape_add_ansi_entry, uptr->fileref); - } - if (((ANSI_TAPE *)uptr->fileref)->file_count > 0) { - r = SCPE_OK; - ansi_tape_add_block ((ANSI_TAPE *)uptr->fileref, NULL, 0); /* Tape Mark */ - uptr->flags |= UNIT_ATT; - uptr->filename = (char *)malloc (strlen (ocptr) + 1); - strcpy (uptr->filename, ocptr); - uptr->tape_eom = ((ANSI_TAPE *)uptr->fileref)->record_count; - } - else { - r = SCPE_ARG; - ansi_free_tape (uptr->fileref); - uptr->fileref = NULL; - } + if ((MT_GET_ANSI_TYP (uptr) == MTAT_F_RT11) || + (MT_GET_ANSI_TYP (uptr) == MTAT_F_RSX11) || + (MT_GET_ANSI_TYP (uptr) == MTAT_F_RSTS)) + uptr->recsize = 512; + if (uptr->recsize == 0) + uptr->recsize = 2048; + else { + if ((uptr->recsize < 512) || (uptr->recsize % 512)) + return sim_messagef (SCPE_ARG, "Block size of %u is below or not a multiple of the required minimum ANSI size of 512.\n", uptr->recsize); + } + tape = ansi_create_tape (label, uptr->recsize, MT_GET_ANSI_TYP (uptr)); + uptr->fileref = (FILE *)tape; + if (!uptr->fileref) + return SCPE_MEM; + while (*cptr != 0) { /* do all mods */ + cptr = get_glyph_nc (cptr, gbuf, ','); /* get filename */ + sim_dir_scan (gbuf, sim_tape_add_ansi_entry, tape); + } + if (tape->file_count > 0) { + r = SCPE_OK; + memory_tape_add_block (tape, NULL, 0); /* Tape Mark */ + uptr->flags |= UNIT_ATT; + uptr->filename = (char *)malloc (strlen (ocptr) + 1); + strcpy (uptr->filename, ocptr); + uptr->tape_eom = tape->record_count; + } + else { + r = SCPE_ARG; + memory_free_tape (uptr->fileref); + uptr->fileref = NULL; + } + } + break; + + case MTUF_F_FIXED: + if (1) { + FILE *f; + size_t max_record_size; + t_bool lf_line_endings; + t_bool crlf_line_endings; + uint8 *block = NULL; + int error = FALSE; + static const uint8 ascii2ebcdic[128] = { + 0000,0001,0002,0003,0067,0055,0056,0057, + 0026,0005,0045,0013,0014,0015,0016,0017, + 0020,0021,0022,0023,0074,0075,0062,0046, + 0030,0031,0077,0047,0034,0035,0036,0037, + 0100,0117,0177,0173,0133,0154,0120,0175, + 0115,0135,0134,0116,0153,0140,0113,0141, + 0360,0361,0362,0363,0364,0365,0366,0367, + 0370,0371,0172,0136,0114,0176,0156,0157, + 0174,0301,0302,0303,0304,0305,0306,0307, + 0310,0311,0321,0322,0323,0324,0325,0326, + 0327,0330,0331,0342,0343,0344,0345,0346, + 0347,0350,0351,0112,0340,0132,0137,0155, + 0171,0201,0202,0203,0204,0205,0206,0207, + 0210,0211,0221,0222,0223,0224,0225,0226, + 0227,0230,0231,0242,0243,0244,0245,0246, + 0247,0250,0251,0300,0152,0320,0241,0007}; + + tape = memory_create_tape (); + uptr->fileref = (FILE *)tape; + if (!uptr->fileref) + return SCPE_MEM; + f = fopen (cptr, "rb"); + if (f == NULL) { + r = sim_messagef (SCPE_OPENERR, "Can't open: %s - %s\n", cptr, strerror (errno)); + break; + } + tape_classify_file_contents (f, &max_record_size, &lf_line_endings, &crlf_line_endings); + if ((!lf_line_endings) && (!crlf_line_endings)) { /* binary file? */ + if (uptr->recsize == 0) { + r = sim_messagef (SCPE_ARG, "Binary file %s must specify a record size with -B\n", cptr); + fclose (f); + break; + } + block = (uint8 *)malloc (uptr->recsize); + tape->block_size = uptr->recsize; + while (!feof(f) && !error) { + size_t data_read = fread (block, 1, tape->block_size, f); + if (data_read > 0) + error = memory_tape_add_block (tape, block, data_read); + } + } + else { /* text file */ + if (uptr->recsize == 0) + uptr->recsize = max_record_size; + if (uptr->recsize < max_record_size) { + r = sim_messagef (SCPE_ARG, "Text file: %s has lines longer than %d\n", cptr, (int)uptr->recsize); + fclose (f); + break; + } + tape->block_size = uptr->recsize; + block = (uint8 *)calloc (1, uptr->recsize + 3); + while (!feof(f) && !error) { + if (fgets ((char *)block, uptr->recsize + 3, f)) { + size_t len = strlen ((char *)block); + + while ((len > 0) && + ((block[len - 1] == '\r') || (block[len - 1] == '\n'))) + --len; + memset (block + len, ' ', uptr->recsize - len); + if (sim_switches & SWMASK ('C')) { + uint32 i; + + for (i=0; irecsize; i++) + block[i] = ascii2ebcdic[block[i]]; + } + error = memory_tape_add_block (tape, block, uptr->recsize); + } + else + error = ferror (f); + } + } + free (block); + fclose (f); + r = SCPE_OK; + memory_tape_add_block (tape, NULL, 0); /* Tape Mark */ + memory_tape_add_block (tape, NULL, 0); /* Tape Mark */ + uptr->flags |= UNIT_ATT; + uptr->filename = (char *)malloc (strlen (cptr) + 1); + strcpy (uptr->filename, cptr); + uptr->tape_eom = tape->record_count; + } + break; + + case MTUF_F_TAR: + if (uptr->recsize == 0) + uptr->recsize = TAR_DFLT_RECSIZE; /* Apply default block size */ + /* fall through */ + default: + r = attach_unit (uptr, (CONST char *)cptr); /* attach unit */ + break; } -else - r = attach_unit (uptr, (CONST char *)cptr); /* attach unit */ -if (r != SCPE_OK) /* error? */ +if (r != SCPE_OK) { /* error? */ + if (auto_format) /* format was specified at attach time? */ + sim_tape_set_fmt (uptr, 0, "SIMH", NULL); /* restore default format */ + uptr->recsize = 0; + uptr->tape_eom = 0; return sim_messagef (r, "Can't open tape image: %s\n", cptr); + } if ((sim_switches & SWMASK ('D')) && !had_debug) { sim_switches |= SWMASK ('E'); @@ -739,6 +860,8 @@ if ((sim_switches & SWMASK ('D')) && !had_debug) sim_set_deboff (0, ""); if (sim_switches & SWMASK ('D')) uptr->dctrl = starting_dctrl; +if ((r == SCPE_OK) && (sim_switches & SWMASK ('X'))) + r = sim_export_tape (uptr, export_file); return r; } @@ -766,11 +889,12 @@ if (ctx) sim_tape_clr_async (uptr); -MT_CLR_INMRK (uptr); /* Not within an AWS or TAR tapemark */ -if (MT_GET_FMT (uptr) == MTUF_F_ANSI) { - r = ansi_free_tape ((void *)uptr->fileref); +MT_CLR_INMRK (uptr); /* Not within a TAR tapemark */ +if (MT_GET_FMT (uptr) >= MTUF_F_ANSI) { + memory_free_tape ((void *)uptr->fileref); uptr->fileref = NULL; uptr->flags &= ~UNIT_ATT; + r = SCPE_OK; } else r = detach_unit (uptr); /* detach unit */ @@ -792,7 +916,7 @@ uptr->recsize = 0; uptr->tape_eom = 0; uptr->pos = 0; MT_CLR_PNU (uptr); -MT_CLR_INMRK (uptr); /* Not within an AWS or TAR tapemark */ +MT_CLR_INMRK (uptr); /* Not within a TAR tapemark */ free (uptr->tape_ctx); uptr->tape_ctx = NULL; uptr->io_flush = NULL; @@ -819,31 +943,41 @@ else fprintf (st, " sim> ATTACH {switches} %s tapefile\n\n", dptr->name); fprintf (st, "Attach command switches\n"); fprintf (st, " -R Attach Read Only.\n"); -fprintf (st, " -E Must Exist (if not specified an attempt to create the indicated\n"); -fprintf (st, " virtual tape will be attempted).\n"); -fprintf (st, " -F Open the indicated tape container in a specific format (default\n"); -fprintf (st, " is SIMH, alternatives are E11, TPC, P7B, AWS, TAR, ANSI-VMS,\n"); -fprintf (st, " ANSI-RT11, ANSI-RSX11 or ANSI-RSTS)\n"); -fprintf (st, " -B For TAR format tapes, the record size for data read from the \n"); +fprintf (st, " -E Must Exist (if not specified, the default behavior is to\n"); +fprintf (st, " attempt to create the indicated virtual tape file).\n"); +fprintf (st, " -F Open the indicated tape container in a specific format\n"); +fprintf (st, " (default is SIMH, alternatives are E11, TPC, P7B, AWS, TAR,\n"); +fprintf (st, " ANSI-VMS, ANSI-RT11, ANSI-RSX11, ANSI-RSTS, ANSI-VAR, FIXED)\n"); +fprintf (st, " -B For TAR format tapes, the record size for data read from the\n"); fprintf (st, " specified file. This record size will be used for all but \n"); fprintf (st, " possibly the last record which will be what remains unread.\n"); -fprintf (st, " The default TAR record size is 10240.\n"); +fprintf (st, " The default TAR record size is 10240. For FIXED format tapes\n"); +fprintf (st, " -B specifies the record size for binary data or the maximum \n"); +fprintf (st, " record size for text data\n"); fprintf (st, " -V Display some summary information about the record structure\n"); -fprintf (st, " contained in the tape image scan performed when it is attached.\n"); +fprintf (st, " observed in the tape image observed during the attach\n"); +fprintf (st, " validation pass\n"); fprintf (st, " -L Display detailed record size counts observed during attach\n"); fprintf (st, " validation pass\n"); -fprintf (st, " contained in the tape image scan performed when it is attached.\n"); fprintf (st, " -D Causes the internal tape structure information to be displayed\n"); -fprintf (st, " while the tape image is scanned.\n\n"); -fprintf (st, "Notes: ANSI-VMS, ANSI-RT11, ANSI-RSTS, ANSI-RSX11 formats allows one or several\n"); -fprintf (st, " files to be presented to as a read only ANSI Level 3 labeled tape with\n"); -fprintf (st, " file labels that make each individual file accessible directly as files\n"); -fprintf (st, " on the tape.\n\n"); +fprintf (st, " while the tape image is scanned.\n"); +fprintf (st, " -C Causes FIXED format tape data sets derived from text files to\n"); +fprintf (st, " be converted from ASCII to EBCDIC.\n"); +fprintf (st, " -X Extract a copy of the attached tape and convert it to a SIMH\n"); +fprintf (st, " format tape image.\n\n"); +fprintf (st, "Notes: ANSI-VMS, ANSI-RT11, ANSI-RSTS, ANSI-RSX11, ANSI-VAR formats allows\n"); +fprintf (st, " one or several files to be presented to as a read only ANSI Level 3\n"); +fprintf (st, " labeled tape with file labels that make each individual file\n"); +fprintf (st, " accessible directly as files on the tape.\n\n"); +fprintf (st, " FIXED format will present the contents of a file (text or binary) as\n"); +fprintf (st, " fixed sized records/blocks with ascii text data optionally converted\n"); +fprintf (st, " to EBCDIC.\n\n"); fprintf (st, "Examples:\n\n"); -fprintf (st, " sim> ATTACH -F %s ANSI-VMS Hobbyist-USE-ONLY-VA.TXT\n\n", dptr->name); -fprintf (st, " sim> ATTACH -F %s ANSI-RSX11 *.TXT,*.ini,*.exe\n", dptr->name); -fprintf (st, " sim> ATTACH -F %s ANSI-RSTS *.TXT,*.SAV\n", dptr->name); -fprintf (st, " sim> ATTACH -F %s ANSI-RT11 *.TXT,*.TSK\n", dptr->name); +fprintf (st, " sim> ATTACH %s -F ANSI-VMS Hobbyist-USE-ONLY-VA.TXT\n", dptr->name); +fprintf (st, " sim> ATTACH %s -F ANSI-RSX11 *.TXT,*.ini,*.exe\n", dptr->name); +fprintf (st, " sim> ATTACH %s -FX ANSI-RSTS RSTS.tap *.TXT,*.SAV\n", dptr->name); +fprintf (st, " sim> ATTACH %s -F ANSI-RT11 *.TXT,*.TSK\n", dptr->name); +fprintf (st, " sim> ATTACH %s -FB FIXED 80 SOMEFILE.TXT\n\n", dptr->name); return SCPE_OK; } @@ -1193,50 +1327,49 @@ switch (f) { /* otherwise the read method break; case MTUF_F_AWS: - MT_CLR_INMRK (uptr); /* Not within an AWS tapemark */ + saved_pos = (t_addr)sim_ftell (uptr->fileref); memset (&awshdr, 0, sizeof (awshdr)); rdcnt = sim_fread (&awshdr, sizeof (t_awslnt), 3, uptr->fileref); - - if (ferror (uptr->fileref)) { /* error? */ - MT_SET_PNU (uptr); /* pos not upd */ + if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); /* pos not upd */ status = sim_tape_ioerr (uptr); break; } - if ((feof (uptr->fileref)) || /* eof? */ + if ((feof (uptr->fileref)) || /* eof? */ (rdcnt < 3)) { - MT_SET_PNU (uptr); /* pos not upd */ + uptr->tape_eom = uptr->pos; + MT_SET_PNU (uptr); /* pos not upd */ status = MTSE_EOM; break; } - uptr->pos += sizeof (t_awshdr); /* spc over AWS header */ - if (awshdr.rectyp == AWS_TMK) { /* tape mark? */ + uptr->pos += sizeof (t_awshdr); /* spc over AWS header */ + if (awshdr.rectyp == AWS_TMK) /* tape mark? */ status = MTSE_TMK; - MT_SET_INMRK (uptr); /* within an AWS tapemark */ + else { + if (awshdr.rectyp != AWS_REC) { /* Unknown record type */ + MT_SET_PNU (uptr); /* pos not upd */ + uptr->tape_eom = uptr->pos; + status = MTSE_INVRL; + break; + } + else + status = MTSE_OK; + } + /* tape data record (or tapemark) */ + *bc = (t_mtrlnt)awshdr.nxtlen; /* save rec lnt */ + uptr->pos += awshdr.nxtlen; /* spc over record */ + memset (&awshdr, 0, sizeof (t_awslnt)); + saved_pos = (t_addr)sim_ftell (uptr->fileref);/* save record data address */ + (void)sim_tape_seek (uptr, uptr->pos); /* for read */ + rdcnt = sim_fread (&awshdr, sizeof (t_awslnt), 3, uptr->fileref); + if ((rdcnt == 3) && + ((awshdr.prelen != *bc) || ((awshdr.rectyp != AWS_REC) && (awshdr.rectyp != AWS_TMK)))) { + status = MTSE_INVRL; + uptr->tape_eom = uptr->pos; + uptr->pos = saved_pos - sizeof (t_awslnt); } else - if (awshdr.rectyp != AWS_REC) { /* Unknown record type */ - MT_SET_PNU (uptr); /* pos not upd */ - status = MTSE_INVRL; - } - else { /* tape data record */ - t_addr saved_pos; - - *bc = (t_mtrlnt)awshdr.nxtlen; /* save rec lnt */ - uptr->pos += awshdr.nxtlen; /* spc over record */ - memset (&awshdr, 0, sizeof (t_awslnt)); - saved_pos = (t_addr)sim_ftell (uptr->fileref); - (void)sim_tape_seek (uptr, uptr->pos); /* for read */ - (void)sim_fread (&awshdr, sizeof (t_awslnt), 3, uptr->fileref); - if (awshdr.rectyp == AWS_TMK) - MT_SET_INMRK (uptr); /* within an AWS tapemark */ - if (awshdr.prelen != *bc) { - status = MTSE_INVRL; - uptr->pos = saved_pos - sizeof (t_awslnt); - MT_CLR_INMRK (uptr); /* not within an AWS tapemark */ - } - else - (void)sim_tape_seek (uptr, saved_pos); /* Move back to the data */ - } + (void)sim_tape_seek (uptr, saved_pos); /* Move back to the data */ break; case MTUF_F_TAR: @@ -1260,8 +1393,9 @@ switch (f) { /* otherwise the read method break; case MTUF_F_ANSI: + case MTUF_F_FIXED: if (1) { - ANSI_TAPE *tape = (ANSI_TAPE *)uptr->fileref; + MEMORY_TAPE *tape = (MEMORY_TAPE *)uptr->fileref; if (uptr->pos >= tape->record_count) status = MTSE_EOM; @@ -1357,9 +1491,9 @@ if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not at return MTSE_UNATT; /* then quit with an error */ if (sim_tape_bot (uptr)) /* if the unit is positioned at the BOT */ - status = MTSE_BOT; /* then reading backward is not possible */ + return MTSE_BOT; /* then reading backward is not possible */ -else switch (f) { /* otherwise the read method depends on the tape format */ +switch (f) { /* otherwise the read method depends on the tape format */ case MTUF_F_STD: case MTUF_F_E11: @@ -1394,10 +1528,10 @@ else switch (f) { /* otherwise the read me bufcap = sizeof (buffer) /* to the full size of the buffer */ / sizeof (buffer [0]); - if (sim_tape_seek (uptr, /* seek back to the location */ + if (sim_tape_seek (uptr, /* seek back to the location */ uptr->pos - bufcap * sizeof (t_mtrlnt))) { /* corresponding to the start */ - /* of the buffer; if it fails */ - status = sim_tape_ioerr (uptr); /* and fail with I/O error status */ + /* of the buffer; if it fails */ + status = sim_tape_ioerr (uptr); /* and fail with I/O error status */ break; } @@ -1422,22 +1556,22 @@ else switch (f) { /* otherwise the read me else if (*bc == MTR_GAP) /* otherwise if the marker is a full gap */ runaway_counter -= sizeof_gap; /* then decrement the gap counter */ - else if ((*bc & MTR_M_RHGAP) == MTR_RHGAP /* otherwise if the marker */ - || *bc == MTR_RRGAP) { /* is a half gap */ - uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* then position forward to resync */ - bufcntr = 0; /* mark the buffer as invalid to force a read */ + else if ((*bc & MTR_M_RHGAP) == MTR_RHGAP /* otherwise if the marker */ + || *bc == MTR_RRGAP) { /* is a half gap */ + uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2;/* then position forward to resync */ + bufcntr = 0; /* mark the buffer as invalid to force a read */ - *bc = (t_mtrlnt)MTR_GAP; /* reset the marker */ - runaway_counter -= sizeof_gap / 2; /* and decrement the gap counter */ + *bc = (t_mtrlnt)MTR_GAP; /* reset the marker */ + runaway_counter -= sizeof_gap / 2; /* and decrement the gap counter */ } - else { /* otherwise it's a record marker */ - sbc = MTR_L (*bc); /* extract the record length */ - uptr->pos = uptr->pos - sizeof (t_mtrlnt) /* position to the start */ - - (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc); /* of the record */ + else { /* otherwise it's a record marker */ + sbc = MTR_L (*bc); /* extract the record length */ + uptr->pos = uptr->pos - sizeof (t_mtrlnt)/* position to the start */ + - (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc);/* of the record */ - if (sim_tape_seek (uptr, /* seek to the start of the data area; if it fails */ - uptr->pos + sizeof (t_mtrlnt))) {/* then return with I/O error status */ + if (sim_tape_seek (uptr, /* seek to the start of the data area; if it fails */ + uptr->pos + sizeof (t_mtrlnt))) {/* then return with I/O error status */ status = sim_tape_ioerr (uptr); break; } @@ -1478,7 +1612,7 @@ else switch (f) { /* otherwise the read me size_t read_size; for (sbc = 1, all_eof = 1; (t_addr) sbc <= uptr->pos ; sbc++) { - if (bytes_in_buf == 0) { /* Need to Fill Buffer */ + if (bytes_in_buf == 0) { /* Need to Fill Buffer */ if (buf_offset < BUF_SZ) { read_size = (size_t)buf_offset; buf_offset = 0; @@ -1489,11 +1623,11 @@ else switch (f) { /* otherwise the read me } (void)sim_tape_seek (uptr, buf_offset); bytes_in_buf = sim_fread (buf, sizeof (uint8), read_size, uptr->fileref); - if (ferror (uptr->fileref)) { /* error? */ + if (ferror (uptr->fileref)) { /* error? */ status = sim_tape_ioerr (uptr); break; } - if (feof (uptr->fileref)) { /* eof? */ + if (feof (uptr->fileref)) { /* eof? */ status = MTSE_EOM; break; } @@ -1506,69 +1640,69 @@ else switch (f) { /* otherwise the read me } if (status == MTSE_OK) { - uptr->pos = uptr->pos - sbc; /* update position */ - *bc = sbc; /* save rec lnt */ - (void)sim_tape_seek (uptr, uptr->pos); /* for next read */ - if (all_eof) /* tape mark? */ + uptr->pos = uptr->pos - sbc; /* update position */ + *bc = sbc; /* save rec lnt */ + (void)sim_tape_seek (uptr, uptr->pos); /* for next read */ + if (all_eof) /* tape mark? */ status = MTSE_TMK; } break; } case MTUF_F_AWS: - *bc = sbc = 0; - while ((sbc == 0) && (status == MTSE_OK)) { - if (sim_tape_bot (uptr)) { /* if we start at BOT */ - status = MTSE_BOT; /* then we're done */ + *bc = 0; + status = MTSE_OK; + (void)sim_tape_seek (uptr, uptr->pos); /* position */ + while (1) { + if (sim_tape_bot (uptr)) { /* if we start at BOT */ + status = MTSE_BOT; /* then we're done */ break; } - (void)sim_tape_seek (uptr, uptr->pos); /* position */ memset (&awshdr, 0, sizeof (awshdr)); rdcnt = sim_fread (&awshdr, sizeof (t_awslnt), 3, uptr->fileref); - if (ferror (uptr->fileref)) { /* error? */ + if (ferror (uptr->fileref)) { /* error? */ status = sim_tape_ioerr (uptr); break; } if (feof (uptr->fileref)) { /* eof? */ if ((uptr->pos > sizeof (t_awshdr)) && (uptr->pos >= sim_fsize (uptr->fileref))) { - if (MT_TST_INMRK (uptr)) { - status = MTSE_TMK; - MT_CLR_INMRK (uptr); - uptr->pos -= sizeof (awshdr); - } - else - status = MTSE_EOM; /* then we're done */ - break; + uptr->tape_eom = uptr->pos; + (void)sim_tape_seek (uptr, uptr->pos - sizeof (t_awshdr));/* position */ + continue; } + status = MTSE_EOM; + break; } if ((rdcnt != 3) || ((awshdr.rectyp != AWS_REC) && (awshdr.rectyp != AWS_TMK))) { status = MTSE_INVRL; - break; } - if (MT_TST_INMRK (uptr)) /* already in a tapemark? */ - awshdr.rectyp = AWS_REC; - MT_CLR_INMRK (uptr); /* No longer in a tapemark */ - *bc = (t_mtrlnt)((awshdr.rectyp == AWS_REC) ? awshdr.prelen : 0);/* save rec lnt */ - sbc = *bc; /* extract the record length */ - if ((awshdr.rectyp != AWS_TMK) || - (awshdr.prelen == 0)) { - uptr->pos -= sizeof (t_awshdr); /* position to the start of the record */ - uptr->pos -= awshdr.prelen; /* Including the data length */ - - if (sim_tape_seek (uptr, /* seek to the start of the data area; if it fails */ - uptr->pos + sizeof (t_awshdr))) { - status = sim_tape_ioerr (uptr); /* then return with I/O error status */ - break; - } - } - if (awshdr.rectyp == AWS_TMK) { /* tape mark? */ + break; + } + if (status != MTSE_OK) + break; + if (awshdr.prelen == 0) + status = MTSE_TMK; + else { + if ((uptr->tape_eom > 0) && + (uptr->pos >= uptr->tape_eom) && + (awshdr.rectyp == AWS_TMK)) { status = MTSE_TMK; - if (awshdr.prelen != 0) - MT_SET_INMRK (uptr); /* Flag processing a tapemark header */ + *bc = 0; /* save rec lnt */ } + else { + status = MTSE_OK; + *bc = (t_mtrlnt)awshdr.prelen; /* save rec lnt */ + } + } + uptr->pos -= sizeof (t_awshdr); /* position to the start of the record */ + uptr->pos -= *bc; /* Including the data length */ + if (sim_tape_seek (uptr, /* seek to the start of the data area; if it fails */ + uptr->pos + sizeof (t_awshdr))) { + status = sim_tape_ioerr (uptr); /* then return with I/O error status */ + break; } break; @@ -1594,8 +1728,9 @@ else switch (f) { /* otherwise the read me break; case MTUF_F_ANSI: + case MTUF_F_FIXED: if (1) { - ANSI_TAPE *tape = (ANSI_TAPE *)uptr->fileref; + MEMORY_TAPE *tape = (MEMORY_TAPE *)uptr->fileref; --uptr->pos; if (tape->records[uptr->pos]->size == 0) @@ -1681,7 +1816,7 @@ if (f < MTUF_F_ANSI) { } } else { - ANSI_TAPE *tape = (ANSI_TAPE *)uptr->fileref; + MEMORY_TAPE *tape = (MEMORY_TAPE *)uptr->fileref; memcpy (buf, tape->records[uptr->pos - 1]->data, rbc); i = rbc; @@ -1751,7 +1886,7 @@ if (f < MTUF_F_ANSI) { return sim_tape_ioerr (uptr); } else { - ANSI_TAPE *tape = (ANSI_TAPE *)uptr->fileref; + MEMORY_TAPE *tape = (MEMORY_TAPE *)uptr->fileref; memcpy (buf, tape->records[uptr->pos]->data, rbc); i = rbc; @@ -2333,7 +2468,7 @@ if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not at else if (sim_tape_wrp (uptr)) /* otherwise if the unit is write protected */ return MTSE_WRP; /* then we cannot write */ -else if (gap_size == 0 || format != MTUF_F_STD) /* otherwise if the gap length is zero or unsupported */ +else if ((gap_size == 0) || (format != MTUF_F_STD)) /* otherwise if the gap length is zero or unsupported */ return MTSE_OK; /* then take no action */ gap_pos = uptr->pos; /* save the starting position */ @@ -2362,7 +2497,7 @@ if (gap_size == meta_size) { /* if the request is for xfer = sim_fwrite (&metadatum, meta_size, /* write the gap marker */ 1, uptr->fileref); - if (ferror (uptr->fileref) || xfer == 0) /* if a file I/O error occurred */ + if (ferror (uptr->fileref) || (xfer == 0)) /* if a file I/O error occurred */ return sim_tape_ioerr (uptr); /* report the error and quit */ else /* otherwise the write succeeded */ status = MTSE_OK; /* so return success */ @@ -2380,8 +2515,8 @@ if (gap_size == meta_size) { /* if the request is for else { /* otherwise it's an erase record request */ status = sim_tape_rdlntr (uptr, &rec_size); /* so get the length of the preceding record */ - if (status == MTSE_OK /* if the read succeeded */ - && gap_size == rec_size + 2 * meta_size) { /* and the gap will exactly overlay the record */ + if ((status == MTSE_OK) && /* if the read succeeded */ + (gap_size == rec_size + 2 * meta_size)) { /* and the gap will exactly overlay the record */ gap_pos = uptr->pos; /* then save the gap start position */ status = tape_erase_fwd (uptr, gap_size); /* erase the record */ @@ -2918,7 +3053,7 @@ if (uptr->flags & UNIT_ATT) { (void)sim_tape_seek (uptr, uptr->pos); } MT_CLR_PNU (uptr); -MT_CLR_INMRK (uptr); /* Not within an AWS or TAR tapemark */ +MT_CLR_INMRK (uptr); /* Not within a TAR tapemark */ return MTSE_OK; } @@ -3020,7 +3155,7 @@ t_bool sim_tape_bot (UNIT *uptr) { uint32 f = MT_GET_FMT (uptr); -return (uptr->pos <= fmts[f].bot)? TRUE: FALSE; +return ((uptr->pos <= fmts[f].bot) && (!MT_TST_INMRK (uptr))) ? TRUE: FALSE; } /* Test for end of tape */ @@ -3205,6 +3340,10 @@ sprintf(msgbuf, "Error %d", stat); return msgbuf; } +#ifndef MAX +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif + static t_stat sim_tape_validate_tape (UNIT *uptr) { t_addr saved_pos = uptr->pos; @@ -3227,6 +3366,8 @@ t_mtrlnt bc_s; t_mtrlnt bc; t_addr pos_f; t_addr pos_r; +t_addr pos_fa; +t_addr pos_sa; t_mtrlnt max = MTR_MAXLEN; if (!(uptr->flags & UNIT_ATT)) @@ -3245,12 +3386,16 @@ if (rec_sizes == NULL) { free (buf_r); return SCPE_MEM; } + r = sim_tape_rewind (uptr); while (r == SCPE_OK) { - if (stop_cpu) /* SIGINT? */ + if (stop_cpu) { /* SIGINT? */ + stop_cpu = FALSE; break; + } pos_f = uptr->pos; r_f = sim_tape_rdrecf (uptr, buf_f, &bc_f, max); + pos_fa = uptr->pos; switch (r_f) { case MTSE_OK: /* no error */ case MTSE_TMK: /* tape mark */ @@ -3268,29 +3413,40 @@ while (r == SCPE_OK) { pos_r = uptr->pos; if (r_r != r_f) { sim_printf ("Forward Record Read returned: %s, Reverse read returned: %s\n", sim_tape_error_text (r_f), sim_tape_error_text (r_r)); + r = MAX(r_f, r_r); break; } if (bc_f != bc_r) { sim_printf ("Forward Record Read record length: %d, Reverse read record length: %d\n", bc_f, bc_r); + r = MTSE_RECE; break; } if (0 != memcmp (buf_f, buf_r, bc_f)) { sim_printf ("%d byte record contents differ when read forward amd backwards start from position %" T_ADDR_FMT "u\n", bc_f, pos_f); + r = MTSE_RECE; break; } memset (buf_f, 0, bc_f); memset (buf_r, 0, bc_r); if (pos_f != pos_r) { - sim_printf ("Unexpected tape file position between forward and reverse record read: (%" T_ADDR_FMT "u, %" T_ADDR_FMT "u\n", pos_f, pos_r); + sim_printf ("Unexpected tape file position between forward and reverse record read: (%" T_ADDR_FMT "u, %" T_ADDR_FMT "u)\n", pos_f, pos_r); + r = MTSE_RECE; break; } r_s = sim_tape_sprecf (uptr, &bc_s); + pos_sa = uptr->pos; if (r_s != r_f) { sim_printf ("Unexpected Space Record Status: %s vs %s\n", sim_tape_error_text (r_s), sim_tape_error_text (r_f)); + r = MAX(r_s, r_f); break; } if (bc_s != bc_f) { sim_printf ("Unexpected Space Record Length: %d vs %d\n", bc_s, bc_f); + r = MTSE_RECE; + break; + } + if (pos_fa != pos_sa) { + sim_printf ("Unexpected tape file position after forward and skip record: (%" T_ADDR_FMT "u, %" T_ADDR_FMT "u)\n", pos_fa, pos_sa); break; } r = SCPE_OK; @@ -3316,29 +3472,25 @@ if ((!stop_cpu) && ((uint32)(sim_tape_size (uptr) - (t_offset)uptr->pos) > fmts[MT_GET_FMT (uptr)].eom_remnant) || (unique_record_sizes > 2 * tapemark_total))) { remaining_data = (uint32)(sim_tape_size (uptr) - (t_offset)uptr->tape_eom); - sim_printf ("Tape Image %s'%s' scanned as %s format.\n", ((MT_GET_FMT (uptr) == MTUF_F_ANSI) ? "made from " : ""), uptr->filename, (MT_GET_FMT (uptr) == MTUF_F_ANSI) ? ansi_args[MT_GET_ANSI_TYP (uptr)].name : fmts[MT_GET_FMT (uptr)].name); - if (r != MTSE_EOM) - sim_printf ("After processing "); - else - sim_printf ("contains "); - sim_printf ("%u bytes of tape data (%u records, %u tapemarks)\n", - data_total, record_total, tapemark_total); + sim_messagef (SCPE_OK, "Tape Image %s'%s' scanned as %s format.\n", ((MT_GET_FMT (uptr) == MTUF_F_ANSI) ? "made from " : ""), uptr->filename, (MT_GET_FMT (uptr) == MTUF_F_ANSI) ? ansi_args[MT_GET_ANSI_TYP (uptr)].name : fmts[MT_GET_FMT (uptr)].name); + sim_messagef (SCPE_OK, "%s %u bytes of tape data (%u records, %u tapemarks)\n", + (r != MTSE_EOM) ? "After processing" : "contains", data_total, record_total, tapemark_total); if ((record_total > 0) && (sim_switches & SWMASK ('L'))) { - sim_printf ("Comprising %d different sized records (in record size order):\n", unique_record_sizes); + sim_messagef (SCPE_OK, "Comprising %d different sized records (in record size order):\n", unique_record_sizes); for (bc = 0; bc <= max; bc++) { if (rec_sizes[bc]) - sim_printf ("%8u %u byte records\n", rec_sizes[bc], (uint32)bc); + sim_messagef (SCPE_OK, "%8u %u byte records\n", rec_sizes[bc], (uint32)bc); } } if (r != MTSE_EOM) - sim_printf ("Read Tape Record Returned Unexpected Status: %s\n", sim_tape_error_text (r)); + sim_messagef (SCPE_OK, "Read Tape Record Returned Unexpected Status: %s\n", sim_tape_error_text (r)); if (remaining_data > fmts[MT_GET_FMT (uptr)].eom_remnant) - sim_printf ("%u bytes of unexamined data remain in the tape image file\n", remaining_data); + sim_messagef (SCPE_OK, "%u bytes of unexamined data remain in the tape image file\n", remaining_data); } if ((!stop_cpu) && (unique_record_sizes > 2 * tapemark_total)) { - sim_printf ("An unreasonable number of record sizes(%u) vs tape marks (%u) have been found\n", unique_record_sizes, tapemark_total); - sim_printf ("The tape format (%s) might not be correct for the '%s' tape image\n", fmts[MT_GET_FMT (uptr)].name, uptr->filename); + sim_messagef (SCPE_OK, "A potentially unreasonable number of record sizes(%u) vs tape marks (%u) have been found\n", unique_record_sizes, tapemark_total); + sim_messagef (SCPE_OK, "The tape format (%s) might not be correct for the '%s' tape image\n", fmts[MT_GET_FMT (uptr)].name, uptr->filename); } free (buf_f); @@ -3364,9 +3516,12 @@ do { p = (lo + hi) >> 1; if (uptr->pos == map[p]) return ((p == 0)? map[p]: map[p - 1]); - else if (uptr->pos < map[p]) - hi = p - 1; - else lo = p + 1; + else { + if (uptr->pos < map[p]) + hi = p - 1; + else + lo = p + 1; + } } while (lo <= hi); return ((p == 0)? map[p]: map[p - 1]); @@ -3552,6 +3707,8 @@ FILE *fE11 = NULL; FILE *fTPC = NULL; FILE *fP7B = NULL; FILE *fAWS = NULL; +FILE *fAWS2 = NULL; +FILE *fAWS3 = NULL; FILE *fTAR = NULL; FILE *fTAR2 = NULL; int i, j, k; @@ -3613,6 +3770,14 @@ sprintf (name, "%s.aws", filename); fAWS = fopen (name, "wb"); if (fAWS == NULL) goto Done_Files; +sprintf (name, "%s.2.aws", filename); +fAWS2 = fopen (name, "wb"); +if (fAWS2 == NULL) + goto Done_Files; +sprintf (name, "%s.3.aws", filename); +fAWS3 = fopen (name, "wb"); +if (fAWS3 == NULL) + goto Done_Files; sprintf (name, "aws %s.aws.tape", filename); (void)remove (name); sim_switches = SWMASK ('F') | (sim_switches & SWMASK ('D')) | SWMASK ('N'); @@ -3637,11 +3802,18 @@ for (i=0; iunits, "TapeTestFile1")); SIM_TEST(sim_tape_test_create_tape_files (dptr->units, "TapeTestFile1", 2, 5, 4096)); sim_switches = saved_switches; -SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1", "tar")); +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1.*", "ansi-vms", 0)); sim_switches = saved_switches; -SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1.2", "tar")); +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1.*", "ansi-rsx11", 0)); sim_switches = saved_switches; -SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1", "aws")); +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1.*", "ansi-rt11", 0)); sim_switches = saved_switches; -SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1", "p7b")); +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1.*", "ansi-rsts", 0)); sim_switches = saved_switches; -SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1", "tpc")); +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1.*", "ansi-var", 0)); sim_switches = saved_switches; -SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1", "e11")); +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1", "tar", 0)); sim_switches = saved_switches; -SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1", "simh")); +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1", "aws", 0)); + +sim_switches = saved_switches; +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1.3", "aws", 0)); + +sim_switches = saved_switches; +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1.2", "aws", 0)); + +sim_switches = saved_switches; +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1.2", "tar", 0)); + +sim_switches = saved_switches; +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1", "aws", 0)); + +sim_switches = saved_switches; +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1", "p7b", 0)); + +sim_switches = saved_switches; +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1", "tpc", 0)); + +sim_switches = saved_switches; +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1", "e11", 0)); + +sim_switches = saved_switches; +SIM_TEST(sim_tape_test_process_tape_file (dptr->units, "TapeTestFile1", "simh", 0)); SIM_TEST(sim_tape_test_remove_tape_files (dptr->units, "TapeTestFile1")); @@ -4020,7 +4254,7 @@ static void ansi_fill_text_buffer (FILE *f, char *buf, size_t buf_size, size_t r free (tmp); } -static t_bool ansi_tape_add_block (ANSI_TAPE *tape, uint8 *block, uint32 size) +static t_bool memory_tape_add_block (MEMORY_TAPE *tape, uint8 *block, uint32 size) { TAPE_RECORD *rec; @@ -4042,10 +4276,10 @@ tape->records[tape->record_count++] = rec; return FALSE; } -static int ansi_free_tape (void *vtape) +static void memory_free_tape (void *vtape) { uint32 i; -ANSI_TAPE *tape = (ANSI_TAPE *)vtape; +MEMORY_TAPE *tape = (MEMORY_TAPE *)vtape; for (i=0; irecord_count; i++) { free (tape->records[i]); @@ -4053,23 +4287,19 @@ for (i=0; irecord_count; i++) { } free (tape->records); free (tape); -return 0; } -ANSI_TAPE *ansi_create_tape (const char *label, uint32 block_size, uint32 ansi_type) +MEMORY_TAPE *memory_create_tape (void) { -ANSI_TAPE *tape = (ANSI_TAPE *)calloc (1, sizeof (*tape)); +MEMORY_TAPE *tape = (MEMORY_TAPE *)calloc (1, sizeof (*tape)); if (NULL == tape) return tape; -tape->block_size = block_size; -tape->ansi_type = ansi_type; -ansi_make_VOL1 (&tape->vol1, label, ansi_type); -ansi_tape_add_block (tape, (uint8 *)&tape->vol1, sizeof (tape->vol1)); +tape->ansi_type = -1; return tape; } -static int ansi_classify_file_contents (FILE *f, size_t *max_record_size, t_bool *lf_line_endings, t_bool *crlf_line_endings) +static int tape_classify_file_contents (FILE *f, size_t *max_record_size, t_bool *lf_line_endings, t_bool *crlf_line_endings) { long pos = -1; long last_cr = -1; @@ -4125,7 +4355,20 @@ else { return 0; } -static int ansi_add_file_to_tape (ANSI_TAPE *tape, const char *filename) +MEMORY_TAPE *ansi_create_tape (const char *label, uint32 block_size, uint32 ansi_type) +{ +MEMORY_TAPE *tape = memory_create_tape (); + +if (NULL == tape) + return tape; +tape->block_size = block_size; +tape->ansi_type = ansi_type; +ansi_make_VOL1 (&tape->vol1, label, ansi_type); +memory_tape_add_block (tape, (uint8 *)&tape->vol1, sizeof (tape->vol1)); +return tape; +} + +static int ansi_add_file_to_tape (MEMORY_TAPE *tape, const char *filename) { FILE *f; struct ansi_tape_parameters *ansi = &ansi_args[tape->ansi_type]; @@ -4147,7 +4390,7 @@ if (f == NULL) { fprintf (stderr, "Can't open: %s - %s\n", filename, strerror(errno)); return errno; } -ansi_classify_file_contents (f, &max_record_size, &lf_line_endings, &crlf_line_endings); +tape_classify_file_contents (f, &max_record_size, &lf_line_endings, &crlf_line_endings); ansi_make_HDR1 (&hdr1, &tape->vol1, &hdr4, filename, tape->ansi_type); sprintf (file_sequence, "%04d", 1 + tape->file_count); memcpy (hdr1.file_sequence, file_sequence, sizeof (hdr1.file_sequence)); @@ -4165,14 +4408,14 @@ if (!ansi->nohdr3) { /* Need HDR3? */ memcpy (&hdr3, ansi->hdr3_crlf_line_endings, sizeof (hdr3)); } } -ansi_tape_add_block (tape, (uint8 *)&hdr1, sizeof (hdr1)); +memory_tape_add_block (tape, (uint8 *)&hdr1, sizeof (hdr1)); if (!ansi->nohdr2) - ansi_tape_add_block (tape, (uint8 *)&hdr2, sizeof (hdr2)); + memory_tape_add_block (tape, (uint8 *)&hdr2, sizeof (hdr2)); if (!ansi->nohdr3) - ansi_tape_add_block (tape, (uint8 *)&hdr3, sizeof (hdr3)); + memory_tape_add_block (tape, (uint8 *)&hdr3, sizeof (hdr3)); if ((0 != memcmp (hdr4.extra_name_used, "00", 2)) && !ansi->nohdr3 && !ansi->nohdr2) - ansi_tape_add_block (tape, (uint8 *)&hdr4, sizeof (hdr4)); -ansi_tape_add_block (tape, NULL, 0); /* Tape Mark */ + memory_tape_add_block (tape, (uint8 *)&hdr4, sizeof (hdr4)); +memory_tape_add_block (tape, NULL, 0); /* Tape Mark */ rewind (f); block = (uint8 *)calloc (tape->block_size, 1); while (!feof(f) && !error) { @@ -4185,27 +4428,27 @@ while (!feof(f) && !error) { else data_read = fread (block, 1, tape->block_size, f); if (data_read > 0) - error = ansi_tape_add_block (tape, block, data_read); + error = memory_tape_add_block (tape, block, data_read); if (!error) ++block_count; } fclose (f); free (block); -ansi_tape_add_block (tape, NULL, 0); /* Tape Mark */ +memory_tape_add_block (tape, NULL, 0); /* Tape Mark */ memcpy (hdr1.type, "EOF", sizeof (hdr1.type)); memcpy (hdr2.type, "EOF", sizeof (hdr2.type)); memcpy (hdr3.type, "EOF", sizeof (hdr3.type)); memcpy (hdr4.type, "EOF", sizeof (hdr4.type)); sprintf (block_count_string, "%06d", block_count); memcpy (hdr1.block_count, block_count_string, sizeof (hdr1.block_count)); -ansi_tape_add_block (tape, (uint8 *)&hdr1, sizeof (hdr1)); +memory_tape_add_block (tape, (uint8 *)&hdr1, sizeof (hdr1)); if (!ansi->nohdr2) - ansi_tape_add_block (tape, (uint8 *)&hdr2, sizeof (hdr2)); + memory_tape_add_block (tape, (uint8 *)&hdr2, sizeof (hdr2)); if (!ansi->nohdr3) - ansi_tape_add_block (tape, (uint8 *)&hdr3, sizeof (hdr3)); + memory_tape_add_block (tape, (uint8 *)&hdr3, sizeof (hdr3)); if ((0 != memcmp (hdr4.extra_name_used, "00", 2)) && !ansi->nohdr3 && !ansi->nohdr2) - ansi_tape_add_block (tape, (uint8 *)&hdr4, sizeof (hdr4)); -ansi_tape_add_block (tape, NULL, 0); /* Tape Mark */ + memory_tape_add_block (tape, (uint8 *)&hdr4, sizeof (hdr4)); +memory_tape_add_block (tape, NULL, 0); /* Tape Mark */ if (sim_switches & SWMASK ('V')) sim_messagef (SCPE_OK, "%17.17s%62.62s\n\t%d blocks of data\n", hdr1.file_ident, hdr4.extra_name, block_count); ++tape->file_count; @@ -4218,7 +4461,7 @@ static void sim_tape_add_ansi_entry (const char *directory, const struct stat *filestat, void *context) { -ANSI_TAPE *tape = (ANSI_TAPE *)context; +MEMORY_TAPE *tape = (MEMORY_TAPE *)context; char FullPath[PATH_MAX + 1]; sprintf (FullPath, "%s%s", directory, filename); @@ -4226,3 +4469,57 @@ sprintf (FullPath, "%s%s", directory, filename); (void)ansi_add_file_to_tape (tape, FullPath); } +/* export an existing tape to a SIMH tape image */ +static t_stat sim_export_tape (UNIT *uptr, const char *export_file) +{ +t_stat r; +FILE *f; +t_addr saved_pos = uptr->pos; +uint8 *buf = NULL; +t_mtrlnt bc, sbc; +t_mtrlnt max = MTR_MAXLEN; + +if ((export_file == NULL) || (*export_file == '\0')) + return sim_messagef (SCPE_ARG, "Missing tape export file specification\n"); +f = fopen (export_file, "wb"); +if (f == NULL) + return sim_messagef (SCPE_OPENERR, "Can't open SIMH tape image file: %s - %s\n", export_file, strerror (errno)); + +buf = (uint8 *)calloc (max, 1); +if (buf == NULL) { + fclose (f); + return SCPE_MEM; + } +r = sim_tape_rewind (uptr); +while (r == SCPE_OK) { + r = sim_tape_rdrecf (uptr, buf, &bc, max); + switch (r) { + case MTSE_OK: + sbc = ((bc + 1) & ~1); /* word alignment for SIMH format data */ + if ((1 != sim_fwrite (&bc, sizeof (bc), 1, f)) || + (sbc != sim_fwrite (buf, 1, sbc, f)) || + (1 != sim_fwrite (&bc, sizeof (bc), 1, f))) + r = sim_messagef (SCPE_IOERR, "Error writing file: %s - %s\n", export_file, strerror (errno)); + else + r = SCPE_OK; + break; + + case MTSE_TMK: + bc = 0; + if (1 != sim_fwrite (&bc, sizeof (bc), 1, f)) + r = sim_messagef (SCPE_IOERR, "Error writing file: %s - %s\n", export_file, strerror (errno)); + else + r = SCPE_OK; + break; + + default: + break; + } + } +if (r == MTSE_EOM) + r = SCPE_OK; +free (buf); +fclose (f); +uptr->pos = saved_pos; +return r; +} \ No newline at end of file diff --git a/sim_tape.h b/sim_tape.h index 4ea8729..2ad2235 100644 --- a/sim_tape.h +++ b/sim_tape.h @@ -99,6 +99,7 @@ typedef struct { #define MTUF_F_AWS 4 /* AWS format */ #define MTUF_F_TAR 5 /* TAR format */ #define MTUF_F_ANSI 6 /* ANSI format */ +#define MTUF_F_FIXED 7 /* FIXED format */ #define MTAT_F_VMS 0 /* VMS ANSI type */ #define MTAT_F_RSX11 1 /* RSX-11 ANSI type */